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 2013/01/25 15:47:06 UTC

svn commit: r1438543 [1/3] - in /syncope/trunk: common/src/main/java/org/apache/syncope/common/mod/ console/src/main/java/org/apache/syncope/console/pages/ console/src/main/resources/ core/ core/src/main/java/org/apache/syncope/core/connid/ core/src/ma...

Author: ilgrosso
Date: Fri Jan 25 14:47:06 2013
New Revision: 1438543

URL: http://svn.apache.org/viewvc?rev=1438543&view=rev
Log:
[SYNCOPE-122] core implementation completed, now moving to console

Modified:
    syncope/trunk/common/src/main/java/org/apache/syncope/common/mod/UserMod.java
    syncope/trunk/console/src/main/java/org/apache/syncope/console/pages/Login.java
    syncope/trunk/console/src/main/resources/applicationContext.xml
    syncope/trunk/core/pom.xml
    syncope/trunk/core/src/main/java/org/apache/syncope/core/connid/ConnObjectUtil.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/PropagationByResource.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PriorityPropagationTaskExecutor.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/AbstractTest.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
    syncope/trunk/core/src/test/resources/content.xml
    syncope/trunk/core/src/test/resources/restClientContext.xml

Modified: syncope/trunk/common/src/main/java/org/apache/syncope/common/mod/UserMod.java
URL: http://svn.apache.org/viewvc/syncope/trunk/common/src/main/java/org/apache/syncope/common/mod/UserMod.java?rev=1438543&r1=1438542&r2=1438543&view=diff
==============================================================================
--- syncope/trunk/common/src/main/java/org/apache/syncope/common/mod/UserMod.java (original)
+++ syncope/trunk/common/src/main/java/org/apache/syncope/common/mod/UserMod.java Fri Jan 25 14:47:06 2013
@@ -20,12 +20,11 @@ package org.apache.syncope.common.mod;
 
 import java.util.HashSet;
 import java.util.Set;
-
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlType;
-
+import org.apache.syncope.common.to.PropagationRequestTO;
 import org.codehaus.jackson.annotate.JsonIgnore;
 
 @XmlRootElement(name = "userMod")
@@ -38,9 +37,11 @@ public class UserMod extends AbstractAtt
 
     private String username;
 
-    private Set<MembershipMod> membershipsToBeAdded;
+    private final Set<MembershipMod> membershipsToBeAdded;
+
+    private final Set<Long> membershipsToBeRemoved;
 
-    private Set<Long> membershipsToBeRemoved;
+    private PropagationRequestTO pwdPropRequest;
 
     public UserMod() {
         super();
@@ -49,12 +50,20 @@ public class UserMod extends AbstractAtt
         membershipsToBeRemoved = new HashSet<Long>();
     }
 
