syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fmarte...@apache.org
Subject [05/54] [abbrv] syncope git commit: [SYNCOPE-710] Merge from 1_2_X
Date Fri, 23 Oct 2015 10:29:06 GMT
[SYNCOPE-710] Merge from 1_2_X


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/2011671c
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/2011671c
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/2011671c

Branch: refs/heads/SYNCOPE-156
Commit: 2011671c93ac23a2397bcc8be39a1ca191ed035f
Parents: 4133c24 2524280
Author: Francesco Chicchiriccò <ilgrosso@apache.org>
Authored: Thu Oct 15 12:05:33 2015 +0200
Committer: Francesco Chicchiriccò <ilgrosso@apache.org>
Committed: Thu Oct 15 12:05:33 2015 +0200

----------------------------------------------------------------------
 .../DefaultAnyObjectProvisioningManager.java    | 28 +++++--------
 .../java/DefaultGroupProvisioningManager.java   | 28 +++++--------
 .../java/DefaultUserProvisioningManager.java    | 42 +++++++++-----------
 .../java/data/UserDataBinderImpl.java           | 13 +++---
 .../propagation/PropagationManagerImpl.java     |  8 +---
 .../activiti/ActivitiUserWorkflowAdapter.java   | 17 +++++++-
 .../workflow/activiti/task/PasswordReset.java   | 24 ++++++++++-
 .../core/workflow/activiti/task/Update.java     | 21 +---------
 .../core/workflow/api/UserWorkflowAdapter.java  |  3 +-
 .../java/AbstractUserWorkflowAdapter.java       |  9 +++--
 .../java/DefaultUserWorkflowAdapter.java        | 33 ++++++---------
 .../processor/AnyObjectUpdateProcessor.java     | 29 +++++---------
 .../camel/processor/GroupUpdateProcessor.java   | 29 +++++---------
 .../processor/UserConfirmPwdResetProcessor.java | 14 ++-----
 .../UserStatusPropagationProcessor.java         | 15 ++++---
 .../camel/processor/UserUpdateProcessor.java    | 21 +++++-----
 .../syncope/fit/core/reference/UserITCase.java  | 34 +++++++++++++++-
 .../fit/core/reference/VirAttrITCase.java       |  4 +-
 18 files changed, 188 insertions(+), 184 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/2011671c/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
