1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.fit.core;
20
21 import static org.awaitility.Awaitility.await;
22 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
23 import static org.junit.jupiter.api.Assertions.assertEquals;
24 import static org.junit.jupiter.api.Assertions.assertFalse;
25 import static org.junit.jupiter.api.Assertions.assertNotEquals;
26 import static org.junit.jupiter.api.Assertions.assertNotNull;
27 import static org.junit.jupiter.api.Assertions.assertTrue;
28 import static org.junit.jupiter.api.Assertions.fail;
29
30 import com.fasterxml.jackson.core.JsonProcessingException;
31 import java.io.File;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.nio.charset.StandardCharsets;
35 import java.nio.file.Files;
36 import java.nio.file.Path;
37 import java.nio.file.Paths;
38 import java.nio.file.StandardOpenOption;
39 import java.time.OffsetDateTime;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Properties;
43 import java.util.Set;
44 import java.util.UUID;
45 import java.util.concurrent.TimeUnit;
46 import java.util.function.Function;
47 import javax.ws.rs.core.Response;
48 import org.apache.commons.io.IOUtils;
49 import org.apache.commons.lang3.SerializationUtils;
50 import org.apache.syncope.client.lib.SyncopeClient;
51 import org.apache.syncope.common.lib.Attr;
52 import org.apache.syncope.common.lib.SyncopeClientException;
53 import org.apache.syncope.common.lib.SyncopeConstants;
54 import org.apache.syncope.common.lib.audit.AuditEntry;
55 import org.apache.syncope.common.lib.audit.EventCategory;
56 import org.apache.syncope.common.lib.request.AnyObjectUR;
57 import org.apache.syncope.common.lib.request.AttrPatch;
58 import org.apache.syncope.common.lib.request.ResourceDR;
59 import org.apache.syncope.common.lib.request.UserUR;
60 import org.apache.syncope.common.lib.to.AnyObjectTO;
61 import org.apache.syncope.common.lib.to.AuditConfTO;
62 import org.apache.syncope.common.lib.to.ConnInstanceTO;
63 import org.apache.syncope.common.lib.to.ConnPoolConfTO;
64 import org.apache.syncope.common.lib.to.GroupTO;
65 import org.apache.syncope.common.lib.to.ImplementationTO;
66 import org.apache.syncope.common.lib.to.PagedResult;
67 import org.apache.syncope.common.lib.to.PullTaskTO;
68 import org.apache.syncope.common.lib.to.PushTaskTO;
69 import org.apache.syncope.common.lib.to.RealmTO;
70 import org.apache.syncope.common.lib.to.ResourceTO;
71 import org.apache.syncope.common.lib.to.UserTO;
72 import org.apache.syncope.common.lib.types.AnyTypeKind;
73 import org.apache.syncope.common.lib.types.AuditElements;
74 import org.apache.syncope.common.lib.types.AuditLoggerName;
75 import org.apache.syncope.common.lib.types.ConnConfProperty;
76 import org.apache.syncope.common.lib.types.ConnectorCapability;
77 import org.apache.syncope.common.lib.types.IdRepoImplementationType;
78 import org.apache.syncope.common.lib.types.ImplementationEngine;
79 import org.apache.syncope.common.lib.types.MatchingRule;
80 import org.apache.syncope.common.lib.types.ResourceDeassociationAction;
81 import org.apache.syncope.common.lib.types.ResourceOperation;
82 import org.apache.syncope.common.lib.types.UnmatchingRule;
83 import org.apache.syncope.common.rest.api.RESTHeaders;
84 import org.apache.syncope.common.rest.api.beans.AnyQuery;
85 import org.apache.syncope.common.rest.api.beans.AuditQuery;
86 import org.apache.syncope.common.rest.api.beans.ReconQuery;
87 import org.apache.syncope.core.logic.ConnectorLogic;
88 import org.apache.syncope.core.logic.GroupLogic;
89 import org.apache.syncope.core.logic.ReportLogic;
90 import org.apache.syncope.core.logic.ResourceLogic;
91 import org.apache.syncope.core.logic.UserLogic;
92 import org.apache.syncope.fit.AbstractITCase;
93 import org.junit.jupiter.api.Test;
94
95 public class AuditITCase extends AbstractITCase {
96
97 private static AuditConfTO buildAuditConf(final String auditLoggerName, final boolean active) {
98 AuditConfTO auditConfTO = new AuditConfTO();
99 auditConfTO.setActive(active);
100 auditConfTO.setKey(auditLoggerName);
101 return auditConfTO;
102 }
103
104 private static AuditEntry queryWithFailure(final AuditQuery query, final int maxWaitSeconds) {
105 List<AuditEntry> results = query(query, maxWaitSeconds);
106 if (results.isEmpty()) {
107 fail("Timeout when executing query for key " + query.getEntityKey());
108 return null;
109 }
110 return results.get(0);
111 }
112
113 @Test
114 public void userReadAndSearchYieldsNoAudit() {
115 UserTO userTO = createUser(UserITCase.getUniqueSample("audit@syncope.org")).getEntity();
116 assertNotNull(userTO.getKey());
117
118 AuditQuery query = new AuditQuery.Builder().entityKey(userTO.getKey()).build();
119 int entriesBefore = query(query, MAX_WAIT_SECONDS).size();
120
121 PagedResult<UserTO> usersTOs = USER_SERVICE.search(
122 new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
123 fiql(SyncopeClient.getUserSearchConditionBuilder().
124 is("username").equalTo(userTO.getUsername()).query()).
125 build());
126 assertNotNull(usersTOs);
127 assertFalse(usersTOs.getResult().isEmpty());
128
129 int entriesAfter = query(query, MAX_WAIT_SECONDS).size();
130 assertEquals(entriesBefore, entriesAfter);
131 }
132
133 @Test
134 public void findByUser() {
135 UserTO userTO = createUser(UserITCase.getUniqueSample("audit@syncope.org")).getEntity();
136 assertNotNull(userTO.getKey());
137
138 AuditQuery query = new AuditQuery.Builder().
139 entityKey(userTO.getKey()).
140 before(OffsetDateTime.now().plusSeconds(30)).
141 page(1).
142 size(1).
143 orderBy("event_date desc").
144 build();
145 AuditEntry entry = queryWithFailure(query, MAX_WAIT_SECONDS);
146 assertNotNull(entry);
147 USER_SERVICE.delete(userTO.getKey());
148 }
149
150 @Test
151 public void findByUserAndOther() {
152 UserTO userTO = createUser(UserITCase.getUniqueSample("audit-2@syncope.org")).getEntity();
153 assertNotNull(userTO.getKey());
154
155 AuditQuery query = new AuditQuery.Builder().
156 entityKey(userTO.getKey()).
157 orderBy("event_date desc").
158 page(1).
159 size(1).
160 type(AuditElements.EventCategoryType.LOGIC).
161 category(UserLogic.class.getSimpleName()).
162 event("create").
163 result(AuditElements.Result.SUCCESS).
164 after(OffsetDateTime.now().minusSeconds(30)).
165 build();
166 AuditEntry entry = queryWithFailure(query, MAX_WAIT_SECONDS);
167 assertNotNull(entry);
168 USER_SERVICE.delete(userTO.getKey());
169 }
170
171 @Test
172 public void findByGroup() {
173 GroupTO groupTO = createGroup(GroupITCase.getBasicSample("AuditGroup")).getEntity();
174 assertNotNull(groupTO.getKey());
175
176 AuditQuery query = new AuditQuery.Builder().entityKey(groupTO.getKey()).orderBy("event_date desc").
177 page(1).size(1).build();
178 AuditEntry entry = queryWithFailure(query, MAX_WAIT_SECONDS);
179 assertNotNull(entry);
180 GROUP_SERVICE.delete(groupTO.getKey());
181 }
182
183 @Test
184 public void groupReadAndSearchYieldsNoAudit() {
185 GroupTO groupTO = createGroup(GroupITCase.getBasicSample("AuditGroupSearch")).getEntity();
186 assertNotNull(groupTO.getKey());
187
188 AuditQuery query = new AuditQuery.Builder().entityKey(groupTO.getKey()).build();
189 int entriesBefore = query(query, MAX_WAIT_SECONDS).size();
190
191 PagedResult<GroupTO> groups = GROUP_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
192 fiql(SyncopeClient.getGroupSearchConditionBuilder().is("name").equalTo(groupTO.getName()).query()).
193 build());
194 assertNotNull(groups);
195 assertFalse(groups.getResult().isEmpty());
196
197 int entriesAfter = query(query, MAX_WAIT_SECONDS).size();
198 assertEquals(entriesBefore, entriesAfter);
199 }
200
201 @Test
202 public void findByAnyObject() {
203 AnyObjectTO anyObjectTO = createAnyObject(AnyObjectITCase.getSample("Italy")).getEntity();
204 assertNotNull(anyObjectTO.getKey());
205 AuditQuery query = new AuditQuery.Builder().entityKey(anyObjectTO.getKey()).
206 orderBy("event_date desc").page(1).size(1).build();
207 AuditEntry entry = queryWithFailure(query, MAX_WAIT_SECONDS);
208 assertNotNull(entry);
209 ANY_OBJECT_SERVICE.delete(anyObjectTO.getKey());
210 }
211
212 @Test
213 public void anyObjectReadAndSearchYieldsNoAudit() {
214 AnyObjectTO anyObjectTO = createAnyObject(AnyObjectITCase.getSample("USA")).getEntity();
215 assertNotNull(anyObjectTO);
216
217 AuditQuery query = new AuditQuery.Builder().entityKey(anyObjectTO.getKey()).build();
218 int entriesBefore = query(query, MAX_WAIT_SECONDS).size();
219
220 PagedResult<AnyObjectTO> anyObjects = ANY_OBJECT_SERVICE.search(
221 new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
222 fiql(SyncopeClient.getAnyObjectSearchConditionBuilder(anyObjectTO.getType()).query()).
223 build());
224 assertNotNull(anyObjects);
225 assertFalse(anyObjects.getResult().isEmpty());
226
227 int entriesAfter = query(query, MAX_WAIT_SECONDS).size();
228 assertEquals(entriesBefore, entriesAfter);
229 }
230
231 @Test
232 public void findByConnector() throws JsonProcessingException {
233 String connectorKey = "74141a3b-0762-4720-a4aa-fc3e374ef3ef";
234
235 AuditQuery query = new AuditQuery.Builder().
236 entityKey(connectorKey).
237 orderBy("event_date desc").
238 type(AuditElements.EventCategoryType.LOGIC).
239 category(ConnectorLogic.class.getSimpleName()).
240 event("update").
241 result(AuditElements.Result.SUCCESS).
242 build();
243 List<AuditEntry> entries = AUDIT_SERVICE.search(query).getResult();
244 int pre = entries.size();
245
246 ConnInstanceTO ldapConn = CONNECTOR_SERVICE.read(connectorKey, null);
247 String originalDisplayName = ldapConn.getDisplayName();
248 Set<ConnectorCapability> originalCapabilities = new HashSet<>(ldapConn.getCapabilities());
249 ConnConfProperty originalConfProp = SerializationUtils.clone(
250 ldapConn.getConf("maintainPosixGroupMembership").get());
251 assertEquals(1, originalConfProp.getValues().size());
252 assertEquals("false", originalConfProp.getValues().get(0));
253
254 ldapConn.setDisplayName(originalDisplayName + " modified");
255 ldapConn.getCapabilities().clear();
256 ldapConn.getConf("maintainPosixGroupMembership").get().getValues().set(0, "true");
257 CONNECTOR_SERVICE.update(ldapConn);
258
259 ldapConn = CONNECTOR_SERVICE.read(connectorKey, null);
260 assertNotEquals(originalDisplayName, ldapConn.getDisplayName());
261 assertNotEquals(originalCapabilities, ldapConn.getCapabilities());
262 assertNotEquals(originalConfProp, ldapConn.getConf("maintainPosixGroupMembership"));
263
264 entries = query(query, MAX_WAIT_SECONDS);
265 assertEquals(pre + 1, entries.size());
266
267 ConnInstanceTO restore = JSON_MAPPER.readValue(entries.get(0).getBefore(), ConnInstanceTO.class);
268 CONNECTOR_SERVICE.update(restore);
269
270 ldapConn = CONNECTOR_SERVICE.read(connectorKey, null);
271 assertEquals(originalDisplayName, ldapConn.getDisplayName());
272 assertEquals(originalCapabilities, ldapConn.getCapabilities());
273 assertEquals(originalConfProp, ldapConn.getConf("maintainPosixGroupMembership").get());
274 }
275
276 @Test
277 public void enableDisable() {
278 AuditLoggerName auditLoggerName = new AuditLoggerName(
279 AuditElements.EventCategoryType.LOGIC,
280 ReportLogic.class.getSimpleName(),
281 null,
282 "deleteExecution",
283 AuditElements.Result.FAILURE);
284
285 List<AuditConfTO> audits = AUDIT_SERVICE.list();
286 assertFalse(audits.stream().anyMatch(a -> a.getKey().equals(auditLoggerName.toAuditKey())));
287
288 AuditConfTO audit = new AuditConfTO();
289 audit.setKey(auditLoggerName.toAuditKey());
290 audit.setActive(true);
291 AUDIT_SERVICE.set(audit);
292
293 audits = AUDIT_SERVICE.list();
294 assertTrue(audits.stream().anyMatch(a -> a.getKey().equals(auditLoggerName.toAuditKey())));
295
296 AUDIT_SERVICE.delete(audit.getKey());
297
298 audits = AUDIT_SERVICE.list();
299 assertFalse(audits.stream().anyMatch(a -> a.getKey().equals(auditLoggerName.toAuditKey())));
300 }
301
302 @Test
303 public void listAuditEvents() {
304 List<EventCategory> events = AUDIT_SERVICE.events();
305
306 boolean found = false;
307
308 for (EventCategory eventCategoryTO : events) {
309 if (UserLogic.class.getSimpleName().equals(eventCategoryTO.getCategory())) {
310 assertEquals(AuditElements.EventCategoryType.LOGIC, eventCategoryTO.getType());
311 assertTrue(eventCategoryTO.getEvents().contains("create"));
312 assertTrue(eventCategoryTO.getEvents().contains("search"));
313 assertFalse(eventCategoryTO.getEvents().contains("doCreate"));
314 assertFalse(eventCategoryTO.getEvents().contains("setStatusOnWfAdapter"));
315 assertFalse(eventCategoryTO.getEvents().contains("resolveReference"));
316 found = true;
317 }
318 }
319 assertTrue(found);
320
321 found = false;
322 for (EventCategory eventCategoryTO : events) {
323 if (GroupLogic.class.getSimpleName().equals(eventCategoryTO.getCategory())) {
324 assertEquals(AuditElements.EventCategoryType.LOGIC, eventCategoryTO.getType());
325 assertTrue(eventCategoryTO.getEvents().contains("create"));
326 assertTrue(eventCategoryTO.getEvents().contains("search"));
327 assertFalse(eventCategoryTO.getEvents().contains("resolveReference"));
328 found = true;
329 }
330 }
331 assertTrue(found);
332
333 found = false;
334 for (EventCategory eventCategoryTO : events) {
335 if (ResourceLogic.class.getSimpleName().equals(eventCategoryTO.getCategory())) {
336 assertEquals(AuditElements.EventCategoryType.LOGIC, eventCategoryTO.getType());
337 assertTrue(eventCategoryTO.getEvents().contains("create"));
338 assertTrue(eventCategoryTO.getEvents().contains("read"));
339 assertTrue(eventCategoryTO.getEvents().contains("delete"));
340 assertFalse(eventCategoryTO.getEvents().contains("resolveReference"));
341 found = true;
342 }
343 }
344 assertTrue(found);
345
346 found = false;
347 for (EventCategory eventCategoryTO : events) {
348 if (AnyTypeKind.USER.name().equals(eventCategoryTO.getCategory())) {
349 if (RESOURCE_NAME_LDAP.equals(eventCategoryTO.getSubcategory())
350 && AuditElements.EventCategoryType.PULL == eventCategoryTO.getType()) {
351
352 assertTrue(eventCategoryTO.getEvents().contains(ResourceOperation.DELETE.name().toLowerCase()));
353 found = true;
354 }
355 }
356 }
357 assertTrue(found);
358
359 found = false;
360 for (EventCategory eventCategoryTO : events) {
361 if (AnyTypeKind.USER.name().equals(eventCategoryTO.getCategory())) {
362 if (RESOURCE_NAME_CSV.equals(eventCategoryTO.getSubcategory())
363 && AuditElements.EventCategoryType.PROPAGATION == eventCategoryTO.getType()) {
364
365 assertTrue(eventCategoryTO.getEvents().contains(ResourceOperation.CREATE.name().toLowerCase()));
366 assertTrue(eventCategoryTO.getEvents().contains(ResourceOperation.UPDATE.name().toLowerCase()));
367 assertTrue(eventCategoryTO.getEvents().contains(ResourceOperation.DELETE.name().toLowerCase()));
368 found = true;
369 }
370 }
371 }
372 assertTrue(found);
373
374 found = false;
375 for (EventCategory eventCategoryTO : events) {
376 if (AuditElements.EventCategoryType.TASK == eventCategoryTO.getType()
377 && "PullJobDelegate".equals(eventCategoryTO.getCategory())) {
378 found = true;
379 }
380 }
381 assertTrue(found);
382 }
383
384 private static void checkLogFileFor(
385 final Path path,
386 final Function<String, Boolean> checker,
387 final int maxWaitSeconds)
388 throws IOException {
389
390 await().atMost(maxWaitSeconds, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until(() -> {
391 try {
392 return checker.apply(Files.readString(path, StandardCharsets.UTF_8));
393 } catch (Exception e) {
394 return false;
395 }
396 });
397 }
398
399 @Test
400 public void saveAuditEvent() {
401 AuditEntry auditEntry = new AuditEntry();
402 auditEntry.setWho("syncope-user " + UUID.randomUUID().toString());
403 auditEntry.setLogger(new AuditLoggerName(
404 AuditElements.EventCategoryType.WA,
405 null,
406 AuditElements.AUTHENTICATION_CATEGORY.toUpperCase(),
407 "validate",
408 AuditElements.Result.SUCCESS));
409 auditEntry.setDate(OffsetDateTime.now());
410 auditEntry.setBefore(UUID.randomUUID().toString());
411 auditEntry.setOutput(UUID.randomUUID().toString());
412 assertDoesNotThrow(() -> AUDIT_SERVICE.create(auditEntry));
413
414 if (IS_EXT_SEARCH_ENABLED) {
415 try {
416 Thread.sleep(2000);
417 } catch (InterruptedException ex) {
418
419 }
420 }
421
422 PagedResult<AuditEntry> events = AUDIT_SERVICE.search(new AuditQuery.Builder().
423 size(1).
424 type(auditEntry.getLogger().getType()).
425 category(auditEntry.getLogger().getCategory()).
426 subcategory(auditEntry.getLogger().getSubcategory()).
427 event(auditEntry.getLogger().getEvent()).
428 result(auditEntry.getLogger().getResult()).
429 build());
430 assertNotNull(events);
431 assertEquals(1, events.getSize());
432 }
433
434 @Test
435 public void saveAuthEvent() {
436 AuditEntry auditEntry = new AuditEntry();
437 auditEntry.setWho("syncope-user " + UUID.randomUUID().toString());
438 auditEntry.setLogger(new AuditLoggerName(
439 AuditElements.EventCategoryType.WA,
440 null,
441 "AuthenticationEvent",
442 "auth",
443 AuditElements.Result.SUCCESS));
444 auditEntry.setDate(OffsetDateTime.now());
445 auditEntry.setBefore(UUID.randomUUID().toString());
446 auditEntry.setOutput(UUID.randomUUID().toString());
447 assertDoesNotThrow(() -> AUDIT_SERVICE.create(auditEntry));
448
449 if (IS_EXT_SEARCH_ENABLED) {
450 try {
451 Thread.sleep(2000);
452 } catch (InterruptedException ex) {
453
454 }
455 }
456
457 PagedResult<AuditEntry> events = AUDIT_SERVICE.search(new AuditQuery.Builder().
458 size(1).
459 type(auditEntry.getLogger().getType()).
460 category(auditEntry.getLogger().getCategory()).
461 subcategory(auditEntry.getLogger().getSubcategory()).
462 event(auditEntry.getLogger().getEvent()).
463 result(auditEntry.getLogger().getResult()).
464 build());
465 assertNotNull(events);
466 assertEquals(1, events.getSize());
467 }
468
469 @Test
470 public void customAuditAppender() throws IOException, InterruptedException {
471 try (InputStream propStream = getClass().getResourceAsStream("/test.properties")) {
472 Properties props = new Properties();
473 props.load(propStream);
474
475 Path auditFilePath = Paths.get(props.getProperty("test.log.dir")
476 + File.separator + "audit_for_Master_file.log");
477 Files.write(auditFilePath, new byte[0], StandardOpenOption.TRUNCATE_EXISTING);
478
479 Path auditNoRewriteFilePath = Paths.get(props.getProperty("test.log.dir")
480 + File.separator + "audit_for_Master_norewrite_file.log");
481 Files.write(auditNoRewriteFilePath, new byte[0], StandardOpenOption.TRUNCATE_EXISTING);
482
483
484 ResourceTO resource = RESOURCE_SERVICE.read(RESOURCE_NAME_CSV);
485 assertNotNull(resource);
486 resource.setPropagationPriority(100);
487 RESOURCE_SERVICE.update(resource);
488
489 ConnInstanceTO connector = CONNECTOR_SERVICE.readByResource(RESOURCE_NAME_CSV, null);
490 assertNotNull(connector);
491 connector.setPoolConf(new ConnPoolConfTO());
492 CONNECTOR_SERVICE.update(connector);
493
494
495 checkLogFileFor(
496 auditFilePath,
497 content -> content.contains(
498 "DEBUG Master.syncope.audit.[LOGIC]:[ResourceLogic]:[]:[update]:[SUCCESS]"
499 + " - This is a static test message"),
500 10);
501
502
503 checkLogFileFor(
504 auditNoRewriteFilePath,
505 content -> !content.contains(
506 "DEBUG Master.syncope.audit.[LOGIC]:[ResourceLogic]:[]:[update]:[SUCCESS]"
507 + " - This is a static test message"),
508 10);
509 } catch (IOException e) {
510 fail("Unable to read/write log files", e);
511 }
512 }
513
514 @Test
515 public void issueSYNCOPE976() {
516 List<EventCategory> events = AUDIT_SERVICE.events();
517 assertNotNull(events);
518
519 EventCategory userLogic = events.stream().
520 filter(object -> "UserLogic".equals(object.getCategory())).findAny().get();
521 assertNotNull(userLogic);
522 assertEquals(1, userLogic.getEvents().stream().filter("create"::equals).count());
523 }
524
525 @Test
526 public void issueSYNCOPE1446() {
527 AuditLoggerName createSuccess = new AuditLoggerName(
528 AuditElements.EventCategoryType.PROPAGATION,
529 AnyTypeKind.ANY_OBJECT.name(),
530 RESOURCE_NAME_DBSCRIPTED,
531 "create",
532 AuditElements.Result.SUCCESS);
533 AuditLoggerName createFailure = new AuditLoggerName(
534 AuditElements.EventCategoryType.PROPAGATION,
535 AnyTypeKind.ANY_OBJECT.name(),
536 RESOURCE_NAME_DBSCRIPTED,
537 "create",
538 AuditElements.Result.FAILURE);
539 AuditLoggerName updateSuccess = new AuditLoggerName(
540 AuditElements.EventCategoryType.PROPAGATION,
541 AnyTypeKind.ANY_OBJECT.name(),
542 RESOURCE_NAME_DBSCRIPTED,
543 "update",
544 AuditElements.Result.SUCCESS);
545 AuditLoggerName updateFailure = new AuditLoggerName(
546 AuditElements.EventCategoryType.PROPAGATION,
547 AnyTypeKind.ANY_OBJECT.name(),
548 RESOURCE_NAME_DBSCRIPTED,
549 "update",
550 AuditElements.Result.FAILURE);
551 try {
552
553 AuditConfTO audit = new AuditConfTO();
554 audit.setKey(createSuccess.toAuditKey());
555 audit.setActive(true);
556 AUDIT_SERVICE.set(audit);
557
558 audit.setKey(createFailure.toAuditKey());
559 AUDIT_SERVICE.set(audit);
560
561 audit.setKey(updateSuccess.toAuditKey());
562 AUDIT_SERVICE.set(audit);
563
564 audit.setKey(updateFailure.toAuditKey());
565 AUDIT_SERVICE.set(audit);
566
567
568 PushTaskTO pushTask = new PushTaskTO();
569 pushTask.setPerformCreate(true);
570 pushTask.setPerformUpdate(true);
571 pushTask.setUnmatchingRule(UnmatchingRule.PROVISION);
572 pushTask.setMatchingRule(MatchingRule.UPDATE);
573 RECONCILIATION_SERVICE.push(new ReconQuery.Builder(PRINTER, RESOURCE_NAME_DBSCRIPTED).
574 anyKey("fc6dbc3a-6c07-4965-8781-921e7401a4a5").build(), pushTask);
575 } catch (Exception e) {
576 LOG.error("Unexpected exception", e);
577 fail(e::getMessage);
578 } finally {
579 try {
580 AUDIT_SERVICE.delete(createSuccess.toAuditKey());
581 } catch (Exception e) {
582
583 }
584 try {
585 AUDIT_SERVICE.delete(createFailure.toAuditKey());
586 } catch (Exception e) {
587
588 }
589 try {
590 AUDIT_SERVICE.delete(updateSuccess.toAuditKey());
591 } catch (Exception e) {
592
593 }
594 try {
595 AUDIT_SERVICE.delete(updateFailure.toAuditKey());
596 } catch (Exception e) {
597
598 }
599 }
600 }
601
602 @Test
603 public void issueSYNCOPE1695() {
604
605 AUDIT_SERVICE.set(buildAuditConf(
606 "syncope.audit.[PULL]:[USER]:[resource-ldap]:[matchingrule_update]:[SUCCESS]", true));
607 AUDIT_SERVICE.set(buildAuditConf(
608 "syncope.audit.[PULL]:[USER]:[resource-ldap]:[unmatchingrule_assign]:[SUCCESS]", true));
609 AUDIT_SERVICE.set(buildAuditConf(
610 "syncope.audit.[PULL]:[USER]:[resource-ldap]:[unmatchingrule_provision]:[SUCCESS]", true));
611
612 UserTO pullFromLDAP = null;
613 try {
614
615 PullTaskTO pullTaskTO = new PullTaskTO();
616 pullTaskTO.setPerformCreate(true);
617 pullTaskTO.setPerformUpdate(true);
618 pullTaskTO.getActions().add("LDAPMembershipPullActions");
619 pullTaskTO.setDestinationRealm(SyncopeConstants.ROOT_REALM);
620 pullTaskTO.setMatchingRule(MatchingRule.UPDATE);
621 pullTaskTO.setUnmatchingRule(UnmatchingRule.ASSIGN);
622 RECONCILIATION_SERVICE.pull(new ReconQuery.Builder(AnyTypeKind.USER.name(), RESOURCE_NAME_LDAP).
623 fiql("uid==pullFromLDAP").build(), pullTaskTO);
624
625
626 pullFromLDAP = updateUser(new UserUR.Builder(USER_SERVICE.read("pullFromLDAP").getKey()).
627 plainAttr(new AttrPatch.Builder(new Attr.Builder("ctype").value("abcdef").build()).build()).
628 build()).getEntity();
629
630
631 if (IS_EXT_SEARCH_ENABLED) {
632 try {
633 Thread.sleep(2000);
634 } catch (InterruptedException ex) {
635
636 }
637 }
638
639 assertEquals(1, AUDIT_SERVICE.search(new AuditQuery.Builder().
640 entityKey(pullFromLDAP.getKey()).
641 page(1).
642 size(10).
643 events(List.of("matchingrule_update", "unmatchingrule_assign", "unmatchingrule_provision")).
644 result(AuditElements.Result.SUCCESS).
645 build()).getTotalCount());
646 } finally {
647 if (pullFromLDAP != null) {
648 USER_SERVICE.deassociate(new ResourceDR.Builder()
649 .key(pullFromLDAP.getKey())
650 .resource(RESOURCE_NAME_LDAP)
651 .action(ResourceDeassociationAction.UNLINK)
652 .build());
653 USER_SERVICE.delete(pullFromLDAP.getKey());
654
655
656 AUDIT_SERVICE.set(buildAuditConf(
657 "syncope.audit.[PULL]:[USER]:[resource-ldap]:[matchingrule_update]:[SUCCESS]", false));
658 AUDIT_SERVICE.set(buildAuditConf(
659 "syncope.audit.[PULL]:[USER]:[resource-ldap]:[unmatchingrule_assign]:[SUCCESS]", false));
660 AUDIT_SERVICE.set(buildAuditConf(
661 "syncope.audit.[PULL]:[USER]:[resource-ldap]:[unmatchingrule_provision]:[SUCCESS]", false));
662 }
663 }
664 }
665
666 @Test
667 public void issueSYNCOPE1791() throws IOException {
668 ImplementationTO logicActions;
669 try {
670 logicActions = IMPLEMENTATION_SERVICE.read(
671 IdRepoImplementationType.LOGIC_ACTIONS, "CustomAuditLogicActions");
672 } catch (SyncopeClientException e) {
673 logicActions = new ImplementationTO();
674 logicActions.setKey("CustomAuditLogicActions");
675 logicActions.setEngine(ImplementationEngine.GROOVY);
676 logicActions.setType(IdRepoImplementationType.LOGIC_ACTIONS);
677 logicActions.setBody(IOUtils.toString(
678 getClass().getResourceAsStream("/CustomAuditLogicActions.groovy"), StandardCharsets.UTF_8));
679 Response response = IMPLEMENTATION_SERVICE.create(logicActions);
680 logicActions = IMPLEMENTATION_SERVICE.read(
681 logicActions.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY));
682 }
683 assertNotNull(logicActions);
684
685 RealmTO root = getRealm(SyncopeConstants.ROOT_REALM).orElseThrow();
686 root.getActions().add(logicActions.getKey());
687 REALM_SERVICE.update(root);
688
689 AuditQuery query = new AuditQuery.Builder().type(AuditElements.EventCategoryType.CUSTOM).build();
690 int before = query(query, MAX_WAIT_SECONDS).size();
691 try {
692 AUDIT_SERVICE.set(buildAuditConf("syncope.audit.[CUSTOM]:[]:[]:[MY_EVENT]:[SUCCESS]", true));
693
694 AnyObjectTO printer = createAnyObject(AnyObjectITCase.getSample("syncope-1791")).getEntity();
695 updateAnyObject(new AnyObjectUR.Builder(printer.getKey()).
696 plainAttr(attrAddReplacePatch("location", "new" + getUUIDString())).
697 build());
698
699 int after = query(query, MAX_WAIT_SECONDS).size();
700 assertEquals(before + 1, after);
701 } finally {
702 AUDIT_SERVICE.set(buildAuditConf("syncope.audit.[CUSTOM]:[]:[]:[MY_EVENT]:[SUCCESS]", false));
703
704 root.getActions().remove(logicActions.getKey());
705 REALM_SERVICE.update(root);
706 }
707 }
708 }