You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/10/14 17:39:27 UTC

syncope git commit: [SYNCOPE-710] Reworking password propagation request and virtual attributes management

Repository: syncope
Updated Branches:
  refs/heads/1_2_X 91cb526c6 -> 2524280eb


[SYNCOPE-710] Reworking password propagation request and virtual attributes management


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

Branch: refs/heads/1_2_X
Commit: 2524280ebeaff46bc351f4692fb4f966148bc87e
Parents: 91cb526
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Oct 14 17:39:02 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Oct 14 17:39:02 2015 +0200

----------------------------------------------------------------------
 .../propagation/impl/PropagationManager.java    | 47 ++--------
 .../core/rest/controller/UserController.java    | 93 +++++++++-----------
 .../syncope/core/rest/data/UserDataBinder.java  | 15 ++--
 .../user/AbstractUserWorkflowAdapter.java       |  9 +-
 .../workflow/user/NoOpUserWorkflowAdapter.java  | 28 +++---
 .../core/workflow/user/UserWorkflowAdapter.java |  3 +-
 .../activiti/ActivitiUserWorkflowAdapter.java   | 18 +++-
 .../user/activiti/task/PasswordReset.java       | 25 +++++-
 .../workflow/user/activiti/task/Update.java     | 13 +--
 .../syncope/core/rest/UserTestITCase.java       | 40 +++++++++
 .../syncope/core/rest/VirAttrTestITCase.java    |  9 +-
 11 files changed, 167 insertions(+), 133 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/2524280e/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java b/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java
