View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.core.provisioning.java.pushpull;
20  
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Optional;
24  import java.util.Set;
25  import org.apache.commons.lang3.exception.ExceptionUtils;
26  import org.apache.commons.lang3.tuple.Pair;
27  import org.apache.syncope.common.lib.SyncopeClientException;
28  import org.apache.syncope.common.lib.SyncopeConstants;
29  import org.apache.syncope.common.lib.to.OrgUnit;
30  import org.apache.syncope.common.lib.to.ProvisioningReport;
31  import org.apache.syncope.common.lib.to.RealmTO;
32  import org.apache.syncope.common.lib.types.AnyTypeKind;
33  import org.apache.syncope.common.lib.types.AuditElements;
34  import org.apache.syncope.common.lib.types.AuditElements.Result;
35  import org.apache.syncope.common.lib.types.ClientExceptionType;
36  import org.apache.syncope.common.lib.types.MatchingRule;
37  import org.apache.syncope.common.lib.types.PullMode;
38  import org.apache.syncope.common.lib.types.ResourceOperation;
39  import org.apache.syncope.common.lib.types.UnmatchingRule;
40  import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
41  import org.apache.syncope.core.persistence.api.dao.CASSPClientAppDAO;
42  import org.apache.syncope.core.persistence.api.dao.OIDCRPClientAppDAO;
43  import org.apache.syncope.core.persistence.api.dao.SAML2SPClientAppDAO;
44  import org.apache.syncope.core.persistence.api.dao.TaskDAO;
45  import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
46  import org.apache.syncope.core.persistence.api.dao.search.AttrCond;
47  import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
48  import org.apache.syncope.core.persistence.api.entity.Realm;
49  import org.apache.syncope.core.persistence.api.entity.task.PullTask;
50  import org.apache.syncope.core.provisioning.api.PropagationByResource;
51  import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
52  import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
53  import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
54  import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
55  import org.apache.syncope.core.provisioning.api.pushpull.RealmPullResultHandler;
56  import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
57  import org.apache.syncope.core.spring.security.AuthContextUtils;
58  import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
59  import org.identityconnectors.framework.common.objects.Attribute;
60  import org.identityconnectors.framework.common.objects.SyncDelta;
61  import org.quartz.JobExecutionException;
62  import org.springframework.beans.factory.annotation.Autowired;
63  import org.springframework.transaction.annotation.Transactional;
64  
65  @Transactional(rollbackFor = Throwable.class)
66  public class DefaultRealmPullResultHandler
67          extends AbstractRealmResultHandler<PullTask, PullActions>
68          implements RealmPullResultHandler {
69  
70      protected static Result and(final Result left, final Result right) {
71          return left == Result.SUCCESS && right == Result.SUCCESS
72                  ? Result.SUCCESS
73                  : Result.FAILURE;
74      }
75  
76      @Autowired
77      protected InboundMatcher inboundMatcher;
78  
79      @Autowired
80      protected ConnObjectUtils connObjectUtils;
81  
82      @Autowired
83      protected AnySearchDAO searchDAO;
84  
85      @Autowired
86      protected TaskDAO taskDAO;
87  
88      @Autowired
89      protected CASSPClientAppDAO casSPClientAppDAO;
90  
91      @Autowired
92      protected OIDCRPClientAppDAO oidcRPClientAppDAO;
93  
94      @Autowired
95      protected SAML2SPClientAppDAO saml2SPClientAppDAO;
96  
97      @Override
98      public boolean handle(final SyncDelta delta) {
99          try {
100             OrgUnit orgUnit = Optional.ofNullable(profile.getTask().getResource().getOrgUnit()).
101                     orElseThrow(() -> new JobExecutionException(
102                     "No orgUnit found on " + profile.getTask().getResource() + " for "
103                     + delta.getObject().getObjectClass()));
104 
105             Result latestResult = doHandle(delta, orgUnit);
106 
107             LOG.debug("Successfully handled {}", delta);
108 
109             if (profile.getTask().getPullMode() != PullMode.INCREMENTAL) {
110                 return true;
111             }
112 
113             return latestResult == Result.SUCCESS;
114         } catch (IgnoreProvisionException e) {
115             ProvisioningReport ignoreResult = new ProvisioningReport();
116             ignoreResult.setOperation(ResourceOperation.NONE);
117             ignoreResult.setStatus(ProvisioningReport.Status.IGNORE);
118             ignoreResult.setAnyType(SyncopeConstants.REALM_ANYTYPE);
119             ignoreResult.setKey(null);
120             ignoreResult.setName(delta.getObject().getName().getNameValue());
121             profile.getResults().add(ignoreResult);
122 
123             LOG.warn("Ignoring during pull", e);
124 
125             return true;
126         } catch (JobExecutionException e) {
127             LOG.error("Pull failed", e);
128 
129             return false;
130         }
131     }
132 
133     protected void throwIgnoreProvisionException(final SyncDelta delta, final Exception exception)
134             throws JobExecutionException {
135 
136         if (exception instanceof IgnoreProvisionException) {
137             throw IgnoreProvisionException.class.cast(exception);
138         }
139 
140         IgnoreProvisionException ipe = null;
141         for (PullActions action : profile.getActions()) {
142             if (ipe == null) {
143                 ipe = action.onError(profile, delta, exception);
144             }
145         }
146         if (ipe != null) {
147             throw ipe;
148         }
149     }
150 
151     protected Result assign(final SyncDelta delta, final OrgUnit orgUnit)
152             throws JobExecutionException {
153 
154         if (!profile.getTask().isPerformCreate()) {
155             LOG.debug("PullTask not configured for create");
156             end(UnmatchingRule.toEventName(UnmatchingRule.ASSIGN), Result.SUCCESS, null, null, delta);
157             return Result.SUCCESS;
158         }
159 
160         RealmTO realmTO = connObjectUtils.getRealmTO(delta.getObject(), orgUnit);
161         if (realmTO.getFullPath() == null) {
162             if (realmTO.getParent() == null) {
163                 realmTO.setParent(profile.getTask().getDestinationRealm().getFullPath());
164             }
165 
166             realmTO.setFullPath(realmTO.getParent() + '/' + realmTO.getName());
167         }
168         realmTO.getResources().add(profile.getTask().getResource().getKey());
169 
170         ProvisioningReport result = new ProvisioningReport();
171         result.setOperation(ResourceOperation.CREATE);
172         result.setAnyType(SyncopeConstants.REALM_ANYTYPE);
173         result.setStatus(ProvisioningReport.Status.SUCCESS);
174         result.setName(realmTO.getFullPath());
175 
176         if (profile.isDryRun()) {
177             result.setKey(null);
178             end(UnmatchingRule.toEventName(UnmatchingRule.ASSIGN), Result.SUCCESS, null, null, delta);
179             return Result.SUCCESS;
180         }
181 
182         for (PullActions action : profile.getActions()) {
183             action.beforeAssign(profile, delta, realmTO);
184         }
185 
186         profile.getResults().add(result);
187 
188         return create(realmTO, delta, UnmatchingRule.ASSIGN, result);
189     }
190 
191     protected Result provision(final SyncDelta delta, final OrgUnit orgUnit)
192             throws JobExecutionException {
193 
194         if (!profile.getTask().isPerformCreate()) {
195             LOG.debug("PullTask not configured for create");
196             end(UnmatchingRule.toEventName(UnmatchingRule.PROVISION), Result.SUCCESS, null, null, delta);
197             return Result.SUCCESS;
198         }
199 
200         RealmTO realmTO = connObjectUtils.getRealmTO(delta.getObject(), orgUnit);
201         if (realmTO.getFullPath() == null) {
202             if (realmTO.getParent() == null) {
203                 realmTO.setParent(profile.getTask().getDestinationRealm().getFullPath());
204             }
205 
206             realmTO.setFullPath(realmTO.getParent() + '/' + realmTO.getName());
207         }
208 
209         ProvisioningReport result = new ProvisioningReport();
210         result.setOperation(ResourceOperation.CREATE);
211         result.setAnyType(SyncopeConstants.REALM_ANYTYPE);
212         result.setStatus(ProvisioningReport.Status.SUCCESS);
213         result.setName(realmTO.getFullPath());
214 
215         if (profile.isDryRun()) {
216             result.setKey(null);
217             end(UnmatchingRule.toEventName(UnmatchingRule.PROVISION), Result.SUCCESS, null, null, delta);
218             return Result.SUCCESS;
219         }
220 
221         for (PullActions action : profile.getActions()) {
222             action.beforeProvision(profile, delta, realmTO);
223         }
224 
225         profile.getResults().add(result);
226 
227         return create(realmTO, delta, UnmatchingRule.PROVISION, result);
228     }
229 
230     protected Result create(
231             final RealmTO realmTO,
232             final SyncDelta delta,
233             final UnmatchingRule unmatchingRule,
234             final ProvisioningReport result)
235             throws JobExecutionException {
236 
237         Object output;
238         Result resultStatus;
239 
240         try {
241             Realm realm = realmDAO.save(binder.create(profile.getTask().getDestinationRealm(), realmTO));
242 
243             PropagationByResource<String> propByRes = new PropagationByResource<>();
244             propByRes.addAll(ResourceOperation.CREATE, realm.getResourceKeys());
245             if (unmatchingRule == UnmatchingRule.ASSIGN) {
246                 List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
247                 taskExecutor.execute(taskInfos, false, securityProperties.getAdminUser());
248             }
249 
250             RealmTO actual = binder.getRealmTO(realm, true);
251 
252             result.setKey(actual.getKey());
253             result.setName(profile.getTask().getDestinationRealm().getFullPath() + '/' + actual.getName());
254 
255             output = actual;
256             resultStatus = Result.SUCCESS;
257 
258             for (PullActions action : profile.getActions()) {
259                 action.after(profile, delta, actual, result);
260             }
261 
262             LOG.debug("Realm {} successfully created", actual.getKey());
263         } catch (PropagationException e) {
264             // A propagation failure doesn't imply a pull failure.
265             // The propagation exception status will be reported into the propagation task execution.
266             LOG.error("Could not propagate Realm {}", delta.getUid().getUidValue(), e);
267             output = e;
268             resultStatus = Result.FAILURE;
269         } catch (Exception e) {
270             throwIgnoreProvisionException(delta, e);
271 
272             result.setStatus(ProvisioningReport.Status.FAILURE);
273             result.setMessage(ExceptionUtils.getRootCauseMessage(e));
274             LOG.error("Could not create Realm {} ", delta.getUid().getUidValue(), e);
275             output = e;
276             resultStatus = Result.FAILURE;
277         }
278 
279         end(UnmatchingRule.toEventName(unmatchingRule), resultStatus, null, output, delta);
280         return resultStatus;
281     }
282 
283     protected Result update(final SyncDelta delta, final List<Realm> realms, final boolean inLink)
284             throws JobExecutionException {
285 
286         if (!profile.getTask().isPerformUpdate()) {
287             LOG.debug("PullTask not configured for update");
288             end(MatchingRule.toEventName(MatchingRule.UPDATE), Result.SUCCESS, null, null, delta);
289             return Result.SUCCESS;
290         }
291 
292         LOG.debug("About to update {}", realms);
293 
294         Result global = Result.SUCCESS;
295         for (Realm realm : realms) {
296             LOG.debug("About to update {}", realm);
297 
298             ProvisioningReport result = new ProvisioningReport();
299             result.setOperation(ResourceOperation.UPDATE);
300             result.setAnyType(SyncopeConstants.REALM_ANYTYPE);
301             result.setStatus(ProvisioningReport.Status.SUCCESS);
302             result.setKey(realm.getKey());
303             result.setName(realm.getFullPath());
304 
305             if (!profile.isDryRun()) {
306                 Result resultStatus;
307                 Object output;
308 
309                 RealmTO before = binder.getRealmTO(realm, true);
310                 try {
311                     if (!inLink) {
312                         for (PullActions action : profile.getActions()) {
313                             action.beforeUpdate(profile, delta, before, null);
314                         }
315                     }
316 
317                     Map<Pair<String, String>, Set<Attribute>> beforeAttrs = propagationManager.prepareAttrs(realm);
318 
319                     PropagationByResource<String> propByRes = binder.update(realm, before);
320                     realm = realmDAO.save(realm);
321                     RealmTO updated = binder.getRealmTO(realm, true);
322 
323                     List<PropagationTaskInfo> taskInfos = propagationManager.setAttributeDeltas(
324                             propagationManager.createTasks(realm, propByRes, null),
325                             beforeAttrs,
326                             null);
327                     taskExecutor.execute(taskInfos, false, securityProperties.getAdminUser());
328 
329                     for (PullActions action : profile.getActions()) {
330                         action.after(profile, delta, updated, result);
331                     }
332 
333                     output = updated;
334                     resultStatus = Result.SUCCESS;
335                     result.setName(updated.getFullPath());
336 
337                     LOG.debug("{} successfully updated", updated);
338                 } catch (PropagationException e) {
339                     // A propagation failure doesn't imply a pull failure.
340                     // The propagation exception status will be reported into the propagation task execution.
341                     LOG.error("Could not propagate Realm {}", delta.getUid().getUidValue(), e);
342                     output = e;
343                     resultStatus = Result.FAILURE;
344                 } catch (Exception e) {
345                     throwIgnoreProvisionException(delta, e);
346 
347                     result.setStatus(ProvisioningReport.Status.FAILURE);
348                     result.setMessage(ExceptionUtils.getRootCauseMessage(e));
349                     LOG.error("Could not update Realm {}", delta.getUid().getUidValue(), e);
350                     output = e;
351                     resultStatus = Result.FAILURE;
352                 }
353 
354                 end(MatchingRule.toEventName(MatchingRule.UPDATE), resultStatus, before, output, delta);
355                 global = and(global, resultStatus);
356             }
357 
358             profile.getResults().add(result);
359         }
360 
361         return global;
362     }
363 
364     protected Result deprovision(final SyncDelta delta, final List<Realm> realms, final boolean unlink)
365             throws JobExecutionException {
366 
367         if (!profile.getTask().isPerformUpdate()) {
368             LOG.debug("PullTask not configured for update");
369             end(unlink
370                     ? MatchingRule.toEventName(MatchingRule.UNASSIGN)
371                     : MatchingRule.toEventName(MatchingRule.DEPROVISION), Result.SUCCESS, null, null, delta);
372             return Result.SUCCESS;
373         }
374 
375         LOG.debug("About to deprovision {}", realms);
376 
377         Result global = Result.SUCCESS;
378         for (Realm realm : realms) {
379             LOG.debug("About to unassign resource {}", realm);
380 
381             ProvisioningReport result = new ProvisioningReport();
382             result.setOperation(ResourceOperation.DELETE);
383             result.setAnyType(SyncopeConstants.REALM_ANYTYPE);
384             result.setStatus(ProvisioningReport.Status.SUCCESS);
385             result.setKey(realm.getKey());
386             result.setName(realm.getFullPath());
387 
388             if (!profile.isDryRun()) {
389                 Object output;
390                 Result resultStatus;
391 
392                 RealmTO before = binder.getRealmTO(realm, true);
393                 try {
394                     if (unlink) {
395                         for (PullActions action : profile.getActions()) {
396                             action.beforeUnassign(profile, delta, before);
397                         }
398                     } else {
399                         for (PullActions action : profile.getActions()) {
400                             action.beforeDeprovision(profile, delta, before);
401                         }
402                     }
403 
404                     PropagationByResource<String> propByRes = new PropagationByResource<>();
405                     propByRes.add(ResourceOperation.DELETE, profile.getTask().getResource().getKey());
406                     taskExecutor.execute(
407                             propagationManager.createTasks(realm, propByRes, null),
408                             false, securityProperties.getAdminUser());
409 
410                     RealmTO realmTO;
411                     if (unlink) {
412                         realm.getResources().remove(profile.getTask().getResource());
413                         realmTO = binder.getRealmTO(realmDAO.save(realm), true);
414                     } else {
415                         realmTO = binder.getRealmTO(realm, true);
416                     }
417                     output = realmTO;
418 
419                     for (PullActions action : profile.getActions()) {
420                         action.after(profile, delta, realmTO, result);
421                     }
422 
423                     resultStatus = Result.SUCCESS;
424 
425                     LOG.debug("{} successfully updated", realm);
426                 } catch (PropagationException e) {
427                     // A propagation failure doesn't imply a pull failure.
428                     // The propagation exception status will be reported into the propagation task execution.
429                     LOG.error("Could not propagate Realm {}", delta.getUid().getUidValue(), e);
430                     output = e;
431                     resultStatus = Result.FAILURE;
432                 } catch (Exception e) {
433                     throwIgnoreProvisionException(delta, e);
434 
435                     result.setStatus(ProvisioningReport.Status.FAILURE);
436                     result.setMessage(ExceptionUtils.getRootCauseMessage(e));
437                     LOG.error("Could not update Realm {}", delta.getUid().getUidValue(), e);
438                     output = e;
439                     resultStatus = Result.FAILURE;
440                 }
441 
442                 end(unlink
443                         ? MatchingRule.toEventName(MatchingRule.UNASSIGN)
444                         : MatchingRule.toEventName(MatchingRule.DEPROVISION),
445                         resultStatus, before, output, delta);
446                 global = and(global, resultStatus);
447             }
448 
449             profile.getResults().add(result);
450         }
451 
452         return global;
453     }
454 
455     protected Result link(final SyncDelta delta, final List<Realm> realms, final boolean unlink)
456             throws JobExecutionException {
457 
458         if (!profile.getTask().isPerformUpdate()) {
459             LOG.debug("PullTask not configured for update");
460             end(unlink
461                     ? MatchingRule.toEventName(MatchingRule.UNLINK)
462                     : MatchingRule.toEventName(MatchingRule.LINK), Result.SUCCESS, null, null, delta);
463             return Result.SUCCESS;
464         }
465 
466         LOG.debug("About to link {}", realms);
467 
468         Result global = Result.SUCCESS;
469         for (Realm realm : realms) {
470             LOG.debug("About to unassign resource {}", realm);
471 
472             ProvisioningReport result = new ProvisioningReport();
473             result.setOperation(ResourceOperation.NONE);
474             result.setAnyType(SyncopeConstants.REALM_ANYTYPE);
475             result.setStatus(ProvisioningReport.Status.SUCCESS);
476             result.setKey(realm.getKey());
477             result.setName(realm.getFullPath());
478 
479             if (!profile.isDryRun()) {
480                 Object output;
481                 Result resultStatus;
482 
483                 RealmTO before = binder.getRealmTO(realm, true);
484                 try {
485                     if (unlink) {
486                         for (PullActions action : profile.getActions()) {
487                             action.beforeUnlink(profile, delta, before);
488                         }
489                     } else {
490                         for (PullActions action : profile.getActions()) {
491                             action.beforeLink(profile, delta, before);
492                         }
493                     }
494 
495                     if (unlink) {
496                         realm.getResources().remove(profile.getTask().getResource());
497                     } else {
498                         realm.add(profile.getTask().getResource());
499                     }
500                     output = update(delta, List.of(realm), true);
501 
502                     resultStatus = Result.SUCCESS;
503 
504                     LOG.debug("{} successfully updated", realm);
505                 } catch (PropagationException e) {
506                     // A propagation failure doesn't imply a pull failure.
507                     // The propagation exception status will be reported into the propagation task execution.
508                     LOG.error("Could not propagate Realm {}", delta.getUid().getUidValue(), e);
509                     output = e;
510                     resultStatus = Result.FAILURE;
511                 } catch (Exception e) {
512                     throwIgnoreProvisionException(delta, e);
513 
514                     result.setStatus(ProvisioningReport.Status.FAILURE);
515                     result.setMessage(ExceptionUtils.getRootCauseMessage(e));
516                     LOG.error("Could not update Realm {}", delta.getUid().getUidValue(), e);
517                     output = e;
518                     resultStatus = Result.FAILURE;
519                 }
520 
521                 end(unlink
522                         ? MatchingRule.toEventName(MatchingRule.UNLINK)
523                         : MatchingRule.toEventName(MatchingRule.LINK),
524                         resultStatus, before, output, delta);
525                 global = and(global, resultStatus);
526             }
527 
528             profile.getResults().add(result);
529         }
530 
531         return global;
532     }
533 
534     protected Result delete(final SyncDelta delta, final List<Realm> realms)
535             throws JobExecutionException {
536 
537         if (!profile.getTask().isPerformDelete()) {
538             LOG.debug("PullTask not configured for delete");
539             end(ResourceOperation.DELETE.name().toLowerCase(), Result.SUCCESS, null, null, delta);
540             return Result.SUCCESS;
541         }
542 
543         LOG.debug("About to delete {}", realms);
544 
545         Result global = Result.SUCCESS;
546         for (Realm realm : realms) {
547             Object output;
548             Result resultStatus = Result.FAILURE;
549 
550             ProvisioningReport result = new ProvisioningReport();
551 
552             RealmTO before = binder.getRealmTO(realm, true);
553             try {
554                 result.setKey(realm.getKey());
555                 result.setName(realm.getFullPath());
556                 result.setOperation(ResourceOperation.DELETE);
557                 result.setAnyType(SyncopeConstants.REALM_ANYTYPE);
558                 result.setStatus(ProvisioningReport.Status.SUCCESS);
559 
560                 if (!profile.isDryRun()) {
561                     for (PullActions action : profile.getActions()) {
562                         action.beforeDelete(profile, delta, before);
563                     }
564 
565                     try {
566                         if (!realmDAO.findChildren(realm).isEmpty()) {
567                             throw SyncopeClientException.build(ClientExceptionType.RealmContains);
568                         }
569 
570                         Set<String> adminRealms = Set.of(realm.getFullPath());
571                         AnyCond keyCond = new AnyCond(AttrCond.Type.ISNOTNULL);
572                         keyCond.setSchema("key");
573                         SearchCond allMatchingCond = SearchCond.getLeaf(keyCond);
574                         int users = searchDAO.count(
575                                 realmDAO.getRoot(), true, adminRealms, allMatchingCond, AnyTypeKind.USER);
576                         int groups = searchDAO.count(
577                                 realmDAO.getRoot(), true, adminRealms, allMatchingCond, AnyTypeKind.GROUP);
578                         int anyObjects = searchDAO.count(
579                                 realmDAO.getRoot(), true, adminRealms, allMatchingCond, AnyTypeKind.ANY_OBJECT);
580                         int macroTasks = taskDAO.findByRealm(realm).size();
581                         int clientApps = casSPClientAppDAO.findByRealm(realm).size()
582                                 + saml2SPClientAppDAO.findByRealm(realm).size()
583                                 + oidcRPClientAppDAO.findByRealm(realm).size();
584 
585                         if (users + groups + anyObjects + macroTasks + clientApps > 0) {
586                             SyncopeClientException realmContains =
587                                     SyncopeClientException.build(ClientExceptionType.RealmContains);
588                             realmContains.getElements().add(users + " user(s)");
589                             realmContains.getElements().add(groups + " group(s)");
590                             realmContains.getElements().add(anyObjects + " anyObject(s)");
591                             realmContains.getElements().add(macroTasks + " command task(s)");
592                             realmContains.getElements().add(clientApps + " client app(s)");
593                             throw realmContains;
594                         }
595 
596                         PropagationByResource<String> propByRes = new PropagationByResource<>();
597                         propByRes.addAll(ResourceOperation.DELETE, realm.getResourceKeys());
598                         List<PropagationTaskInfo> taskInfos = propagationManager.createTasks(realm, propByRes, null);
599                         taskExecutor.execute(taskInfos, false, securityProperties.getAdminUser());
600 
601                         realmDAO.delete(realm);
602 
603                         output = null;
604                         resultStatus = Result.SUCCESS;
605 
606                         for (PullActions action : profile.getActions()) {
607                             action.after(profile, delta, before, result);
608                         }
609                     } catch (Exception e) {
610                         throwIgnoreProvisionException(delta, e);
611 
612                         result.setStatus(ProvisioningReport.Status.FAILURE);
613                         result.setMessage(ExceptionUtils.getRootCauseMessage(e));
614                         LOG.error("Could not delete {}", realm, e);
615                         output = e;
616                     }
617 
618                     end(ResourceOperation.DELETE.name().toLowerCase(), resultStatus, before, output, delta);
619                     global = and(global, resultStatus);
620                 }
621 
622                 profile.getResults().add(result);
623             } catch (DelegatedAdministrationException e) {
624                 LOG.error("Not allowed to read Realm {}", realm, e);
625             } catch (Exception e) {
626                 LOG.error("Could not delete Realm {}", realm, e);
627             }
628         }
629 
630         return global;
631     }
632 
633     protected Result ignore(final SyncDelta delta, final boolean matching) throws JobExecutionException {
634         LOG.debug("Any to ignore {}", delta.getObject().getUid().getUidValue());
635 
636         ProvisioningReport report = new ProvisioningReport();
637         report.setKey(null);
638         report.setName(delta.getObject().getUid().getUidValue());
639         report.setOperation(ResourceOperation.NONE);
640         report.setAnyType(SyncopeConstants.REALM_ANYTYPE);
641         report.setStatus(ProvisioningReport.Status.SUCCESS);
642         profile.getResults().add(report);
643 
644         if (!profile.isDryRun()) {
645             end(matching
646                     ? MatchingRule.toEventName(MatchingRule.IGNORE)
647                     : UnmatchingRule.toEventName(UnmatchingRule.IGNORE), Result.SUCCESS, null, null, delta);
648             return Result.SUCCESS;
649         }
650 
651         return Result.SUCCESS;
652     }
653 
654     protected Result doHandle(final SyncDelta delta, final OrgUnit orgUnit) throws JobExecutionException {
655         LOG.debug("Process {} for {} as {}",
656                 delta.getDeltaType(), delta.getUid().getUidValue(), delta.getObject().getObjectClass());
657 
658         SyncDelta finalDelta = delta;
659         for (PullActions action : profile.getActions()) {
660             finalDelta = action.preprocess(profile, finalDelta);
661         }
662 
663         LOG.debug("Transformed {} for {} as {}",
664                 finalDelta.getDeltaType(), finalDelta.getUid().getUidValue(), finalDelta.getObject().getObjectClass());
665 
666         List<Realm> realms = inboundMatcher.match(finalDelta, orgUnit);
667         LOG.debug("Match found for {} as {}: {}",
668                 finalDelta.getUid().getUidValue(), finalDelta.getObject().getObjectClass(), realms);
669 
670         if (realms.size() > 1) {
671             switch (profile.getConflictResolutionAction()) {
672                 case IGNORE:
673                     throw new IgnoreProvisionException("More than one match found for "
674                             + finalDelta.getObject().getUid().getUidValue() + ": " + realms);
675 
676                 case FIRSTMATCH:
677                     realms = realms.subList(0, 1);
678                     break;
679 
680                 case LASTMATCH:
681                     realms = realms.subList(realms.size() - 1, realms.size());
682                     break;
683 
684                 default:
685                 // keep keys unmodified
686                 }
687         }
688 
689         Result result = Result.SUCCESS;
690         try {
691             switch (delta.getDeltaType()) {
692                 case CREATE:
693                 case UPDATE:
694                 case CREATE_OR_UPDATE:
695                     if (realms.isEmpty()) {
696                         switch (profile.getTask().getUnmatchingRule()) {
697                             case ASSIGN:
698                                 result = assign(finalDelta, orgUnit);
699                                 break;
700 
701                             case PROVISION:
702                                 result = provision(finalDelta, orgUnit);
703                                 break;
704 
705                             case IGNORE:
706                                 result = ignore(finalDelta, false);
707                                 break;
708 
709                             default:
710                             // do nothing
711                         }
712                     } else {
713                         switch (profile.getTask().getMatchingRule()) {
714                             case UPDATE:
715                                 result = update(finalDelta, realms, false);
716                                 break;
717 
718                             case DEPROVISION:
719                                 result = deprovision(finalDelta, realms, false);
720                                 break;
721 
722                             case UNASSIGN:
723                                 result = deprovision(finalDelta, realms, true);
724                                 break;
725 
726                             case LINK:
727                                 result = link(finalDelta, realms, false);
728                                 break;
729 
730                             case UNLINK:
731                                 result = link(finalDelta, realms, true);
732                                 break;
733 
734                             case IGNORE:
735                                 result = ignore(finalDelta, true);
736                                 break;
737 
738                             default:
739                             // do nothing
740                         }
741                     }
742                     break;
743 
744                 case DELETE:
745                     if (realms.isEmpty()) {
746                         end(ResourceOperation.DELETE.name().toLowerCase(), Result.SUCCESS, null, null, finalDelta);
747                         LOG.debug("No match found for deletion");
748                     } else {
749                         result = delete(finalDelta, realms);
750                     }
751                     break;
752 
753                 default:
754             }
755         } catch (IllegalStateException | IllegalArgumentException e) {
756             LOG.warn(e.getMessage());
757         }
758 
759         return result;
760     }
761 
762     protected void end(
763             final String event,
764             final Result result,
765             final Object before,
766             final Object output,
767             final SyncDelta delta) {
768 
769         notificationManager.createTasks(
770                 AuthContextUtils.getWho(),
771                 AuditElements.EventCategoryType.PULL,
772                 SyncopeConstants.REALM_ANYTYPE.toLowerCase(),
773                 profile.getTask().getResource().getKey(),
774                 event,
775                 result,
776                 before,
777                 output,
778                 delta);
779 
780         auditManager.audit(
781                 AuthContextUtils.getWho(),
782                 AuditElements.EventCategoryType.PULL,
783                 SyncopeConstants.REALM_ANYTYPE.toLowerCase(),
784                 profile.getTask().getResource().getKey(),
785                 event,
786                 result,
787                 before,
788                 output,
789                 delta);
790     }
791 }