/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.syncope.core.provisioning.java.pushpull; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.IteratorUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.syncope.common.lib.patch.AnyPatch; import org.apache.syncope.common.lib.patch.StringPatchItem; import org.apache.syncope.common.lib.to.AnyTO; import org.apache.syncope.common.lib.types.AuditElements; import org.apache.syncope.common.lib.types.AuditElements.Result; import org.apache.syncope.common.lib.types.MatchingRule; import org.apache.syncope.common.lib.types.PatchOperation; import org.apache.syncope.core.provisioning.api.PropagationByResource; import org.apache.syncope.common.lib.types.ResourceOperation; import org.apache.syncope.common.lib.types.UnmatchingRule; import org.apache.syncope.core.persistence.api.entity.task.PushTask; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport; import org.apache.syncope.core.provisioning.api.pushpull.PushActions; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.AnyUtils; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.resource.MappingItem; import org.apache.syncope.core.persistence.api.entity.resource.Provision; import org.apache.syncope.core.provisioning.api.MappingManager; import org.apache.syncope.core.provisioning.api.TimeoutException; import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException; import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler; import org.apache.syncope.core.provisioning.api.utils.EntityUtils; import org.apache.syncope.core.provisioning.java.utils.MappingUtils; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.Uid; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHandler<PushTask, PushActions> implements SyncopePushResultHandler { @Autowired protected MappingManager mappingManager; protected abstract String getName(Any<?> any); protected void deprovision(final Any<?> any) { AnyTO before = getAnyTO(any.getKey()); List<String> noPropResources = new ArrayList<>(before.getResources()); noPropResources.remove(profile.getTask().getResource().getKey()); taskExecutor.execute(propagationManager.getDeleteTasks( any.getType().getKind(), any.getKey(), null, noPropResources)); } protected void provision(final Any<?> any, final Boolean enabled) { AnyTO before = getAnyTO(any.getKey()); List<String> noPropResources = new ArrayList<>(before.getResources()); noPropResources.remove(profile.getTask().getResource().getKey()); PropagationByResource propByRes = new PropagationByResource(); propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey()); taskExecutor.execute(propagationManager.getCreateTasks( any.getType().getKind(), any.getKey(), propByRes, before.getVirAttrs(), noPropResources)); } protected void link(final Any<?> any, final Boolean unlink) { AnyPatch patch = newPatch(any.getKey()); patch.getResources().add(new StringPatchItem.Builder(). operation(unlink ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE). value(profile.getTask().getResource().getKey()).build()); update(patch); } protected void unassign(final Any<?> any) { AnyPatch patch = newPatch(any.getKey()); patch.getResources().add(new StringPatchItem.Builder(). operation(PatchOperation.DELETE). value(profile.getTask().getResource().getKey()).build()); update(patch); deprovision(any); } protected void assign(final Any<?> any, final Boolean enabled) { AnyPatch patch = newPatch(any.getKey()); patch.getResources().add(new StringPatchItem.Builder(). operation(PatchOperation.ADD_REPLACE). value(profile.getTask().getResource().getKey()).build()); update(patch); provision(any, enabled); } protected ConnectorObject getRemoteObject(final String connObjectKey, final ObjectClass objectClass) { ConnectorObject obj = null; try { Uid uid = new Uid(connObjectKey); obj = profile.getConnector().getObject(objectClass, uid, MappingUtils.buildOperationOptions(IteratorUtils.<MappingItem>emptyIterator())); } catch (TimeoutException toe) { LOG.debug("Request timeout", toe); throw toe; } catch (RuntimeException ignore) { LOG.debug("While resolving {}", connObjectKey, ignore); } return obj; } @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public boolean handle(final String anyKey) { Any<?> any = null; try { any = getAny(anyKey); doHandle(any); return true; } catch (IgnoreProvisionException e) { ProvisioningReport result = new ProvisioningReport(); result.setOperation(ResourceOperation.NONE); result.setAnyType(any == null ? null : any.getType().getKey()); result.setStatus(ProvisioningReport.Status.IGNORE); result.setKey(anyKey); profile.getResults().add(result); LOG.warn("Ignoring during push", e); return true; } catch (JobExecutionException e) { LOG.error("Push failed", e); return false; } } private void doHandle(final Any<?> any) throws JobExecutionException { AnyUtils anyUtils = anyUtilsFactory.getInstance(any); ProvisioningReport result = new ProvisioningReport(); profile.getResults().add(result); result.setKey(any.getKey()); result.setAnyType(any.getType().getKey()); result.setName(getName(any)); Boolean enabled = any instanceof User && profile.getTask().isSyncStatus() ? ((User) any).isSuspended() ? Boolean.FALSE : Boolean.TRUE : null; LOG.debug("Propagating {} with key {} towards {}", anyUtils.getAnyTypeKind(), any.getKey(), profile.getTask().getResource()); Object output = null; Result resultStatus = null; String operation = null; // Try to read remote object BEFORE any actual operation Provision provision = profile.getTask().getResource().getProvision(any.getType()); String connObjecKey = mappingManager.getConnObjectKeyValue(any, provision); ConnectorObject beforeObj = getRemoteObject(connObjecKey, provision.getObjectClass()); Boolean status = profile.getTask().isSyncStatus() ? enabled : null; if (profile.isDryRun()) { if (beforeObj == null) { result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule())); } else { result.setOperation(getResourceOperation(profile.getTask().getMatchingRule())); } result.setStatus(ProvisioningReport.Status.SUCCESS); } else { try { if (beforeObj == null) { operation = UnmatchingRule.toEventName(profile.getTask().getUnmatchingRule()); result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule())); switch (profile.getTask().getUnmatchingRule()) { case ASSIGN: for (PushActions action : profile.getActions()) { action.beforeAssign(profile, any); } if (!profile.getTask().isPerformCreate()) { LOG.debug("PushTask not configured for create"); } else { assign(any, status); } break; case PROVISION: for (PushActions action : profile.getActions()) { action.beforeProvision(profile, any); } if (!profile.getTask().isPerformCreate()) { LOG.debug("PushTask not configured for create"); } else { provision(any, status); } break; case UNLINK: for (PushActions action : profile.getActions()) { action.beforeUnlink(profile, any); } if (!profile.getTask().isPerformUpdate()) { LOG.debug("PushTask not configured for update"); } else { link(any, true); } break; case IGNORE: LOG.debug("Ignored any: {}", any); break; default: // do nothing } } else { operation = MatchingRule.toEventName(profile.getTask().getMatchingRule()); result.setOperation(getResourceOperation(profile.getTask().getMatchingRule())); switch (profile.getTask().getMatchingRule()) { case UPDATE: for (PushActions action : profile.getActions()) { action.beforeUpdate(profile, any); } if (!profile.getTask().isPerformUpdate()) { LOG.debug("PushTask not configured for update"); } else { update(any); } break; case DEPROVISION: for (PushActions action : profile.getActions()) { action.beforeDeprovision(profile, any); } if (!profile.getTask().isPerformDelete()) { LOG.debug("PushTask not configured for delete"); } else { deprovision(any); } break; case UNASSIGN: for (PushActions action : profile.getActions()) { action.beforeUnassign(profile, any); } if (!profile.getTask().isPerformDelete()) { LOG.debug("PushTask not configured for delete"); } else { unassign(any); } break; case LINK: for (PushActions action : profile.getActions()) { action.beforeLink(profile, any); } if (!profile.getTask().isPerformUpdate()) { LOG.debug("PushTask not configured for update"); } else { link(any, false); } break; case UNLINK: for (PushActions action : profile.getActions()) { action.beforeUnlink(profile, any); } if (!profile.getTask().isPerformUpdate()) { LOG.debug("PushTask not configured for update"); } else { link(any, true); } break; case IGNORE: LOG.debug("Ignored any: {}", any); break; default: // do nothing } } for (PushActions action : profile.getActions()) { action.after(profile, any, result); } result.setStatus(ProvisioningReport.Status.SUCCESS); resultStatus = AuditElements.Result.SUCCESS; output = getRemoteObject(connObjecKey, provision.getObjectClass()); } catch (IgnoreProvisionException e) { throw e; } catch (Exception e) { result.setStatus(ProvisioningReport.Status.FAILURE); result.setMessage(ExceptionUtils.getRootCauseMessage(e)); resultStatus = AuditElements.Result.FAILURE; output = e; LOG.warn("Error pushing {} towards {}", any, profile.getTask().getResource(), e); for (PushActions action : profile.getActions()) { action.onError(profile, any, result, e); } throw new JobExecutionException(e); } finally { notificationManager.createTasks(AuditElements.EventCategoryType.PUSH, any.getType().getKind().name().toLowerCase(), profile.getTask().getResource().getKey(), operation, resultStatus, beforeObj, output, any); auditManager.audit(AuditElements.EventCategoryType.PUSH, any.getType().getKind().name().toLowerCase(), profile.getTask().getResource().getKey(), operation, resultStatus, beforeObj, output, any); } } } private ResourceOperation getResourceOperation(final UnmatchingRule rule) { switch (rule) { case ASSIGN: case PROVISION: return ResourceOperation.CREATE; default: return ResourceOperation.NONE; } } private ResourceOperation getResourceOperation(final MatchingRule rule) { switch (rule) { case UPDATE: return ResourceOperation.UPDATE; case DEPROVISION: case UNASSIGN: return ResourceOperation.DELETE; default: return ResourceOperation.NONE; } } private Any<?> update(final Any<?> any) { boolean changepwd; Collection<String> resourceKeys; if (any instanceof User) { changepwd = true; resourceKeys = CollectionUtils.collect( userDAO.findAllResources((User) any), EntityUtils.keyTransformer()); } else if (any instanceof AnyObject) { changepwd = false; resourceKeys = CollectionUtils.collect( anyObjectDAO.findAllResources((AnyObject) any), EntityUtils.keyTransformer()); } else { changepwd = false; resourceKeys = ((Group) any).getResourceKeys(); } List<String> noPropResources = new ArrayList<>(resourceKeys); noPropResources.remove(profile.getTask().getResource().getKey()); PropagationByResource propByRes = new PropagationByResource(); propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey()); taskExecutor.execute(propagationManager.getUpdateTasks( any.getType().getKind(), any.getKey(), changepwd, null, propByRes, null, noPropResources)); return getAny(any.getKey()); } }