-    public boolean addMembershipToBeAdded(MembershipMod membershipMod) {
-        return membershipsToBeAdded.add(membershipMod);
+    public String getUsername() {
+        return username;
     }
 
-    public boolean removeMembershipToBeAdded(MembershipMod membershipMod) {
-        return membershipsToBeAdded.remove(membershipMod);
+    public void setUsername(final String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(final String password) {
+        this.password = password;
     }
 
     @XmlElementWrapper(name = "membershipsToBeAdded")
@@ -63,48 +72,62 @@ public class UserMod extends AbstractAtt
         return membershipsToBeAdded;
     }
 
-    public void setMembershipsToBeAdded(Set<MembershipMod> membershipMods) {
-        this.membershipsToBeAdded = membershipMods;
-    }
-
-    public String getUsername() {
-        return username;
+    public boolean addMembershipToBeAdded(final MembershipMod membershipMod) {
+        return membershipsToBeAdded.add(membershipMod);
     }
 
-    public void setUsername(String username) {
-        this.username = username;
+    public boolean removeMembershipToBeAdded(final MembershipMod membershipMod) {
+        return membershipsToBeAdded.remove(membershipMod);
     }
 
-    public String getPassword() {
-        return password;
+    public void setMembershipsToBeAdded(final Set<MembershipMod> membershipsToBeAdded) {
+        if (this.membershipsToBeAdded != membershipsToBeAdded) {
+            this.membershipsToBeAdded.clear();
+            if (membershipsToBeAdded != null && !membershipsToBeAdded.isEmpty()) {
+                this.membershipsToBeAdded.addAll(membershipsToBeAdded);
+            }
+        }
     }
 
-    public void setPassword(String password) {
-        this.password = password;
+    @XmlElementWrapper(name = "membershipsToBeRemoved")
+    @XmlElement(name = "membership")
+    public Set<Long> getMembershipsToBeRemoved() {
+        return membershipsToBeRemoved;
     }
 
-    public boolean addMembershipToBeRemoved(Long membershipToBeRemoved) {
+    public boolean addMembershipToBeRemoved(final Long membershipToBeRemoved) {
         return membershipsToBeRemoved.add(membershipToBeRemoved);
     }
 
-    public boolean removeMembershipToBeRemoved(Long membershipToBeRemoved) {
+    public boolean removeMembershipToBeRemoved(final Long membershipToBeRemoved) {
         return membershipsToBeRemoved.remove(membershipToBeRemoved);
     }
 
-    @XmlElementWrapper(name = "membershipsToBeRemoved")
-    @XmlElement(name = "membership")
-    public Set<Long> getMembershipsToBeRemoved() {
-        return membershipsToBeRemoved;
+    public void setMembershipsToBeRemoved(final Set<Long> membershipsToBeRemoved) {
+        if (this.membershipsToBeRemoved != membershipsToBeRemoved) {
+            this.membershipsToBeRemoved.clear();
+            if (membershipsToBeRemoved != null && !membershipsToBeRemoved.isEmpty()) {
+                this.membershipsToBeRemoved.addAll(membershipsToBeRemoved);
+            }
+        }
+    }
+
+    public PropagationRequestTO getPwdPropRequest() {
+        return pwdPropRequest;
     }
 
-    public void setMembershipsToBeRemoved(Set<Long> membershipsToBeRemoved) {
-        this.membershipsToBeRemoved = membershipsToBeRemoved;
+    public void setPwdPropRequest(final PropagationRequestTO pwdPropRequest) {
+        this.pwdPropRequest = pwdPropRequest;
     }
 
     @JsonIgnore
     @Override
     public boolean isEmpty() {
-        return super.isEmpty() && password == null && username == null && membershipsToBeAdded.isEmpty()
-                && membershipsToBeRemoved.isEmpty();
+        return super.isEmpty()
+                && password == null
+                && username == null
+                && membershipsToBeAdded.isEmpty()
+                && membershipsToBeRemoved.isEmpty()
+                && pwdPropRequest == null;
     }
 }

Modified: syncope/trunk/console/src/main/java/org/apache/syncope/console/pages/Login.java
URL: http://svn.apache.org/viewvc/syncope/trunk/console/src/main/java/org/apache/syncope/console/pages/Login.java?rev=1438543&r1=1438542&r2=1438543&view=diff
==============================================================================
--- syncope/trunk/console/src/main/java/org/apache/syncope/console/pages/Login.java (original)
+++ syncope/trunk/console/src/main/java/org/apache/syncope/console/pages/Login.java Fri Jan 25 14:47:06 2013
@@ -22,7 +22,6 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.Set;
-
 import org.apache.http.HttpResponse;
 import org.apache.http.auth.UsernamePasswordCredentials;
 import org.apache.http.client.methods.HttpGet;
@@ -123,8 +122,9 @@ public class Login extends WebPage {
                 } catch (HttpClientErrorException e) {
                     error(getString("login-error"));
 
-                    PreemptiveAuthHttpRequestFactory requestFactory = ((PreemptiveAuthHttpRequestFactory) SyncopeSession.
-                            get().getRestTemplate().getRequestFactory());
+                    PreemptiveAuthHttpRequestFactory requestFactory =
+                            ((PreemptiveAuthHttpRequestFactory) SyncopeSession.get().getRestTemplate().
+                            getRequestFactory());
 
                     ((DefaultHttpClient) requestFactory.getHttpClient()).getCredentialsProvider().clear();
                 }
@@ -251,18 +251,21 @@ public class Login extends WebPage {
             setChoiceRenderer(new LocaleRenderer());
             setModel(new IModel<Locale>() {
 
+                private static final long serialVersionUID = -6985170095629312963L;
+
                 @Override
                 public Locale getObject() {
                     return getSession().getLocale();
                 }
 
                 @Override
-                public void setObject(Locale object) {
+                public void setObject(final Locale object) {
                     getSession().setLocale(object);
                 }
 
                 @Override
                 public void detach() {
+                    // Empty.
                 }
             });
 

Modified: syncope/trunk/console/src/main/resources/applicationContext.xml
URL: http://svn.apache.org/viewvc/syncope/trunk/console/src/main/resources/applicationContext.xml?rev=1438543&r1=1438542&r2=1438543&view=diff
==============================================================================
--- syncope/trunk/console/src/main/resources/applicationContext.xml (original)
+++ syncope/trunk/console/src/main/resources/applicationContext.xml Fri Jan 25 14:47:06 2013
@@ -16,7 +16,6 @@ software distributed under the License i
 KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
-
 -->
 <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -26,15 +25,15 @@ under the License.
        xmlns:oxm="http://www.springframework.org/schema/oxm"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
-       http://www.springframework.org/schema/beans/spring-beans.xsd
-       http://www.springframework.org/schema/context
-       http://www.springframework.org/schema/context/spring-context.xsd
-       http://www.springframework.org/schema/oxm
-       http://www.springframework.org/schema/oxm/spring-oxm.xsd
-       http://www.springframework.org/schema/tx
-       http://www.springframework.org/schema/tx/spring-tx.xsd
-       http://www.springframework.org/schema/aop
-       http://www.springframework.org/schema/aop/spring-aop.xsd">
+                           http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context
+                           http://www.springframework.org/schema/context/spring-context.xsd
+                           http://www.springframework.org/schema/oxm
+                           http://www.springframework.org/schema/oxm/spring-oxm.xsd
+                           http://www.springframework.org/schema/tx
+                           http://www.springframework.org/schema/tx/spring-tx.xsd
+                           http://www.springframework.org/schema/aop
+                           http://www.springframework.org/schema/aop/spring-aop.xsd">
 
   <context:component-scan base-package="org.apache.syncope.console.rest"/>
 

Modified: syncope/trunk/core/pom.xml
URL: http://svn.apache.org/viewvc/syncope/trunk/core/pom.xml?rev=1438543&r1=1438542&r2=1438543&view=diff
==============================================================================
--- syncope/trunk/core/pom.xml (original)
+++ syncope/trunk/core/pom.xml Fri Jan 25 14:47:06 2013
@@ -94,22 +94,22 @@ under the License.
       <artifactId>activiti-spring</artifactId>
     </dependency>
 	
-	<dependency>
-	  <groupId>org.apache.cxf</groupId>
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
       <artifactId>cxf-rt-frontend-jaxrs</artifactId>
     </dependency>
-  	<dependency>
-	  <groupId>org.apache.cxf</groupId>
-	  <artifactId>cxf-rt-rs-extension-providers</artifactId>
-	</dependency>
-	<dependency>
-	  <groupId>org.apache.cxf</groupId>
-	  <artifactId>cxf-rt-rs-extension-search</artifactId>
-	</dependency>
-	<dependency>
-	  <groupId>org.apache.cxf</groupId>
-	  <artifactId>cxf-rt-frontend-jaxws</artifactId>
-	</dependency>
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-rs-extension-providers</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-rs-extension-search</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-frontend-jaxws</artifactId>
+    </dependency>
 
     <dependency>
       <groupId>org.springframework</groupId>
@@ -186,10 +186,10 @@ under the License.
       <artifactId>aspectjweaver</artifactId>
     </dependency>
 	
-	<dependency>
-	  <groupId>org.codehaus.jackson</groupId>
-	  <artifactId>jackson-jaxrs</artifactId>
-	</dependency>
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-jaxrs</artifactId>
+    </dependency>
 
     <dependency>
       <groupId>com.thoughtworks.xstream</groupId>
@@ -292,11 +292,11 @@ under the License.
     </dependency> 
 
     <!-- TEST -->
-	<dependency>
+    <dependency>
       <groupId>org.apache.syncope</groupId>
       <artifactId>syncope-client</artifactId>
       <version>${project.version}</version>
-	  <scope>test</scope>
+      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.apache.syncope</groupId>

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/connid/ConnObjectUtil.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/connid/ConnObjectUtil.java?rev=1438543&r1=1438542&r2=1438543&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/connid/ConnObjectUtil.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/connid/ConnObjectUtil.java Fri Jan 25 14:47:06 2013
@@ -432,7 +432,11 @@ public class ConnObjectUtil {
             if (attr.getValue() != null) {
                 for (Object value : attr.getValue()) {
                     if (value != null) {
-                        attrTO.addValue(value.toString());
+                        if (value instanceof GuardedString || value instanceof GuardedByteArray) {
+                            attrTO.addValue(getPassword(value));
+                        } else {
+                            attrTO.addValue(value.toString());
+                        }
                     }
                 }
             }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/PropagationByResource.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/PropagationByResource.java?rev=1438543&r1=1438542&r2=1438543&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/PropagationByResource.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/PropagationByResource.java Fri Jan 25 14:47:06 2013
@@ -19,12 +19,12 @@
 package org.apache.syncope.core.propagation;
 
 import java.io.Serializable;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-
 import org.apache.syncope.common.types.ResourceOperation;
 
 /**
@@ -37,22 +37,22 @@ public class PropagationByResource imple
     /**
      * Resources for creation.
      */
-    private Set<String> toBeCreated;
+    private final Set<String> toBeCreated;
 
     /**
      * Resources for update.
      */
-    private Set<String> toBeUpdated;
+    private final Set<String> toBeUpdated;
 
     /**
      * Resources for deletion.
      */
-    private Set<String> toBeDeleted;
+    private final Set<String> toBeDeleted;
 
     /**
      * Mapping target resource names to old account ids (when applicable).
      */
-    private Map<String, String> oldAccountIds;
+    private final Map<String, String> oldAccountIds;
 
     /**
      * Default constructor.
@@ -159,6 +159,61 @@ public class PropagationByResource imple
         return result;
     }
 
+    /**
+     * Remove some elements.
+     *
+     * @param type resource operation type
+     * @param resourceNames target resources
+     * @return whether the operation was successful or not
+     */
+    public boolean removeAll(final ResourceOperation type, final Set<String> resourceNames) {
+        Set<String> set;
+        switch (type) {
+            case CREATE:
+                set = toBeCreated;
+                break;
+
+            case UPDATE:
+                set = toBeUpdated;
+                break;
+
+            case DELETE:
+            default:
+                set = toBeDeleted;
+                break;
+        }
+
+        return set.removeAll(resourceNames);
+    }
+
+    /**
+     * Removes only the resource names in the underlying resource name sets that are contained in the specified
+     * collection.
+     *
+     * @param resourceNames collection containing resource names to be retained in the underlying resource name sets
+     * @return <tt>true</tt> if the underlying resource name sets changed as a result of the call
+     * @see Collection#removeAll(java.util.Collection)
+     */
+    public boolean removeAll(final Collection<String> resourceNames) {
+        return toBeCreated.removeAll(resourceNames)
+                || toBeUpdated.removeAll(resourceNames)
+                || toBeDeleted.removeAll(resourceNames);
+    }
+
+    /**
+     * Retains only the resource names in the underlying resource name sets that are contained in the specified
+     * collection.
+     *
+     * @param resourceNames collection containing resource names to be retained in the underlying resource name sets
+     * @return <tt>true</tt> if the underlying resource name sets changed as a result of the call
+     * @see Collection#retainAll(java.util.Collection)
+     */
+    public boolean retainAll(final Collection<String> resourceNames) {
+        return toBeCreated.retainAll(resourceNames)
+                || toBeUpdated.retainAll(resourceNames)
+                || toBeDeleted.retainAll(resourceNames);
+    }
+
     public boolean contains(final ResourceOperation type, final String resourceName) {
         boolean result = false;
 
@@ -188,7 +243,7 @@ public class PropagationByResource imple
      * @return resource matching the given type
      */
     public final Set<String> get(final ResourceOperation type) {
-        Set<String> result = Collections.emptySet();
+        Set<String> result = Collections.<String>emptySet();
 
         switch (type) {
             case CREATE:

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java?rev=1438543&r1=1438542&r2=1438543&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java Fri Jan 25 14:47:06 2013
@@ -26,7 +26,6 @@ import java.util.Date;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-
 import org.apache.commons.lang.StringUtils;
 import org.apache.syncope.common.types.PropagationMode;
 import org.apache.syncope.common.types.PropagationTaskExecStatus;
@@ -375,8 +374,7 @@ public abstract class AbstractPropagatio
     }
 
     @Override
-    public void execute(final Collection<PropagationTask> tasks)
-            throws PropagationException {
+    public void execute(final Collection<PropagationTask> tasks) throws PropagationException {
         execute(tasks, null);
     }
 

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PriorityPropagationTaskExecutor.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PriorityPropagationTaskExecutor.java?rev=1438543&r1=1438542&r2=1438543&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PriorityPropagationTaskExecutor.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PriorityPropagationTaskExecutor.java Fri Jan 25 14:47:06 2013
@@ -18,12 +18,12 @@
  */
 package org.apache.syncope.core.propagation.impl;
 
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
-
 import org.apache.syncope.common.types.PropagationTaskExecStatus;
 import org.apache.syncope.core.persistence.beans.PropagationTask;
 import org.apache.syncope.core.persistence.beans.TaskExec;
@@ -64,13 +64,22 @@ public class PriorityPropagationTaskExec
         }
     }
 
-    protected static class PriorityComparator implements Comparator<PropagationTask> {
+    protected static class PriorityComparator implements Comparator<PropagationTask>, Serializable {
+
+        private static final long serialVersionUID = -1969355670784448878L;
 
         @Override
         public int compare(final PropagationTask task1, final PropagationTask task2) {
-            return task1.getResource().getPropagationPriority() > task2.getResource().getPropagationPriority()
+            int prop1 = task1.getResource().getPropagationPriority() == null
+                    ? Integer.MIN_VALUE
+                    : task1.getResource().getPropagationPriority().intValue();
+            int prop2 = task2.getResource().getPropagationPriority() == null
+                    ? Integer.MIN_VALUE
+                    : task2.getResource().getPropagationPriority().intValue();
+
+            return prop1 > prop2
                     ? -1
-                    : task1.getResource().getPropagationPriority() == task2.getResource().getPropagationPriority()
+                    : prop1 == prop2
                     ? 0
                     : 1;
         }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java?rev=1438543&r1=1438542&r2=1438543&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/propagation/impl/PropagationManager.java Fri Jan 25 14:47:06 2013
@@ -44,7 +44,6 @@ import org.apache.syncope.core.rest.data
 import org.apache.syncope.core.rest.data.RoleDataBinder;
 import org.apache.syncope.core.rest.data.UserDataBinder;
 import org.apache.syncope.core.util.AttributableUtil;
-import org.apache.syncope.core.util.JexlUtil;
 import org.apache.syncope.core.util.MappingUtil;
 import org.apache.syncope.core.util.NotFoundException;
 import org.apache.syncope.core.workflow.WorkflowResult;
@@ -92,12 +91,6 @@ public class PropagationManager {
     private ConnObjectUtil connObjectUtil;
 
     /**
-     * JEXL engine for evaluating connector's account link.
-     */
-    @Autowired
-    private JexlUtil jexlUtil;
-
-    /**
      * Create the user on every associated resource.
      *
      * @param wfResult user to be propagated (and info associated), as per result from workflow
@@ -120,13 +113,13 @@ public class PropagationManager {
      * @param wfResult user to be propagated (and info associated), as per result from workflow
      * @param password to be set
      * @param vAttrs virtual attributes to be set
-     * @param syncResourceNames external resources performing sync, hence not to be considered for propagation
+     * @param noPropResourceNames external resources not to be considered for propagation
      * @return list of propagation tasks
      * @throws NotFoundException if user is not found
      * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given user
      */
     public List<PropagationTask> getUserCreateTaskIds(final WorkflowResult<Map.Entry<Long, Boolean>> wfResult,
-            final String password, final List<AttributeTO> vAttrs, final Set<String> syncResourceNames)
+            final String password, final List<AttributeTO> vAttrs, final Set<String> noPropResourceNames)
             throws NotFoundException, UnauthorizedRoleException {
 
         SyncopeUser user = userDataBinder.getUserFromId(wfResult.getResult().getKey());
@@ -134,7 +127,7 @@ public class PropagationManager {
             userDataBinder.fillVirtual(user, vAttrs, AttributableUtil.getInstance(AttributableType.USER));
         }
         return getCreateTaskIds(user, password, vAttrs,
-                wfResult.getResult().getValue(), wfResult.getPropByRes(), syncResourceNames);
+                wfResult.getResult().getValue(), wfResult.getPropByRes(), noPropResourceNames);
     }
 
     /**
@@ -158,32 +151,32 @@ public class PropagationManager {
      *
      * @param wfResult role to be propagated (and info associated), as per result from workflow
      * @param vAttrs virtual attributes to be set
-     * @param syncResourceNames external resources performing sync, hence not to be considered for propagation
+     * @param noPropResourceNames external resources performing not to be considered for propagation
      * @return list of propagation tasks
      * @throws NotFoundException if role is not found
      * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
      */
     public List<PropagationTask> getRoleCreateTaskIds(final WorkflowResult<Long> wfResult,
-            final List<AttributeTO> vAttrs, final Set<String> syncResourceNames)
+            final List<AttributeTO> vAttrs, final Set<String> noPropResourceNames)
             throws NotFoundException, UnauthorizedRoleException {
 
         SyncopeRole role = roleDataBinder.getRoleFromId(wfResult.getResult());
         if (vAttrs != null && !vAttrs.isEmpty()) {
             roleDataBinder.fillVirtual(role, vAttrs, AttributableUtil.getInstance(AttributableType.ROLE));
         }
-        return getCreateTaskIds(role, null, vAttrs, null, wfResult.getPropByRes(), syncResourceNames);
+        return getCreateTaskIds(role, null, vAttrs, null, wfResult.getPropByRes(), noPropResourceNames);
     }
 
     protected List<PropagationTask> getCreateTaskIds(final AbstractAttributable attributable,
             final String password, final List<AttributeTO> vAttrs, final Boolean enable,
-            final PropagationByResource propByRes, final Set<String> syncResourceNames) {
+            final PropagationByResource propByRes, final Set<String> noPropResourceNames) {
 
         if (propByRes == null || propByRes.isEmpty()) {
             return Collections.<PropagationTask>emptyList();
         }
 
-        if (syncResourceNames != null) {
-            propByRes.get(ResourceOperation.CREATE).removeAll(syncResourceNames);
+        if (noPropResourceNames != null) {
+            propByRes.get(ResourceOperation.CREATE).removeAll(noPropResourceNames);
         }
 
         return createTasks(attributable, password, null, null, enable, false, propByRes);
@@ -194,13 +187,12 @@ public class PropagationManager {
      *
      * @param user to be propagated
      * @param enable whether user must be enabled or not
-     * @param syncResourceNames external resource names not to be considered for propagation. Use this during sync and
-     * disable/enable actions limited to the external resources only
+     * @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> syncResourceNames)
+            final Set<String> noPropResourceNames)
             throws NotFoundException {
 
         return getUpdateTaskIds(
@@ -210,7 +202,7 @@ public class PropagationManager {
                 Collections.<String>emptySet(), // no virtual attributes to be managed
                 Collections.<AttributeMod>emptySet(), // no virtual attributes to be managed
                 null, // no propagation by resources
-                syncResourceNames);
+                noPropResourceNames);
     }
 
     /**
@@ -253,20 +245,19 @@ public class PropagationManager {
      * @param password to be updated
      * @param vAttrsToBeRemoved virtual attributes to be removed
      * @param vAttrsToBeUpdated virtual attributes to be added
-     * @param syncResourceNames external resource names not to be considered for propagation. Use this during sync and
-     * disable/enable actions limited to the external resources only
+     * @param noPropResourceNames external resources not to be considered for propagation
      * @return list of propagation tasks
      * @throws NotFoundException if user is not found
      * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given user
      */
     public List<PropagationTask> getUserUpdateTaskIds(final WorkflowResult<Map.Entry<Long, Boolean>> wfResult,
             final String password, final Set<String> vAttrsToBeRemoved, final Set<AttributeMod> vAttrsToBeUpdated,
-            final Set<String> syncResourceNames)
+            final Set<String> noPropResourceNames)
             throws NotFoundException, UnauthorizedRoleException {
 
         SyncopeUser user = userDataBinder.getUserFromId(wfResult.getResult().getKey());
         return getUpdateTaskIds(user, password, wfResult.getResult().getValue(),
-                vAttrsToBeRemoved, vAttrsToBeUpdated, wfResult.getPropByRes(), syncResourceNames);
+                vAttrsToBeRemoved, vAttrsToBeUpdated, wfResult.getPropByRes(), noPropResourceNames);
     }
 
     /**
@@ -292,26 +283,25 @@ public class PropagationManager {
      * @param wfResult role to be propagated (and info associated), as per result from workflow
      * @param vAttrsToBeRemoved virtual attributes to be removed
      * @param vAttrsToBeUpdated virtual attributes to be added
-     * @param syncResourceNames external resource names not to be considered for propagation. Use this during sync and
-     * disable/enable actions limited to the external resources only
+     * @param noPropResourceNames external resource names not to be considered for propagation
      * @return list of propagation tasks
      * @throws NotFoundException if role is not found
      * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
      */
     public List<PropagationTask> getRoleUpdateTaskIds(final WorkflowResult<Long> wfResult,
             final Set<String> vAttrsToBeRemoved, final Set<AttributeMod> vAttrsToBeUpdated,
-            final Set<String> syncResourceNames)
+            final Set<String> noPropResourceNames)
             throws NotFoundException, UnauthorizedRoleException {
 
         SyncopeRole role = roleDataBinder.getRoleFromId(wfResult.getResult());
         return getUpdateTaskIds(role, null, null,
-                vAttrsToBeRemoved, vAttrsToBeUpdated, wfResult.getPropByRes(), syncResourceNames);
+                vAttrsToBeRemoved, vAttrsToBeUpdated, wfResult.getPropByRes(), noPropResourceNames);
     }
 
     protected List<PropagationTask> getUpdateTaskIds(final AbstractAttributable attributable,
             final String password, final Boolean enable,
             final Set<String> vAttrsToBeRemoved, final Set<AttributeMod> vAttrsToBeUpdated,
-            final PropagationByResource propByRes, final Set<String> syncResourceNames)
+            final PropagationByResource propByRes, final Set<String> noPropResourceNames)
             throws NotFoundException {
 
         AbstractAttributableDataBinder binder = attributable instanceof SyncopeUser
@@ -329,10 +319,8 @@ public class PropagationManager {
             localPropByRes.merge(propByRes);
         }
 
-        if (syncResourceNames != null) {
-            localPropByRes.get(ResourceOperation.CREATE).removeAll(syncResourceNames);
-            localPropByRes.get(ResourceOperation.UPDATE).removeAll(syncResourceNames);
-            localPropByRes.get(ResourceOperation.DELETE).removeAll(syncResourceNames);
+        if (noPropResourceNames != null) {
+            localPropByRes.removeAll(noPropResourceNames);
         }
 
         Map<String, AttributeMod> vAttrsToBeUpdatedMap = null;
@@ -369,16 +357,16 @@ public class PropagationManager {
      * the creation fails onto a mandatory resource.
      *
      * @param userId to be deleted
-     * @param syncResourceName name of external resource performing sync, hence not to be considered for propagation
+     * @param noPropResourceNames name of external resource not to be considered for propagation
      * @return list of propagation tasks
      * @throws NotFoundException if user is not found
      * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given user
      */
-    public List<PropagationTask> getUserDeleteTaskIds(final Long userId, final String syncResourceName)
+    public List<PropagationTask> getUserDeleteTaskIds(final Long userId, final String noPropResourceNames)
             throws NotFoundException, UnauthorizedRoleException {
 
         SyncopeUser user = userDataBinder.getUserFromId(userId);
-        return getDeleteTaskIds(user, syncResourceName);
+        return getDeleteTaskIds(user, noPropResourceNames);
     }
 
     /**
@@ -403,25 +391,25 @@ public class PropagationManager {
      * the creation fails onto a mandatory resource.
      *
      * @param roleId to be deleted
-     * @param syncResourceName name of external resource performing sync, hence not to be considered for propagation
+     * @param noPropResourceName name of external resource not to be considered for propagation
      * @return list of propagation tasks
      * @throws NotFoundException if role is not found
      * @throws UnauthorizedRoleException if caller doesn't own enough entitlements to administer the given role
      */
-    public List<PropagationTask> getRoleDeleteTaskIds(final Long roleId, final String syncResourceName)
+    public List<PropagationTask> getRoleDeleteTaskIds(final Long roleId, final String noPropResourceName)
             throws NotFoundException, UnauthorizedRoleException {
 
         SyncopeRole role = roleDataBinder.getRoleFromId(roleId);
-        return getDeleteTaskIds(role, syncResourceName);
+        return getDeleteTaskIds(role, noPropResourceName);
     }
 
     protected List<PropagationTask> getDeleteTaskIds(final AbstractAttributable attributable,
-            final String syncResourceName) {
+            final String noPropResourceName) {
 
         final PropagationByResource propByRes = new PropagationByResource();
         propByRes.set(ResourceOperation.DELETE, attributable.getResourceNames());
-        if (syncResourceName != null) {
-            propByRes.get(ResourceOperation.DELETE).remove(syncResourceName);
+        if (noPropResourceName != null) {
+            propByRes.get(ResourceOperation.DELETE).remove(noPropResourceName);
         }
         return createTasks(attributable, null, null, null, false, true, propByRes);
     }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java?rev=1438543&r1=1438542&r2=1438543&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/ResourceController.java Fri Jan 25 14:47:06 2013
@@ -223,11 +223,9 @@ public class ResourceController extends 
         }
 
         final Set<Attribute> attributes = connectorObject.getAttributes();
-
         if (AttributeUtil.find(Uid.NAME, attributes) == null) {
             attributes.add(connectorObject.getUid());
         }
-
         if (AttributeUtil.find(Name.NAME, attributes) == null) {
             attributes.add(connectorObject.getName());
         }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java?rev=1438543&r1=1438542&r2=1438543&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java Fri Jan 25 14:47:06 2013
@@ -37,6 +37,7 @@ import org.apache.syncope.common.types.A
 import org.apache.syncope.common.types.AuditElements.Category;
 import org.apache.syncope.common.types.AuditElements.Result;
 import org.apache.syncope.common.types.AuditElements.UserSubCategory;
+import org.apache.syncope.common.types.ResourceOperation;
 import org.apache.syncope.core.audit.AuditManager;
 import org.apache.syncope.core.connid.ConnObjectUtil;
 import org.apache.syncope.core.notification.NotificationManager;
@@ -45,6 +46,7 @@ import org.apache.syncope.core.persisten
 import org.apache.syncope.core.persistence.dao.AttributableSearchDAO;
 import org.apache.syncope.core.persistence.dao.InvalidSearchConditionException;
 import org.apache.syncope.core.persistence.dao.UserDAO;
+import org.apache.syncope.core.propagation.PropagationByResource;
 import org.apache.syncope.core.propagation.PropagationException;
 import org.apache.syncope.core.propagation.PropagationTaskExecutor;
 import org.apache.syncope.core.propagation.impl.DefaultPropagationHandler;
@@ -314,16 +316,55 @@ public class UserController {
 
         LOG.debug("User update called with {}", userMod);
 
+        final String changedPwd = userMod.getPassword();
+
+        // 1. update password internally only if required
+        if (userMod.getPwdPropRequest() != null && !userMod.getPwdPropRequest().isOnSyncope()) {
+            userMod.setPassword(null);
+        }
         WorkflowResult<Map.Entry<Long, Boolean>> updated = uwfAdapter.update(userMod);
 
-        List<PropagationTask> tasks = propagationManager.getUserUpdateTaskIds(updated, userMod.getPassword(),
-                userMod.getVirtualAttributesToBeRemoved(), userMod.getVirtualAttributesToBeUpdated());
+        // 2. propagate password update only to requested resources
+        List<PropagationTask> tasks;
+        if (userMod.getPwdPropRequest() == null) {
+            // 2a. no specific password propagation request: generate propagation tasks for any resource associated
+            tasks = propagationManager.getUserUpdateTaskIds(updated, changedPwd,
+                    userMod.getVirtualAttributesToBeRemoved(), userMod.getVirtualAttributesToBeUpdated());
+        } else {
+            // 2b. generate the propagation task list in two phases: first the ones containing password, 
+            // the the rest (with no password)
+            final PropagationByResource origPropByRes = new PropagationByResource();
+            origPropByRes.merge(updated.getPropByRes());
+            SyncopeUser user = dataBinder.getUserFromId(updated.getResult().getKey());
+            origPropByRes.addAll(ResourceOperation.UPDATE, user.getResourceNames());
+            origPropByRes.purge();
+
+            final PropagationByResource pwdPropByRes = new PropagationByResource();
+            pwdPropByRes.merge(origPropByRes);
+            pwdPropByRes.retainAll(userMod.getPwdPropRequest().getResources());
+            updated.setPropByRes(pwdPropByRes);
+
+            tasks = propagationManager.getUserUpdateTaskIds(updated, changedPwd,
+                    userMod.getVirtualAttributesToBeRemoved(), userMod.getVirtualAttributesToBeUpdated());
+
+            final PropagationByResource nonPwdPropByRes = new PropagationByResource();
+            nonPwdPropByRes.merge(origPropByRes);
+            nonPwdPropByRes.removeAll(userMod.getPwdPropRequest().getResources());
+            updated.setPropByRes(nonPwdPropByRes);
+
+            tasks.addAll(propagationManager.getUserUpdateTaskIds(updated, null,
+                    userMod.getVirtualAttributesToBeRemoved(), userMod.getVirtualAttributesToBeUpdated()));
+
+            updated.setPropByRes(origPropByRes);
+        }
 
         final List<PropagationStatusTO> propagations = new ArrayList<PropagationStatusTO>();
         taskExecutor.execute(tasks, new DefaultPropagationHandler(connObjectUtil, propagations));
 
+        // 3. create notification tasks
         notificationManager.createTasks(updated.getResult().getKey(), updated.getPerformedTasks());
 
+        // 4. prepare result, including propagation status on external resources
         final UserTO updatedTO = dataBinder.getUserTO(updated.getResult().getKey());
         updatedTO.setPropagationStatusTOs(propagations);
 
@@ -357,10 +398,7 @@ public class UserController {
 
         LOG.debug("About to activate " + userId);
 
-        SyncopeUser user = userDAO.find(userId);
-        if (user == null) {
-            throw new NotFoundException("User " + userId);
-        }
+        SyncopeUser user = dataBinder.getUserFromId(userId);
 
         return setStatus(user, token, propagationRequestTO, true, "activate");
     }
@@ -387,10 +425,7 @@ public class UserController {
 
         LOG.debug("About to activate " + username);
 
-        SyncopeUser user = userDAO.find(username);
-        if (user == null) {
-            throw new NotFoundException("User " + username);
-        }
+        SyncopeUser user = dataBinder.getUserFromUsername(username);
 
         return setStatus(user, token, propagationRequestTO, true, "activate");
     }
@@ -413,10 +448,7 @@ public class UserController {
 
         LOG.debug("About to suspend " + userId);
 
-        SyncopeUser user = userDAO.find(userId);
-        if (user == null) {
-            throw new NotFoundException("User " + userId);
-        }
+        SyncopeUser user = dataBinder.getUserFromId(userId);
 
         return setStatus(user, null, propagationRequestTO, false, "suspend");
     }
@@ -439,11 +471,7 @@ public class UserController {
 
         LOG.debug("About to suspend " + username);
 
-        SyncopeUser user = userDAO.find(username);
-
-        if (user == null) {
-            throw new NotFoundException("User " + username);
-        }
+        SyncopeUser user = dataBinder.getUserFromUsername(username);
 
         return setStatus(user, null, propagationRequestTO, false, "suspend");
     }
@@ -466,10 +494,7 @@ public class UserController {
 
         LOG.debug("About to reactivate " + userId);
 
-        SyncopeUser user = userDAO.find(userId);
-        if (user == null) {
-            throw new NotFoundException("User " + userId);
-        }
+        SyncopeUser user = dataBinder.getUserFromId(userId);
 
         return setStatus(user, null, propagationRequestTO, true, "reactivate");
     }
@@ -491,10 +516,7 @@ public class UserController {
 
         LOG.debug("About to reactivate " + username);
 
-        SyncopeUser user = userDAO.find(username);
-        if (user == null) {
-            throw new NotFoundException("User " + username);
-        }
+        SyncopeUser user = dataBinder.getUserFromUsername(username);
 
         return setStatus(user, null, propagationRequestTO, true, "reactivate");
     }
@@ -610,6 +632,7 @@ public class UserController {
                 updated.getPerformedTasks()),
                 updated.getResult().getValue(),
                 null,
+                null,
                 null);
         taskExecutor.execute(tasks);
 
@@ -642,7 +665,7 @@ public class UserController {
             updated = new WorkflowResult<Long>(user.getId(), null, task);
         }
 
-        // Resources to exclude from propagation.
+        // Resources to exclude from propagation
         Set<String> resourcesToBeExcluded = new HashSet<String>(user.getResourceNames());
         if (propagationRequestTO != null) {
             resourcesToBeExcluded.removeAll(propagationRequestTO.getResources());

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/AbstractTest.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/AbstractTest.java?rev=1438543&r1=1438542&r2=1438543&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/AbstractTest.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/AbstractTest.java Fri Jan 25 14:47:06 2013
@@ -20,7 +20,6 @@ package org.apache.syncope.core.rest;
 
 import static org.junit.Assert.assertNotNull;
 
-import java.net.URI;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -296,7 +295,7 @@ public abstract class AbstractTest {
 
     @Parameters
     public static Collection<Object[]> data() {
-      Object[][] data = new Object[][]{{"application/json"}};
-      return Arrays.asList(data);
+        Object[][] data = new Object[][]{{"application/json"}};
+        return Arrays.asList(data);
     }
 }

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java?rev=1438543&r1=1438542&r2=1438543&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java Fri Jan 25 14:47:06 2013
@@ -20,6 +20,7 @@ package org.apache.syncope.core.rest;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -31,9 +32,7 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
-
 import javax.ws.rs.core.Response;
-
 import org.apache.syncope.common.mod.AttributeMod;
 import org.apache.syncope.common.mod.MembershipMod;
 import org.apache.syncope.common.mod.UserMod;
@@ -331,7 +330,7 @@ public class UserTestITCase extends Abst
 
         SyncopeClientException sce = null;
         try {
-            userTO = userService.update(userMod.getId(), userMod);
+            userService.update(userMod.getId(), userMod);
         } catch (SyncopeClientCompositeErrorException scce) {
             sce = scce.getException(SyncopeClientExceptionType.RequiredValuesMissing);
         }
@@ -1881,4 +1880,64 @@ public class UserTestITCase extends Abst
         userTO.addResource("ws-target-resource-3");
         userService.create(userTO);
     }
+
+    @Test
+    public void issueSYNCOPE122() {
+        // 1. create user on testdb and testdb2
+        UserTO userTO = getSampleTO("syncope123@apache.org");
+        userTO.getResources().clear();
+        userTO.addResource("resource-testdb");
+        userTO.addResource("resource-testdb2");
+        userTO = userService.create(userTO);
+        assertNotNull(userTO);
+        assertTrue(userTO.getResources().contains("resource-testdb"));
+        assertTrue(userTO.getResources().contains("resource-testdb2"));
+
+        final String pwdOnSyncope = userTO.getPassword();
+
+        ConnObjectTO userOnDb =
+                resourceService.getConnector("resource-testdb", AttributableType.USER, userTO.getUsername());
+        final AttributeTO pwdOnTestDbAttr = userOnDb.getAttributeMap().get(OperationalAttributes.PASSWORD_NAME);
+        assertNotNull(pwdOnTestDbAttr);
+        assertNotNull(pwdOnTestDbAttr.getValues());
+        assertFalse(pwdOnTestDbAttr.getValues().isEmpty());
+        final String pwdOnTestDb = pwdOnTestDbAttr.getValues().iterator().next();
+
+        ConnObjectTO userOnDb2 =
+                resourceService.getConnector("resource-testdb2", AttributableType.USER, userTO.getUsername());
+        final AttributeTO pwdOnTestDb2Attr = userOnDb2.getAttributeMap().get(OperationalAttributes.PASSWORD_NAME);
+        assertNotNull(pwdOnTestDb2Attr);
+        assertNotNull(pwdOnTestDb2Attr.getValues());
+        assertFalse(pwdOnTestDb2Attr.getValues().isEmpty());
+        final String pwdOnTestDb2 = pwdOnTestDb2Attr.getValues().iterator().next();
+
+        // 2. request to change password only on testdb (no Syncope, no testdb2)
+        UserMod userMod = new UserMod();
+        userMod.setId(userTO.getId());
+        userMod.setPassword(getUUIDString());
+        PropagationRequestTO pwdPropRequest = new PropagationRequestTO();
+        pwdPropRequest.addResource("resource-testdb");
+        userMod.setPwdPropRequest(pwdPropRequest);
+
+        userTO = userService.update(userMod.getId(), userMod);
+
+        // 3a. verify that password hasn't changed on Syncope
+        assertEquals(pwdOnSyncope, userTO.getPassword());
+
+        // 3b. verify that password *has* changed on testdb
+        userOnDb = resourceService.getConnector("resource-testdb", AttributableType.USER, userTO.getUsername());
+        final AttributeTO pwdOnTestDbAttrAfter = userOnDb.getAttributeMap().get(OperationalAttributes.PASSWORD_NAME);
+        assertNotNull(pwdOnTestDbAttrAfter);
+        assertNotNull(pwdOnTestDbAttrAfter.getValues());
+        assertFalse(pwdOnTestDbAttrAfter.getValues().isEmpty());
+        assertNotEquals(pwdOnTestDb, pwdOnTestDbAttrAfter.getValues().iterator().next());
+
+        // 3c. verify that password hasn't changed on testdb2
+        userOnDb2 = resourceService.getConnector("resource-testdb2", AttributableType.USER, userTO.getUsername());
+        final AttributeTO pwdOnTestDb2AttrAfter = userOnDb2.getAttributeMap().get(OperationalAttributes.PASSWORD_NAME);
+        assertNotNull(pwdOnTestDb2AttrAfter);
+        assertNotNull(pwdOnTestDb2AttrAfter.getValues());
+        assertFalse(pwdOnTestDb2AttrAfter.getValues().isEmpty());
+        assertEquals(pwdOnTestDb2, pwdOnTestDb2AttrAfter.getValues().iterator().next());
+    }
 }