index eddbbb4..166017a 100644
--- a/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java
+++ b/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java
@@ -248,31 +248,6 @@ public class PropagationManager {
     }
 
     /**
-     * Performs update on each resource associated to the user excluding the specified into 'resourceNames' parameter.
-     *
-     * @param user to be propagated
-     * @param enable whether user must be enabled or not
-     * @param noPropResourceNames external resource names not to be considered for propagation
-     * @return list of propagation tasks
-     * @throws NotFoundException if user is not found
-     */
-    public List<PropagationTask> getUserUpdateTaskIds(final SyncopeUser user, final Boolean enable,
-            final Set<String> noPropResourceNames)
-            throws NotFoundException {
-
-        return getUpdateTaskIds(
-                user, // SyncopeUser to be updated on external resources
-                null, // no password
-                false,
-                enable, // status to be propagated
-                Collections.<String>emptySet(), // no virtual attributes to be managed
-                Collections.<AttributeMod>emptySet(), // no virtual attributes to be managed
-                null, // no propagation by resources
-                noPropResourceNames,
-                Collections.<MembershipMod>emptySet());
-    }
-
-    /**
      * Performs update on each resource associated to the user.
      *
      * @param wfResult user to be propagated (and info associated), as per result from workflow
@@ -388,8 +363,12 @@ public class PropagationManager {
         PropagationByResource localPropByRes = binder.fillVirtual(subject, vAttrsToBeRemoved == null
                 ? Collections.<String>emptySet()
                 : vAttrsToBeRemoved, vAttrsToBeUpdated == null
-                ? Collections.<AttributeMod>emptySet()
-                : vAttrsToBeUpdated, AttributableUtil.getInstance(subject));
+                        ? Collections.<AttributeMod>emptySet()
+                        : vAttrsToBeUpdated, AttributableUtil.getInstance(subject));
+        localPropByRes.merge(propByRes);
+        if (noPropResourceNames != null) {
+            localPropByRes.removeAll(noPropResourceNames);
+        }
 
         // SYNCOPE-458 fill membership virtual attributes
         if (subject instanceof SyncopeUser) {
@@ -402,23 +381,13 @@ public class PropagationManager {
                                 ? Collections.<String>emptySet()
                                 : membershipMod.getVirAttrsToRemove(),
                                 membershipMod.getVirAttrsToUpdate() == null ? Collections.<AttributeMod>emptySet()
-                                : membershipMod.getVirAttrsToUpdate(), AttributableUtil.getInstance(
-                                AttributableType.MEMBERSHIP));
+                                        : membershipMod.getVirAttrsToUpdate(), AttributableUtil.getInstance(
+                                        AttributableType.MEMBERSHIP));
                     }
                 }
             }
         }
 
-        if (propByRes == null || propByRes.isEmpty()) {
-            localPropByRes.addAll(ResourceOperation.UPDATE, subject.getResourceNames());
-        } else {
-            localPropByRes.merge(propByRes);
-        }
-
-        if (noPropResourceNames != null) {
-            localPropByRes.removeAll(noPropResourceNames);
-        }
-
         Map<String, AttributeMod> vAttrsToBeUpdatedMap = null;
         if (vAttrsToBeUpdated != null) {
             vAttrsToBeUpdatedMap = new HashMap<String, AttributeMod>();

http://git-wip-us.apache.org/repos/asf/syncope/blob/2524280e/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java b/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
index 68843a7..93a6859 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
@@ -20,7 +20,6 @@ package org.apache.syncope.core.rest.controller;
 
 import java.lang.reflect.Method;
 import java.security.AccessControlException;
-import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -41,6 +40,7 @@ import org.apache.syncope.common.types.ClientExceptionType;
 import org.apache.syncope.common.SyncopeClientException;
 import org.apache.syncope.common.mod.AttributeMod;
 import org.apache.syncope.common.mod.MembershipMod;
+import org.apache.syncope.common.types.ResourceOperation;
 import org.apache.syncope.common.types.SubjectType;
 import org.apache.syncope.core.persistence.beans.PropagationTask;
 import org.apache.syncope.core.persistence.beans.role.SyncopeRole;
@@ -258,54 +258,44 @@ public class UserController extends AbstractSubjectController<UserTO, UserMod> {
         UserMod actual = attrTransformer.transform(userMod);
         LOG.debug("Transformed: {}", actual);
 
-        // SYNCOPE-501: check if there are memberships to be removed with virtual attributes assigned
-        boolean removeMemberships = false;
+        PropagationByResource propByResVirAttr = new PropagationByResource();
         for (Long membershipId : actual.getMembershipsToRemove()) {
-            if (!binder.fillMembershipVirtual(
+            propByResVirAttr.merge(binder.fillMembershipVirtual(
                     null,
                     null,
                     membershipId,
                     Collections.<String>emptySet(),
                     Collections.<AttributeMod>emptySet(),
-                    true).isEmpty()) {
-
-                removeMemberships = true;
-            }
+                    true));
         }
 
         // Actual operations: workflow, propagation, notification
         WorkflowResult<Map.Entry<UserMod, Boolean>> updated = uwfAdapter.update(actual);
 
-        List<PropagationTask> tasks = propagationManager.getUserUpdateTaskIds(updated);
-        if (tasks.isEmpty()) {
-            // SYNCOPE-459: take care of user virtual attributes ...
-            PropagationByResource propByResVirAttr = binder.fillVirtual(
+        // SYNCOPE-459: take care of user virtual attributes ...
+        propByResVirAttr.merge(binder.fillVirtual(
+                updated.getResult().getKey().getId(),
+                actual.getVirAttrsToRemove(),
+                actual.getVirAttrsToUpdate()));
+        for (MembershipMod membershipMod : actual.getMembershipsToAdd()) {
+            propByResVirAttr.merge(binder.fillMembershipVirtual(
                     updated.getResult().getKey().getId(),
-                    actual.getVirAttrsToRemove(),
-                    actual.getVirAttrsToUpdate());
-            // SYNCOPE-501: update only virtual attributes (if any of them changed), password propagation is
-            // not required, take care also of membership virtual attributes
-            boolean addOrUpdateMemberships = false;
-            for (MembershipMod membershipMod : actual.getMembershipsToAdd()) {
-                if (!binder.fillMembershipVirtual(
-                        updated.getResult().getKey().getId(),
-                        membershipMod.getRole(),
-                        null,
-                        membershipMod.getVirAttrsToRemove(),
-                        membershipMod.getVirAttrsToUpdate(),
-                        false).isEmpty()) {
-
-                    addOrUpdateMemberships = true;
-                }
-            }
-            tasks.addAll(!propByResVirAttr.isEmpty() || addOrUpdateMemberships || removeMemberships
-                    ? propagationManager.getUserUpdateTaskIds(updated, false, null)
-                    : Collections.<PropagationTask>emptyList());
+                    membershipMod.getRole(),
+                    null,
+                    membershipMod.getVirAttrsToRemove(),
+                    membershipMod.getVirAttrsToUpdate(),
+                    false));
+        }
+        if (updated.getPropByRes() == null) {
+            updated.setPropByRes(propByResVirAttr);
+        } else {
+            updated.getPropByRes().merge(propByResVirAttr);
         }
 
+        List<PropagationTask> tasks = propagationManager.getUserUpdateTaskIds(updated);
+
         PropagationReporter propagationReporter = ApplicationContextProvider.getApplicationContext().
                 getBean(PropagationReporter.class);
-
         if (!tasks.isEmpty()) {
             try {
                 taskExecutor.execute(tasks, propagationReporter);
@@ -347,19 +337,22 @@ public class UserController extends AbstractSubjectController<UserTO, UserMod> {
     public UserTO status(final StatusMod statusMod) {
         SyncopeUser user = binder.getUserFromId(statusMod.getId());
 
-        WorkflowResult<Long> updated;
         if (statusMod.isOnSyncope()) {
-            updated = setStatusOnWfAdapter(user, statusMod);
-        } else {
-            updated = new WorkflowResult<Long>(user.getId(), null, statusMod.getType().name().toLowerCase());
+            setStatusOnWfAdapter(user, statusMod);
         }
 
-        // Resources to exclude from propagation
-        Set<String> resourcesToBeExcluded = new HashSet<String>(user.getResourceNames());
-        resourcesToBeExcluded.removeAll(statusMod.getResourceNames());
-
-        List<PropagationTask> tasks = propagationManager.getUserUpdateTaskIds(
-                user, statusMod.getType() != StatusMod.ModType.SUSPEND, resourcesToBeExcluded);
+        PropagationByResource propByRes = new PropagationByResource();
+        propByRes.addAll(ResourceOperation.UPDATE, statusMod.getResourceNames());
+        List<PropagationTask> tasks = propagationManager.getUpdateTaskIds(
+                user, // SyncopeUser to be updated on external resources
+                null, // no password
+                false,
+                statusMod.getType() != StatusMod.ModType.SUSPEND, // status to be propagated
+                Collections.<String>emptySet(), // no virtual attributes to be managed
+                Collections.<AttributeMod>emptySet(), // no virtual attributes to be managed
+                propByRes,
+                null,
+                Collections.<MembershipMod>emptySet());
         PropagationReporter propReporter =
                 ApplicationContextProvider.getApplicationContext().getBean(PropagationReporter.class);
         try {
@@ -369,7 +362,7 @@ public class UserController extends AbstractSubjectController<UserTO, UserMod> {
             propReporter.onPrimaryResourceFailure(tasks);
         }
 
-        final UserTO savedTO = binder.getUserTO(updated.getResult());
+        final UserTO savedTO = binder.getUserTO(user.getId());
         savedTO.getPropagationStatusTOs().addAll(propReporter.getStatuses());
         return savedTO;
     }
@@ -403,16 +396,10 @@ public class UserController extends AbstractSubjectController<UserTO, UserMod> {
             throw new NotFoundException("User with token " + token);
         }
 
-        uwfAdapter.confirmPasswordReset(user.getId(), token, password);
+        WorkflowResult<Map.Entry<UserMod, Boolean>> updated =
+                uwfAdapter.confirmPasswordReset(user.getId(), token, password);
 
-        UserMod userMod = new UserMod();
-        userMod.setId(user.getId());
-        userMod.setPassword(password);
-
-        List<PropagationTask> tasks = propagationManager.getUserUpdateTaskIds(
-                new WorkflowResult<Map.Entry<UserMod, Boolean>>(
-                        new AbstractMap.SimpleEntry<UserMod, Boolean>(userMod, null), null, "confirmPasswordReset"),
-                true, null);
+        List<PropagationTask> tasks = propagationManager.getUserUpdateTaskIds(updated);
         PropagationReporter propReporter =
                 ApplicationContextProvider.getApplicationContext().getBean(PropagationReporter.class);
         try {

http://git-wip-us.apache.org/repos/asf/syncope/blob/2524280e/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java b/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
index fcd67c2..89752aa 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
@@ -272,16 +272,21 @@ public class UserDataBinder extends AbstractAttributableDataBinder {
 
         // password
         if (StringUtils.isNotBlank(userMod.getPassword())) {
-            setPassword(user, userMod.getPassword(), scce);
-            user.setChangePwdDate(new Date());
-            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
+            if (userMod.getPwdPropRequest() == null || userMod.getPwdPropRequest().isOnSyncope()) {
+                setPassword(user, userMod.getPassword(), scce);
+                user.setChangePwdDate(new Date());
+            }
+            if (userMod.getPwdPropRequest() == null) {
+                propByRes.addAll(ResourceOperation.UPDATE, currentResources);
+            } else {
+                propByRes.addAll(ResourceOperation.UPDATE, userMod.getPwdPropRequest().getResourceNames());
+            }
         }
 
         // username
         if (userMod.getUsername() != null && !userMod.getUsername().equals(user.getUsername())) {
-            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
-
             user.setUsername(userMod.getUsername());
+            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
         }
 
         // security question / answer:

http://git-wip-us.apache.org/repos/asf/syncope/blob/2524280e/core/src/main/java/org/apache/syncope/core/workflow/user/AbstractUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/workflow/user/AbstractUserWorkflowAdapter.java b/core/src/main/java/org/apache/syncope/core/workflow/user/AbstractUserWorkflowAdapter.java
index 9425f17..b3e1b0b 100644
--- a/core/src/main/java/org/apache/syncope/core/workflow/user/AbstractUserWorkflowAdapter.java
+++ b/core/src/main/java/org/apache/syncope/core/workflow/user/AbstractUserWorkflowAdapter.java
@@ -126,14 +126,15 @@ public abstract class AbstractUserWorkflowAdapter implements UserWorkflowAdapter
         doRequestPasswordReset(dataBinder.getUserFromId(userId));
     }
 
-    protected abstract void doConfirmPasswordReset(SyncopeUser user, String token, String password) 
-            throws WorkflowException;
+    protected abstract WorkflowResult<Map.Entry<UserMod, Boolean>> doConfirmPasswordReset(
+            SyncopeUser user, String token, String password) throws WorkflowException;
 
     @Override
-    public void confirmPasswordReset(final Long userId, final String token, final String password)
+    public WorkflowResult<Map.Entry<UserMod, Boolean>> confirmPasswordReset(
+            final Long userId, final String token, final String password)
             throws UnauthorizedRoleException, WorkflowException {
 
-        doConfirmPasswordReset(dataBinder.getUserFromId(userId), token, password);
+        return doConfirmPasswordReset(dataBinder.getUserFromId(userId), token, password);
     }
 
     protected abstract void doDelete(SyncopeUser user) throws WorkflowException;

http://git-wip-us.apache.org/repos/asf/syncope/blob/2524280e/core/src/main/java/org/apache/syncope/core/workflow/user/NoOpUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/workflow/user/NoOpUserWorkflowAdapter.java b/core/src/main/java/org/apache/syncope/core/workflow/user/NoOpUserWorkflowAdapter.java
index 1ffc4c7..83924e4 100644
--- a/core/src/main/java/org/apache/syncope/core/workflow/user/NoOpUserWorkflowAdapter.java
+++ b/core/src/main/java/org/apache/syncope/core/workflow/user/NoOpUserWorkflowAdapter.java
@@ -24,7 +24,7 @@ import java.util.AbstractMap.SimpleEntry;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import org.apache.commons.lang3.SerializationUtils;
+import org.apache.syncope.common.mod.StatusMod;
 import org.apache.syncope.common.mod.UserMod;
 import org.apache.syncope.common.to.UserTO;
 import org.apache.syncope.common.to.WorkflowFormTO;
@@ -115,13 +115,7 @@ public class NoOpUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
     protected WorkflowResult<Map.Entry<UserMod, Boolean>> doUpdate(final SyncopeUser user, final UserMod userMod)
             throws WorkflowException {
 
-        // update password internally only if required
-        UserMod actualMod = SerializationUtils.clone(userMod);
-        if (actualMod.getPwdPropRequest() != null && !actualMod.getPwdPropRequest().isOnSyncope()) {
-            actualMod.setPassword(null);
-        }
-        // update SyncopeUser
-        PropagationByResource propByRes = dataBinder.update(user, actualMod);
+        PropagationByResource propByRes = dataBinder.update(user, userMod);
 
         SyncopeUser updated = userDAO.save(user);
 
@@ -155,16 +149,26 @@ public class NoOpUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
     }
 
     @Override
-    protected void doConfirmPasswordReset(final SyncopeUser user, final String token, final String password)
-            throws WorkflowException {
+    protected WorkflowResult<Map.Entry<UserMod, Boolean>> doConfirmPasswordReset(
+            final SyncopeUser user, final String token, final String password) throws WorkflowException {
 
         if (!user.checkToken(token)) {
             throw new WorkflowException(new IllegalArgumentException("Wrong token: " + token + " for " + user));
         }
 
         user.removeToken();
-        user.setPassword(password, user.getCipherAlgorithm());
-        userDAO.save(user);
+
+        UserMod userMod = new UserMod();
+        userMod.setId(user.getId());
+        userMod.setPassword(password);
+        StatusMod pwdPropRequest = new StatusMod();
+
+        pwdPropRequest.setId(user.getId());
+        pwdPropRequest.setOnSyncope(true);
+        pwdPropRequest.getResourceNames().addAll(user.getResourceNames());
+        userMod.setPwdPropRequest(pwdPropRequest);
+
+        return doUpdate(user, userMod);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/2524280e/core/src/main/java/org/apache/syncope/core/workflow/user/UserWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/workflow/user/UserWorkflowAdapter.java b/core/src/main/java/org/apache/syncope/core/workflow/user/UserWorkflowAdapter.java
index c2844db..955e8fc 100644
--- a/core/src/main/java/org/apache/syncope/core/workflow/user/UserWorkflowAdapter.java
+++ b/core/src/main/java/org/apache/syncope/core/workflow/user/UserWorkflowAdapter.java
@@ -151,8 +151,9 @@ public interface UserWorkflowAdapter extends WorkflowAdapter {
      * @param password new password value
      * @throws UnauthorizedRoleException authorization exception
      * @throws WorkflowException workflow exception
+     * @return user just updated and propagations to be performed
      */
