You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/01/12 17:31:45 UTC
[06/52] [abbrv] [partial] syncope git commit: [SYNCOPE-620] Unit
tests all in
http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/PropagationManagerImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/PropagationManagerImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/PropagationManagerImpl.java
new file mode 100644
index 0000000..cf4f309
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/PropagationManagerImpl.java
@@ -0,0 +1,772 @@
+/*
+ * 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.server.provisioning.java.propagation;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.mod.MembershipMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.server.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.server.persistence.api.dao.NotFoundException;
+import org.apache.syncope.server.persistence.api.dao.RoleDAO;
+import org.apache.syncope.server.persistence.api.dao.UserDAO;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtilFactory;
+import org.apache.syncope.server.persistence.api.entity.EntityFactory;
+import org.apache.syncope.server.persistence.api.entity.ExternalResource;
+import org.apache.syncope.server.persistence.api.entity.MappingItem;
+import org.apache.syncope.server.persistence.api.entity.Subject;
+import org.apache.syncope.server.persistence.api.entity.VirAttr;
+import org.apache.syncope.server.persistence.api.entity.membership.Membership;
+import org.apache.syncope.server.persistence.api.entity.role.Role;
+import org.apache.syncope.server.persistence.api.entity.task.PropagationTask;
+import org.apache.syncope.server.persistence.api.entity.user.User;
+import org.apache.syncope.server.provisioning.api.WorkflowResult;
+import org.apache.syncope.server.provisioning.api.propagation.PropagationManager;
+import org.apache.syncope.server.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.server.provisioning.java.VirAttrHandler;
+import org.apache.syncope.server.misc.security.UnauthorizedRoleException;
+import org.apache.syncope.server.misc.ConnObjectUtil;
+import org.apache.syncope.server.misc.MappingUtil;
+import org.apache.syncope.server.misc.jexl.JexlUtil;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.AttributeUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Manage the data propagation to external resources.
+ */
+@Transactional(rollbackFor = { Throwable.class })
+public class PropagationManagerImpl implements PropagationManager {
+
+ /**
+ * Logger.
+ */
+ protected static final Logger LOG = LoggerFactory.getLogger(PropagationManager.class);
+
+ /**
+ * User DAO.
+ */
+ @Autowired
+ protected UserDAO userDAO;
+
+ /**
+ * Role DAO.
+ */
+ @Autowired
+ protected RoleDAO roleDAO;
+
+ /**
+ * Resource DAO.
+ */
+ @Autowired
+ protected ExternalResourceDAO resourceDAO;
+
+ @Autowired
+ protected EntityFactory entityFactory;
+
+ /**
+ * ConnObjectUtil.
+ */
+ @Autowired
+ protected ConnObjectUtil connObjectUtil;
+
+ @Autowired
+ protected AttributableUtilFactory attrUtilFactory;
+
+ @Autowired
+ protected VirAttrHandler virAttrHandler;
+
+ /**
+ * Create the user on every associated resource.
+ *
+ * @param wfResult user to be propagated (and info associated), as per result from workflow
+ * @param password to be set
+ * @param vAttrs virtual attributes to be set
+ * @param membershipTOs user memberships
+ * @return list of propagation tasks
+ * @throws NotFoundException if user is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given user
+ */
+ @Override
+ public List<PropagationTask> getUserCreateTaskIds(final WorkflowResult<Map.Entry<Long, Boolean>> wfResult,
+ final String password, final List<AttrTO> vAttrs, final List<MembershipTO> membershipTOs)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ return getUserCreateTaskIds(wfResult, password, vAttrs, null, membershipTOs);
+ }
+
+ /**
+ * Create the user on every associated resource.
+ *
+ * @param wfResult user to be propagated (and info associated), as per result from workflow
+ * @param password to be set
+ * @param vAttrs virtual attributes to be set
+ * @param noPropResourceNames external resources not to be considered for propagation
+ * @param membershipTOs user memberships
+ * @return list of propagation tasks
+ * @throws NotFoundException if user is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given user
+ */
+ @Override
+ public List<PropagationTask> getUserCreateTaskIds(final WorkflowResult<Map.Entry<Long, Boolean>> wfResult,
+ final String password, final Collection<AttrTO> vAttrs,
+ final Set<String> noPropResourceNames, final List<MembershipTO> membershipTOs)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ return getUserCreateTaskIds(
+ wfResult.getResult().getKey(),
+ wfResult.getResult().getValue(),
+ wfResult.getPropByRes(),
+ password,
+ vAttrs,
+ membershipTOs,
+ noPropResourceNames);
+ }
+
+ @Override
+ public List<PropagationTask> getUserCreateTaskIds(
+ final Long key,
+ final Boolean enabled,
+ final PropagationByResource propByRes,
+ final String password,
+ final Collection<AttrTO> vAttrs,
+ final Collection<MembershipTO> membershipTOs,
+ final Collection<String> noPropResourceNames)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ User user = userDAO.authFetch(key);
+ if (vAttrs != null && !vAttrs.isEmpty()) {
+ virAttrHandler.fillVirtual(user, vAttrs, attrUtilFactory.getInstance(AttributableType.USER));
+
+ }
+ for (Membership membership : user.getMemberships()) {
+ MembershipTO membershipTO;
+ if (membership.getVirAttrs() != null && !membership.getVirAttrs().isEmpty()) {
+ membershipTO = findMembershipTO(membership, membershipTOs);
+ if (membershipTO != null) {
+ virAttrHandler.fillVirtual(membership,
+ membershipTO.getVirAttrs(), attrUtilFactory.getInstance(AttributableType.MEMBERSHIP));
+ }
+ }
+ }
+ return getCreateTaskIds(user, password, enabled, propByRes, noPropResourceNames);
+ }
+
+ /**
+ * Create the role on every associated resource.
+ *
+ * @param wfResult user to be propagated (and info associated), as per result from workflow
+ * @param vAttrs virtual attributes to be set
+ * @return list of propagation tasks
+ * @throws NotFoundException if role is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
+ */
+ @Override
+ public List<PropagationTask> getRoleCreateTaskIds(final WorkflowResult<Long> wfResult, final List<AttrTO> vAttrs)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ return getRoleCreateTaskIds(wfResult, vAttrs, null);
+ }
+
+ /**
+ * Create the role on every associated resource.
+ *
+ * @param wfResult role to be propagated (and info associated), as per result from workflow
+ * @param vAttrs virtual attributes to be set
+ * @param noPropResourceNames external resources performing not to be considered for propagation
+ * @return list of propagation tasks
+ * @throws NotFoundException if role is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
+ */
+ @Override
+ public List<PropagationTask> getRoleCreateTaskIds(
+ final WorkflowResult<Long> wfResult,
+ final Collection<AttrTO> vAttrs,
+ final Collection<String> noPropResourceNames)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ return getRoleCreateTaskIds(wfResult.getResult(), vAttrs, wfResult.getPropByRes(), noPropResourceNames);
+ }
+
+ /**
+ * Create the role on every associated resource.
+ *
+ * @param key role key
+ * @param vAttrs virtual attributes to be set
+ * @param propByRes operation to be performed per resource
+ * @param noPropResourceNames external resources performing not to be considered for propagation
+ * @return list of propagation tasks
+ * @throws NotFoundException if role is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
+ */
+ @Override
+ public List<PropagationTask> getRoleCreateTaskIds(
+ final Long key,
+ final Collection<AttrTO> vAttrs,
+ final PropagationByResource propByRes,
+ final Collection<String> noPropResourceNames)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ Role role = roleDAO.authFetch(key);
+ if (vAttrs != null && !vAttrs.isEmpty()) {
+ virAttrHandler.fillVirtual(role, vAttrs, attrUtilFactory.getInstance(AttributableType.ROLE));
+ }
+
+ return getCreateTaskIds(role, null, null, propByRes, noPropResourceNames);
+ }
+
+ protected List<PropagationTask> getCreateTaskIds(final Subject<?, ?, ?> subject,
+ final String password, final Boolean enable,
+ final PropagationByResource propByRes,
+ final Collection<String> noPropResourceNames) {
+
+ if (propByRes == null || propByRes.isEmpty()) {
+ return Collections.<PropagationTask>emptyList();
+ }
+
+ if (noPropResourceNames != null) {
+ propByRes.get(ResourceOperation.CREATE).removeAll(noPropResourceNames);
+ }
+
+ return createTasks(subject, password, true, null, null, null, null, enable, false, propByRes);
+ }
+
+ /**
+ * Performs update on each resource associated to the user excluding the specified into 'resourceNames' parameter.
+ *
+ * @param user to be propagated
+ * @param enable whether user must be enabled or not
+ * @param noPropResourceNames external resource names not to be considered for propagation
+ * @return list of propagation tasks
+ * @throws NotFoundException if user is not found
+ */
+ @Override
+ public List<PropagationTask> getUserUpdateTaskIds(final User user, final Boolean enable,
+ final Set<String> noPropResourceNames) throws NotFoundException {
+
+ return getUpdateTaskIds(
+ user, // SyncopeUser to be updated on external resources
+ null, // no password
+ false,
+ enable, // status to be propagated
+ Collections.<String>emptySet(), // no virtual attributes to be managed
+ Collections.<AttrMod>emptySet(), // no virtual attributes to be managed
+ null, // no propagation by resources
+ noPropResourceNames,
+ Collections.<MembershipMod>emptySet());
+ }
+
+ /**
+ * Performs update on each resource associated to the user.
+ *
+ * @param wfResult user to be propagated (and info associated), as per result from workflow
+ * @param changePwd whether password should be included for propagation attributes or not
+ * @param noPropResourceNames external resources not to be considered for propagation
+ * @return list of propagation tasks
+ * @throws NotFoundException if user is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given user
+ */
+ @Override
+ public List<PropagationTask> getUserUpdateTaskIds(final WorkflowResult<Map.Entry<UserMod, Boolean>> wfResult,
+ final boolean changePwd, final Collection<String> noPropResourceNames)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ User user = userDAO.authFetch(wfResult.getResult().getKey().getKey());
+ return getUpdateTaskIds(user,
+ wfResult.getResult().getKey().getPassword(),
+ changePwd,
+ wfResult.getResult().getValue(),
+ wfResult.getResult().getKey().getVirAttrsToRemove(),
+ wfResult.getResult().getKey().getVirAttrsToUpdate(),
+ wfResult.getPropByRes(),
+ noPropResourceNames,
+ wfResult.getResult().getKey().getMembershipsToAdd());
+ }
+
+ @Override
+ public List<PropagationTask> getUserUpdateTaskIds(final WorkflowResult<Map.Entry<UserMod, Boolean>> wfResult) {
+ UserMod userMod = wfResult.getResult().getKey();
+
+ // Propagate password update only to requested resources
+ List<PropagationTask> tasks = new ArrayList<>();
+ if (userMod.getPwdPropRequest() == null) {
+ // a. no specific password propagation request: generate propagation tasks for any resource associated
+ tasks = getUserUpdateTaskIds(wfResult, true, null);
+ } else {
+ // b. generate the propagation task list in two phases: first the ones containing password,
+ // the the rest (with no password)
+ final PropagationByResource origPropByRes = new PropagationByResource();
+ origPropByRes.merge(wfResult.getPropByRes());
+
+ Set<String> pwdResourceNames = new HashSet<>(userMod.getPwdPropRequest().getResourceNames());
+ Set<String> currentResourceNames = userDAO.authFetch(userMod.getKey()).getResourceNames();
+ pwdResourceNames.retainAll(currentResourceNames);
+ PropagationByResource pwdPropByRes = new PropagationByResource();
+ pwdPropByRes.addAll(ResourceOperation.UPDATE, pwdResourceNames);
+ if (!pwdPropByRes.isEmpty()) {
+ Set<String> toBeExcluded = new HashSet<>(currentResourceNames);
+ toBeExcluded.addAll(userMod.getResourcesToAdd());
+ toBeExcluded.removeAll(pwdResourceNames);
+ tasks.addAll(getUserUpdateTaskIds(wfResult, true, toBeExcluded));
+ }
+
+ final PropagationByResource nonPwdPropByRes = new PropagationByResource();
+ nonPwdPropByRes.merge(origPropByRes);
+ nonPwdPropByRes.removeAll(pwdResourceNames);
+ nonPwdPropByRes.purge();
+ if (!nonPwdPropByRes.isEmpty()) {
+ tasks.addAll(getUserUpdateTaskIds(wfResult, false, pwdResourceNames));
+ }
+ }
+
+ return tasks;
+ }
+
+ /**
+ * Performs update on each resource associated to the role.
+ *
+ * @param wfResult role to be propagated (and info associated), as per result from workflow
+ * @param vAttrsToBeRemoved virtual attributes to be removed
+ * @param vAttrsToBeUpdated virtual attributes to be added
+ * @return list of propagation tasks
+ * @throws NotFoundException if role is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
+ */
+ @Override
+ public List<PropagationTask> getRoleUpdateTaskIds(final WorkflowResult<Long> wfResult,
+ final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ return getRoleUpdateTaskIds(wfResult, vAttrsToBeRemoved, vAttrsToBeUpdated, null);
+ }
+
+ /**
+ * Performs update on each resource associated to the role.
+ *
+ * @param wfResult role to be propagated (and info associated), as per result from workflow
+ * @param vAttrsToBeRemoved virtual attributes to be removed
+ * @param vAttrsToBeUpdated virtual attributes to be added
+ * @param noPropResourceNames external resource names not to be considered for propagation
+ * @return list of propagation tasks
+ * @throws NotFoundException if role is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
+ */
+ public List<PropagationTask> getRoleUpdateTaskIds(final WorkflowResult<Long> wfResult,
+ final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated,
+ final Set<String> noPropResourceNames)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ Role role = roleDAO.authFetch(wfResult.getResult());
+ return getUpdateTaskIds(role, null, false, null,
+ vAttrsToBeRemoved, vAttrsToBeUpdated, wfResult.getPropByRes(), noPropResourceNames,
+ Collections.<MembershipMod>emptySet());
+ }
+
+ @Override
+ public List<PropagationTask> getUpdateTaskIds(final Subject<?, ?, ?> subject,
+ final String password, final boolean changePwd, final Boolean enable,
+ final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated,
+ final PropagationByResource propByRes, final Collection<String> noPropResourceNames,
+ final Set<MembershipMod> membershipsToAdd)
+ throws NotFoundException {
+
+ PropagationByResource localPropByRes = virAttrHandler.fillVirtual(subject, vAttrsToBeRemoved == null
+ ? Collections.<String>emptySet()
+ : vAttrsToBeRemoved, vAttrsToBeUpdated == null
+ ? Collections.<AttrMod>emptySet()
+ : vAttrsToBeUpdated, attrUtilFactory.getInstance(subject));
+
+ // SYNCOPE-458 fill membership virtual attributes
+ if (subject instanceof User) {
+ final User user = (User) subject;
+ for (Membership membership : user.getMemberships()) {
+ if (membership.getVirAttrs() != null && !membership.getVirAttrs().isEmpty()) {
+ final MembershipMod membershipMod = findMembershipMod(membership, membershipsToAdd);
+ if (membershipMod != null) {
+ virAttrHandler.fillVirtual(membership, membershipMod.getVirAttrsToRemove() == null
+ ? Collections.<String>emptySet()
+ : membershipMod.getVirAttrsToRemove(),
+ membershipMod.getVirAttrsToUpdate() == null ? Collections.<AttrMod>emptySet()
+ : membershipMod.getVirAttrsToUpdate(), attrUtilFactory.getInstance(
+ AttributableType.MEMBERSHIP));
+ }
+ }
+ }
+ }
+
+ if (propByRes == null || propByRes.isEmpty()) {
+ localPropByRes.addAll(ResourceOperation.UPDATE, subject.getResourceNames());
+ } else {
+ localPropByRes.merge(propByRes);
+ }
+
+ if (noPropResourceNames != null) {
+ localPropByRes.removeAll(noPropResourceNames);
+ }
+
+ Map<String, AttrMod> vAttrsToBeUpdatedMap = null;
+ if (vAttrsToBeUpdated != null) {
+ vAttrsToBeUpdatedMap = new HashMap<>();
+ for (AttrMod attrMod : vAttrsToBeUpdated) {
+ vAttrsToBeUpdatedMap.put(attrMod.getSchema(), attrMod);
+ }
+ }
+
+ // SYNCOPE-458 fill membership virtual attributes to be updated map
+ Map<String, AttrMod> membVAttrsToBeUpdatedMap = new HashMap<>();
+ for (MembershipMod membershipMod : membershipsToAdd) {
+ for (AttrMod attrMod : membershipMod.getVirAttrsToUpdate()) {
+ membVAttrsToBeUpdatedMap.put(attrMod.getSchema(), attrMod);
+ }
+ }
+
+ // SYNCOPE-458 fill membership virtual attributes to be removed set
+ final Set<String> membVAttrsToBeRemoved = new HashSet<>();
+ for (MembershipMod membershipMod : membershipsToAdd) {
+ membVAttrsToBeRemoved.addAll(membershipMod.getVirAttrsToRemove());
+ }
+
+ return createTasks(subject, password, changePwd,
+ vAttrsToBeRemoved, vAttrsToBeUpdatedMap, membVAttrsToBeRemoved, membVAttrsToBeUpdatedMap, enable, false,
+ localPropByRes);
+ }
+
+ /**
+ * Perform delete on each resource associated to the user. It is possible to ask for a mandatory provisioning for
+ * some resources specifying a set of resource names. Exceptions won't be ignored and the process will be stopped if
+ * the creation fails onto a mandatory resource.
+ *
+ * @param userKey to be deleted
+ * @return list of propagation tasks
+ * @throws NotFoundException if user is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given user
+ */
+ @Override
+ public List<PropagationTask> getUserDeleteTaskIds(final Long userKey)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ return getUserDeleteTaskIds(userKey, Collections.<String>emptySet());
+ }
+
+ /**
+ * Perform delete on each resource associated to the user. It is possible to ask for a mandatory provisioning for
+ * some resources specifying a set of resource names. Exceptions won't be ignored and the process will be stopped if
+ * the creation fails onto a mandatory resource.
+ *
+ * @param userKey to be deleted
+ * @param noPropResourceName name of external resource not to be considered for propagation
+ * @return list of propagation tasks
+ * @throws NotFoundException if user is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given user
+ */
+ @Override
+ public List<PropagationTask> getUserDeleteTaskIds(final Long userKey, final String noPropResourceName)
+ throws NotFoundException, UnauthorizedRoleException {
+ return getUserDeleteTaskIds(userKey, Collections.<String>singleton(noPropResourceName));
+ }
+
+ /**
+ * Perform delete on each resource associated to the user. It is possible to ask for a mandatory provisioning for
+ * some resources specifying a set of resource names. Exceptions won't be ignored and the process will be stopped if
+ * the creation fails onto a mandatory resource.
+ *
+ * @param userKey to be deleted
+ * @param noPropResourceNames name of external resources not to be considered for propagation
+ * @return list of propagation tasks
+ * @throws NotFoundException if user is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given user
+ */
+ @Override
+ public List<PropagationTask> getUserDeleteTaskIds(final Long userKey, final Collection<String> noPropResourceNames)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ User user = userDAO.authFetch(userKey);
+ return getDeleteTaskIds(user, user.getResourceNames(), noPropResourceNames);
+ }
+
+ /**
+ * Perform delete on each resource associated to the user. It is possible to ask for a mandatory provisioning for
+ * some resources specifying a set of resource names. Exceptions won't be ignored and the process will be stopped if
+ * the creation fails onto a mandatory resource.
+ *
+ * @param userKey to be deleted
+ * @param resourceNames resource from which user is to be deleted
+ * @param noPropResourceNames name of external resources not to be considered for propagation
+ * @return list of propagation tasks
+ * @throws NotFoundException if user is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given user
+ */
+ @Override
+ public List<PropagationTask> getUserDeleteTaskIds(
+ final Long userKey, final Set<String> resourceNames, final Collection<String> noPropResourceNames)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ User user = userDAO.authFetch(userKey);
+ return getDeleteTaskIds(user, resourceNames, noPropResourceNames);
+ }
+
+ /**
+ * Perform delete on each resource associated to the user. It is possible to ask for a mandatory provisioning for
+ * some resources specifying a set of resource names. Exceptions won't be ignored and the process will be stopped if
+ * the creation fails onto a mandatory resource.
+ *
+ * @param wfResult user to be propagated (and info associated), as per result from workflow
+ * @return list of propagation tasks
+ */
+ @Override
+ public List<PropagationTask> getUserDeleteTaskIds(final WorkflowResult<Long> wfResult) {
+ User user = userDAO.authFetch(wfResult.getResult());
+ return createTasks(user, null, false, null, null, null, null, false, true, wfResult.getPropByRes());
+ }
+
+ /**
+ * Perform delete on each resource associated to the role. It is possible to ask for a mandatory provisioning for
+ * some resources specifying a set of resource names. Exceptions won't be ignored and the process will be stopped if
+ * the creation fails onto a mandatory resource.
+ *
+ * @param roleKey to be deleted
+ * @return list of propagation tasks
+ * @throws NotFoundException if role is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
+ */
+ @Override
+ public List<PropagationTask> getRoleDeleteTaskIds(final Long roleKey)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ return getRoleDeleteTaskIds(roleKey, Collections.<String>emptySet());
+ }
+
+ /**
+ * Perform delete on each resource associated to the role. It is possible to ask for a mandatory provisioning for
+ * some resources specifying a set of resource names. Exceptions won't be ignored and the process will be stopped if
+ * the creation fails onto a mandatory resource.
+ *
+ * @param roleKey to be deleted
+ * @param noPropResourceName name of external resource not to be considered for propagation
+ * @return list of propagation tasks
+ * @throws NotFoundException if role is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
+ */
+ @Override
+ public List<PropagationTask> getRoleDeleteTaskIds(final Long roleKey, final String noPropResourceName)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ return getRoleDeleteTaskIds(roleKey, Collections.<String>singleton(noPropResourceName));
+ }
+
+ /**
+ * Perform delete on each resource associated to the user. It is possible to ask for a mandatory provisioning for
+ * some resources specifying a set of resource names. Exceptions won't be ignored and the process will be stopped if
+ * the creation fails onto a mandatory resource.
+ *
+ * @param roleKey to be deleted
+ * @param noPropResourceNames name of external resources not to be considered for propagation
+ * @return list of propagation tasks
+ * @throws NotFoundException if role is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
+ */
+ @Override
+ public List<PropagationTask> getRoleDeleteTaskIds(final Long roleKey, final Collection<String> noPropResourceNames)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ Role role = roleDAO.authFetch(roleKey);
+ return getDeleteTaskIds(role, role.getResourceNames(), noPropResourceNames);
+ }
+
+ /**
+ * Perform delete on each resource associated to the user. It is possible to ask for a mandatory provisioning for
+ * some resources specifying a set of resource names. Exceptions won't be ignored and the process will be stopped if
+ * the creation fails onto a mandatory resource.
+ *
+ * @param roleKey to be deleted
+ * @param resourceNames resource from which role is to be deleted
+ * @param noPropResourceNames name of external resources not to be considered for propagation
+ * @return list of propagation tasks
+ * @throws NotFoundException if role is not found
+ * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
+ */
+ @Override
+ public List<PropagationTask> getRoleDeleteTaskIds(
+ final Long roleKey, final Set<String> resourceNames, final Collection<String> noPropResourceNames)
+ throws NotFoundException, UnauthorizedRoleException {
+
+ Role role = roleDAO.authFetch(roleKey);
+ return getDeleteTaskIds(role, resourceNames, noPropResourceNames);
+ }
+
+ protected List<PropagationTask> getDeleteTaskIds(
+ final Subject<?, ?, ?> subject,
+ final Set<String> resourceNames,
+ final Collection<String> noPropResourceNames) {
+
+ final PropagationByResource propByRes = new PropagationByResource();
+ propByRes.set(ResourceOperation.DELETE, resourceNames);
+ if (noPropResourceNames != null && !noPropResourceNames.isEmpty()) {
+ propByRes.get(ResourceOperation.DELETE).removeAll(noPropResourceNames);
+ }
+ return createTasks(subject, null, false, null, null, null, null, false, true, propByRes);
+ }
+
+ /**
+ * Create propagation tasks.
+ *
+ * @param subject user / role to be provisioned
+ * @param password cleartext password to be provisioned
+ * @param changePwd whether password should be included for propagation attributes or not
+ * @param vAttrsToBeRemoved virtual attributes to be removed
+ * @param vAttrsToBeUpdated virtual attributes to be added
+ * @param membVAttrsToBeRemoved membership virtual attributes to be removed
+ * @param membVAttrsToBeUpdatedMap membership virtual attributes to be added
+ * @param enable whether user must be enabled or not
+ * @param deleteOnResource whether user / role must be deleted anyway from external resource or not
+ * @param propByRes operation to be performed per resource
+ * @return list of propagation tasks created
+ */
+ protected List<PropagationTask> createTasks(final Subject<?, ?, ?> subject,
+ final String password, final boolean changePwd,
+ final Set<String> vAttrsToBeRemoved, final Map<String, AttrMod> vAttrsToBeUpdated,
+ final Set<String> membVAttrsToBeRemoved, final Map<String, AttrMod> membVAttrsToBeUpdatedMap,
+ final Boolean enable, final boolean deleteOnResource, final PropagationByResource propByRes) {
+
+ LOG.debug("Provisioning subject {}:\n{}", subject, propByRes);
+
+ final AttributableUtil attrUtil = attrUtilFactory.getInstance(subject);
+
+ if (!propByRes.get(ResourceOperation.CREATE).isEmpty()
+ && vAttrsToBeRemoved != null && vAttrsToBeUpdated != null) {
+
+ connObjectUtil.retrieveVirAttrValues(subject, attrUtil);
+
+ // update vAttrsToBeUpdated as well
+ for (VirAttr virAttr : subject.getVirAttrs()) {
+ final String schema = virAttr.getSchema().getKey();
+
+ final AttrMod attributeMod = new AttrMod();
+ attributeMod.setSchema(schema);
+ attributeMod.getValuesToBeAdded().addAll(virAttr.getValues());
+
+ vAttrsToBeUpdated.put(schema, attributeMod);
+ }
+ }
+
+ // Avoid duplicates - see javadoc
+ propByRes.purge();
+ LOG.debug("After purge: {}", propByRes);
+
+ final List<PropagationTask> tasks = new ArrayList<>();
+
+ for (ResourceOperation operation : ResourceOperation.values()) {
+ for (String resourceName : propByRes.get(operation)) {
+ final ExternalResource resource = resourceDAO.find(resourceName);
+ if (resource == null) {
+ LOG.error("Invalid resource name specified: {}, ignoring...", resourceName);
+ } else if (attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION).isEmpty()) {
+ LOG.warn("Requesting propagation for {} but no propagation mapping provided for {}",
+ attrUtil.getType(), resource);
+ } else {
+ PropagationTask task = entityFactory.newEntity(PropagationTask.class);
+ task.setResource(resource);
+ task.setObjectClassName(connObjectUtil.fromSubject(subject).getObjectClassValue());
+ task.setSubjectType(attrUtil.getType());
+ if (!deleteOnResource) {
+ task.setSubjectKey(subject.getKey());
+ }
+ task.setPropagationOperation(operation);
+ task.setPropagationMode(resource.getPropagationMode());
+ task.setOldAccountId(propByRes.getOldAccountId(resource.getKey()));
+
+ Map.Entry<String, Set<Attribute>> preparedAttrs = MappingUtil.prepareAttributes(attrUtil, subject,
+ password, changePwd, vAttrsToBeRemoved, vAttrsToBeUpdated, membVAttrsToBeRemoved,
+ membVAttrsToBeUpdatedMap, enable, resource);
+ task.setAccountId(preparedAttrs.getKey());
+
+ // Check if any of mandatory attributes (in the mapping) is missing or not received any value:
+ // if so, add special attributes that will be evaluated by PropagationTaskExecutor
+ List<String> mandatoryMissing = new ArrayList<>();
+ List<String> mandatoryNullOrEmpty = new ArrayList<>();
+ for (MappingItem item : attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) {
+ if (!item.isAccountid()
+ && JexlUtil.evaluateMandatoryCondition(item.getMandatoryCondition(), subject)) {
+
+ Attribute attr = AttributeUtil.find(item.getExtAttrName(), preparedAttrs.getValue());
+ if (attr == null) {
+ mandatoryMissing.add(item.getExtAttrName());
+ } else if (attr.getValue() == null || attr.getValue().isEmpty()) {
+ mandatoryNullOrEmpty.add(item.getExtAttrName());
+ }
+ }
+ }
+ if (!mandatoryMissing.isEmpty()) {
+ preparedAttrs.getValue().add(AttributeBuilder.build(
+ PropagationTaskExecutor.MANDATORY_MISSING_ATTR_NAME, mandatoryMissing));
+ }
+ if (!mandatoryNullOrEmpty.isEmpty()) {
+ preparedAttrs.getValue().add(AttributeBuilder.build(
+ PropagationTaskExecutor.MANDATORY_NULL_OR_EMPTY_ATTR_NAME, mandatoryNullOrEmpty));
+ }
+
+ task.setAttributes(preparedAttrs.getValue());
+ tasks.add(task);
+
+ LOG.debug("PropagationTask created: {}", task);
+ }
+ }
+ }
+
+ return tasks;
+ }
+
+ protected MembershipTO findMembershipTO(final Membership membership, final Collection<MembershipTO> memberships) {
+ for (MembershipTO membershipTO : memberships) {
+ if (membershipTO.getRoleId() == membership.getRole().getKey()) {
+ return membershipTO;
+ }
+ }
+ LOG.error("No MembershipTO found for membership {}", membership);
+ return null;
+ }
+
+ protected MembershipMod findMembershipMod(final Membership membership, final Set<MembershipMod> membershipMods) {
+ for (MembershipMod membershipMod : membershipMods) {
+ if (membershipMod.getRole() == membership.getRole().getKey()) {
+ return membershipMod;
+ }
+ }
+ LOG.error("No MembershipMod found for membership {}", membership);
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractProvisioningJob.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractProvisioningJob.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractProvisioningJob.java
new file mode 100644
index 0000000..f99e179
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractProvisioningJob.java
@@ -0,0 +1,372 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.lang.reflect.ParameterizedType;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.common.lib.types.TraceLevel;
+import org.apache.syncope.server.persistence.api.dao.EntitlementDAO;
+import org.apache.syncope.server.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.server.persistence.api.dao.PolicyDAO;
+import org.apache.syncope.server.persistence.api.entity.Entitlement;
+import org.apache.syncope.server.persistence.api.entity.role.RMapping;
+import org.apache.syncope.server.persistence.api.entity.task.ProvisioningTask;
+import org.apache.syncope.server.persistence.api.entity.task.PushTask;
+import org.apache.syncope.server.persistence.api.entity.task.SyncTask;
+import org.apache.syncope.server.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.server.persistence.api.entity.user.UMapping;
+import org.apache.syncope.server.provisioning.api.Connector;
+import org.apache.syncope.server.provisioning.api.ConnectorFactory;
+import org.apache.syncope.server.provisioning.api.sync.ProvisioningActions;
+import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
+import org.apache.syncope.server.provisioning.java.job.AbstractTaskJob;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+
+/**
+ * Job for executing synchronization tasks.
+ *
+ * @see AbstractTaskJob
+ * @see SyncTask
+ * @see PushTask
+ */
+public abstract class AbstractProvisioningJob<T extends ProvisioningTask, A extends ProvisioningActions>
+ extends AbstractTaskJob {
+
+ /**
+ * ConnInstance loader.
+ */
+ @Autowired
+ protected ConnectorFactory connFactory;
+
+ /**
+ * Resource DAO.
+ */
+ @Autowired
+ protected ExternalResourceDAO resourceDAO;
+
+ /**
+ * Entitlement DAO.
+ */
+ @Autowired
+ protected EntitlementDAO entitlementDAO;
+
+ /**
+ * Policy DAO.
+ */
+ @Autowired
+ protected PolicyDAO policyDAO;
+
+ /**
+ * SyncJob actions.
+ */
+ protected List<A> actions;
+
+ public void setActions(final List<A> actions) {
+ this.actions = actions;
+ }
+
+ /**
+ * Create a textual report of the synchronization, based on the trace level.
+ *
+ * @param provResults Sync results
+ * @param syncTraceLevel Sync trace level
+ * @param dryRun dry run?
+ * @return report as string
+ */
+ protected String createReport(final List<ProvisioningResult> provResults, final TraceLevel syncTraceLevel,
+ final boolean dryRun) {
+
+ if (syncTraceLevel == TraceLevel.NONE) {
+ return null;
+ }
+
+ StringBuilder report = new StringBuilder();
+
+ if (dryRun) {
+ report.append("==>Dry run only, no modifications were made<==\n\n");
+ }
+
+ List<ProvisioningResult> uSuccCreate = new ArrayList<>();
+ List<ProvisioningResult> uFailCreate = new ArrayList<>();
+ List<ProvisioningResult> uSuccUpdate = new ArrayList<>();
+ List<ProvisioningResult> uFailUpdate = new ArrayList<>();
+ List<ProvisioningResult> uSuccDelete = new ArrayList<>();
+ List<ProvisioningResult> uFailDelete = new ArrayList<>();
+ List<ProvisioningResult> rSuccCreate = new ArrayList<>();
+ List<ProvisioningResult> rFailCreate = new ArrayList<>();
+ List<ProvisioningResult> rSuccUpdate = new ArrayList<>();
+ List<ProvisioningResult> rFailUpdate = new ArrayList<>();
+ List<ProvisioningResult> rSuccDelete = new ArrayList<>();
+ List<ProvisioningResult> rFailDelete = new ArrayList<>();
+
+ for (ProvisioningResult provResult : provResults) {
+ switch (provResult.getStatus()) {
+ case SUCCESS:
+ switch (provResult.getOperation()) {
+ case CREATE:
+ switch (provResult.getSubjectType()) {
+ case USER:
+ uSuccCreate.add(provResult);
+ break;
+
+ case ROLE:
+ rSuccCreate.add(provResult);
+ break;
+
+ default:
+ }
+ break;
+
+ case UPDATE:
+ switch (provResult.getSubjectType()) {
+ case USER:
+ uSuccUpdate.add(provResult);
+ break;
+
+ case ROLE:
+ rSuccUpdate.add(provResult);
+ break;
+
+ default:
+ }
+ break;
+
+ case DELETE:
+ switch (provResult.getSubjectType()) {
+ case USER:
+ uSuccDelete.add(provResult);
+ break;
+
+ case ROLE:
+ rSuccDelete.add(provResult);
+ break;
+
+ default:
+ }
+ break;
+
+ default:
+ }
+ break;
+
+ case FAILURE:
+ switch (provResult.getOperation()) {
+ case CREATE:
+ switch (provResult.getSubjectType()) {
+ case USER:
+ uFailCreate.add(provResult);
+ break;
+
+ case ROLE:
+ rFailCreate.add(provResult);
+ break;
+
+ default:
+ }
+ break;
+
+ case UPDATE:
+ switch (provResult.getSubjectType()) {
+ case USER:
+ uFailUpdate.add(provResult);
+ break;
+
+ case ROLE:
+ rFailUpdate.add(provResult);
+ break;
+
+ default:
+ }
+ break;
+
+ case DELETE:
+ switch (provResult.getSubjectType()) {
+ case USER:
+ uFailDelete.add(provResult);
+ break;
+
+ case ROLE:
+ rFailDelete.add(provResult);
+ break;
+
+ default:
+ }
+ break;
+
+ default:
+ }
+ break;
+
+ default:
+ }
+ }
+
+ // Summary, also to be included for FAILURE and ALL, so create it anyway.
+ report.append("Users ").
+ append("[created/failures]: ").append(uSuccCreate.size()).append('/').append(uFailCreate.size()).
+ append(' ').
+ append("[updated/failures]: ").append(uSuccUpdate.size()).append('/').append(uFailUpdate.size()).
+ append(' ').
+ append("[deleted/failures]: ").append(uSuccDelete.size()).append('/').append(uFailDelete.size()).
+ append('\n');
+ report.append("Roles ").
+ append("[created/failures]: ").append(rSuccCreate.size()).append('/').append(rFailCreate.size()).
+ append(' ').
+ append("[updated/failures]: ").append(rSuccUpdate.size()).append('/').append(rFailUpdate.size()).
+ append(' ').
+ append("[deleted/failures]: ").append(rSuccDelete.size()).append('/').append(rFailDelete.size());
+
+ // Failures
+ if (syncTraceLevel == TraceLevel.FAILURES || syncTraceLevel == TraceLevel.ALL) {
+ if (!uFailCreate.isEmpty()) {
+ report.append("\n\nUsers failed to create: ");
+ report.append(ProvisioningResult.produceReport(uFailCreate, syncTraceLevel));
+ }
+ if (!uFailUpdate.isEmpty()) {
+ report.append("\nUsers failed to update: ");
+ report.append(ProvisioningResult.produceReport(uFailUpdate, syncTraceLevel));
+ }
+ if (!uFailDelete.isEmpty()) {
+ report.append("\nUsers failed to delete: ");
+ report.append(ProvisioningResult.produceReport(uFailDelete, syncTraceLevel));
+ }
+
+ if (!rFailCreate.isEmpty()) {
+ report.append("\n\nRoles failed to create: ");
+ report.append(ProvisioningResult.produceReport(rFailCreate, syncTraceLevel));
+ }
+ if (!rFailUpdate.isEmpty()) {
+ report.append("\nRoles failed to update: ");
+ report.append(ProvisioningResult.produceReport(rFailUpdate, syncTraceLevel));
+ }
+ if (!rFailDelete.isEmpty()) {
+ report.append("\nRoles failed to delete: ");
+ report.append(ProvisioningResult.produceReport(rFailDelete, syncTraceLevel));
+ }
+ }
+
+ // Succeeded, only if on 'ALL' level
+ if (syncTraceLevel == TraceLevel.ALL) {
+ report.append("\n\nUsers created:\n")
+ .append(ProvisioningResult.produceReport(uSuccCreate, syncTraceLevel))
+ .append("\nUsers updated:\n")
+ .append(ProvisioningResult.produceReport(uSuccUpdate, syncTraceLevel))
+ .append("\nUsers deleted:\n")
+ .append(ProvisioningResult.produceReport(uSuccDelete, syncTraceLevel));
+ report.append("\n\nRoles created:\n")
+ .append(ProvisioningResult.produceReport(rSuccCreate, syncTraceLevel))
+ .append("\nRoles updated:\n")
+ .append(ProvisioningResult.produceReport(rSuccUpdate, syncTraceLevel))
+ .append("\nRoles deleted:\n")
+ .append(ProvisioningResult.produceReport(rSuccDelete, syncTraceLevel));
+ }
+
+ return report.toString();
+ }
+
+ @Override
+ protected String doExecute(final boolean dryRun) throws JobExecutionException {
+ // PRE: grant all authorities (i.e. setup the SecurityContextHolder)
+ final List<GrantedAuthority> authorities = new ArrayList<>();
+
+ for (Entitlement entitlement : entitlementDAO.findAll()) {
+ authorities.add(new SimpleGrantedAuthority(entitlement.getKey()));
+ }
+
+ final UserDetails userDetails = new User("admin", "FAKE_PASSWORD", true, true, true, true, authorities);
+
+ SecurityContextHolder.getContext().setAuthentication(
+ new UsernamePasswordAuthenticationToken(userDetails, "FAKE_PASSWORD", authorities));
+
+ try {
+ final Class<T> clazz = getTaskClassReference();
+ if (!clazz.isAssignableFrom(task.getClass())) {
+ throw new JobExecutionException("Task " + taskId + " isn't a SyncTask");
+ }
+
+ final T syncTask = clazz.cast(this.task);
+
+ final Connector connector;
+ try {
+ connector = connFactory.getConnector(syncTask.getResource());
+ } catch (Exception e) {
+ final String msg = String.
+ format("Connector instance bean for resource %s and connInstance %s not found",
+ syncTask.getResource(), syncTask.getResource().getConnector());
+
+ throw new JobExecutionException(msg, e);
+ }
+
+ final UMapping uMapping = syncTask.getResource().getUmapping();
+ if (uMapping != null && uMapping.getAccountIdItem() == null) {
+ throw new JobExecutionException(
+ "Invalid user account id mapping for resource " + syncTask.getResource());
+ }
+ final RMapping rMapping = syncTask.getResource().getRmapping();
+ if (rMapping != null && rMapping.getAccountIdItem() == null) {
+ throw new JobExecutionException(
+ "Invalid role account id mapping for resource " + syncTask.getResource());
+ }
+ if (uMapping == null && rMapping == null) {
+ return "No mapping configured for both users and roles: aborting...";
+ }
+
+ return executeWithSecurityContext(
+ syncTask,
+ connector,
+ uMapping,
+ rMapping,
+ dryRun);
+
+ } finally {
+ // POST: clean up the SecurityContextHolder
+ SecurityContextHolder.clearContext();
+ }
+ }
+
+ protected abstract String executeWithSecurityContext(
+ final T task,
+ final Connector connector,
+ final UMapping uMapping,
+ final RMapping rMapping,
+ final boolean dryRun) throws JobExecutionException;
+
+ @Override
+ protected boolean hasToBeRegistered(final TaskExec execution) {
+ final ProvisioningTask provTask = (ProvisioningTask) task;
+
+ // True if either failed and failures have to be registered, or if ALL has to be registered.
+ return (Status.valueOf(execution.getStatus()) == Status.FAILURE
+ && provTask.getResource().getSyncTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
+ || provTask.getResource().getSyncTraceLevel().ordinal() >= TraceLevel.SUMMARY.ordinal();
+ }
+
+ @SuppressWarnings("unchecked")
+ private Class<T> getTaskClassReference() {
+ return (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectPushResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectPushResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectPushResultHandler.java
new file mode 100644
index 0000000..f0b70cd
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSubjectPushResultHandler.java
@@ -0,0 +1,371 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.mod.MembershipMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.Result;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MatchingRule;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.server.persistence.api.entity.Mapping;
+import org.apache.syncope.server.persistence.api.entity.MappingItem;
+import org.apache.syncope.server.persistence.api.entity.Subject;
+import org.apache.syncope.server.persistence.api.entity.VirAttr;
+import org.apache.syncope.server.persistence.api.entity.membership.Membership;
+import org.apache.syncope.server.persistence.api.entity.task.PushTask;
+import org.apache.syncope.server.persistence.api.entity.user.User;
+import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
+import org.apache.syncope.server.provisioning.api.sync.PushActions;
+import org.apache.syncope.server.misc.MappingUtil;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.quartz.JobExecutionException;
+import org.springframework.transaction.annotation.Transactional;
+
+public abstract class AbstractSubjectPushResultHandler extends AbstractSyncopeResultHandler<PushTask, PushActions> {
+
+ protected abstract String getName(final Subject<?, ?, ?> subject);
+
+ protected abstract Mapping<?> getMapping();
+
+ protected abstract AbstractSubjectTO getSubjectTO(final long key);
+
+ protected abstract Subject<?, ?, ?> getSubject(final long key);
+
+ protected abstract Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj);
+
+ protected abstract Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled);
+
+ protected abstract Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink);
+
+ protected abstract Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj);
+
+ protected abstract Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, Boolean enabled);
+
+ protected abstract ConnectorObject getRemoteObject(final String accountId);
+
+ @Transactional
+ public boolean handle(final long subjectId) {
+ try {
+ doHandle(subjectId);
+ return true;
+ } catch (JobExecutionException e) {
+ LOG.error("Synchronization failed", e);
+ return false;
+ }
+ }
+
+ protected final void doHandle(final long subjectId)
+ throws JobExecutionException {
+
+ final Subject<?, ?, ?> subject = getSubject(subjectId);
+
+ final AttributableUtil attrUtil = attrUtilFactory.getInstance(subject);
+
+ final ProvisioningResult result = new ProvisioningResult();
+ profile.getResults().add(result);
+
+ result.setId(subject.getKey());
+ result.setSubjectType(attrUtil.getType());
+ result.setName(getName(subject));
+
+ final Boolean enabled = subject instanceof User && profile.getTask().isSyncStatus()
+ ? ((User) subject).isSuspended() ? Boolean.FALSE : Boolean.TRUE
+ : null;
+
+ LOG.debug("Propagating {} with key {} towards {}",
+ attrUtil.getType(), subject.getKey(), profile.getTask().getResource());
+
+ Object output = null;
+ Result resultStatus = null;
+ ConnectorObject beforeObj = null;
+ String operation = null;
+
+ // Try to read remote object (user / group) BEFORE any actual operation
+ final String accountId = MappingUtil.getAccountIdValue(
+ subject, profile.getTask().getResource(), getMapping().getAccountIdItem());
+
+ beforeObj = getRemoteObject(accountId);
+
+ 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(ProvisioningResult.Status.SUCCESS);
+ } else {
+ try {
+ if (beforeObj == null) {
+ operation = profile.getTask().getUnmatchingRule().name().toLowerCase();
+ result.setOperation(getResourceOperation(profile.getTask().getUnmatchingRule()));
+
+ switch (profile.getTask().getUnmatchingRule()) {
+ case ASSIGN:
+ for (PushActions action : profile.getActions()) {
+ action.beforeAssign(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformCreate()) {
+ LOG.debug("PushTask not configured for create");
+ } else {
+ assign(subject, status);
+ }
+
+ break;
+ case PROVISION:
+ for (PushActions action : profile.getActions()) {
+ action.beforeProvision(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformCreate()) {
+ LOG.debug("PushTask not configured for create");
+ } else {
+ provision(subject, status);
+ }
+
+ break;
+ case UNLINK:
+ for (PushActions action : profile.getActions()) {
+ action.beforeUnlink(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformUpdate()) {
+ LOG.debug("PushTask not configured for update");
+ } else {
+ link(subject, true);
+ }
+
+ break;
+ default:
+ // do nothing
+ }
+
+ } else {
+ operation = profile.getTask().getMatchingRule().name().toLowerCase();
+ result.setOperation(getResourceOperation(profile.getTask().getMatchingRule()));
+
+ switch (profile.getTask().getMatchingRule()) {
+ case UPDATE:
+ for (PushActions action : profile.getActions()) {
+ action.beforeUpdate(this.getProfile(), subject);
+ }
+ if (!profile.getTask().isPerformUpdate()) {
+ LOG.debug("PushTask not configured for update");
+ } else {
+ update(subject, status);
+ }
+
+ break;
+ case DEPROVISION:
+ for (PushActions action : profile.getActions()) {
+ action.beforeDeprovision(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformDelete()) {
+ LOG.debug("PushTask not configured for delete");
+ } else {
+ deprovision(subject);
+ }
+
+ break;
+ case UNASSIGN:
+ for (PushActions action : profile.getActions()) {
+ action.beforeUnassign(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformDelete()) {
+ LOG.debug("PushTask not configured for delete");
+ } else {
+ unassign(subject);
+ }
+
+ break;
+ case LINK:
+ for (PushActions action : profile.getActions()) {
+ action.beforeLink(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformUpdate()) {
+ LOG.debug("PushTask not configured for update");
+ } else {
+ link(subject, false);
+ }
+
+ break;
+ case UNLINK:
+ for (PushActions action : profile.getActions()) {
+ action.beforeUnlink(this.getProfile(), subject);
+ }
+
+ if (!profile.getTask().isPerformUpdate()) {
+ LOG.debug("PushTask not configured for update");
+ } else {
+ link(subject, true);
+ }
+
+ break;
+ default:
+ // do nothing
+ }
+ }
+
+ for (PushActions action : profile.getActions()) {
+ action.after(this.getProfile(), subject, result);
+ }
+
+ result.setStatus(ProvisioningResult.Status.SUCCESS);
+ resultStatus = AuditElements.Result.SUCCESS;
+ output = getRemoteObject(accountId);
+ } catch (Exception e) {
+ result.setStatus(ProvisioningResult.Status.FAILURE);
+ result.setMessage(ExceptionUtils.getRootCauseMessage(e));
+ resultStatus = AuditElements.Result.FAILURE;
+ output = e;
+
+ LOG.warn("Error pushing {} towards {}", subject, profile.getTask().getResource(), e);
+ throw new JobExecutionException(e);
+ } finally {
+ notificationManager.createTasks(
+ AuditElements.EventCategoryType.PUSH,
+ AttributableType.USER.name().toLowerCase(),
+ profile.getTask().getResource().getKey(),
+ operation,
+ resultStatus,
+ beforeObj,
+ output,
+ subject);
+ auditManager.audit(
+ AuditElements.EventCategoryType.PUSH,
+ AttributableType.USER.name().toLowerCase(),
+ profile.getTask().getResource().getKey(),
+ operation,
+ resultStatus,
+ beforeObj,
+ output,
+ subject);
+ }
+ }
+ }
+
+ 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;
+ }
+ }
+
+ protected Subject<?, ?, ?> update(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+
+ final Set<MembershipMod> membsToAdd = new HashSet<>();
+ final Set<String> vattrToBeRemoved = new HashSet<>();
+ final Set<String> membVattrToBeRemoved = new HashSet<>();
+ final Set<AttrMod> vattrToBeUpdated = new HashSet<>();
+
+ // Search for all mapped vattrs
+ final Mapping<?> umapping = getMapping();
+ for (MappingItem mappingItem : umapping.getItems()) {
+ if (mappingItem.getIntMappingType() == IntMappingType.UserVirtualSchema) {
+ vattrToBeRemoved.add(mappingItem.getIntAttrName());
+ } else if (mappingItem.getIntMappingType() == IntMappingType.MembershipVirtualSchema) {
+ membVattrToBeRemoved.add(mappingItem.getIntAttrName());
+ }
+ }
+
+ // Search for all user's vattrs and:
+ // 1. add mapped vattrs not owned by the user to the set of vattrs to be removed
+ // 2. add all vattrs owned by the user to the set of vattrs to be update
+ for (VirAttr vattr : sbj.getVirAttrs()) {
+ vattrToBeRemoved.remove(vattr.getSchema().getKey());
+ final AttrMod mod = new AttrMod();
+ mod.setSchema(vattr.getSchema().getKey());
+ mod.getValuesToBeAdded().addAll(vattr.getValues());
+ vattrToBeUpdated.add(mod);
+ }
+
+ final boolean changepwd;
+
+ if (sbj instanceof User) {
+ changepwd = true;
+
+ // Search for memberships
+ for (Membership membership : User.class.cast(sbj).getMemberships()) {
+ final MembershipMod membershipMod = new MembershipMod();
+ membershipMod.setKey(membership.getKey());
+ membershipMod.setRole(membership.getRole().getKey());
+
+ for (VirAttr vattr : membership.getVirAttrs()) {
+ membVattrToBeRemoved.remove(vattr.getSchema().getKey());
+ final AttrMod mod = new AttrMod();
+ mod.setSchema(vattr.getSchema().getKey());
+ mod.getValuesToBeAdded().addAll(vattr.getValues());
+ membershipMod.getVirAttrsToUpdate().add(mod);
+ }
+
+ membsToAdd.add(membershipMod);
+ }
+
+ if (!membsToAdd.isEmpty()) {
+ membsToAdd.iterator().next().getVirAttrsToRemove().addAll(membVattrToBeRemoved);
+ }
+ } else {
+ changepwd = false;
+ }
+
+ final List<String> noPropResources = new ArrayList<>(sbj.getResourceNames());
+ noPropResources.remove(profile.getTask().getResource().getKey());
+
+ final PropagationByResource propByRes = new PropagationByResource();
+ propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+ taskExecutor.execute(propagationManager.getUpdateTaskIds(
+ sbj, null, changepwd, enabled, vattrToBeRemoved, vattrToBeUpdated, propByRes, noPropResources,
+ membsToAdd));
+
+ return userDAO.authFetch(sbj.getKey());
+ }
+}