1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.provisioning.java.pushpull;
20
21 import java.time.OffsetDateTime;
22 import java.util.List;
23 import java.util.Set;
24 import java.util.stream.Collectors;
25 import org.apache.commons.lang3.StringUtils;
26 import org.apache.commons.lang3.exception.ExceptionUtils;
27 import org.apache.syncope.common.lib.AnyOperations;
28 import org.apache.syncope.common.lib.request.AnyCR;
29 import org.apache.syncope.common.lib.request.AnyUR;
30 import org.apache.syncope.common.lib.request.StringPatchItem;
31 import org.apache.syncope.common.lib.to.AnyTO;
32 import org.apache.syncope.common.lib.to.Provision;
33 import org.apache.syncope.common.lib.to.ProvisioningReport;
34 import org.apache.syncope.common.lib.types.AnyTypeKind;
35 import org.apache.syncope.common.lib.types.AuditElements;
36 import org.apache.syncope.common.lib.types.AuditElements.Result;
37 import org.apache.syncope.common.lib.types.MatchType;
38 import org.apache.syncope.common.lib.types.MatchingRule;
39 import org.apache.syncope.common.lib.types.PatchOperation;
40 import org.apache.syncope.common.lib.types.PullMode;
41 import org.apache.syncope.common.lib.types.ResourceOperation;
42 import org.apache.syncope.common.lib.types.TaskType;
43 import org.apache.syncope.common.lib.types.UnmatchingRule;
44 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
45 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
46 import org.apache.syncope.core.persistence.api.dao.RemediationDAO;
47 import org.apache.syncope.core.persistence.api.dao.TaskDAO;
48 import org.apache.syncope.core.persistence.api.dao.UserDAO;
49 import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
50 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
51 import org.apache.syncope.core.persistence.api.entity.Remediation;
52 import org.apache.syncope.core.persistence.api.entity.task.PullTask;
53 import org.apache.syncope.core.provisioning.api.AuditManager;
54 import org.apache.syncope.core.provisioning.api.PropagationByResource;
55 import org.apache.syncope.core.provisioning.api.ProvisioningManager;
56 import org.apache.syncope.core.provisioning.api.cache.VirAttrCache;
57 import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheKey;
58 import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheValue;
59 import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
60 import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
61 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
62 import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
63 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler;
64 import org.apache.syncope.core.provisioning.api.rules.PullMatch;
65 import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
66 import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
67 import org.identityconnectors.framework.common.objects.Attribute;
68 import org.identityconnectors.framework.common.objects.SyncDelta;
69 import org.quartz.JobExecutionException;
70 import org.springframework.beans.factory.annotation.Autowired;
71 import org.springframework.transaction.annotation.Propagation;
72 import org.springframework.transaction.annotation.Transactional;
73
74 public abstract class AbstractPullResultHandler
75 extends AbstractSyncopeResultHandler<PullTask, PullActions>
76 implements SyncopePullResultHandler {
77
78 protected static Result and(final Result left, final Result right) {
79 return left == Result.SUCCESS && right == Result.SUCCESS
80 ? Result.SUCCESS
81 : Result.FAILURE;
82 }
83
84 @Autowired
85 protected InboundMatcher inboundMatcher;
86
87 @Autowired
88 protected NotificationManager notificationManager;
89
90 @Autowired
91 protected AuditManager auditManager;
92
93 @Autowired
94 protected ConnObjectUtils connObjectUtils;
95
96 @Autowired
97 protected UserDAO userDAO;
98
99 @Autowired
100 protected AnyTypeDAO anyTypeDAO;
101
102 @Autowired
103 protected TaskDAO taskDAO;
104
105 @Autowired
106 protected RemediationDAO remediationDAO;
107
108 @Autowired
109 protected VirSchemaDAO virSchemaDAO;
110
111 @Autowired
112 protected VirAttrCache virAttrCache;
113
114 @Autowired
115 protected EntityFactory entityFactory;
116
117 protected abstract String getName(AnyTO anyTO);
118
119 protected abstract String getName(AnyCR anyCR);
120
121 protected abstract ProvisioningManager<?, ?> getProvisioningManager();
122
123 protected abstract AnyTO doCreate(AnyCR anyCR, SyncDelta delta);
124
125 protected abstract AnyUR doUpdate(AnyTO before, AnyUR anyUR, SyncDelta delta, ProvisioningReport result);
126
127 @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRES_NEW)
128 @Override
129 public boolean handle(final SyncDelta delta) {
130 Provision provision = null;
131 try {
132 provision = profile.getTask().getResource().
133 getProvisionByObjectClass(delta.getObject().getObjectClass().getObjectClassValue()).
134 orElseThrow(() -> new JobExecutionException(
135 "No provision found on " + profile.getTask().getResource()
136 + " for " + delta.getObject().getObjectClass()));
137
138 Result latestResult = doHandle(delta, provision, anyTypeDAO.find(provision.getAnyType()).getKind());
139
140 LOG.debug("Successfully handled {}", delta);
141
142 if (profile.getTask().getPullMode() != PullMode.INCREMENTAL) {
143 return true;
144 }
145 return latestResult == Result.SUCCESS;
146 } catch (IgnoreProvisionException e) {
147 ProvisioningReport ignoreResult = new ProvisioningReport();
148 ignoreResult.setOperation(ResourceOperation.NONE);
149 ignoreResult.setAnyType(provision == null
150 ? getAnyUtils().anyTypeKind().name() : provision.getAnyType());
151 ignoreResult.setStatus(ProvisioningReport.Status.IGNORE);
152 ignoreResult.setMessage(e.getMessage());
153 ignoreResult.setKey(null);
154 ignoreResult.setUidValue(delta.getUid().getUidValue());
155 ignoreResult.setName(delta.getObject().getName().getNameValue());
156 profile.getResults().add(ignoreResult);
157
158 LOG.warn("Ignoring during pull", e);
159
160 return true;
161 } catch (JobExecutionException e) {
162 LOG.error("Pull failed", e);
163
164 return false;
165 }
166 }
167
168 protected void throwIgnoreProvisionException(final SyncDelta delta, final Exception exception)
169 throws JobExecutionException {
170
171 if (exception instanceof IgnoreProvisionException) {
172 throw IgnoreProvisionException.class.cast(exception);
173 }
174
175 IgnoreProvisionException ipe = null;
176 for (PullActions action : profile.getActions()) {
177 if (ipe == null) {
178 ipe = action.onError(profile, delta, exception);
179 }
180 }
181 if (ipe != null) {
182 throw ipe;
183 }
184 }
185
186 protected Result provision(
187 final UnmatchingRule rule,
188 final SyncDelta delta,
189 final AnyTypeKind anyTypeKind,
190 final Provision provision) throws JobExecutionException {
191
192 if (!profile.getTask().isPerformCreate()) {
193 LOG.debug("PullTask not configured for create");
194 end(provision.getAnyType(), UnmatchingRule.toEventName(rule), Result.SUCCESS, null, null, delta);
195 return Result.SUCCESS;
196 }
197
198 AnyCR anyCR = connObjectUtils.getAnyCR(
199 delta.getObject(),
200 profile.getTask(),
201 anyTypeKind,
202 provision,
203 true);
204 if (rule == UnmatchingRule.ASSIGN) {
205 anyCR.getResources().add(profile.getTask().getResource().getKey());
206 }
207
208 ProvisioningReport result = new ProvisioningReport();
209 result.setOperation(ResourceOperation.CREATE);
210 result.setAnyType(provision.getAnyType());
211 result.setStatus(ProvisioningReport.Status.SUCCESS);
212 result.setName(getName(anyCR));
213 result.setUidValue(delta.getUid().getUidValue());
214
215 if (profile.isDryRun()) {
216 result.setKey(null);
217 end(provision.getAnyType(), UnmatchingRule.toEventName(rule), Result.SUCCESS, null, null, delta);
218 return Result.SUCCESS;
219 }
220
221 Object output;
222 Result resultStatus;
223 try {
224 for (PullActions action : profile.getActions()) {
225 if (rule == UnmatchingRule.ASSIGN) {
226 action.beforeAssign(profile, delta, anyCR);
227 } else if (rule == UnmatchingRule.PROVISION) {
228 action.beforeProvision(profile, delta, anyCR);
229 }
230 }
231 result.setName(getName(anyCR));
232
233 AnyTO created = doCreate(anyCR, delta);
234 output = created;
235 result.setKey(created.getKey());
236 result.setName(getName(created));
237 resultStatus = Result.SUCCESS;
238
239 for (PullActions action : profile.getActions()) {
240 action.after(profile, delta, created, result);
241 }
242
243 LOG.debug("{} {} successfully created", created.getType(), created.getKey());
244 } catch (PropagationException e) {
245
246
247 LOG.error("Could not propagate {} {}",
248 provision.getAnyType(), delta.getUid().getUidValue(), e);
249 output = e;
250 resultStatus = Result.FAILURE;
251 } catch (Exception e) {
252 throwIgnoreProvisionException(delta, e);
253
254 result.setStatus(ProvisioningReport.Status.FAILURE);
255 result.setMessage(ExceptionUtils.getRootCauseMessage(e));
256 LOG.error("Could not create {} {} ", provision.getAnyType(), delta.getUid().getUidValue(), e);
257 output = e;
258
259 if (profile.getTask().isRemediation()) {
260
261 resultStatus = Result.SUCCESS;
262 createRemediation(provision.getAnyType(), null, anyCR, null, result, delta);
263 } else {
264 resultStatus = Result.FAILURE;
265 }
266 }
267
268 end(provision.getAnyType(), UnmatchingRule.toEventName(rule), resultStatus, null, output, delta);
269 profile.getResults().add(result);
270 return resultStatus;
271 }
272
273 protected Result update(
274 final SyncDelta delta,
275 final List<PullMatch> matches,
276 final Provision provision) throws JobExecutionException {
277
278 if (!profile.getTask().isPerformUpdate()) {
279 LOG.debug("PullTask not configured for update");
280 end(provision.getAnyType(),
281 MatchingRule.toEventName(MatchingRule.UPDATE), Result.SUCCESS, null, null, delta);
282 return Result.SUCCESS;
283 }
284
285 LOG.debug("About to update {}", matches);
286
287 Result global = Result.SUCCESS;
288 for (PullMatch match : matches) {
289 LOG.debug("About to update {}", match);
290
291 ProvisioningReport result = new ProvisioningReport();
292 result.setOperation(ResourceOperation.UPDATE);
293 result.setAnyType(provision.getAnyType());
294 result.setStatus(ProvisioningReport.Status.SUCCESS);
295 result.setKey(match.getAny().getKey());
296 result.setUidValue(delta.getUid().getUidValue());
297
298 AnyTO before = getAnyTO(match.getAny());
299 if (before == null) {
300 result.setStatus(ProvisioningReport.Status.FAILURE);
301 result.setMessage(String.format("Any '%s(%s)' not found", provision.getAnyType(), match));
302 } else {
303 result.setName(getName(before));
304 }
305
306 if (!profile.isDryRun()) {
307 Result resultStatus;
308 Object output;
309 AnyUR effectiveReq = null;
310
311 if (before == null) {
312 resultStatus = Result.FAILURE;
313 output = null;
314 } else {
315 AnyUR anyUR = null;
316 try {
317 anyUR = connObjectUtils.getAnyUR(
318 before.getKey(),
319 delta.getObject(),
320 before,
321 profile.getTask(),
322 match.getAny().getType().getKind(),
323 provision);
324
325 for (PullActions action : profile.getActions()) {
326 action.beforeUpdate(profile, delta, before, anyUR);
327 }
328
329 effectiveReq = doUpdate(before, anyUR, delta, result);
330 AnyTO updated = AnyOperations.patch(before, effectiveReq);
331
332 for (PullActions action : profile.getActions()) {
333 action.after(profile, delta, updated, result);
334 }
335
336 output = updated;
337 resultStatus = Result.SUCCESS;
338 result.setName(getName(updated));
339
340 LOG.debug("{} {} successfully updated", provision.getAnyType(), match);
341 } catch (PropagationException e) {
342
343
344 LOG.error("Could not propagate {} {}",
345 provision.getAnyType(), delta.getUid().getUidValue(), e);
346 output = e;
347 resultStatus = Result.FAILURE;
348 } catch (Exception e) {
349 throwIgnoreProvisionException(delta, e);
350
351 result.setStatus(ProvisioningReport.Status.FAILURE);
352 result.setMessage(ExceptionUtils.getRootCauseMessage(e));
353 LOG.error("Could not update {} {}",
354 provision.getAnyType(), delta.getUid().getUidValue(), e);
355 output = e;
356
357 if (profile.getTask().isRemediation()) {
358
359 resultStatus = Result.SUCCESS;
360 createRemediation(provision.getAnyType(), null, null, anyUR, result, delta);
361 } else {
362 resultStatus = Result.FAILURE;
363 }
364 }
365 }
366 end(provision.getAnyType(),
367 MatchingRule.toEventName(MatchingRule.UPDATE),
368 resultStatus, before, output, delta, effectiveReq);
369 global = and(global, resultStatus);
370 }
371
372 profile.getResults().add(result);
373 }
374
375 return global;
376 }
377
378 protected Result deprovision(
379 final MatchingRule matchingRule,
380 final SyncDelta delta,
381 final List<PullMatch> matches,
382 final Provision provision)
383 throws JobExecutionException {
384
385 if (!profile.getTask().isPerformUpdate()) {
386 LOG.debug("PullTask not configured for update");
387 end(provision.getAnyType(),
388 MatchingRule.toEventName(matchingRule), Result.SUCCESS, null, null, delta);
389 return Result.SUCCESS;
390 }
391
392 LOG.debug("About to deprovision {}", matches);
393
394 Result global = Result.SUCCESS;
395 for (PullMatch match : matches) {
396 LOG.debug("About to unassign resource {}", match);
397
398 ProvisioningReport result = new ProvisioningReport();
399 result.setOperation(ResourceOperation.DELETE);
400 result.setAnyType(provision.getAnyType());
401 result.setStatus(ProvisioningReport.Status.SUCCESS);
402 result.setKey(match.getAny().getKey());
403 result.setUidValue(delta.getUid().getUidValue());
404
405 AnyTO before = getAnyTO(match.getAny());
406
407 if (before == null) {
408 result.setStatus(ProvisioningReport.Status.FAILURE);
409 result.setMessage(String.format("Any '%s(%s)' not found", provision.getAnyType(), match));
410 }
411
412 if (!profile.isDryRun()) {
413 Object output;
414 Result resultStatus;
415
416 if (before == null) {
417 resultStatus = Result.FAILURE;
418 output = null;
419 } else {
420 result.setName(getName(before));
421
422 try {
423 if (matchingRule == MatchingRule.UNASSIGN) {
424 for (PullActions action : profile.getActions()) {
425 action.beforeUnassign(profile, delta, before);
426 }
427 } else if (matchingRule == MatchingRule.DEPROVISION) {
428 for (PullActions action : profile.getActions()) {
429 action.beforeDeprovision(profile, delta, before);
430 }
431 }
432
433 PropagationByResource<String> propByRes = new PropagationByResource<>();
434 propByRes.add(ResourceOperation.DELETE, profile.getTask().getResource().getKey());
435
436 taskExecutor.execute(propagationManager.getDeleteTasks(
437 match.getAny().getType().getKind(),
438 match.getAny().getKey(),
439 propByRes,
440 null,
441 null),
442 false,
443 securityProperties.getAdminUser());
444
445 AnyUR anyUR = null;
446 if (matchingRule == MatchingRule.UNASSIGN) {
447 anyUR = getAnyUtils().newAnyUR(match.getAny().getKey());
448 anyUR.getResources().add(new StringPatchItem.Builder().
449 operation(PatchOperation.DELETE).
450 value(profile.getTask().getResource().getKey()).build());
451 }
452 if (anyUR == null) {
453 output = getAnyTO(match.getAny());
454 } else {
455 output = doUpdate(before, anyUR, delta, result);
456 }
457
458 for (PullActions action : profile.getActions()) {
459 action.after(profile, delta, AnyTO.class.cast(output), result);
460 }
461
462 resultStatus = Result.SUCCESS;
463
464 LOG.debug("{} {} successfully updated", provision.getAnyType(), match);
465 } catch (PropagationException e) {
466
467
468 LOG.error("Could not propagate {} {}",
469 provision.getAnyType(), delta.getUid().getUidValue(), e);
470 output = e;
471 resultStatus = Result.FAILURE;
472 } catch (Exception e) {
473 throwIgnoreProvisionException(delta, e);
474
475 result.setStatus(ProvisioningReport.Status.FAILURE);
476 result.setMessage(ExceptionUtils.getRootCauseMessage(e));
477 LOG.error("Could not update {} {}",
478 provision.getAnyType(), delta.getUid().getUidValue(), e);
479 output = e;
480 resultStatus = Result.FAILURE;
481 }
482 }
483 end(provision.getAnyType(),
484 MatchingRule.toEventName(matchingRule),
485 resultStatus, before, output, delta);
486 global = and(global, resultStatus);
487 }
488
489 profile.getResults().add(result);
490 }
491
492 return global;
493 }
494
495 protected Result link(
496 final SyncDelta delta,
497 final List<PullMatch> matches,
498 final Provision provision,
499 final boolean unlink)
500 throws JobExecutionException {
501
502 if (!profile.getTask().isPerformUpdate()) {
503 LOG.debug("PullTask not configured for update");
504 end(provision.getAnyType(),
505 unlink
506 ? MatchingRule.toEventName(MatchingRule.UNLINK)
507 : MatchingRule.toEventName(MatchingRule.LINK),
508 Result.SUCCESS, null, null, delta);
509 return Result.SUCCESS;
510 }
511
512 LOG.debug("About to update {}", matches);
513
514 Result global = Result.SUCCESS;
515 for (PullMatch match : matches) {
516 LOG.debug("About to unassign resource {}", match);
517
518 ProvisioningReport result = new ProvisioningReport();
519 result.setOperation(ResourceOperation.NONE);
520 result.setAnyType(provision.getAnyType());
521 result.setStatus(ProvisioningReport.Status.SUCCESS);
522 result.setKey(match.getAny().getKey());
523 result.setUidValue(delta.getUid().getUidValue());
524
525 AnyTO before = getAnyTO(match.getAny());
526
527 if (before == null) {
528 result.setStatus(ProvisioningReport.Status.FAILURE);
529 result.setMessage(String.format("Any '%s(%s)' not found", provision.getAnyType(), match));
530 }
531
532 if (!profile.isDryRun()) {
533 Result resultStatus;
534 Object output;
535 AnyUR effectiveReq = null;
536
537 if (before == null) {
538 resultStatus = Result.FAILURE;
539 output = null;
540 } else {
541 result.setName(getName(before));
542
543 try {
544 if (unlink) {
545 for (PullActions action : profile.getActions()) {
546 action.beforeUnlink(profile, delta, before);
547 }
548 } else {
549 for (PullActions action : profile.getActions()) {
550 action.beforeLink(profile, delta, before);
551 }
552 }
553
554 AnyUR anyUR = getAnyUtils().newAnyUR(before.getKey());
555 anyUR.getResources().add(new StringPatchItem.Builder().
556 operation(unlink ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE).
557 value(profile.getTask().getResource().getKey()).build());
558
559 effectiveReq = update(anyUR).getResult();
560 output = AnyOperations.patch(before, effectiveReq);
561
562 for (PullActions action : profile.getActions()) {
563 action.after(profile, delta, AnyTO.class.cast(output), result);
564 }
565
566 resultStatus = Result.SUCCESS;
567
568 LOG.debug("{} {} successfully updated", provision.getAnyType(), match);
569 } catch (PropagationException e) {
570
571
572 LOG.error("Could not propagate {} {}",
573 provision.getAnyType(), delta.getUid().getUidValue(), e);
574 output = e;
575 resultStatus = Result.FAILURE;
576 } catch (Exception e) {
577 throwIgnoreProvisionException(delta, e);
578
579 result.setStatus(ProvisioningReport.Status.FAILURE);
580 result.setMessage(ExceptionUtils.getRootCauseMessage(e));
581 LOG.error("Could not update {} {}",
582 provision.getAnyType(), delta.getUid().getUidValue(), e);
583 output = e;
584 resultStatus = Result.FAILURE;
585 }
586 }
587 end(provision.getAnyType(),
588 unlink
589 ? MatchingRule.toEventName(MatchingRule.UNLINK)
590 : MatchingRule.toEventName(MatchingRule.LINK),
591 resultStatus, before, output, delta, effectiveReq);
592 global = and(global, resultStatus);
593 }
594
595 profile.getResults().add(result);
596 }
597
598 return global;
599 }
600
601 protected Result delete(
602 final SyncDelta delta,
603 final List<PullMatch> matches,
604 final Provision provision)
605 throws JobExecutionException {
606
607 if (!profile.getTask().isPerformDelete()) {
608 LOG.debug("PullTask not configured for delete");
609 end(provision.getAnyType(),
610 ResourceOperation.DELETE.name().toLowerCase(), Result.SUCCESS, null, null, delta);
611 return Result.SUCCESS;
612 }
613
614 LOG.debug("About to delete {}", matches);
615
616 Result global = Result.SUCCESS;
617 for (PullMatch match : matches) {
618 Object output;
619 Result resultStatus = Result.FAILURE;
620
621 ProvisioningReport result = new ProvisioningReport();
622
623 try {
624 AnyTO before = getAnyTO(match.getAny());
625
626 result.setKey(match.getAny().getKey());
627 result.setName(getName(before));
628 result.setOperation(ResourceOperation.DELETE);
629 result.setAnyType(provision.getAnyType());
630 result.setStatus(ProvisioningReport.Status.SUCCESS);
631 result.setUidValue(delta.getUid().getUidValue());
632
633 if (!profile.isDryRun()) {
634 for (PullActions action : profile.getActions()) {
635 action.beforeDelete(profile, delta, before);
636 }
637
638 try {
639 getProvisioningManager().delete(
640 match.getAny().getKey(),
641 Set.of(profile.getTask().getResource().getKey()),
642 true,
643 profile.getExecutor(),
644 getContext());
645 output = null;
646 resultStatus = Result.SUCCESS;
647
648 for (PullActions action : profile.getActions()) {
649 action.after(profile, delta, before, result);
650 }
651 } catch (Exception e) {
652 throwIgnoreProvisionException(delta, e);
653
654 result.setStatus(ProvisioningReport.Status.FAILURE);
655 result.setMessage(ExceptionUtils.getRootCauseMessage(e));
656 LOG.error("Could not delete {} {}", provision.getAnyType(), match, e);
657 output = e;
658
659 if (profile.getTask().isRemediation()) {
660
661 resultStatus = Result.SUCCESS;
662 createRemediation(
663 provision.getAnyType(), match.getAny().getKey(), null, null, result, delta);
664 }
665 }
666
667 end(provision.getAnyType(),
668 ResourceOperation.DELETE.name().toLowerCase(),
669 resultStatus, before, output, delta);
670 global = and(global, resultStatus);
671 }
672
673 profile.getResults().add(result);
674 } catch (NotFoundException e) {
675 LOG.error("Could not find {} {}", provision.getAnyType(), match, e);
676 } catch (DelegatedAdministrationException e) {
677 LOG.error("Not allowed to read {} {}", provision.getAnyType(), match, e);
678 } catch (Exception e) {
679 LOG.error("Could not delete {} {}", provision.getAnyType(), match, e);
680 }
681 }
682
683 return global;
684 }
685
686 protected Result ignore(
687 final SyncDelta delta,
688 final List<PullMatch> matches,
689 final Provision provision,
690 final boolean matching,
691 final String... message)
692 throws JobExecutionException {
693
694 LOG.debug("Any to ignore {}", delta.getObject().getUid().getUidValue());
695
696 if (matches == null) {
697 ProvisioningReport report = new ProvisioningReport();
698 report.setKey(null);
699 report.setName(delta.getObject().getUid().getUidValue());
700 report.setOperation(ResourceOperation.NONE);
701 report.setAnyType(provision.getAnyType());
702 report.setStatus(ProvisioningReport.Status.SUCCESS);
703 report.setUidValue(delta.getUid().getUidValue());
704 if (message != null && message.length >= 1) {
705 report.setMessage(message[0]);
706 }
707
708 profile.getResults().add(report);
709 } else {
710 matches.forEach(match -> {
711 ProvisioningReport report = new ProvisioningReport();
712 report.setKey(match.getAny().getKey());
713 report.setName(delta.getObject().getUid().getUidValue());
714 report.setOperation(ResourceOperation.NONE);
715 report.setAnyType(provision.getAnyType());
716 report.setStatus(ProvisioningReport.Status.SUCCESS);
717 report.setUidValue(delta.getUid().getUidValue());
718 if (message != null && message.length >= 1) {
719 report.setMessage(message[0]);
720 }
721
722 profile.getResults().add(report);
723 });
724 }
725
726 end(provision.getAnyType(),
727 matching
728 ? MatchingRule.toEventName(MatchingRule.IGNORE)
729 : UnmatchingRule.toEventName(UnmatchingRule.IGNORE),
730 Result.SUCCESS, null, null, delta);
731
732 return Result.SUCCESS;
733 }
734
735 protected Result handleAnys(
736 final SyncDelta delta,
737 final List<PullMatch> matches,
738 final AnyTypeKind anyTypeKind,
739 final Provision provision) throws JobExecutionException {
740
741 if (matches.isEmpty()) {
742 LOG.debug("Nothing to do");
743 return Result.SUCCESS;
744 }
745
746 Result result = Result.SUCCESS;
747 switch (delta.getDeltaType()) {
748 case CREATE:
749 case UPDATE:
750 case CREATE_OR_UPDATE:
751 if (matches.get(0).getAny() == null) {
752 switch (profile.getTask().getUnmatchingRule()) {
753 case ASSIGN:
754 case PROVISION:
755 result = provision(profile.getTask().getUnmatchingRule(), delta, anyTypeKind, provision);
756 break;
757
758 case IGNORE:
759 result = ignore(delta, null, provision, false);
760 break;
761
762 default:
763
764 }
765 } else {
766
767 virSchemaDAO.find(
768 profile.getTask().getResource().getKey(),
769 matches.get(0).getAny().getType().getKey()).
770 forEach(vs -> {
771 Attribute attr = delta.getObject().getAttributeByName(vs.getExtAttrName());
772 matches.forEach(match -> {
773 VirAttrCacheKey cacheKey = new VirAttrCacheKey(
774 provision.getAnyType(), match.getAny().getKey(),
775 vs.getKey());
776 if (attr == null) {
777 virAttrCache.expire(cacheKey);
778 } else {
779 virAttrCache.put(cacheKey, new VirAttrCacheValue(attr.getValue()));
780 }
781 });
782 });
783
784 switch (profile.getTask().getMatchingRule()) {
785 case UPDATE:
786 result = update(delta, matches, provision);
787 break;
788
789 case DEPROVISION:
790 case UNASSIGN:
791 result = deprovision(profile.getTask().getMatchingRule(), delta, matches, provision);
792 break;
793
794 case LINK:
795 result = link(delta, matches, provision, false);
796 break;
797
798 case UNLINK:
799 result = link(delta, matches, provision, true);
800 break;
801
802 case IGNORE:
803 result = ignore(delta, matches, provision, true);
804 break;
805
806 default:
807
808 }
809 }
810 break;
811
812 case DELETE:
813
814 result = matches.get(0).getAny() == null ? Result.SUCCESS : delete(delta, matches, provision);
815 break;
816
817 default:
818 }
819
820 return result;
821 }
822
823 protected Result handleLinkedAccounts(
824 final SyncDelta delta,
825 final List<PullMatch> matches,
826 final Provision provision) throws JobExecutionException {
827
828 if (matches.isEmpty()) {
829 LOG.debug("Nothing to do");
830 return Result.SUCCESS;
831 }
832
833
834 LOG.warn("Unexpected linked accounts found for {}: {}", provision.getAnyType(), matches);
835 return Result.SUCCESS;
836 }
837
838
839
840
841
842
843
844
845
846
847 protected Result doHandle(
848 final SyncDelta delta,
849 final Provision provision,
850 final AnyTypeKind anyTypeKind) throws JobExecutionException {
851
852 LOG.debug("Process {} for {} as {}",
853 delta.getDeltaType(), delta.getUid().getUidValue(), delta.getObject().getObjectClass());
854
855 SyncDelta finalDelta = delta;
856 for (PullActions action : profile.getActions()) {
857 finalDelta = action.preprocess(profile, finalDelta);
858 }
859
860 LOG.debug("Transformed {} for {} as {}",
861 finalDelta.getDeltaType(), finalDelta.getUid().getUidValue(), finalDelta.getObject().getObjectClass());
862
863 Result result = Result.SUCCESS;
864 try {
865 List<PullMatch> matches = inboundMatcher.match(
866 finalDelta,
867 profile.getTask().getResource(),
868 provision,
869 anyTypeKind);
870 LOG.debug("Match(es) found for {} as {}: {}",
871 finalDelta.getUid().getUidValue(), finalDelta.getObject().getObjectClass(), matches);
872
873 if (matches.size() > 1) {
874 switch (profile.getConflictResolutionAction()) {
875 case IGNORE:
876 throw new IgnoreProvisionException("More than one match found for "
877 + finalDelta.getObject().getUid().getUidValue() + ": " + matches);
878
879 case FIRSTMATCH:
880 matches = matches.subList(0, 1);
881 break;
882
883 case LASTMATCH:
884 matches = matches.subList(matches.size() - 1, matches.size());
885 break;
886
887 default:
888
889 }
890 }
891
892
893 Result anys = handleAnys(
894 finalDelta,
895 matches.stream().
896 filter(match -> match.getMatchTarget() == MatchType.ANY).
897 collect(Collectors.toList()),
898 anyTypeKind,
899 provision);
900
901
902 Result linkedAccounts = handleLinkedAccounts(
903 finalDelta,
904 matches.stream().
905 filter(match -> match.getMatchTarget() == MatchType.LINKED_ACCOUNT).
906 collect(Collectors.toList()), provision);
907
908 result = and(anys, linkedAccounts);
909 } catch (IllegalStateException | IllegalArgumentException e) {
910 LOG.warn(e.getMessage());
911 }
912
913 return result;
914 }
915
916 protected void end(
917 final String anyType,
918 final String event,
919 final Result result,
920 final Object before,
921 final Object output,
922 final SyncDelta delta,
923 final Object... furtherInput) {
924
925 notificationManager.createTasks(
926 profile.getExecutor(),
927 AuditElements.EventCategoryType.PULL,
928 anyType,
929 profile.getTask().getResource().getKey(),
930 event,
931 result,
932 before,
933 output,
934 delta,
935 furtherInput);
936
937 auditManager.audit(
938 profile.getExecutor(),
939 AuditElements.EventCategoryType.PULL,
940 anyType,
941 profile.getTask().getResource().getKey(),
942 event,
943 result,
944 before,
945 output,
946 delta,
947 furtherInput);
948 }
949
950 protected void createRemediationIfNeeded(
951 final AnyUR anyUR,
952 final SyncDelta delta,
953 final ProvisioningReport result) {
954
955 if (ProvisioningReport.Status.FAILURE == result.getStatus() && profile.getTask().isRemediation()) {
956 createRemediation(result.getAnyType(), null, null, anyUR, result, delta);
957 }
958 }
959
960 protected void createRemediation(
961 final String anyType,
962 final String anyKey,
963 final AnyCR anyCR,
964 final AnyUR anyUR,
965 final ProvisioningReport result,
966 final SyncDelta delta) {
967
968 Remediation remediation = entityFactory.newEntity(Remediation.class);
969
970 remediation.setAnyType(anyTypeDAO.find(anyType));
971 remediation.setOperation(anyUR == null ? ResourceOperation.CREATE : ResourceOperation.UPDATE);
972 if (StringUtils.isNotBlank(anyKey)) {
973 remediation.setPayload(anyKey);
974 } else if (anyCR != null) {
975 remediation.setPayload(anyCR);
976 } else if (anyUR != null) {
977 remediation.setPayload(anyUR);
978 }
979 remediation.setError(result.getMessage());
980 remediation.setInstant(OffsetDateTime.now());
981 remediation.setRemoteName(delta.getObject().getName().getNameValue());
982 if (taskDAO.exists(TaskType.PULL, profile.getTask().getKey())) {
983 remediation.setPullTask((PullTask) taskDAO.find(TaskType.PULL, profile.getTask().getKey()));
984 }
985
986 remediation = remediationDAO.save(remediation);
987
988 ProvisioningReport remediationResult = new ProvisioningReport();
989 remediationResult.setOperation(remediation.getOperation());
990 remediationResult.setAnyType(anyType);
991 remediationResult.setStatus(ProvisioningReport.Status.FAILURE);
992 remediationResult.setMessage(remediation.getError());
993 if (StringUtils.isNotBlank(anyKey)) {
994 remediationResult.setKey(anyKey);
995 } else if (anyUR != null) {
996 remediationResult.setKey(anyUR.getKey());
997 }
998 remediationResult.setUidValue(delta.getUid().getUidValue());
999 remediationResult.setName(remediation.getRemoteName());
1000 profile.getResults().add(remediationResult);
1001 }
1002 }