-    void confirmPasswordReset(Long userId, String token, String password) 
+    WorkflowResult<Map.Entry<UserMod, Boolean>> confirmPasswordReset(Long userId, String token, String password)
             throws UnauthorizedRoleException, WorkflowException;
 
     /**

http://git-wip-us.apache.org/repos/asf/syncope/blob/2524280e/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java b/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java
index f974267..08a1d91 100644
--- a/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java
+++ b/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/ActivitiUserWorkflowAdapter.java
@@ -401,8 +401,8 @@ public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
     }
 
     @Override
-    protected void doConfirmPasswordReset(final SyncopeUser user, final String token, final String password)
-            throws WorkflowException {
+    protected WorkflowResult<Map.Entry<UserMod, Boolean>> doConfirmPasswordReset(
+            final SyncopeUser user, final String token, final String password) throws WorkflowException {
 
         Map<String, Object> variables = new HashMap<String, Object>(4);
         variables.put(TOKEN, token);
@@ -410,8 +410,20 @@ public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
         variables.put(USER_TO, userDataBinder.getUserTO(user, true));
         variables.put(EVENT, "confirmPasswordReset");
 
-        doExecuteTask(user, "confirmPasswordReset", variables);
+        Set<String> tasks = doExecuteTask(user, "confirmPasswordReset", variables);
+
         userDAO.save(user);
+
+        PropagationByResource propByRes =
+                runtimeService.getVariable(user.getWorkflowId(), PROP_BY_RESOURCE, PropagationByResource.class);
+        UserMod userMod =
+                runtimeService.getVariable(user.getWorkflowId(), USER_MOD, UserMod.class);
+
+        Boolean propagateEnable = runtimeService.getVariable(user.getWorkflowId(), PROPAGATE_ENABLE, Boolean.class);
+
+        return new WorkflowResult<Map.Entry<UserMod, Boolean>>(
+                new SimpleEntry<UserMod, Boolean>(userMod, propagateEnable), propByRes, tasks);
+
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/2524280e/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/task/PasswordReset.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/task/PasswordReset.java b/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/task/PasswordReset.java
index e9d59a3..0e9e982 100644
--- a/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/task/PasswordReset.java
+++ b/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/task/PasswordReset.java
@@ -18,14 +18,22 @@
  */
 package org.apache.syncope.core.workflow.user.activiti.task;
 