index bc1dbc2,0000000..d332e23
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
@@@ -1,226 -1,0 +1,220 @@@
 +/*
 + * 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;
 +
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.Set;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.lang3.tuple.ImmutablePair;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.patch.AnyObjectPatch;
 +import org.apache.syncope.common.lib.to.PropagationStatus;
 +import org.apache.syncope.common.lib.to.AnyObjectTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 +import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
 +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.apache.syncope.core.misc.spring.ApplicationContextProvider;
 +import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 +import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.beans.factory.annotation.Autowired;
 +
 +public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisioningManager {
 +
 +    private static final Logger LOG = LoggerFactory.getLogger(AnyObjectProvisioningManager.class);
 +
 +    @Autowired
 +    protected AnyObjectWorkflowAdapter awfAdapter;
 +
 +    @Autowired
 +    protected PropagationManager propagationManager;
 +
 +    @Autowired
 +    protected PropagationTaskExecutor taskExecutor;
 +
 +    @Autowired
 +    protected VirAttrHandler virtAttrHandler;
 +
 +    @Autowired
 +    protected AnyObjectDAO anyObjectDAO;
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> create(final AnyObjectTO anyObjectTO) {
 +        return create(anyObjectTO, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> create(
 +            final AnyObjectTO anyObjectTO, final Set<String> excludedResources) {
 +
 +        WorkflowResult<Long> created = awfAdapter.create(anyObjectTO);
 +
 +        List<PropagationTask> tasks = propagationManager.getCreateTasks(
 +                AnyTypeKind.ANY_OBJECT,
 +                created.getResult(),
 +                created.getPropByRes(),
 +                anyObjectTO.getVirAttrs(),
 +                excludedResources);
 +        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);
 +        }
 +
 +        return new ImmutablePair<>(created.getResult(), propagationReporter.getStatuses());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> update(final AnyObjectPatch anyObjectPatch) {
 +        return update(anyObjectPatch, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> update(
 +            final AnyObjectPatch anyObjectPatch, final Set<String> excludedResources) {
 +
 +        WorkflowResult<Long> updated = awfAdapter.update(anyObjectPatch);
 +
++        // SYNCOPE-459: take care of user virtual attributes ...
++        PropagationByResource propByResVirAttr = virtAttrHandler.updateVirtual(
++                updated.getResult(),
++                AnyTypeKind.ANY_OBJECT,
++                anyObjectPatch.getVirAttrs());
++        if (updated.getPropByRes() == null) {
++            updated.setPropByRes(propByResVirAttr);
++        } else {
++            updated.getPropByRes().merge(propByResVirAttr);
++        }
++
 +        List<PropagationTask> tasks = propagationManager.getUpdateTasks(
 +                AnyTypeKind.ANY_OBJECT,
 +                updated.getResult(),
 +                false,
 +                null,
 +                updated.getPropByRes(),
 +                anyObjectPatch.getVirAttrs(),
 +                excludedResources);
-         if (tasks.isEmpty()) {
-             // SYNCOPE-459: take care of user virtual attributes ...
-             PropagationByResource propByResVirAttr = virtAttrHandler.updateVirtual(
-                     updated.getResult(),
-                     AnyTypeKind.ANY_OBJECT,
-                     anyObjectPatch.getVirAttrs());
-             tasks.addAll(!propByResVirAttr.isEmpty()
-                     ? propagationManager.getUpdateTasks(
-                             AnyTypeKind.ANY_OBJECT,
-                             updated.getResult(),
-                             false,
-                             null,
-                             updated.getPropByRes(),
-                             anyObjectPatch.getVirAttrs(),
-                             null)
-                     : Collections.<PropagationTask>emptyList());
-         }
 +
 +        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);
 +        }
 +
 +        return new ImmutablePair<>(updated.getResult(), propagationReporter.getStatuses());
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> delete(final Long key) {
 +        return delete(key, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> delete(final Long key, final Set<String> excludedResources) {
 +        List<PropagationTask> tasks = propagationManager.getDeleteTasks(
 +                AnyTypeKind.ANY_OBJECT,
 +                key,
 +                null,
 +                excludedResources);
 +        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);
 +        }
 +
 +        awfAdapter.delete(key);
 +
 +        return propagationReporter.getStatuses();
 +    }
 +
 +    @Override
 +    public Long unlink(final AnyObjectPatch anyObjectPatch) {
 +        return awfAdapter.update(anyObjectPatch).getResult();
 +    }
 +
 +    @Override
 +    public Long link(final AnyObjectPatch anyObjectPatch) {
 +        return awfAdapter.update(anyObjectPatch).getResult();
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> provision(final Long key, final Collection<String> resources) {
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.addAll(ResourceOperation.UPDATE, resources);
 +
 +        List<PropagationTask> tasks = propagationManager.getUpdateTasks(
 +                AnyTypeKind.ANY_OBJECT,
 +                key,
 +                false,
 +                null,
 +                propByRes,
 +                null,
 +                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);
 +        }
 +        return propagationReporter.getStatuses();
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> deprovision(final Long key, final Collection<String> resources) {
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.addAll(ResourceOperation.DELETE, resources);
 +
 +        List<PropagationTask> tasks = propagationManager.getDeleteTasks(
 +                AnyTypeKind.ANY_OBJECT,
 +                key,
 +                propByRes,
 +                CollectionUtils.removeAll(anyObjectDAO.findAllResourceNames(anyObjectDAO.authFind(key)), resources));
 +        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);
 +        }
 +        return propagationReporter.getStatuses();
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2011671c/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
index b933488,0000000..0fff2a3
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
@@@ -1,285 -1,0 +1,279 @@@
 +/*
 + * 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;
 +
 +import java.util.ArrayList;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.Set;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.beans.factory.annotation.Autowired;
 +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.patch.GroupPatch;
 +import org.apache.syncope.common.lib.to.AttrTO;
 +import org.apache.syncope.common.lib.to.PropagationStatus;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 +import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
 +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.apache.syncope.core.misc.spring.ApplicationContextProvider;
 +import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 +import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
 +
 +public class DefaultGroupProvisioningManager implements GroupProvisioningManager {
 +
 +    private static final Logger LOG = LoggerFactory.getLogger(GroupProvisioningManager.class);
 +
 +    @Autowired
 +    protected GroupWorkflowAdapter gwfAdapter;
 +
 +    @Autowired
 +    protected PropagationManager propagationManager;
 +
 +    @Autowired
 +    protected PropagationTaskExecutor taskExecutor;
 +
 +    @Autowired
 +    protected GroupDAO groupDAO;
 +
 +    @Autowired
 +    protected VirAttrHandler virtAttrHandler;
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> create(final GroupTO group) {
 +        return create(group, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> create(final GroupTO groupTO, final Set<String> excludedResources) {
 +        WorkflowResult<Long> created = gwfAdapter.create(groupTO);
 +
 +        List<PropagationTask> tasks = propagationManager.getCreateTasks(
 +                AnyTypeKind.GROUP,
 +                created.getResult(),
 +                created.getPropByRes(),
 +                groupTO.getVirAttrs(),
 +                excludedResources);
 +        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);
 +        }
 +
 +        return new ImmutablePair<>(created.getResult(), propagationReporter.getStatuses());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> create(
 +            final GroupTO groupTO, final Map<Long, String> groupOwnerMap, final Set<String> excludedResources) {
 +
 +        WorkflowResult<Long> created = gwfAdapter.create(groupTO);
 +
 +        // see ConnObjectUtils#getAnyTOFromConnObject for GroupOwnerSchema
 +        AttrTO groupOwner = groupTO.getPlainAttrMap().get(StringUtils.EMPTY);
 +        if (groupOwner != null) {
 +            groupOwnerMap.put(created.getResult(), groupOwner.getValues().iterator().next());
 +        }
 +
 +        List<PropagationTask> tasks = propagationManager.getCreateTasks(
 +                AnyTypeKind.GROUP,
 +                created.getResult(),
 +                created.getPropByRes(),
 +                groupTO.getVirAttrs(),
 +                excludedResources);
 +        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);
 +        }
 +
 +        return new ImmutablePair<>(created.getResult(), null);
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> update(final GroupPatch groupPatch) {
 +        return update(groupPatch, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> update(
 +            final GroupPatch groupPatch, final Set<String> excludedResources) {
 +
 +        WorkflowResult<Long> updated = gwfAdapter.update(groupPatch);
 +
++        // SYNCOPE-459: take care of user virtual attributes ...
++        PropagationByResource propByResVirAttr = virtAttrHandler.updateVirtual(
++                updated.getResult(),
++                AnyTypeKind.GROUP,
++                groupPatch.getVirAttrs());
++        if (updated.getPropByRes() == null) {
++            updated.setPropByRes(propByResVirAttr);
++        } else {
++            updated.getPropByRes().merge(propByResVirAttr);
++        }
++
 +        List<PropagationTask> tasks = propagationManager.getUpdateTasks(
 +                AnyTypeKind.GROUP,
 +                updated.getResult(),
 +                false,
 +                null,
 +                updated.getPropByRes(),
 +                groupPatch.getVirAttrs(),
 +                excludedResources);
-         if (tasks.isEmpty()) {
-             // SYNCOPE-459: take care of user virtual attributes ...
-             PropagationByResource propByResVirAttr = virtAttrHandler.updateVirtual(
-                     updated.getResult(),
-                     AnyTypeKind.GROUP,
-                     groupPatch.getVirAttrs());
-             tasks.addAll(!propByResVirAttr.isEmpty()
-                     ? propagationManager.getUpdateTasks(
-                             AnyTypeKind.GROUP,
-                             updated.getResult(),
-                             false,
-                             null,
-                             updated.getPropByRes(),
-                             groupPatch.getVirAttrs(),
-                             excludedResources)
-                     : Collections.<PropagationTask>emptyList());
-         }
 +
 +        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);
 +        }
 +
 +        return new ImmutablePair<>(updated.getResult(), propagationReporter.getStatuses());
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> delete(final Long key) {
 +        return delete(key, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> delete(final Long key, final Set<String> excludedResources) {
 +        List<PropagationTask> tasks = new ArrayList<>();
 +
 +        // Generate propagation tasks for deleting users and any objects from group resources, 
 +        // if they are on those resources only because of the reason being deleted (see SYNCOPE-357)
 +        for (Map.Entry<Long, PropagationByResource> entry
 +                : groupDAO.findUsersWithTransitiveResources(key).entrySet()) {
 +
 +            tasks.addAll(propagationManager.getDeleteTasks(
 +                    AnyTypeKind.USER,
 +                    entry.getKey(),
 +                    entry.getValue(),
 +                    excludedResources));
 +        }
 +        for (Map.Entry<Long, PropagationByResource> entry
 +                : groupDAO.findAnyObjectsWithTransitiveResources(key).entrySet()) {
 +
 +            tasks.addAll(propagationManager.getDeleteTasks(
 +                    AnyTypeKind.ANY_OBJECT,
 +                    entry.getKey(),
 +                    entry.getValue(),
 +                    excludedResources));
 +        }
 +
 +        // Generate propagation tasks for deleting this group from resources
 +        tasks.addAll(propagationManager.getDeleteTasks(
 +                AnyTypeKind.GROUP,
 +                key,
 +                null,
 +                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);
 +        }
 +
 +        gwfAdapter.delete(key);
 +
 +        return propagationReporter.getStatuses();
 +    }
 +
 +    @Override
 +    public Long unlink(final GroupPatch groupPatch) {
 +        WorkflowResult<Long> updated = gwfAdapter.update(groupPatch);
 +        return updated.getResult();
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> provision(final Long key, final Collection<String> resources) {
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.addAll(ResourceOperation.UPDATE, resources);
 +
 +        List<PropagationTask> tasks = propagationManager.getUpdateTasks(
 +                AnyTypeKind.GROUP,
 +                key,
 +                false,
 +                null,
 +                propByRes,
 +                null,
 +                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);
 +        }
 +        return propagationReporter.getStatuses();
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> deprovision(final Long key, final Collection<String> resources) {
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.addAll(ResourceOperation.DELETE, resources);
 +
 +        List<PropagationTask> tasks = propagationManager.getDeleteTasks(
 +                AnyTypeKind.GROUP,
 +                key,
 +                propByRes,
 +                CollectionUtils.removeAll(groupDAO.authFind(key).getResourceNames(), resources));
 +        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);
 +        }
 +        return propagationReporter.getStatuses();
 +    }
 +
 +    @Override
 +    public Long link(final GroupPatch groupPatch) {
 +        return gwfAdapter.update(groupPatch).getResult();
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2011671c/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
index 4a302f2,0000000..a59337c
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
@@@ -1,413 -1,0 +1,409 @@@
 +/*
 + * 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;
 +
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Set;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.tuple.ImmutablePair;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.patch.PasswordPatch;
 +import org.apache.syncope.common.lib.patch.StatusPatch;
 +import org.apache.syncope.common.lib.patch.StringPatchItem;
 +import org.apache.syncope.common.lib.patch.UserPatch;
 +import org.apache.syncope.common.lib.to.PropagationStatus;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.PatchOperation;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +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.UserProvisioningManager;
 +import org.apache.syncope.core.provisioning.api.WorkflowResult;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.common.lib.types.StatusPatchType;
 +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.apache.syncope.core.provisioning.api.sync.ProvisioningResult;
 +import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
 +import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 +import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.beans.factory.annotation.Autowired;
 +
 +public class DefaultUserProvisioningManager implements UserProvisioningManager {
 +
 +    private static final Logger LOG = LoggerFactory.getLogger(UserProvisioningManager.class);
 +
 +    @Autowired
 +    protected UserWorkflowAdapter uwfAdapter;
 +
 +    @Autowired
 +    protected PropagationManager propagationManager;
 +
 +    @Autowired
 +    protected PropagationTaskExecutor taskExecutor;
 +
 +    @Autowired
 +    protected VirAttrHandler virtAttrHandler;
 +
 +    @Autowired
 +    protected UserDAO userDAO;
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> create(final UserTO userTO) {
 +        return create(userTO, true, false, null, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> create(final UserTO userTO, final boolean storePassword) {
 +        return create(userTO, storePassword, false, null, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> create(final UserTO userTO, final Set<String> excludedResources) {
 +        return create(userTO, false, false, null, excludedResources);
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> create(final UserTO userTO, final boolean storePassword,
 +            final boolean disablePwdPolicyCheck, final Boolean enabled, final Set<String> excludedResources) {
 +
 +        WorkflowResult<Pair<Long, Boolean>> created =
 +                uwfAdapter.create(userTO, disablePwdPolicyCheck, enabled, storePassword);
 +
 +        List<PropagationTask> tasks = propagationManager.getUserCreateTasks(
 +                created.getResult().getKey(),
 +                userTO.getPassword(),
 +                created.getResult().getValue(),
 +                created.getPropByRes(),
 +                userTO.getVirAttrs(),
 +                excludedResources);
 +        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);
 +        }
 +
 +        return new ImmutablePair<>(created.getResult().getKey(), propagationReporter.getStatuses());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> update(final UserPatch userPatch) {
 +        WorkflowResult<Pair<UserPatch, Boolean>> updated = uwfAdapter.update(userPatch);
 +
-         List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(updated);
-         if (tasks.isEmpty()) {
-             // SYNCOPE-459: take care of user virtual attributes ...
-             PropagationByResource propByResVirAttr = virtAttrHandler.updateVirtual(
-                     updated.getResult().getKey().getKey(),
-                     AnyTypeKind.USER,
-                     userPatch.getVirAttrs());
-             if (!propByResVirAttr.isEmpty()) {
-                 tasks.addAll(propagationManager.getUserUpdateTasks(updated, false, null));
-             }
++        // SYNCOPE-459: take care of user virtual attributes ...
++        PropagationByResource propByResVirAttr = virtAttrHandler.updateVirtual(
++                updated.getResult().getKey().getKey(),
++                AnyTypeKind.USER,
++                userPatch.getVirAttrs());
++        if (updated.getPropByRes() == null) {
++            updated.setPropByRes(propByResVirAttr);
++        } else {
++            updated.getPropByRes().merge(propByResVirAttr);
 +        }
++
++        List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(updated);
++
 +        PropagationReporter propagationReporter = ApplicationContextProvider.getBeanFactory().
 +                getBean(PropagationReporter.class);
 +        if (!tasks.isEmpty()) {
 +            try {
 +                taskExecutor.execute(tasks, propagationReporter);
 +            } catch (PropagationException e) {
 +                LOG.error("Error propagation primary resource", e);
 +                propagationReporter.onPrimaryResourceFailure(tasks);
 +            }
 +        }
 +
 +        return new ImmutablePair<>(updated.getResult().getKey().getKey(), propagationReporter.getStatuses());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> update(final UserPatch userPatch, final Set<String> excludedResources) {
 +        return update(userPatch, userPatch.getKey(), new ProvisioningResult(), null, excludedResources);
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> update(final UserPatch userPatch, final Long key,
 +            final ProvisioningResult result, final Boolean enabled, final Set<String> excludedResources) {
 +
 +        WorkflowResult<Pair<UserPatch, Boolean>> updated;
 +        try {
 +            updated = uwfAdapter.update(userPatch);
 +        } catch (Exception e) {
 +            LOG.error("Update of user {} failed, trying to sync its status anyway (if configured)", key, e);
 +
 +            result.setStatus(ProvisioningResult.Status.FAILURE);
 +            result.setMessage("Update failed, trying to sync status anyway (if configured)\n" + e.getMessage());
 +
 +            updated = new WorkflowResult<Pair<UserPatch, Boolean>>(
 +                    new ImmutablePair<>(userPatch, false), new PropagationByResource(),
 +                    new HashSet<String>());
 +        }
 +
 +        if (enabled != null) {
 +            User user = userDAO.find(key);
 +
 +            WorkflowResult<Long> enableUpdate = null;
 +            if (user.isSuspended() == null) {
 +                enableUpdate = uwfAdapter.activate(key, null);
 +            } else if (enabled && user.isSuspended()) {
 +                enableUpdate = uwfAdapter.reactivate(key);
 +            } else if (!enabled && !user.isSuspended()) {
 +                enableUpdate = uwfAdapter.suspend(key);
 +            }
 +
 +            if (enableUpdate != null) {
 +                if (enableUpdate.getPropByRes() != null) {
 +                    updated.getPropByRes().merge(enableUpdate.getPropByRes());
 +                    updated.getPropByRes().purge();
 +                }
 +                updated.getPerformedTasks().addAll(enableUpdate.getPerformedTasks());
 +            }
 +        }
 +
 +        List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
 +                updated, updated.getResult().getKey().getPassword() != null, excludedResources);
 +        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);
 +        }
 +
 +        return new ImmutablePair<>(updated.getResult().getKey().getKey(), propagationReporter.getStatuses());
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> delete(final Long key) {
 +        return delete(key, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> delete(final Long key, final Set<String> excludedResources) {
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceNames(userDAO.authFind(key)));
 +
 +        // Note here that we can only notify about "delete", not any other
 +        // task defined in workflow process definition: this because this
 +        // information could only be available after uwfAdapter.delete(), which
 +        // will also effectively remove user from db, thus making virtually
 +        // impossible by NotificationManager to fetch required user information
 +        List<PropagationTask> tasks = propagationManager.getDeleteTasks(
 +                AnyTypeKind.USER,
 +                key,
 +                propByRes,
 +                excludedResources);
 +        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);
 +        }
 +
 +        try {
 +            uwfAdapter.delete(key);
 +        } catch (PropagationException e) {
 +            throw e;
 +        }
 +
 +        return propagationReporter.getStatuses();
 +    }
 +
 +    @Override
 +    public Long unlink(final UserPatch userPatch) {
 +        WorkflowResult<Pair<UserPatch, Boolean>> updated = uwfAdapter.update(userPatch);
 +        return updated.getResult().getKey().getKey();
 +    }
 +
 +    @Override
 +    public Long link(final UserPatch userPatch) {
 +        return uwfAdapter.update(userPatch).getResult().getKey().getKey();
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> activate(final StatusPatch statusPatch) {
 +        WorkflowResult<Long> updated = statusPatch.isOnSyncope()
 +                ? uwfAdapter.activate(statusPatch.getKey(), statusPatch.getToken())
 +                : new WorkflowResult<>(statusPatch.getKey(), null, statusPatch.getType().name().toLowerCase());
 +
 +        return new ImmutablePair<>(updated.getResult(), propagateStatus(statusPatch));
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> reactivate(final StatusPatch statusPatch) {
 +        WorkflowResult<Long> updated = statusPatch.isOnSyncope()
 +                ? uwfAdapter.reactivate(statusPatch.getKey())
 +                : new WorkflowResult<>(statusPatch.getKey(), null, statusPatch.getType().name().toLowerCase());
 +
 +        return new ImmutablePair<>(updated.getResult(), propagateStatus(statusPatch));
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> suspend(final StatusPatch statusPatch) {
 +        WorkflowResult<Long> updated = statusPatch.isOnSyncope()
 +                ? uwfAdapter.suspend(statusPatch.getKey())
 +                : new WorkflowResult<>(statusPatch.getKey(), null, statusPatch.getType().name().toLowerCase());
 +
 +        return new ImmutablePair<>(updated.getResult(), propagateStatus(statusPatch));
 +    }
 +
 +    protected List<PropagationStatus> propagateStatus(final StatusPatch statusPatch) {
-         Collection<String> noPropResourceNames = CollectionUtils.removeAll(
-                 userDAO.findAllResourceNames(userDAO.find(statusPatch.getKey())), statusPatch.getResources());
- 
++        PropagationByResource propByRes = new PropagationByResource();
++        propByRes.addAll(ResourceOperation.UPDATE, statusPatch.getResources());
 +        List<PropagationTask> tasks = propagationManager.getUpdateTasks(
 +                AnyTypeKind.USER,
 +                statusPatch.getKey(),
 +                false,
 +                statusPatch.getType() != StatusPatchType.SUSPEND,
++                propByRes,
 +                null,
-                 null,
-                 noPropResourceNames);
++                null);
++
 +        PropagationReporter propReporter =
 +                ApplicationContextProvider.getBeanFactory().getBean(PropagationReporter.class);
 +        try {
 +            taskExecutor.execute(tasks, propReporter);
 +        } catch (PropagationException e) {
 +            LOG.error("Error propagation primary resource", e);
 +            propReporter.onPrimaryResourceFailure(tasks);
 +        }
 +
 +        return propReporter.getStatuses();
 +
 +    }
 +
 +    @Override
 +    public void internalSuspend(final Long key) {
 +        Pair<WorkflowResult<Long>, Boolean> updated = uwfAdapter.internalSuspend(key);
 +
 +        // propagate suspension if and only if it is required by policy
 +        if (updated != null && updated.getValue()) {
 +            UserPatch userPatch = new UserPatch();
 +            userPatch.setKey(updated.getKey().getResult());
 +
 +            List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
 +                    new WorkflowResult<Pair<UserPatch, Boolean>>(
 +                            new ImmutablePair<>(userPatch, Boolean.FALSE),
 +                            updated.getKey().getPropByRes(), updated.getKey().getPerformedTasks()));
 +            taskExecutor.execute(tasks);
 +        }
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> provision(
 +            final Long key, final boolean changePwd, final String password, final Collection<String> resources) {
 +
 +        UserPatch userPatch = new UserPatch();
 +        userPatch.setKey(key);
 +        userPatch.getResources().addAll(CollectionUtils.collect(resources,
 +                new Transformer<String, StringPatchItem>() {
 +
 +                    @Override
 +                    public StringPatchItem transform(final String input) {
 +                        return new StringPatchItem.Builder().operation(PatchOperation.ADD_REPLACE).value(input).build();
 +                    }
 +                }, new HashSet<StringPatchItem>()));
 +
 +        if (changePwd) {
 +            PasswordPatch passwordPatch = new PasswordPatch();
 +            passwordPatch.setOnSyncope(false);
 +            passwordPatch.getResources().addAll(resources);
 +            passwordPatch.setValue(password);
 +            userPatch.setPassword(passwordPatch);
 +        }
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.addAll(ResourceOperation.UPDATE, resources);
 +
 +        WorkflowResult<Pair<UserPatch, Boolean>> wfResult = new WorkflowResult<Pair<UserPatch, Boolean>>(
 +                ImmutablePair.of(userPatch, (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);
 +        }
 +
 +        return propagationReporter.getStatuses();
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> deprovision(final Long key, final Collection<String> resources) {
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.set(ResourceOperation.DELETE, resources);
 +
 +        List<PropagationTask> tasks = propagationManager.getDeleteTasks(
 +                AnyTypeKind.USER,
 +                key,
 +                propByRes,
 +                CollectionUtils.removeAll(userDAO.findAllResourceNames(userDAO.authFind(key)), resources));
 +        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);
 +        }
 +
 +        return propagationReporter.getStatuses();
 +    }
 +
 +    @Override
 +    public void requestPasswordReset(final Long key) {
 +        uwfAdapter.requestPasswordReset(key);
 +    }
 +
 +    @Override
 +    public void confirmPasswordReset(final Long key, final String token, final String password) {
-         uwfAdapter.confirmPasswordReset(key, token, password);
++        WorkflowResult<Pair<UserPatch, Boolean>> updated = uwfAdapter.confirmPasswordReset(key, token, password);
 +
-         UserPatch userPatch = new UserPatch();
-         userPatch.setKey(key);
-         userPatch.setPassword(new PasswordPatch.Builder().value(password).build());
++        List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(updated);
 +
-         List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
-                 new WorkflowResult<Pair<UserPatch, Boolean>>(
-                         new ImmutablePair<UserPatch, Boolean>(userPatch, null), null, "confirmPasswordReset"),
-                 true, null);
 +        PropagationReporter propReporter =
 +                ApplicationContextProvider.getBeanFactory().getBean(PropagationReporter.class);
 +        try {
 +            taskExecutor.execute(tasks, propReporter);
 +        } catch (PropagationException e) {
 +            LOG.error("Error propagation primary resource", e);
 +            propReporter.onPrimaryResourceFailure(tasks);
 +        }
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2011671c/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 82dfbc0,0000000..3420d27
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,499 -1,0 +1,502 @@@
 +/*
 + * 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.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.patch.LongPatchItem;
 +import org.apache.syncope.common.lib.patch.MembershipPatch;
 +import org.apache.syncope.common.lib.patch.PasswordPatch;
 +import org.apache.syncope.common.lib.patch.RelationshipPatch;
 +import org.apache.syncope.common.lib.patch.UserPatch;
 +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.PatchOperation;
 +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.RelationshipType;
 +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 {
 +            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) {
 +                LOG.debug("Ignoring invalid anyObject " + relationshipTO.getRightKey());
 +            } else {
 +                RelationshipType relationshipType = relationshipTypeDAO.find(relationshipTO.getType());
 +                URelationship relationship = null;
 +                if (user.getKey() != null) {
 +                    relationship = user.getRelationship(relationshipType, anyObject.getKey());
 +                }
 +                if (relationship == null) {
 +                    relationship = entityFactory.newEntity(URelationship.class);
 +                    relationship.setType(relationshipType);
 +                    relationship.setRightEnd(anyObject);
 +                    relationship.setLeftEnd(user);
 +
 +                    user.add(relationship);
 +                }
 +            }
 +        }
 +
 +        // memberships
 +        for (MembershipTO membershipTO : userTO.getMemberships()) {
 +            Group group = groupDAO.find(membershipTO.getRightKey());
 +
 +            if (group == null) {
 +                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());
 +
 +        user.setMustChangePassword(userTO.isMustChangePassword());
 +    }
 +
 +    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 UserPatch userPatch) {
 +        // 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, userPatch);
 +
 +        // password
 +        if (userPatch.getPassword() != null && StringUtils.isNotBlank(userPatch.getPassword().getValue())) {
-             setPassword(user, userPatch.getPassword().getValue(), scce);
-             user.setChangePwdDate(new Date());
-             propByRes.addAll(ResourceOperation.UPDATE, currentResources);
++            if (userPatch.getPassword().isOnSyncope()) {
++                setPassword(user, userPatch.getPassword().getValue(), scce);
++                user.setChangePwdDate(new Date());
++            }
++
++            propByRes.addAll(ResourceOperation.UPDATE, userPatch.getPassword().getResources());
 +        }
 +
 +        // username
 +        if (userPatch.getUsername() != null && StringUtils.isNotBlank(userPatch.getUsername().getValue())) {
-             propByRes.addAll(ResourceOperation.UPDATE, currentResources);
- 
 +            String oldUsername = user.getUsername();
 +            user.setUsername(userPatch.getUsername().getValue());
 +
 +            if (oldUsername.equals(AuthContextUtils.getUsername())) {
 +                AuthContextUtils.updateUsername(userPatch.getUsername().getValue());
 +            }
++
++            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
 +        }
 +
 +        // security question / answer:
 +        if (userPatch.getSecurityQuestion() != null) {
 +            if (userPatch.getSecurityQuestion().getValue() == null) {
 +                user.setSecurityQuestion(null);
 +                user.setSecurityAnswer(null);
 +            } else {
 +                SecurityQuestion securityQuestion =
 +                        securityQuestionDAO.find(userPatch.getSecurityQuestion().getValue());
 +                if (securityQuestion != null) {
 +                    user.setSecurityQuestion(securityQuestion);
 +                    user.setSecurityAnswer(userPatch.getSecurityAnswer().getValue());
 +                }
 +            }
 +        }
 +
 +        if (userPatch.getMustChangePassword() != null) {
 +            user.setMustChangePassword(userPatch.getMustChangePassword().getValue());
 +        }
 +
 +        // roles
 +        for (LongPatchItem patch : userPatch.getRoles()) {
 +            Role role = roleDAO.find(patch.getValue());
 +            if (role == null) {
 +                LOG.warn("Ignoring unknown role with key {}", patch.getValue());
 +            } else {
 +                switch (patch.getOperation()) {
 +                    case ADD_REPLACE:
 +                        user.add(role);
 +                        break;
 +
 +                    case DELETE:
 +                    default:
 +                        user.remove(role);
 +                }
 +            }
 +        }
 +
 +        // attributes, derived attributes, virtual attributes and resources
 +        propByRes.merge(fill(user, userPatch, anyUtilsFactory.getInstance(AnyTypeKind.USER), scce));
 +
 +        Set<String> toBeDeprovisioned = new HashSet<>();
 +        Set<String> toBeProvisioned = new HashSet<>();
 +
 +        // relationships
 +        for (RelationshipPatch patch : userPatch.getRelationships()) {
 +            if (patch.getRelationshipTO() != null) {
 +                RelationshipType relationshipType = relationshipTypeDAO.find(patch.getRelationshipTO().getType());
 +                URelationship relationship =
 +                        user.getRelationship(relationshipType, patch.getRelationshipTO().getRightKey());
 +                if (relationship != null) {
 +                    user.remove(relationship);
 +                    toBeDeprovisioned.addAll(relationship.getRightEnd().getResourceNames());
 +                }
 +
 +                if (patch.getOperation() == PatchOperation.ADD_REPLACE) {
 +                    AnyObject otherEnd = anyObjectDAO.find(patch.getRelationshipTO().getRightKey());
 +                    if (otherEnd == null) {
 +                        LOG.debug("Ignoring invalid any object {}", patch.getRelationshipTO().getRightKey());
 +                    } else {
 +
 +                        relationship = entityFactory.newEntity(URelationship.class);
 +                        relationship.setType(relationshipType);
 +                        relationship.setRightEnd(otherEnd);
 +                        relationship.setLeftEnd(user);
 +
 +                        user.add(relationship);
 +
 +                        toBeProvisioned.addAll(otherEnd.getResourceNames());
 +                    }
 +                }
 +            }
 +        }
 +
 +        // memberships
 +        for (MembershipPatch patch : userPatch.getMemberships()) {
 +            if (patch.getMembershipTO() != null) {
 +                UMembership membership = user.getMembership(patch.getMembershipTO().getRightKey());
 +                if (membership != null) {
 +                    user.remove(membership);
 +                    toBeDeprovisioned.addAll(membership.getRightEnd().getResourceNames());
 +                }
 +
 +                if (patch.getOperation() == PatchOperation.ADD_REPLACE) {
 +                    Group group = groupDAO.find(patch.getMembershipTO().getRightKey());
 +                    if (group == null) {
 +                        LOG.debug("Ignoring invalid group {}", patch.getMembershipTO().getRightKey());
 +                    } else {
 +                        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()) {
 +                            if (userPatch.getPassword() == null) {
 +                                userPatch.setPassword(new PasswordPatch());
 +                            }
 +                            for (ExternalResource resource : group.getResources()) {
 +                                if (isPasswordMapped(resource)) {
 +                                    userPatch.getPassword().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/2011671c/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 ebf9c99,0000000..0d15ffd
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,426 -1,0 +1,420 @@@
 +/*
 + * 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.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.patch.AttrPatch;
 +import org.apache.syncope.common.lib.patch.StringPatchItem;
 +import org.apache.syncope.common.lib.patch.UserPatch;
 +import org.apache.syncope.common.lib.to.AttrTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +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.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.AnyDAO;
 +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.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;
 +
 +    @Autowired
 +    protected MappingUtils mappingUtils;
 +
 +    protected Any<?, ?, ?> find(final AnyTypeKind kind, final Long key) {
 +        AnyDAO<? extends Any<?, ?, ?>> dao;
 +        switch (kind) {
 +            case ANY_OBJECT:
 +                dao = anyObjectDAO;
 +                break;
 +
 +            case GROUP:
 +                dao = groupDAO;
 +                break;
 +
 +            case USER:
 +            default:
 +                dao = userDAO;
 +        }
 +
 +        return dao.authFind(key);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getCreateTasks(
 +            final AnyTypeKind kind,
 +            final Long key,
 +            final PropagationByResource propByRes,
 +            final Collection<AttrTO> vAttrs,
 +            final Collection<String> noPropResourceNames) {
 +
 +        Any<?, ?, ?> any = find(kind, key);
 +        if (vAttrs != null && !vAttrs.isEmpty()) {
 +            virAttrHandler.createVirtual(any, vAttrs);
 +        }
 +
 +        return getCreateTasks(any, null, null, propByRes, noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserCreateTasks(
 +            final Long key,
 +            final String password,
 +            final Boolean enable,
 +            final PropagationByResource propByRes,
 +            final Collection<AttrTO> vAttrs,
 +            final Collection<String> noPropResourceNames) {
 +
 +        User user = userDAO.authFind(key);
 +        if (vAttrs != null && !vAttrs.isEmpty()) {
 +            virAttrHandler.createVirtual(user, vAttrs);
 +        }
 +
 +        return getCreateTasks(user, password, enable, propByRes, noPropResourceNames);
 +    }
 +
 +    protected List<PropagationTask> getCreateTasks(
 +            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, enable, false, propByRes);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUpdateTasks(
 +            final AnyTypeKind kind,
 +            final Long key,
 +            final boolean changePwd,
 +            final Boolean enable,
 +            final PropagationByResource propByRes,
 +            final Collection<AttrPatch> vAttrs,
 +            final Collection<String> noPropResourceNames) {
 +
 +        return getUpdateTasks(find(kind, key), null, changePwd, enable, propByRes, vAttrs, noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserUpdateTasks(
 +            final WorkflowResult<Pair<UserPatch, Boolean>> wfResult,
 +            final boolean changePwd,
 +            final Collection<String> noPropResourceNames) {
 +
 +        return getUpdateTasks(
 +                userDAO.authFind(wfResult.getResult().getKey().getKey()),
 +                wfResult.getResult().getKey().getPassword() == null
 +                        ? null
 +                        : wfResult.getResult().getKey().getPassword().getValue(),
 +                changePwd,
 +                wfResult.getResult().getValue(),
 +                wfResult.getPropByRes(),
 +                wfResult.getResult().getKey().getVirAttrs(),
 +                noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserUpdateTasks(final WorkflowResult<Pair<UserPatch, Boolean>> wfResult) {
 +        UserPatch userPatch = wfResult.getResult().getKey();
 +
 +        // Propagate password update only to requested resources
 +        List<PropagationTask> tasks = new ArrayList<>();
 +        if (userPatch.getPassword() == 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<>(userPatch.getPassword().getResources());
 +            Collection<String> currentResourceNames =
 +                    userDAO.findAllResourceNames(userDAO.authFind(userPatch.getKey()));
 +            pwdResourceNames.retainAll(currentResourceNames);
 +            PropagationByResource pwdPropByRes = new PropagationByResource();
 +            pwdPropByRes.addAll(ResourceOperation.UPDATE, pwdResourceNames);
 +            if (!pwdPropByRes.isEmpty()) {
 +                Set<String> toBeExcluded = new HashSet<>(currentResourceNames);
 +                toBeExcluded.addAll(CollectionUtils.collect(userPatch.getResources(),
 +                        new Transformer<StringPatchItem, String>() {
 +
 +                            @Override
 +                            public String transform(final StringPatchItem input) {
 +                                return input.getValue();
 +                            }
 +                        }));
 +                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;
 +    }
 +
 +    protected List<PropagationTask> getUpdateTasks(
 +            final Any<?, ?, ?> any,
 +            final String password,
 +            final boolean changePwd,
 +            final Boolean enable,
 +            final PropagationByResource propByRes,
 +            final Collection<AttrPatch> vAttrs,
 +            final Collection<String> noPropResourceNames) {
 +
 +        PropagationByResource localPropByRes = virAttrHandler.updateVirtual(
 +                any,
 +                vAttrs == null ? Collections.<AttrPatch>emptySet() : vAttrs);
- 
-         if (propByRes == null || propByRes.isEmpty()) {
-             localPropByRes.addAll(ResourceOperation.UPDATE, any.getResourceNames());
-         } else {
-             localPropByRes.merge(propByRes);
-         }
- 
++        localPropByRes.merge(propByRes);
 +        if (noPropResourceNames != null) {
 +            localPropByRes.removeAll(noPropResourceNames);
 +        }
 +
 +        Map<String, AttrPatch> vAttrsMap = null;
 +        if (vAttrs != null) {
 +            vAttrsMap = new HashMap<>();
 +            for (AttrPatch attrPatch : vAttrs) {
 +                vAttrsMap.put(attrPatch.getAttrTO().getSchema(), attrPatch);
 +            }
 +        }
 +
 +        return createTasks(any, password, changePwd, vAttrsMap, enable, false, localPropByRes);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getDeleteTasks(
 +            final AnyTypeKind kind,
 +            final Long key,
 +            final PropagationByResource propByRes,
 +            final Collection<String> noPropResourceNames) {
 +
 +        Any<?, ?, ?> any = find(kind, key);
 +
 +        PropagationByResource localPropByRes = new PropagationByResource();
 +
 +        if (propByRes == null || propByRes.isEmpty()) {
 +            localPropByRes.addAll(ResourceOperation.DELETE, any.getResourceNames());
 +        } else {
 +            localPropByRes.merge(propByRes);
 +        }
 +
 +        if (noPropResourceNames != null) {
 +            localPropByRes.removeAll(noPropResourceNames);
 +        }
 +
 +        return getDeleteTasks(any, localPropByRes, noPropResourceNames);
 +    }
 +
 +    protected List<PropagationTask> getDeleteTasks(
 +            final Any<?, ?, ?> any,
 +            final PropagationByResource propByRes,
 +            final Collection<String> noPropResourceNames) {
 +
 +        return createTasks(any, null, false, 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 vAttrs virtual attributes to be maaged
 +     * @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 Map<String, AttrPatch> vAttrs,
 +            final Boolean enable, final boolean deleteOnResource, final PropagationByResource propByRes) {
 +
 +        LOG.debug("Provisioning any {}:\n{}", any, propByRes);
 +
 +        if (!propByRes.get(ResourceOperation.CREATE).isEmpty() && vAttrs != null) {
 +            virAttrHandler.retrieveVirAttrValues(any);
 +
 +            // update vAttrsToBeUpdated as well
 +            for (VirAttr<?> virAttr : any.getVirAttrs()) {
 +                String schema = virAttr.getSchema().getKey();
 +
 +                vAttrs.put(schema, new AttrPatch.Builder().
 +                        attrTO(new AttrTO.Builder().schema(schema).values(virAttr.getValues()).build()).
 +                        build());
 +            }
 +        }
 +
 +        // 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.getPropagationMappingItems(provision).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.setOperation(operation);
 +                    task.setOldConnObjectKey(propByRes.getOldConnObjectKey(resource.getKey()));
 +
 +                    Pair<String, Set<Attribute>> preparedAttrs = mappingUtils.prepareAttrs(
 +                            any, password, changePwd, vAttrs, 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.getPropagationMappingItems(provision)) {
 +                        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;
 +    }
 +}


Mime
View raw message