syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ilgro...@apache.org
Subject [6/7] syncope git commit: [SYNCOPE-686] Merge from 1_2_X
Date Wed, 26 Aug 2015 14:54:14 GMT
http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index 459c052,0000000..2e1224b
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@@ -1,477 -1,0 +1,517 @@@
 +/*
 + * 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.data;
 +
 +import java.util.Collection;
 +import java.util.Date;
 +import java.util.HashSet;
 +import java.util.Map;
 +import java.util.Set;
 +import javax.annotation.Resource;
 +import org.apache.commons.collections4.Closure;
 +import org.apache.commons.collections4.CollectionUtils;
++import org.apache.commons.collections4.Predicate;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.common.lib.SyncopeClientCompositeException;
 +import org.apache.syncope.common.lib.SyncopeClientException;
++import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.MembershipTO;
 +import org.apache.syncope.common.lib.to.RelationshipTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.CipherAlgorithm;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 +import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 +import org.apache.syncope.core.misc.security.AuthContextUtils;
 +import org.apache.syncope.core.misc.security.Encryptor;
 +import org.apache.syncope.core.misc.spring.BeanUtils;
++import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 +import org.apache.syncope.core.persistence.api.dao.RoleDAO;
 +import org.apache.syncope.core.persistence.api.entity.Role;
 +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
++import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
++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.persistence.api.entity.user.UMembership;
 +import org.apache.syncope.core.persistence.api.entity.user.URelationship;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.stereotype.Component;
 +import org.springframework.transaction.annotation.Transactional;
 +
 +@Component
 +@Transactional(rollbackFor = { Throwable.class })
 +public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDataBinder {
 +
 +    private static final String[] IGNORE_PROPERTIES = {
 +        "type", "realm", "auxClasses", "roles", "dynRoles", "relationships", "memberships", "dynGroups",
 +        "plainAttrs", "derAttrs", "virAttrs", "resources", "securityQuestion", "securityAnswer"
 +    };
 +
 +    @Autowired
 +    private RoleDAO roleDAO;
 +
 +    @Autowired
 +    private ConfDAO confDAO;
 +
 +    @Autowired
 +    private SecurityQuestionDAO securityQuestionDAO;
 +
++    @Autowired
++    private AnyTypeDAO anyTypeDAO;
++
 +    @Resource(name = "adminUser")
 +    private String adminUser;
 +
 +    @Resource(name = "anonymousUser")
 +    private String anonymousUser;
 +
 +    private final Encryptor encryptor = Encryptor.getInstance();
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getAuthenticatedUserTO() {
 +        final UserTO authUserTO;
 +
 +        String authUsername = AuthContextUtils.getUsername();
 +        if (anonymousUser.equals(authUsername)) {
 +            authUserTO = new UserTO();
 +            authUserTO.setKey(-2);
 +            authUserTO.setUsername(anonymousUser);
 +        } else if (adminUser.equals(authUsername)) {
 +            authUserTO = new UserTO();
 +            authUserTO.setKey(-1);
 +            authUserTO.setUsername(adminUser);
 +        } else {
 +            User authUser = userDAO.find(authUsername);
 +            authUserTO = getUserTO(authUser, true);
 +        }
 +
 +        return authUserTO;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public boolean verifyPassword(final String username, final String password) {
 +        return verifyPassword(userDAO.authFind(username), password);
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public boolean verifyPassword(final User user, final String password) {
 +        return encryptor.verify(password, user.getCipherAlgorithm(), user.getPassword());
 +    }
 +
 +    private void setPassword(final User user, final String password, final SyncopeClientCompositeException scce) {
 +        try {
 +            final String algorithm = confDAO.find(
 +                    "password.cipher.algorithm", CipherAlgorithm.AES.name()).getValues().get(0).getStringValue();
 +            CipherAlgorithm predefined = CipherAlgorithm.valueOf(algorithm);
 +            user.setPassword(password, predefined);
 +        } catch (IllegalArgumentException e) {
 +            final SyncopeClientException invalidCiperAlgorithm =
 +                    SyncopeClientException.build(ClientExceptionType.NotFound);
 +            invalidCiperAlgorithm.getElements().add(e.getMessage());
 +            scce.addException(invalidCiperAlgorithm);
 +
 +            throw scce;
 +        }
 +    }
 +
 +    @Override
 +    public void create(final User user, final UserTO userTO, final boolean storePassword) {
 +        SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
 +
 +        // roles
 +        for (Long roleKey : userTO.getRoles()) {
 +            Role role = roleDAO.find(roleKey);
 +            if (role == null) {
 +                LOG.warn("Ignoring unknown role with id {}", roleKey);
 +            } else {
 +                user.add(role);
 +            }
 +        }
 +
 +        // relationships
 +        for (RelationshipTO relationshipTO : userTO.getRelationships()) {
 +            AnyObject anyObject = anyObjectDAO.find(relationshipTO.getRightKey());
 +
 +            if (anyObject == null) {
 +                if (LOG.isDebugEnabled()) {
 +                    LOG.debug("Ignoring invalid anyObject " + relationshipTO.getRightKey());
 +                }
 +            } else {
 +                URelationship relationship = null;
 +                if (user.getKey() != null) {
 +                    relationship = user.getRelationship(anyObject.getKey());
 +                }
 +                if (relationship == null) {
 +                    relationship = entityFactory.newEntity(URelationship.class);
 +                    relationship.setRightEnd(anyObject);
 +                    relationship.setLeftEnd(user);
 +
 +                    user.add(relationship);
 +                }
 +            }
 +        }
 +
 +        // memberships
 +        for (MembershipTO membershipTO : userTO.getMemberships()) {
 +            Group group = groupDAO.find(membershipTO.getRightKey());
 +
 +            if (group == null) {
 +                if (LOG.isDebugEnabled()) {
 +                    LOG.debug("Ignoring invalid group " + membershipTO.getGroupName());
 +                }
 +            } else {
 +                UMembership membership = null;
 +                if (user.getKey() != null) {
 +                    membership = user.getMembership(group.getKey());
 +                }
 +                if (membership == null) {
 +                    membership = entityFactory.newEntity(UMembership.class);
 +                    membership.setRightEnd(group);
 +                    membership.setLeftEnd(user);
 +
 +                    user.add(membership);
 +                }
 +            }
 +        }
 +
 +        // realm, attributes, derived attributes, virtual attributes and resources
 +        fill(user, userTO, anyUtilsFactory.getInstance(AnyTypeKind.USER), scce);
 +
 +        // set password
 +        if (StringUtils.isBlank(userTO.getPassword()) || !storePassword) {
 +            LOG.debug("Password was not provided or not required to be stored");
 +        } else {
 +            setPassword(user, userTO.getPassword(), scce);
 +        }
 +
 +        // set username
 +        user.setUsername(userTO.getUsername());
 +
 +        // security question / answer
 +        if (userTO.getSecurityQuestion() != null) {
 +            SecurityQuestion securityQuestion = securityQuestionDAO.find(userTO.getSecurityQuestion());
 +            if (securityQuestion != null) {
 +                user.setSecurityQuestion(securityQuestion);
 +            }
 +        }
 +        user.setSecurityAnswer(userTO.getSecurityAnswer());
 +    }
 +
++    private boolean isPasswordMapped(final ExternalResource resource) {
++        boolean result = false;
++
++        Provision provision = resource.getProvision(anyTypeDAO.findUser());
++        if (provision != null && provision.getMapping() != null) {
++            result = CollectionUtils.exists(provision.getMapping().getItems(), new Predicate<MappingItem>() {
++
++                @Override
++                public boolean evaluate(final MappingItem item) {
++                    return item.isPassword();
++                }
++            });
++        }
++
++        return result;
++    }
++
 +    @Override
 +    public PropagationByResource update(final User toBeUpdated, final UserMod userMod) {
 +        // Re-merge any pending change from workflow tasks
 +        final User user = userDAO.save(toBeUpdated);
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +
 +        SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
 +
 +        Collection<String> currentResources = userDAO.findAllResourceNames(user);
 +
 +        // fetch connObjectKeys before update
 +        Map<String, String> oldConnObjectKeys = getConnObjectKeys(user);
 +
 +        // realm
 +        setRealm(user, userMod);
 +
 +        // password
 +        if (StringUtils.isNotBlank(userMod.getPassword())) {
 +            setPassword(user, userMod.getPassword(), scce);
 +            user.setChangePwdDate(new Date());
 +            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
 +        }
 +
 +        // username
 +        if (userMod.getUsername() != null && !userMod.getUsername().equals(user.getUsername())) {
 +            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
 +
 +            String oldUsername = user.getUsername();
 +            user.setUsername(userMod.getUsername());
 +
 +            if (oldUsername.equals(AuthContextUtils.getUsername())) {
 +                AuthContextUtils.updateUsername(userMod.getUsername());
 +            }
 +        }
 +
 +        // security question / answer:
 +        // userMod.getSecurityQuestion() is null => remove user security question and answer
 +        // userMod.getSecurityQuestion() == 0 => don't change anything
 +        // userMod.getSecurityQuestion() > 0 => update user security question and answer
 +        if (userMod.getSecurityQuestion() == null) {
 +            user.setSecurityQuestion(null);
 +            user.setSecurityAnswer(null);
 +        } else if (userMod.getSecurityQuestion() > 0) {
 +            SecurityQuestion securityQuestion = securityQuestionDAO.find(userMod.getSecurityQuestion());
 +            if (securityQuestion != null) {
 +                user.setSecurityQuestion(securityQuestion);
 +                user.setSecurityAnswer(userMod.getSecurityAnswer());
 +            }
 +        }
 +
 +        // roles
 +        CollectionUtils.forAllDo(userMod.getRolesToRemove(), new Closure<Long>() {
 +
 +            @Override
 +            public void execute(final Long roleKey) {
 +                Role role = roleDAO.find(roleKey);
 +                if (role == null) {
 +                    LOG.warn("Ignoring unknown role with id {}", roleKey);
 +                } else {
 +                    user.remove(role);
 +                }
 +            }
 +        });
 +        CollectionUtils.forAllDo(userMod.getRolesToAdd(), new Closure<Long>() {
 +
 +            @Override
 +            public void execute(final Long roleKey) {
 +                Role role = roleDAO.find(roleKey);
 +                if (role == null) {
 +                    LOG.warn("Ignoring unknown role with id {}", roleKey);
 +                } else {
 +                    user.add(role);
 +                }
 +            }
 +        });
 +
 +        // attributes, derived attributes, virtual attributes and resources
 +        propByRes.merge(fill(user, userMod, anyUtilsFactory.getInstance(AnyTypeKind.USER), scce));
 +
 +        Set<String> toBeDeprovisioned = new HashSet<>();
 +        Set<String> toBeProvisioned = new HashSet<>();
 +
 +        // relationships to be removed
 +        for (Long anyObjectKey : userMod.getRelationshipsToRemove()) {
 +            LOG.debug("Relationship to be removed for any object {}", anyObjectKey);
 +
 +            URelationship relationship = user.getRelationship(anyObjectKey);
 +            if (relationship == null) {
 +                LOG.warn("Invalid anyObject key specified for relationship to be removed: {}", anyObjectKey);
 +            } else {
 +                if (!userMod.getRelationshipsToAdd().contains(anyObjectKey)) {
 +                    user.remove(relationship);
 +                    toBeDeprovisioned.addAll(relationship.getRightEnd().getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // relationships to be added
 +        for (Long anyObjectKey : userMod.getRelationshipsToAdd()) {
 +            LOG.debug("Relationship to be added for any object {}", anyObjectKey);
 +
 +            AnyObject otherEnd = anyObjectDAO.find(anyObjectKey);
 +            if (otherEnd == null) {
 +                LOG.debug("Ignoring invalid any object {}", anyObjectKey);
 +            } else {
 +                URelationship relationship = user.getRelationship(otherEnd.getKey());
 +                if (relationship == null) {
 +                    relationship = entityFactory.newEntity(URelationship.class);
 +                    relationship.setRightEnd(otherEnd);
 +                    relationship.setLeftEnd(user);
 +
 +                    user.add(relationship);
 +
 +                    toBeProvisioned.addAll(otherEnd.getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // memberships to be removed
 +        for (Long groupKey : userMod.getMembershipsToRemove()) {
 +            LOG.debug("Membership to be removed for group {}", groupKey);
 +
 +            UMembership membership = user.getMembership(groupKey);
 +            if (membership == null) {
 +                LOG.debug("Invalid group key specified for membership to be removed: {}", groupKey);
 +            } else {
 +                if (!userMod.getMembershipsToAdd().contains(groupKey)) {
 +                    user.remove(membership);
 +                    toBeDeprovisioned.addAll(membership.getRightEnd().getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // memberships to be added
 +        for (Long groupKey : userMod.getMembershipsToAdd()) {
 +            LOG.debug("Membership to be added for group {}", groupKey);
 +
 +            Group group = groupDAO.find(groupKey);
 +            if (group == null) {
 +                LOG.debug("Ignoring invalid group {}", groupKey);
 +            } else {
 +                UMembership membership = user.getMembership(group.getKey());
 +                if (membership == null) {
 +                    membership = entityFactory.newEntity(UMembership.class);
 +                    membership.setRightEnd(group);
 +                    membership.setLeftEnd(user);
 +
 +                    user.add(membership);
 +
 +                    toBeProvisioned.addAll(group.getResourceNames());
++
++                    // SYNCOPE-686: if password is invertible and we are adding resources with password mapping,
++                    // ensure that they are counted for password propagation
++                    if (toBeUpdated.canDecodePassword()) {
++                        for (ExternalResource resource : group.getResources()) {
++                            if (isPasswordMapped(resource)) {
++                                if (userMod.getPwdPropRequest() == null) {
++                                    userMod.setPwdPropRequest(new StatusMod());
++                                }
++
++                                userMod.getPwdPropRequest().getResources().add(resource.getKey());
++                            }
++                        }
++                    }
 +                }
 +            }
 +        }
 +
 +        propByRes.addAll(ResourceOperation.DELETE, toBeDeprovisioned);
 +        propByRes.addAll(ResourceOperation.UPDATE, toBeProvisioned);
 +
 +        // In case of new memberships all current resources need to be updated in order to propagate new group
 +        // attribute values.
 +        if (!toBeDeprovisioned.isEmpty() || !toBeProvisioned.isEmpty()) {
 +            currentResources.removeAll(toBeDeprovisioned);
 +            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
 +        }
 +
 +        // check if some connObjectKey was changed by the update above
 +        Map<String, String> newcCnnObjectKeys = getConnObjectKeys(user);
 +        for (Map.Entry<String, String> entry : oldConnObjectKeys.entrySet()) {
 +            if (newcCnnObjectKeys.containsKey(entry.getKey())
 +                    && !entry.getValue().equals(newcCnnObjectKeys.get(entry.getKey()))) {
 +
 +                propByRes.addOldConnObjectKey(entry.getKey(), entry.getValue());
 +                propByRes.add(ResourceOperation.UPDATE, entry.getKey());
 +            }
 +        }
 +
 +        return propByRes;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getUserTO(final User user, final boolean details) {
 +        UserTO userTO = new UserTO();
 +
 +        BeanUtils.copyProperties(user, userTO, IGNORE_PROPERTIES);
 +
 +        if (user.getSecurityQuestion() != null) {
 +            userTO.setSecurityQuestion(user.getSecurityQuestion().getKey());
 +        }
 +
 +        if (details) {
 +            virAttrHander.retrieveVirAttrValues(user);
 +        }
 +
 +        fillTO(userTO, user.getRealm().getFullPath(), user.getAuxClasses(),
 +                user.getPlainAttrs(), user.getDerAttrs(), user.getVirAttrs(), userDAO.findAllResources(user));
 +
 +        if (details) {
 +            // roles
 +            CollectionUtils.collect(user.getRoles(), new Transformer<Role, Long>() {
 +
 +                @Override
 +                public Long transform(final Role role) {
 +                    return role.getKey();
 +                }
 +            }, userTO.getRoles());
 +
 +            // relationships
 +            CollectionUtils.collect(user.getRelationships(), new Transformer<URelationship, RelationshipTO>() {
 +
 +                @Override
 +                public RelationshipTO transform(final URelationship relationship) {
 +                    return UserDataBinderImpl.this.getRelationshipTO(relationship);
 +                }
 +
 +            }, userTO.getRelationships());
 +
 +            // memberships
 +            CollectionUtils.collect(user.getMemberships(), new Transformer<UMembership, MembershipTO>() {
 +
 +                @Override
 +                public MembershipTO transform(final UMembership membership) {
 +                    return UserDataBinderImpl.this.getMembershipTO(membership);
 +                }
 +            }, userTO.getMemberships());
 +
 +            // dynamic memberships
 +            CollectionUtils.collect(userDAO.findDynRoleMemberships(user), new Transformer<Role, Long>() {
 +
 +                @Override
 +                public Long transform(final Role role) {
 +                    return role.getKey();
 +                }
 +            }, userTO.getDynRoles());
 +            CollectionUtils.collect(userDAO.findDynGroupMemberships(user), new Transformer<Group, Long>() {
 +
 +                @Override
 +                public Long transform(final Group group) {
 +                    return group.getKey();
 +                }
 +            }, userTO.getDynGroups());
 +        }
 +
 +        return userTO;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getUserTO(final String username) {
 +        return getUserTO(userDAO.authFind(username), true);
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getUserTO(final Long key) {
 +        return getUserTO(userDAO.authFind(key), true);
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
index 1c704df,0000000..631cbe3
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
@@@ -1,498 -1,0 +1,498 @@@
 +/*
 + * 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.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.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.mod.AttrMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.AttrTO;
 +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.core.persistence.api.dao.ExternalResourceDAO;
 +import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 +import org.apache.syncope.core.persistence.api.entity.VirAttr;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.core.provisioning.api.WorkflowResult;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 +import org.apache.syncope.core.misc.ConnObjectUtils;
 +import org.apache.syncope.core.misc.MappingUtils;
 +import org.apache.syncope.core.misc.jexl.JexlUtils;
 +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 +import org.apache.syncope.core.persistence.api.entity.Any;
 +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 +import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 +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.VirAttrHandler;
 +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 {
 +
 +    protected static final Logger LOG = LoggerFactory.getLogger(PropagationManager.class);
 +
 +    @Autowired
 +    protected AnyObjectDAO anyObjectDAO;
 +
 +    /**
 +     * User DAO.
 +     */
 +    @Autowired
 +    protected UserDAO userDAO;
 +
 +    /**
 +     * Group DAO.
 +     */
 +    @Autowired
 +    protected GroupDAO groupDAO;
 +
 +    /**
 +     * Resource DAO.
 +     */
 +    @Autowired
 +    protected ExternalResourceDAO resourceDAO;
 +
 +    @Autowired
 +    protected EntityFactory entityFactory;
 +
 +    /**
 +     * ConnObjectUtils.
 +     */
 +    @Autowired
 +    protected ConnObjectUtils connObjectUtils;
 +
 +    @Autowired
 +    protected VirAttrHandler virAttrHandler;
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectCreateTasks(
 +            final WorkflowResult<Long> wfResult,
 +            final Collection<AttrTO> vAttrs,
 +            final Collection<String> noPropResourceNames) {
 +
 +        return getAnyObjectCreateTasks(wfResult.getResult(), vAttrs, wfResult.getPropByRes(), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectCreateTasks(
 +            final Long key,
 +            final Collection<AttrTO> vAttrs,
 +            final PropagationByResource propByRes,
 +            final Collection<String> noPropResourceNames) {
 +
 +        AnyObject anyObject = anyObjectDAO.authFind(key);
 +        if (vAttrs != null && !vAttrs.isEmpty()) {
 +            virAttrHandler.fillVirtual(anyObject, vAttrs);
 +        }
 +
 +        return getCreateTaskIds(anyObject, null, null, propByRes, noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserCreateTasks(
 +            final Long key,
 +            final Boolean enable,
 +            final PropagationByResource propByRes,
 +            final String password,
 +            final Collection<AttrTO> vAttrs,
 +            final Collection<String> noPropResourceNames) {
 +
 +        User user = userDAO.authFind(key);
 +        if (vAttrs != null && !vAttrs.isEmpty()) {
 +            virAttrHandler.fillVirtual(user, vAttrs);
 +        }
 +        return getCreateTaskIds(user, password, enable, propByRes, noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupCreateTasks(
 +            final WorkflowResult<Long> wfResult,
 +            final Collection<AttrTO> vAttrs,
 +            final Collection<String> noPropResourceNames) {
 +
 +        return getGroupCreateTasks(wfResult.getResult(), vAttrs, wfResult.getPropByRes(), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupCreateTasks(
 +            final Long key,
 +            final Collection<AttrTO> vAttrs,
 +            final PropagationByResource propByRes,
 +            final Collection<String> noPropResourceNames) {
 +
 +        Group group = groupDAO.authFind(key);
 +        if (vAttrs != null && !vAttrs.isEmpty()) {
 +            virAttrHandler.fillVirtual(group, vAttrs);
 +        }
 +
 +        return getCreateTaskIds(group, null, null, propByRes, noPropResourceNames);
 +    }
 +
 +    protected List<PropagationTask> getCreateTaskIds(final Any<?, ?, ?> any,
 +            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(any, password, true, null, null, enable, false, propByRes);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectUpdateTasks(
 +            final WorkflowResult<Long> wfResult,
 +            final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated,
 +            final Set<String> noPropResourceNames) {
 +
 +        AnyObject anyObject = anyObjectDAO.authFind(wfResult.getResult());
 +        return getUpdateTasks(anyObject, null, false, null,
 +                vAttrsToBeRemoved, vAttrsToBeUpdated, wfResult.getPropByRes(), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserUpdateTasks(final Long key, final Boolean enable,
 +            final Collection<String> noPropResourceNames) {
 +
 +        return getUpdateTasks(
 +                userDAO.find(key), // user 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);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserUpdateTasks(final WorkflowResult<Pair<UserMod, Boolean>> wfResult,
 +            final boolean changePwd, final Collection<String> noPropResourceNames) {
 +
 +        User user = userDAO.authFind(wfResult.getResult().getKey().getKey());
 +        return getUpdateTasks(user,
 +                wfResult.getResult().getKey().getPassword(),
 +                changePwd,
 +                wfResult.getResult().getValue(),
 +                wfResult.getResult().getKey().getVirAttrsToRemove(),
 +                wfResult.getResult().getKey().getVirAttrsToUpdate(),
 +                wfResult.getPropByRes(),
 +                noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserUpdateTasks(final WorkflowResult<Pair<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 = getUserUpdateTasks(wfResult, false, null);
 +        } else {
 +            // b. generate the propagation task list in two phases: first the ones containing password,
 +            // the the rest (with no password)
 +            PropagationByResource origPropByRes = new PropagationByResource();
 +            origPropByRes.merge(wfResult.getPropByRes());
 +
-             Set<String> pwdResourceNames = new HashSet<>(userMod.getPwdPropRequest().getResourceNames());
++            Set<String> pwdResourceNames = new HashSet<>(userMod.getPwdPropRequest().getResources());
 +            Collection<String> currentResourceNames = userDAO.findAllResourceNames(userDAO.authFind(userMod.getKey()));
 +            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(getUserUpdateTasks(wfResult, true, toBeExcluded));
 +            }
 +
 +            PropagationByResource nonPwdPropByRes = new PropagationByResource();
 +            nonPwdPropByRes.merge(origPropByRes);
 +            nonPwdPropByRes.removeAll(pwdResourceNames);
 +            nonPwdPropByRes.purge();
 +            if (!nonPwdPropByRes.isEmpty()) {
 +                tasks.addAll(getUserUpdateTasks(wfResult, false, pwdResourceNames));
 +            }
 +        }
 +
 +        return tasks;
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupUpdateTasks(final WorkflowResult<Long> wfResult,
 +            final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated,
 +            final Set<String> noPropResourceNames) {
 +
 +        Group group = groupDAO.authFind(wfResult.getResult());
 +        return getUpdateTasks(group, null, false, null,
 +                vAttrsToBeRemoved, vAttrsToBeUpdated, wfResult.getPropByRes(), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUpdateTasks(final Any<?, ?, ?> any,
 +            final String password, final boolean changePwd, final Boolean enable,
 +            final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated,
 +            final PropagationByResource propByRes, final Collection<String> noPropResourceNames) {
 +
 +        PropagationByResource localPropByRes = virAttrHandler.fillVirtual(
 +                any,
 +                vAttrsToBeRemoved == null
 +                        ? Collections.<String>emptySet()
 +                        : vAttrsToBeRemoved, vAttrsToBeUpdated == null
 +                        ? Collections.<AttrMod>emptySet()
 +                        : vAttrsToBeUpdated);
 +
 +        if (propByRes == null || propByRes.isEmpty()) {
 +            localPropByRes.addAll(ResourceOperation.UPDATE, any.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);
 +            }
 +        }
 +
 +        return createTasks(
 +                any, password, changePwd, vAttrsToBeRemoved, vAttrsToBeUpdatedMap, enable, false, localPropByRes);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectDeleteTasks(final Long anyObjectKey) {
 +        return getAnyObjectDeleteTasks(anyObjectKey, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectDeleteTasks(final Long anyObjectKey, final String noPropResourceName) {
 +        return getAnyObjectDeleteTasks(anyObjectKey, Collections.<String>singleton(noPropResourceName));
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectDeleteTasks(
 +            final Long anyObjectKey, final Collection<String> noPropResourceNames) {
 +
 +        AnyObject anyObject = anyObjectDAO.authFind(anyObjectKey);
 +        return getDeleteTaskIds(anyObject, anyObject.getResourceNames(), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectDeleteTasks(
 +            final Long groupKey, final Set<String> resourceNames, final Collection<String> noPropResourceNames) {
 +
 +        AnyObject anyObject = anyObjectDAO.authFind(groupKey);
 +        return getDeleteTaskIds(anyObject, resourceNames, noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserDeleteTasks(final Long userKey, final Collection<String> noPropResourceNames) {
 +        User user = userDAO.authFind(userKey);
 +        return getDeleteTaskIds(user, userDAO.findAllResourceNames(user), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserDeleteTasks(
 +            final Long userKey, final Set<String> resourceNames, final Collection<String> noPropResourceNames) {
 +
 +        User user = userDAO.authFind(userKey);
 +        return getDeleteTaskIds(user, resourceNames, noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupDeleteTasks(final Long groupKey) {
 +        return getGroupDeleteTasks(groupKey, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupDeleteTasks(final Long groupKey, final String noPropResourceName) {
 +        return getGroupDeleteTasks(groupKey, Collections.<String>singleton(noPropResourceName));
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupDeleteTasks(
 +            final Long groupKey, final Collection<String> noPropResourceNames) {
 +
 +        Group group = groupDAO.authFind(groupKey);
 +        return getDeleteTaskIds(group, group.getResourceNames(), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupDeleteTasks(
 +            final Long groupKey, final Set<String> resourceNames, final Collection<String> noPropResourceNames) {
 +
 +        Group group = groupDAO.authFind(groupKey);
 +        return getDeleteTaskIds(group, resourceNames, noPropResourceNames);
 +    }
 +
 +    protected List<PropagationTask> getDeleteTaskIds(
 +            final Any<?, ?, ?> any,
 +            final Collection<String> resourceNames,
 +            final Collection<String> noPropResourceNames) {
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.set(ResourceOperation.DELETE, resourceNames);
 +        if (noPropResourceNames != null && !noPropResourceNames.isEmpty()) {
 +            propByRes.get(ResourceOperation.DELETE).removeAll(noPropResourceNames);
 +        }
 +        return createTasks(any, null, false, null, null, false, true, propByRes);
 +    }
 +
 +    /**
 +     * Create propagation tasks.
 +     *
 +     * @param any user / group 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 enable whether user must be enabled or not
 +     * @param deleteOnResource whether user / group 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 Any<?, ?, ?> any,
 +            final String password, final boolean changePwd,
 +            final Set<String> vAttrsToBeRemoved, final Map<String, AttrMod> vAttrsToBeUpdated,
 +            final Boolean enable, final boolean deleteOnResource, final PropagationByResource propByRes) {
 +
 +        LOG.debug("Provisioning any {}:\n{}", any, propByRes);
 +
 +        if (!propByRes.get(ResourceOperation.CREATE).isEmpty()
 +                && vAttrsToBeRemoved != null && vAttrsToBeUpdated != null) {
 +
 +            virAttrHandler.retrieveVirAttrValues(any);
 +
 +            // update vAttrsToBeUpdated as well
 +            for (VirAttr<?> virAttr : any.getVirAttrs()) {
 +                String schema = virAttr.getSchema().getKey();
 +
 +                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);
 +
 +        List<PropagationTask> tasks = new ArrayList<>();
 +
 +        for (ResourceOperation operation : ResourceOperation.values()) {
 +            for (String resourceName : propByRes.get(operation)) {
 +                ExternalResource resource = resourceDAO.find(resourceName);
 +                Provision provision = resource == null ? null : resource.getProvision(any.getType());
 +                if (resource == null) {
 +                    LOG.error("Invalid resource name specified: {}, ignoring...", resourceName);
 +                } else if (provision == null) {
 +                    LOG.error("No provision specified on resource {} for type {}, ignoring...",
 +                            resource, any.getType());
 +                } else if (MappingUtils.getMappingItems(provision, MappingPurpose.PROPAGATION).isEmpty()) {
 +                    LOG.warn("Requesting propagation for {} but no propagation mapping provided for {}",
 +                            any.getType(), resource);
 +                } else {
 +                    PropagationTask task = entityFactory.newEntity(PropagationTask.class);
 +                    task.setResource(resource);
 +                    task.setObjectClassName(
 +                            resource.getProvision(any.getType()).getObjectClass().getObjectClassValue());
 +                    task.setAnyTypeKind(any.getType().getKind());
 +                    if (!deleteOnResource) {
 +                        task.setAnyKey(any.getKey());
 +                    }
 +                    task.setPropagationOperation(operation);
 +                    task.setPropagationMode(resource.getPropagationMode());
 +                    task.setOldConnObjectKey(propByRes.getOldConnObjectKey(resource.getKey()));
 +
 +                    Pair<String, Set<Attribute>> preparedAttrs = MappingUtils.prepareAttrs(
 +                            any, password, changePwd, vAttrsToBeRemoved, vAttrsToBeUpdated, enable, provision);
 +                    task.setConnObjectKey(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 : MappingUtils.getMappingItems(provision, MappingPurpose.PROPAGATION)) {
 +                        if (!item.isConnObjectKey()
 +                                && JexlUtils.evaluateMandatoryCondition(item.getMandatoryCondition(), any)) {
 +
 +                            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;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --cc core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
index 7a4ba38,0000000..db95d8d
mode 100644,000000..100644
--- a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
+++ b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
@@@ -1,857 -1,0 +1,857 @@@
 +/*
 + * 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.workflow.activiti;
 +
 +import com.fasterxml.jackson.databind.JsonNode;
 +import com.fasterxml.jackson.databind.ObjectMapper;
 +import com.fasterxml.jackson.databind.node.ObjectNode;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.OutputStream;
 +import java.util.ArrayList;
 +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 javax.annotation.Resource;
 +import javax.ws.rs.NotFoundException;
 +import org.activiti.bpmn.converter.BpmnXMLConverter;
 +import org.activiti.bpmn.model.BpmnModel;
 +import org.activiti.editor.constants.ModelDataJsonConstants;
 +import org.activiti.editor.language.json.converter.BpmnJsonConverter;
 +import org.activiti.engine.ActivitiException;
 +import org.activiti.engine.ProcessEngine;
 +import org.activiti.engine.form.FormProperty;
 +import org.activiti.engine.form.FormType;
 +import org.activiti.engine.form.TaskFormData;
 +import org.activiti.engine.history.HistoricActivityInstance;
 +import org.activiti.engine.history.HistoricDetail;
 +import org.activiti.engine.history.HistoricTaskInstance;
 +import org.activiti.engine.impl.persistence.entity.HistoricFormPropertyEntity;
 +import org.activiti.engine.query.Query;
 +import org.activiti.engine.repository.Model;
 +import org.activiti.engine.repository.ProcessDefinition;
 +import org.activiti.engine.runtime.ProcessInstance;
 +import org.activiti.engine.task.Task;
 +import org.apache.commons.io.IOUtils;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.commons.lang3.tuple.ImmutablePair;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.to.WorkflowFormPropertyTO;
 +import org.apache.syncope.common.lib.to.WorkflowFormTO;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.common.lib.types.WorkflowFormPropertyType;
 +import org.apache.syncope.core.misc.security.AuthContextUtils;
 +import org.apache.syncope.core.misc.spring.BeanUtils;
 +import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
 +import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.core.provisioning.api.WorkflowResult;
 +import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 +import org.apache.syncope.core.workflow.api.WorkflowDefinitionFormat;
 +import org.apache.syncope.core.workflow.api.WorkflowException;
 +import org.apache.syncope.core.workflow.java.AbstractUserWorkflowAdapter;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.transaction.annotation.Transactional;
 +
 +/**
 + * Activiti {@link http://www.activiti.org/} based implementation.
 + */
 +public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
 +
 +    protected static final String[] PROPERTY_IGNORE_PROPS = { "type" };
 +
 +    public static final String WF_PROCESS_ID = "userWorkflow";
 +
 +    public static final String WF_PROCESS_RESOURCE = "userWorkflow.bpmn20.xml";
 +
 +    public static final String WF_DGRM_RESOURCE = "userWorkflow.userWorkflow.png";
 +
 +    public static final String USER = "user";
 +
 +    public static final String WF_EXECUTOR = "wfExecutor";
 +
 +    public static final String FORM_SUBMITTER = "formSubmitter";
 +
 +    public static final String USER_TO = "userTO";
 +
 +    public static final String ENABLED = "enabled";
 +
 +    public static final String USER_MOD = "userMod";
 +
 +    public static final String EMAIL_KIND = "emailKind";
 +
 +    public static final String TASK = "task";
 +
 +    public static final String TOKEN = "token";
 +
 +    public static final String PASSWORD = "password";
 +
 +    public static final String PROP_BY_RESOURCE = "propByResource";
 +
 +    public static final String PROPAGATE_ENABLE = "propagateEnable";
 +
 +    public static final String ENCRYPTED_PWD = "encryptedPwd";
 +
 +    public static final String TASK_IS_FORM = "taskIsForm";
 +
 +    public static final String MODEL_DATA_JSON_MODEL = "model";
 +
 +    public static final String STORE_PASSWORD = "storePassword";
 +
 +    public static final String EVENT = "event";
 +
 +    @Resource(name = "adminUser")
 +    protected String adminUser;
 +
 +    @Autowired
 +    protected ProcessEngine engine;
 +
 +    @Autowired
 +    protected UserDataBinder userDataBinder;
 +
 +    @Override
 +    public String getPrefix() {
 +        return "ACT_";
 +    }
 +
 +    protected void throwException(final ActivitiException e, final String defaultMessage) {
 +        if (e.getCause() != null) {
 +            if (e.getCause().getCause() instanceof SyncopeClientException) {
 +                throw (SyncopeClientException) e.getCause().getCause();
 +            } else if (e.getCause().getCause() instanceof ParsingValidationException) {
 +                throw (ParsingValidationException) e.getCause().getCause();
 +            } else if (e.getCause().getCause() instanceof InvalidEntityException) {
 +                throw (InvalidEntityException) e.getCause().getCause();
 +            }
 +        }
 +
 +        throw new WorkflowException(defaultMessage, e);
 +    }
 +
 +    protected void updateStatus(final User user) {
 +        List<Task> tasks = engine.getTaskService().createTaskQuery().processInstanceId(user.getWorkflowId()).list();
 +        if (tasks.isEmpty() || tasks.size() > 1) {
 +            LOG.warn("While setting user status: unexpected task number ({})", tasks.size());
 +        } else {
 +            user.setStatus(tasks.get(0).getTaskDefinitionKey());
 +        }
 +    }
 +
 +    protected String getFormTask(final User user) {
 +        String result = null;
 +
 +        List<Task> tasks = engine.getTaskService().createTaskQuery().processInstanceId(user.getWorkflowId()).list();
 +        if (tasks.isEmpty() || tasks.size() > 1) {
 +            LOG.warn("While checking if form task: unexpected task number ({})", tasks.size());
 +        } else {
 +            try {
 +                TaskFormData formData = engine.getFormService().getTaskFormData(tasks.get(0).getId());
 +                if (formData != null && !formData.getFormProperties().isEmpty()) {
 +                    result = tasks.get(0).getId();
 +                }
 +            } catch (ActivitiException e) {
 +                LOG.warn("Could not get task form data", e);
 +            }
 +        }
 +
 +        return result;
 +    }
 +
 +    protected Set<String> getPerformedTasks(final User user) {
 +        final Set<String> result = new HashSet<>();
 +
 +        for (HistoricActivityInstance task : engine.getHistoryService().createHistoricActivityInstanceQuery().
 +                executionId(user.getWorkflowId()).list()) {
 +
 +            result.add(task.getActivityId());
 +        }
 +
 +        return result;
 +    }
 +
 +    /**
 +     * Saves resources to be propagated and password for later - after form submission - propagation.
 +     */
 +    protected void saveForFormSubmit(final User user, final String password, final PropagationByResource propByRes) {
 +        String formTaskId = getFormTask(user);
 +        if (formTaskId != null) {
 +            // SYNCOPE-238: This is needed to simplify the task query in this.getForms()
 +            engine.getTaskService().setVariableLocal(formTaskId, TASK_IS_FORM, Boolean.TRUE);
 +            engine.getRuntimeService().setVariable(user.getWorkflowId(), PROP_BY_RESOURCE, propByRes);
 +            if (propByRes != null) {
 +                propByRes.clear();
 +            }
 +
 +            if (StringUtils.isNotBlank(password)) {
 +                engine.getRuntimeService().setVariable(user.getWorkflowId(), ENCRYPTED_PWD, encrypt(password));
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public WorkflowResult<Pair<Long, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
 +            final boolean storePassword) {
 +
 +        return create(userTO, disablePwdPolicyCheck, null, storePassword);
 +    }
 +
 +    @Override
 +    public WorkflowResult<Pair<Long, Boolean>> create(final UserTO userTO, final boolean storePassword) {
 +        return create(userTO, false, storePassword);
 +    }
 +
 +    @Override
 +    public WorkflowResult<Pair<Long, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
 +            final Boolean enabled, final boolean storePassword) {
 +
 +        Map<String, Object> variables = new HashMap<>();
 +        variables.put(WF_EXECUTOR, AuthContextUtils.getUsername());
 +        variables.put(USER_TO, userTO);
 +        variables.put(ENABLED, enabled);
 +        variables.put(STORE_PASSWORD, storePassword);
 +
 +        ProcessInstance processInstance = null;
 +        try {
 +            processInstance = engine.getRuntimeService().startProcessInstanceByKey(WF_PROCESS_ID, variables);
 +        } catch (ActivitiException e) {
 +            throwException(e, "While starting " + WF_PROCESS_ID + " instance");
 +        }
 +
 +        User user = engine.getRuntimeService().getVariable(processInstance.getProcessInstanceId(), USER, User.class);
 +
 +        Boolean updatedEnabled =
 +                engine.getRuntimeService().getVariable(processInstance.getProcessInstanceId(), ENABLED, Boolean.class);
 +        if (updatedEnabled != null) {
 +            user.setSuspended(!updatedEnabled);
 +        }
 +
 +        // this will make UserValidator not to consider password policies at all
 +        if (disablePwdPolicyCheck) {
 +            user.removeClearPassword();
 +        }
 +
 +        updateStatus(user);
 +        user = userDAO.save(user);
 +
 +        Boolean propagateEnable = engine.getRuntimeService().getVariable(
 +                processInstance.getProcessInstanceId(), PROPAGATE_ENABLE, Boolean.class);
 +        if (propagateEnable == null) {
 +            propagateEnable = enabled;
 +        }
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.set(ResourceOperation.CREATE, userDAO.findAllResourceNames(user));
 +
 +        saveForFormSubmit(user, userTO.getPassword(), propByRes);
 +
 +        return new WorkflowResult<Pair<Long, Boolean>>(
 +                new ImmutablePair<>(user.getKey(), propagateEnable), propByRes, getPerformedTasks(user));
 +    }
 +
 +    protected Set<String> doExecuteTask(final User user, final String task, final Map<String, Object> moreVariables) {
 +        Set<String> preTasks = getPerformedTasks(user);
 +
 +        Map<String, Object> variables = new HashMap<>();
 +        variables.put(WF_EXECUTOR, AuthContextUtils.getUsername());
 +        variables.put(TASK, task);
 +
 +        // using BeanUtils to access all user's properties and trigger lazy loading - we are about to
 +        // serialize a User instance for availability within workflow tasks, and this breaks transactions
 +        BeanUtils.copyProperties(user, entityFactory.newEntity(User.class));
 +        variables.put(USER, user);
 +
 +        if (moreVariables != null && !moreVariables.isEmpty()) {
 +            variables.putAll(moreVariables);
 +        }
 +
 +        if (StringUtils.isBlank(user.getWorkflowId())) {
 +            throw new WorkflowException(new NotFoundException("Empty workflow id for " + user));
 +        }
 +
 +        List<Task> tasks = engine.getTaskService().createTaskQuery().processInstanceId(user.getWorkflowId()).list();
 +        if (tasks.size() == 1) {
 +            try {
 +                engine.getTaskService().complete(tasks.get(0).getId(), variables);
 +            } catch (ActivitiException e) {
 +                throwException(e, "While completing task '" + tasks.get(0).getName() + "' for " + user);
 +            }
 +        } else {
 +            LOG.warn("Expected a single task, found {}", tasks.size());
 +        }
 +
 +        Set<String> postTasks = getPerformedTasks(user);
 +        postTasks.removeAll(preTasks);
 +        postTasks.add(task);
 +        return postTasks;
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Long> doActivate(final User user, final String token) {
 +        Set<String> tasks = doExecuteTask(user, "activate", Collections.singletonMap(TOKEN, (Object) token));
 +
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, tasks);
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Pair<UserMod, Boolean>> doUpdate(final User user, final UserMod userMod) {
 +        Set<String> tasks = doExecuteTask(user, "update", Collections.singletonMap(USER_MOD, (Object) userMod));
 +
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        PropagationByResource propByRes = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), PROP_BY_RESOURCE, PropagationByResource.class);
 +        UserMod updatedMod = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), USER_MOD, UserMod.class);
 +
 +        saveForFormSubmit(updated, updatedMod.getPassword(), propByRes);
 +
 +        Boolean propagateEnable = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), PROPAGATE_ENABLE, Boolean.class);
 +
 +        return new WorkflowResult<Pair<UserMod, Boolean>>(
 +                new ImmutablePair<>(updatedMod, propagateEnable), propByRes, tasks);
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Long> doSuspend(final User user) {
 +        Set<String> performedTasks = doExecuteTask(user, "suspend", null);
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, performedTasks);
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Long> doReactivate(final User user) {
 +        Set<String> performedTasks = doExecuteTask(user, "reactivate", null);
 +        updateStatus(user);
 +
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, performedTasks);
 +    }
 +
 +    @Override
 +    protected void doRequestPasswordReset(final User user) {
 +        Map<String, Object> variables = new HashMap<>(2);
 +        variables.put(USER_TO, userDataBinder.getUserTO(user, true));
 +        variables.put(EVENT, "requestPasswordReset");
 +
 +        doExecuteTask(user, "requestPasswordReset", variables);
 +        userDAO.save(user);
 +    }
 +
 +    @Override
 +    protected void doConfirmPasswordReset(final User user, final String token, final String password) {
 +        Map<String, Object> variables = new HashMap<>(4);
 +        variables.put(TOKEN, token);
 +        variables.put(PASSWORD, password);
 +        variables.put(USER_TO, userDataBinder.getUserTO(user, true));
 +        variables.put(EVENT, "confirmPasswordReset");
 +
 +        doExecuteTask(user, "confirmPasswordReset", variables);
 +        userDAO.save(user);
 +    }
 +
 +    @Override
 +    protected void doDelete(final User user) {
 +        doExecuteTask(user, "delete", null);
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceNames(user));
 +
 +        saveForFormSubmit(user, null, propByRes);
 +
 +        if (engine.getRuntimeService().createProcessInstanceQuery().
 +                processInstanceId(user.getWorkflowId()).active().list().isEmpty()) {
 +
 +            userDAO.delete(user.getKey());
 +
 +            if (!engine.getHistoryService().createHistoricProcessInstanceQuery().
 +                    processInstanceId(user.getWorkflowId()).list().isEmpty()) {
 +
 +                engine.getHistoryService().deleteHistoricProcessInstance(user.getWorkflowId());
 +            }
 +        } else {
 +            updateStatus(user);
 +            userDAO.save(user);
 +        }
 +    }
 +
 +    @Override
 +    public WorkflowResult<Long> execute(final UserTO userTO, final String taskId) {
 +        User user = userDAO.authFind(userTO.getKey());
 +
 +        final Map<String, Object> variables = new HashMap<>();
 +        variables.put(USER_TO, userTO);
 +
 +        Set<String> performedTasks = doExecuteTask(user, taskId, variables);
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, performedTasks);
 +    }
 +
 +    protected ProcessDefinition getProcessDefinition() {
 +        try {
 +            return engine.getRepositoryService().createProcessDefinitionQuery().processDefinitionKey(
 +                    ActivitiUserWorkflowAdapter.WF_PROCESS_ID).latestVersion().singleResult();
 +        } catch (ActivitiException e) {
 +            throw new WorkflowException("While accessing process " + ActivitiUserWorkflowAdapter.WF_PROCESS_ID, e);
 +        }
 +
 +    }
 +
 +    protected Model getModel(final ProcessDefinition procDef) {
 +        try {
 +            Model model = engine.getRepositoryService().createModelQuery().
 +                    deploymentId(procDef.getDeploymentId()).singleResult();
 +            if (model == null) {
 +                throw new NotFoundException("Could not find Model for deployment " + procDef.getDeploymentId());
 +            }
 +            return model;
 +        } catch (Exception e) {
 +            throw new WorkflowException("While accessing process " + ActivitiUserWorkflowAdapter.WF_PROCESS_ID, e);
 +        }
 +    }
 +
 +    protected void exportProcessResource(final String resourceName, final OutputStream os) {
 +        ProcessDefinition procDef = getProcessDefinition();
 +
 +        InputStream procDefIS = engine.getRepositoryService().getResourceAsStream(procDef.getDeploymentId(),
 +                resourceName);
 +        try {
 +            IOUtils.copy(procDefIS, os);
 +        } catch (IOException e) {
 +            LOG.error("While exporting workflow definition {}", procDef.getKey(), e);
 +        } finally {
 +            IOUtils.closeQuietly(procDefIS);
 +        }
 +    }
 +
 +    protected void exportProcessModel(final OutputStream os) {
 +        Model model = getModel(getProcessDefinition());
 +
 +        ObjectMapper objectMapper = new ObjectMapper();
 +        try {
 +            ObjectNode modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
 +            modelNode.put(ModelDataJsonConstants.MODEL_ID, model.getId());
 +            modelNode.replace(MODEL_DATA_JSON_MODEL,
 +                    objectMapper.readTree(engine.getRepositoryService().getModelEditorSource(model.getId())));
 +
 +            os.write(modelNode.toString().getBytes());
 +        } catch (IOException e) {
 +            LOG.error("While exporting workflow definition {}", model.getId(), e);
 +        }
 +    }
 +
 +    @Override
 +    public void exportDefinition(final WorkflowDefinitionFormat format, final OutputStream os) {
 +        switch (format) {
 +            case JSON:
 +                exportProcessModel(os);
 +                break;
 +
 +            case XML:
 +            default:
 +                exportProcessResource(WF_PROCESS_RESOURCE, os);
 +        }
 +    }
 +
 +    @Override
 +    public void exportDiagram(final OutputStream os) {
 +        exportProcessResource(WF_DGRM_RESOURCE, os);
 +    }
 +
 +    @Override
 +    public void importDefinition(final WorkflowDefinitionFormat format, final String definition) {
 +        Model model = getModel(getProcessDefinition());
 +        switch (format) {
 +            case JSON:
 +                JsonNode definitionNode;
 +                try {
 +                    definitionNode = new ObjectMapper().readTree(definition);
 +                    if (definitionNode.has(MODEL_DATA_JSON_MODEL)) {
 +                        definitionNode = definitionNode.get(MODEL_DATA_JSON_MODEL);
 +                    }
 +                    if (!definitionNode.has(BpmnJsonConverter.EDITOR_CHILD_SHAPES)) {
 +                        throw new IllegalArgumentException(
 +                                "Could not find JSON node " + BpmnJsonConverter.EDITOR_CHILD_SHAPES);
 +                    }
 +
 +                    BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(definitionNode);
 +                    ActivitiImportUtils.fromXML(engine, new BpmnXMLConverter().convertToXML(bpmnModel));
 +                } catch (Exception e) {
 +                    throw new WorkflowException("While updating process "
 +                            + ActivitiUserWorkflowAdapter.WF_PROCESS_RESOURCE, e);
 +                }
 +
 +                ActivitiImportUtils.fromJSON(
 +                        engine, definitionNode.toString().getBytes(), getProcessDefinition(), model);
 +                break;
 +
 +            case XML:
 +            default:
 +                ActivitiImportUtils.fromXML(engine, definition.getBytes());
 +
 +                ActivitiImportUtils.fromJSON(engine, getProcessDefinition(), model);
 +        }
 +    }
 +
 +    protected WorkflowFormPropertyType fromActivitiFormType(final FormType activitiFormType) {
 +        WorkflowFormPropertyType result = WorkflowFormPropertyType.String;
 +
 +        if ("string".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.String;
 +        }
 +        if ("long".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Long;
 +        }
 +        if ("enum".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Enum;
 +        }
 +        if ("date".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Date;
 +        }
 +        if ("boolean".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Boolean;
 +        }
 +
 +        return result;
 +    }
 +
 +    protected WorkflowFormTO getFormTO(final Task task) {
 +        return getFormTO(task, engine.getFormService().getTaskFormData(task.getId()));
 +    }
 +
 +    protected WorkflowFormTO getFormTO(final Task task, final TaskFormData fd) {
 +        final WorkflowFormTO formTO =
 +                getFormTO(task.getProcessInstanceId(), task.getId(), fd.getFormKey(), fd.getFormProperties());
 +
 +        BeanUtils.copyProperties(task, formTO);
 +        return formTO;
 +    }
 +
 +    protected WorkflowFormTO getFormTO(final HistoricTaskInstance task) {
 +        final List<HistoricFormPropertyEntity> props = new ArrayList<>();
 +
 +        for (HistoricDetail historicDetail
 +                : engine.getHistoryService().createHistoricDetailQuery().taskId(task.getId()).list()) {
 +
 +            if (historicDetail instanceof HistoricFormPropertyEntity) {
 +                props.add((HistoricFormPropertyEntity) historicDetail);
 +            }
 +        }
 +
 +        final WorkflowFormTO formTO = getHistoricFormTO(
 +                task.getProcessInstanceId(), task.getId(), task.getFormKey(), props);
 +        BeanUtils.copyProperties(task, formTO);
 +
 +        final HistoricActivityInstance historicActivityInstance = engine.getHistoryService().
 +                createHistoricActivityInstanceQuery().
 +                executionId(task.getExecutionId()).activityType("userTask").activityName(task.getName()).singleResult();
 +
 +        if (historicActivityInstance != null) {
 +            formTO.setCreateTime(historicActivityInstance.getStartTime());
 +            formTO.setDueDate(historicActivityInstance.getEndTime());
 +        }
 +
 +        return formTO;
 +    }
 +
 +    protected WorkflowFormTO getHistoricFormTO(
 +            final String processInstanceId,
 +            final String taskId,
 +            final String formKey,
 +            final List<HistoricFormPropertyEntity> props) {
 +
 +        WorkflowFormTO formTO = new WorkflowFormTO();
 +
 +        User user = userDAO.findByWorkflowId(processInstanceId);
 +        if (user == null) {
 +            throw new NotFoundException("User with workflow id " + processInstanceId);
 +        }
 +        formTO.setUserKey(user.getKey());
 +
 +        formTO.setTaskId(taskId);
 +        formTO.setKey(formKey);
 +
 +        for (HistoricFormPropertyEntity prop : props) {
 +            WorkflowFormPropertyTO propertyTO = new WorkflowFormPropertyTO();
 +            propertyTO.setId(prop.getPropertyId());
 +            propertyTO.setName(prop.getPropertyId());
 +            propertyTO.setValue(prop.getPropertyValue());
 +            formTO.addProperty(propertyTO);
 +        }
 +
 +        return formTO;
 +    }
 +
 +    @SuppressWarnings("unchecked")
 +    protected WorkflowFormTO getFormTO(
 +            final String processInstanceId,
 +            final String taskId,
 +            final String formKey,
 +            final List<FormProperty> properties) {
 +
 +        WorkflowFormTO formTO = new WorkflowFormTO();
 +
 +        User user = userDAO.findByWorkflowId(processInstanceId);
 +        if (user == null) {
 +            throw new NotFoundException("User with workflow id " + processInstanceId);
 +        }
 +        formTO.setUserKey(user.getKey());
 +
 +        formTO.setTaskId(taskId);
 +        formTO.setKey(formKey);
 +
 +        for (FormProperty fProp : properties) {
 +            WorkflowFormPropertyTO propertyTO = new WorkflowFormPropertyTO();
 +            BeanUtils.copyProperties(fProp, propertyTO, PROPERTY_IGNORE_PROPS);
 +            propertyTO.setType(fromActivitiFormType(fProp.getType()));
 +
 +            if (propertyTO.getType() == WorkflowFormPropertyType.Date) {
 +                propertyTO.setDatePattern((String) fProp.getType().getInformation("datePattern"));
 +            }
 +            if (propertyTO.getType() == WorkflowFormPropertyType.Enum) {
 +                propertyTO.getEnumValues().putAll((Map<String, String>) fProp.getType().getInformation("values"));
 +            }
 +
 +            formTO.addProperty(propertyTO);
 +        }
 +
 +        return formTO;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public List<WorkflowFormTO> getForms() {
 +        List<WorkflowFormTO> forms = new ArrayList<>();
 +
 +        String authUser = AuthContextUtils.getUsername();
 +        if (adminUser.equals(authUser)) {
 +            forms.addAll(getForms(engine.getTaskService().createTaskQuery().
 +                    taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE)));
 +        } else {
 +            User user = userDAO.find(authUser);
 +            if (user == null) {
 +                throw new NotFoundException("Syncope User " + authUser);
 +            }
 +
 +            forms.addAll(getForms(engine.getTaskService().createTaskQuery().
 +                    taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE).
 +                    taskCandidateOrAssigned(user.getKey().toString())));
 +
 +            List<String> candidateGroups = new ArrayList<>();
 +            for (Long groupId : userDAO.findAllGroupKeys(user)) {
 +                candidateGroups.add(groupId.toString());
 +            }
 +            if (!candidateGroups.isEmpty()) {
 +                forms.addAll(getForms(engine.getTaskService().createTaskQuery().
 +                        taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE).
 +                        taskCandidateGroupIn(candidateGroups)));
 +            }
 +        }
 +
 +        return forms;
 +    }
 +
 +    @Override
 +    public List<WorkflowFormTO> getForms(final String workflowId, final String name) {
 +        List<WorkflowFormTO> forms = getForms(
 +                engine.getTaskService().createTaskQuery().processInstanceId(workflowId).taskName(name).
 +                taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE));
 +
 +        forms.addAll(getForms(engine.getHistoryService().createHistoricTaskInstanceQuery().taskName(name).
 +                taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE)));
 +
 +        return forms;
 +    }
 +
 +    protected <T extends Query<?, ?>, U extends Object> List<WorkflowFormTO> getForms(final Query<T, U> query) {
 +        List<WorkflowFormTO> forms = new ArrayList<>();
 +
 +        for (U obj : query.list()) {
 +            try {
 +                if (obj instanceof HistoricTaskInstance) {
 +                    forms.add(getFormTO((HistoricTaskInstance) obj));
 +                } else if (obj instanceof Task) {
 +                    forms.add(getFormTO((Task) obj));
 +                } else {
 +                    throw new ActivitiException(
 +                            "Failure retrieving form", new IllegalArgumentException("Invalid task type"));
 +                }
 +            } catch (ActivitiException e) {
 +                LOG.debug("No form found for task {}", obj, e);
 +            }
 +        }
 +
 +        return forms;
 +    }
 +
 +    @Override
 +    public WorkflowFormTO getForm(final String workflowId) {
 +        Task task;
 +        try {
 +            task = engine.getTaskService().createTaskQuery().processInstanceId(workflowId).singleResult();
 +        } catch (ActivitiException e) {
 +            throw new WorkflowException("While reading form for workflow instance " + workflowId, e);
 +        }
 +
 +        TaskFormData formData;
 +        try {
 +            formData = engine.getFormService().getTaskFormData(task.getId());
 +        } catch (ActivitiException e) {
 +            LOG.debug("No form found for task {}", task.getId(), e);
 +            formData = null;
 +        }
 +
 +        WorkflowFormTO result = null;
 +        if (formData != null && !formData.getFormProperties().isEmpty()) {
 +            result = getFormTO(task);
 +        }
 +
 +        return result;
 +    }
 +
 +    protected Pair<Task, TaskFormData> checkTask(final String taskId, final String authUser) {
 +        Task task;
 +        try {
 +            task = engine.getTaskService().createTaskQuery().taskId(taskId).singleResult();
 +        } catch (ActivitiException e) {
 +            throw new NotFoundException("Activiti Task " + taskId, e);
 +        }
 +
 +        TaskFormData formData;
 +        try {
 +            formData = engine.getFormService().getTaskFormData(task.getId());
 +        } catch (ActivitiException e) {
 +            throw new NotFoundException("Form for Activiti Task " + taskId, e);
 +        }
 +
 +        if (!adminUser.equals(authUser)) {
 +            User user = userDAO.find(authUser);
 +            if (user == null) {
 +                throw new NotFoundException("Syncope User " + authUser);
 +            }
 +        }
 +
 +        return new ImmutablePair<>(task, formData);
 +    }
 +
 +    @Override
 +    public WorkflowFormTO claimForm(final String taskId) {
 +        String authUser = AuthContextUtils.getUsername();
 +        Pair<Task, TaskFormData> checked = checkTask(taskId, authUser);
 +
 +        if (!adminUser.equals(authUser)) {
 +            List<Task> tasksForUser = engine.getTaskService().createTaskQuery().taskId(taskId).taskCandidateUser(
 +                    authUser).list();
 +            if (tasksForUser.isEmpty()) {
 +                throw new WorkflowException(
 +                        new IllegalArgumentException(authUser + " is not candidate for task " + taskId));
 +            }
 +        }
 +
 +        Task task;
 +        try {
 +            engine.getTaskService().setOwner(taskId, authUser);
 +            task = engine.getTaskService().createTaskQuery().taskId(taskId).singleResult();
 +        } catch (ActivitiException e) {
 +            throw new WorkflowException("While reading task " + taskId, e);
 +        }
 +
 +        return getFormTO(task, checked.getValue());
 +    }
 +
 +    @Override
 +    public WorkflowResult<UserMod> submitForm(final WorkflowFormTO form) {
 +        String authUser = AuthContextUtils.getUsername();
 +        Pair<Task, TaskFormData> checked = checkTask(form.getTaskId(), authUser);
 +
 +        if (!checked.getKey().getOwner().equals(authUser)) {
 +            throw new WorkflowException(new IllegalArgumentException("Task " + form.getTaskId() + " assigned to "
 +                    + checked.getKey().getOwner() + " but submitted by " + authUser));
 +        }
 +
 +        User user = userDAO.findByWorkflowId(checked.getKey().getProcessInstanceId());
 +        if (user == null) {
 +            throw new NotFoundException("User with workflow id " + checked.getKey().getProcessInstanceId());
 +        }
 +
 +        Set<String> preTasks = getPerformedTasks(user);
 +        try {
 +            engine.getFormService().submitTaskFormData(form.getTaskId(), form.getPropertiesForSubmit());
 +            engine.getRuntimeService().setVariable(user.getWorkflowId(), FORM_SUBMITTER, authUser);
 +        } catch (ActivitiException e) {
 +            throwException(e, "While submitting form for task " + form.getTaskId());
 +        }
 +
 +        Set<String> postTasks = getPerformedTasks(user);
 +        postTasks.removeAll(preTasks);
 +        postTasks.add(form.getTaskId());
 +
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        // see if there is any propagation to be done
 +        PropagationByResource propByRes = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), PROP_BY_RESOURCE, PropagationByResource.class);
 +
 +        // fetch - if available - the encrypted password
 +        String clearPassword = null;
 +        String encryptedPwd = engine.getRuntimeService().getVariable(user.getWorkflowId(), ENCRYPTED_PWD, String.class);
 +        if (StringUtils.isNotBlank(encryptedPwd)) {
 +            clearPassword = decrypt(encryptedPwd);
 +        }
 +
 +        // supports approval chains
 +        saveForFormSubmit(user, clearPassword, propByRes);
 +
 +        UserMod userMod = engine.getRuntimeService().getVariable(user.getWorkflowId(), USER_MOD, UserMod.class);
 +        if (userMod == null) {
 +            userMod = new UserMod();
 +            userMod.setKey(updated.getKey());
 +            userMod.setPassword(clearPassword);
 +
 +            if (propByRes != null) {
-                 final StatusMod st = new StatusMod();
++                StatusMod st = new StatusMod();
 +                userMod.setPwdPropRequest(st);
 +                st.setOnSyncope(true);
 +                for (String res : propByRes.get(ResourceOperation.CREATE)) {
-                     st.getResourceNames().add(res);
++                    st.getResources().add(res);
 +                }
 +            }
 +        }
 +
 +        return new WorkflowResult<>(userMod, propByRes, postTasks);
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserProvisionProcessor.java
----------------------------------------------------------------------
diff --cc ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserProvisionProcessor.java
index f85c373,0000000..b77b61f
mode 100644,000000..100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserProvisionProcessor.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserProvisionProcessor.java
@@@ -1,98 -1,0 +1,98 @@@
 +/*
 + * 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.camel.processor;
 +
 +import java.util.List;
 +import org.apache.camel.Exchange;
 +import org.apache.camel.Processor;
 +import org.apache.commons.lang3.tuple.ImmutablePair;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 +import org.apache.syncope.core.provisioning.api.WorkflowResult;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.stereotype.Component;
 +
 +@Component
 +public class UserProvisionProcessor implements Processor {
 +
 +    private static final Logger LOG = LoggerFactory.getLogger(UserProvisionProcessor.class);
 +
 +    @Autowired
 +    protected PropagationManager propagationManager;
 +
 +    @Autowired
 +    protected PropagationTaskExecutor taskExecutor;
 +
 +    @Autowired
 +    protected UserDAO userDAO;
 +
 +    @Override
 +    public void process(final Exchange exchange) {
 +        Long key = exchange.getIn().getBody(Long.class);
 +        Boolean changePwd = exchange.getProperty("changePwd", Boolean.class);
 +        String password = exchange.getProperty("password", String.class);
 +        @SuppressWarnings("unchecked")
 +        List<String> resources = exchange.getProperty("resources", List.class);
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(key);
 +        userMod.getResourcesToAdd().addAll(resources);
 +
 +        if (changePwd) {
 +            StatusMod statusMod = new StatusMod();
 +            statusMod.setOnSyncope(false);
-             statusMod.getResourceNames().addAll(resources);
++            statusMod.getResources().addAll(resources);
 +            userMod.setPwdPropRequest(statusMod);
 +            userMod.setPassword(password);
 +        }
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        for (String resource : resources) {
 +            propByRes.add(ResourceOperation.UPDATE, resource);
 +        }
 +
 +        WorkflowResult<Pair<UserMod, Boolean>> wfResult = new WorkflowResult<Pair<UserMod, Boolean>>(
 +                ImmutablePair.of(userMod, (Boolean) null), propByRes, "update");
 +
 +        List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(wfResult, changePwd, null);
 +        PropagationReporter propagationReporter =
 +                ApplicationContextProvider.getBeanFactory().getBean(PropagationReporter.class);
 +        try {
 +            taskExecutor.execute(tasks, propagationReporter);
 +        } catch (PropagationException e) {
 +            LOG.error("Error propagation primary resource", e);
 +            propagationReporter.onPrimaryResourceFailure(tasks);
 +        }
 +
 +        exchange.getOut().setBody(propagationReporter.getStatuses());
 +    }
 +
 +}


Mime
View raw message