+import org.apache.syncope.common.mod.StatusMod;
+import org.apache.syncope.common.mod.UserMod;
 import org.apache.syncope.core.persistence.beans.user.SyncopeUser;
+import org.apache.syncope.core.propagation.PropagationByResource;
+import org.apache.syncope.core.rest.data.UserDataBinder;
 import org.apache.syncope.core.workflow.WorkflowException;
 import org.apache.syncope.core.workflow.user.activiti.ActivitiUserWorkflowAdapter;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 @Component
 public class PasswordReset extends AbstractActivitiServiceTask {
 
+    @Autowired
+    private UserDataBinder dataBinder;
+
     @Override
     protected void doExecute(final String executionId) {
         SyncopeUser user =
@@ -38,8 +46,23 @@ public class PasswordReset extends AbstractActivitiServiceTask {
         }
 
         user.removeToken();
-        user.setPassword(password, user.getCipherAlgorithm());
+
+        UserMod userMod = new UserMod();
+        userMod.setId(user.getId());
+        userMod.setPassword(password);
+
+        StatusMod pwdPropRequest = new StatusMod();
+        pwdPropRequest.setId(user.getId());
+        pwdPropRequest.setOnSyncope(true);
+        pwdPropRequest.getResourceNames().addAll(user.getResourceNames());
+        userMod.setPwdPropRequest(pwdPropRequest);
+
+        PropagationByResource propByRes = dataBinder.update(user, userMod);
+
+        // report updated user and propagation by resource as result
         runtimeService.setVariable(executionId, ActivitiUserWorkflowAdapter.SYNCOPE_USER, user);
+        runtimeService.setVariable(executionId, ActivitiUserWorkflowAdapter.USER_MOD, userMod);
+        runtimeService.setVariable(executionId, ActivitiUserWorkflowAdapter.PROP_BY_RESOURCE, propByRes);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2524280e/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/task/Update.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/task/Update.java b/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/task/Update.java
index e110df9..f366119 100644
--- a/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/task/Update.java
+++ b/core/src/main/java/org/apache/syncope/core/workflow/user/activiti/task/Update.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.workflow.user.activiti.task;
 
-import org.apache.commons.lang3.SerializationUtils;
 import org.apache.syncope.common.mod.UserMod;
 import org.apache.syncope.core.persistence.beans.user.SyncopeUser;
 import org.apache.syncope.core.propagation.PropagationByResource;
@@ -40,19 +39,11 @@ public class Update extends AbstractActivitiServiceTask {
         UserMod userMod =
                 runtimeService.getVariable(executionId, ActivitiUserWorkflowAdapter.USER_MOD, UserMod.class);
 
-        // update password internally only if required
-        UserMod updatedMod = SerializationUtils.clone(userMod);
-        String updatedPwd = updatedMod.getPassword();
-        if (updatedMod.getPwdPropRequest() != null && !updatedMod.getPwdPropRequest().isOnSyncope()) {
-            updatedMod.setPassword(null);
-        }
-        // update SyncopeUser
-        PropagationByResource propByRes = dataBinder.update(user, updatedMod);
-        updatedMod.setPassword(updatedPwd);
+        PropagationByResource propByRes = dataBinder.update(user, userMod);
 
         // report updated user and propagation by resource as result
         runtimeService.setVariable(executionId, ActivitiUserWorkflowAdapter.SYNCOPE_USER, user);
-        runtimeService.setVariable(executionId, ActivitiUserWorkflowAdapter.USER_MOD, updatedMod);
+        runtimeService.setVariable(executionId, ActivitiUserWorkflowAdapter.USER_MOD, userMod);
         runtimeService.setVariable(executionId, ActivitiUserWorkflowAdapter.PROP_BY_RESOURCE, propByRes);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2524280e/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java b/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
index 269f0a8..3c9b0f2 100644
--- a/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
+++ b/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
@@ -2584,4 +2584,44 @@ public class UserTestITCase extends AbstractTest {
             configurationService.set(pwdCipherAlgo.getSchema(), pwdCipherAlgo);
         }
     }
+
+    @Test
+    public void issueSYNCOPE710() {
+        // 1. create roles for indirect resource assignment
+        RoleTO ldapRole = RoleTestITCase.buildBasicRoleTO("syncope710.ldap");
+        ldapRole.getResources().add(RESOURCE_NAME_LDAP);
+        ldapRole = createRole(ldapRole);
+
+        RoleTO dbRole = RoleTestITCase.buildBasicRoleTO("syncope710.db");
+        dbRole.getResources().add(RESOURCE_NAME_TESTDB);
+        dbRole = createRole(dbRole);
+
+        // 2. create user with memberships for the roles created above
+        UserTO userTO = getUniqueSampleTO("syncope710@syncope.apache.org");
+        userTO.getResources().clear();
+        userTO.getMemberships().clear();
+        MembershipTO memb = new MembershipTO();
+        memb.setRoleId(ldapRole.getId());
+        userTO.getMemberships().add(memb);
+        memb = new MembershipTO();
+        memb.setRoleId(dbRole.getId());
+        userTO.getMemberships().add(memb);
+
+        userTO = createUser(userTO);
+        assertEquals(2, userTO.getPropagationStatusTOs().size());
+
+        // 3. request to propagate passwod only to db
+        StatusMod pwdPropRequest = new StatusMod();
+        pwdPropRequest.setOnSyncope(false);
+        pwdPropRequest.getResourceNames().add(RESOURCE_NAME_TESTDB);
+
+        UserMod userMod = new UserMod();
+        userMod.setId(userTO.getId());
+        userMod.setPassword("newpassword123");
+        userMod.setPwdPropRequest(pwdPropRequest);
+
+        userTO = updateUser(userMod);
+        assertEquals(1, userTO.getPropagationStatusTOs().size());
+        assertEquals(RESOURCE_NAME_TESTDB, userTO.getPropagationStatusTOs().get(0).getResource());
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2524280e/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java b/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java
index e065998..e7cae5b 100644
--- a/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java
+++ b/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java
@@ -297,7 +297,7 @@ public class VirAttrTestITCase extends AbstractTest {
             assertTrue(found);
 
             // create a new user
-            UserTO userTO = UserTestITCase.getUniqueSampleTO("syncope397@syncope.apache.org");
+            UserTO userTO = UserTestITCase.getUniqueSampleTO("397@syncope.apache.org");
             userTO.getResources().clear();
             userTO.getMemberships().clear();
             userTO.getDerAttrs().clear();
@@ -332,7 +332,7 @@ public class VirAttrTestITCase extends AbstractTest {
 
             toBeUpdated = updateUser(userMod);
             assertNotNull(toBeUpdated);
-            assertEquals("test@testoneone.com", toBeUpdated.getVirAttrs().get(0).getValues().get(0));
+            assertTrue(toBeUpdated.getVirAttrs().get(0).getValues().contains("test@testoneone.com"));
             // check if propagates correctly with assertEquals on size of tasks list
             assertEquals(2, toBeUpdated.getPropagationStatusTOs().size());
         } finally {
@@ -640,8 +640,9 @@ public class VirAttrTestITCase extends AbstractTest {
         assertNotNull(userTO);
         // 3. check again after update if membership has virtual attribute populated with new value
         assertNotNull(userTO.getMemberships().get(0).getVirAttrMap().get("mvirtualdata"));
-        assertEquals("syncope458_NEW@syncope.apache.org", userTO.getMemberships().get(0).getVirAttrMap().get(
-                "mvirtualdata").getValues().get(0));
+        assertEquals(
+                "syncope458_NEW@syncope.apache.org",
+                userTO.getMemberships().get(0).getVirAttrMap().get("mvirtualdata").getValues().get(0));
 
         // ----------------------------------------
         // force cache expiring without any modification