Author: andreapatricelli
Date: Tue Jun 17 09:22:49 2014
New Revision: 1603100

URL: http://svn.apache.org/r1603100
Log:
[SYNCOPE-501] improved membership virtual attributes management, added 
propagation of only membership virtual attributes

Modified:
    
syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
    
syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java
    
syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
    
syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java

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=1603100&r1=1603099&r2=1603100&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
 Tue Jun 17 09:22:49 2014
@@ -39,6 +39,8 @@ import org.apache.syncope.common.to.User
 import org.apache.syncope.common.types.AttributableType;
 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.core.persistence.beans.PropagationTask;
 import org.apache.syncope.core.persistence.beans.role.SyncopeRole;
 import org.apache.syncope.core.persistence.beans.user.SyncopeUser;
@@ -249,6 +251,20 @@ public class UserController extends Abst
         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 = Boolean.FALSE;
+
+        for (Long membershipId : actual.getMembershipsToRemove()) {
+            if (!binder.fillMembershipVirtual(
+                    null,
+                    null,
+                    membershipId,
+                    Collections.<String>emptySet(),
+                    Collections.<AttributeMod>emptySet(),
+                    Boolean.TRUE).isEmpty()) {
+                removeMemberships = Boolean.TRUE;
+            }
+        }
         //Actual operations: workflow, propagation, notification
         WorkflowResult<Map.Entry<UserMod, Boolean>> updated = 
uwfAdapter.update(actual);
 
@@ -259,11 +275,23 @@ public class UserController extends Abst
                     updated.getResult().getKey().getId(),
                     actual.getVirAttrsToRemove(),
                     actual.getVirAttrsToUpdate());
-            // SYNCOPE-501: update only virtual attributes (if any of them 
changed), password propagation is 
-            // not required
-            tasks.addAll(propByResVirAttr.isEmpty()
-                    ? Collections.<PropagationTask>emptyList()
-                    : propagationManager.getUserUpdateTaskIds(updated, false, 
null));
+            // 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 = Boolean.FALSE;
+            for (MembershipMod membershipMod : actual.getMembershipsToAdd()) {
+                if (!binder.fillMembershipVirtual(
+                        updated.getResult().getKey().getId(),
+                        membershipMod.getRole(),
+                        null,
+                        membershipMod.getVirAttrsToRemove(),
+                        membershipMod.getVirAttrsToUpdate(),
+                        Boolean.FALSE).isEmpty()) {
+                    addOrUpdateMemberships = Boolean.TRUE;
+                }
+            }
+            tasks.addAll(!propByResVirAttr.isEmpty() || addOrUpdateMemberships 
|| removeMemberships
+                    ? propagationManager.getUserUpdateTaskIds(updated, false, 
null)
+                    : Collections.<PropagationTask>emptyList());
         }
 
         PropagationReporter propagationReporter = 
ApplicationContextProvider.getApplicationContext().

Modified: 
syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java
URL: 
http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java?rev=1603100&r1=1603099&r2=1603100&view=diff
==============================================================================
--- 
syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java
 (original)
+++ 
syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/data/AbstractAttributableDataBinder.java
 Tue Jun 17 09:22:49 2014
@@ -420,6 +420,14 @@ public abstract class AbstractAttributab
 
         PropagationByResource propByRes = new PropagationByResource();
 
+        final Set<ExternalResource> externalResources = new 
HashSet<ExternalResource>();
+        externalResources.addAll(attributable.getResources());
+
+        if (attributable instanceof Membership) {
+            externalResources.clear();
+            externalResources.addAll(((Membership) 
attributable).getSyncopeUser().getResources());
+        }
+
         // 1. virtual attributes to be removed
         for (String vAttrToBeRemoved : vAttrsToBeRemoved) {
             AbstractVirSchema virSchema = getVirSchema(vAttrToBeRemoved, 
attrUtil.virSchemaClass());
@@ -436,7 +444,7 @@ public abstract class AbstractAttributab
                     for (AbstractMappingItem mapItem : 
attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) {
                         if 
(virSchema.getName().equals(mapItem.getIntAttrName())
                                 && mapItem.getIntMappingType() == 
attrUtil.virIntMappingType()
-                                && 
attributable.getResources().contains(resource)) {
+                                && externalResources.contains(resource)) {
 
                             propByRes.add(ResourceOperation.UPDATE, 
resource.getName());
 
@@ -474,7 +482,7 @@ public abstract class AbstractAttributab
                     for (AbstractMappingItem mapItem : 
attrUtil.getMappingItems(resource, MappingPurpose.PROPAGATION)) {
                         if 
(virSchema.getName().equals(mapItem.getIntAttrName())
                                 && mapItem.getIntMappingType() == 
attrUtil.virIntMappingType()
-                                && 
attributable.getResources().contains(resource)) {
+                                && externalResources.contains(resource)) {
 
                             propByRes.add(ResourceOperation.UPDATE, 
resource.getName());
                         }

Modified: 
syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
URL: 
http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java?rev=1603100&r1=1603099&r2=1603100&view=diff
==============================================================================
--- 
syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
 (original)
+++ 
syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
 Tue Jun 17 09:22:49 2014
@@ -18,8 +18,10 @@
  */
 package org.apache.syncope.core.rest.data;
 
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import javax.annotation.Resource;
 import org.apache.commons.lang3.StringUtils;
@@ -108,6 +110,20 @@ public class UserDataBinder extends Abst
     }
 
     @Transactional(readOnly = true)
+    public Membership getMembershipFromId(final Long membershipId) {
+        if (membershipId == null) {
+            throw new NotFoundException("Null membership id");
+        }
+
+        Membership membership = membershipDAO.find(membershipId);
+        if (membership == null) {
+            throw new NotFoundException("Membership " + membershipId);
+        }
+
+        return membership;
+    }
+
+    @Transactional(readOnly = true)
     public Set<String> getResourceNamesForUserId(final Long userId) {
         return getUserFromId(userId).getResourceNames();
     }
@@ -454,4 +470,45 @@ public class UserDataBinder extends Abst
                 vAttrsToBeUpdated,
                 AttributableUtil.getInstance(AttributableType.USER));
     }
+
+    /**
+     * SYNCOPE-501: build membership virtual attribute changes in case no 
other changes were made.
+     *
+     * @param userId user id
+     * @param roleId role id
+     * @param membershipId membership id
+     * @param vAttrsToBeRemoved virtual attributes to be removed.
+     * @param vAttrsToBeUpdated virtual attributes to be updated.
+     * @param isRemoval flag to check if fill is on removed or added membership
+     * @return operations to be performed on external resources formembership 
virtual attributes changes
+     */
+    public PropagationByResource fillMembershipVirtual(
+            final Long userId, final Long roleId, final Long membershipId, 
final Set<String> vAttrsToBeRemoved,
+            final Set<AttributeMod> vAttrsToBeUpdated, final Boolean 
isRemoval) {
+        final Membership membership = membershipId == null
+                ? getUserFromId(userId).getMembership(roleId)
+                : getMembershipFromId(membershipId);
+
+        return membership == null ? new PropagationByResource() : isRemoval
+                ? fillVirtual(
+                        membership,
+                        membership.getVirAttrs() == null
+                        ? Collections.<String>emptySet()
+                        : getAttributeNames(membership.getVirAttrs()),
+                        vAttrsToBeUpdated,
+                        
AttributableUtil.getInstance(AttributableType.MEMBERSHIP))
+                : fillVirtual(
+                        membership,
+                        vAttrsToBeRemoved,
+                        vAttrsToBeUpdated,
+                        
AttributableUtil.getInstance(AttributableType.MEMBERSHIP));
+    }
+
+    private Set<String> getAttributeNames(final List<? extends 
AbstractVirAttr> virAttrs) {
+        final HashSet<String> virAttrNames = new HashSet<String>();
+        for (AbstractVirAttr attr : virAttrs) {
+            virAttrNames.add(attr.getSchema().getName());
+        }
+        return virAttrNames;
+    }
 }

Modified: 
syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java
URL: 
http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java?rev=1603100&r1=1603099&r2=1603100&view=diff
==============================================================================
--- 
syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java
 (original)
+++ 
syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/VirAttrTestITCase.java
 Tue Jun 17 09:22:49 2014
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertTru
 import java.util.Collections;
 import org.apache.commons.lang3.SerializationUtils;
 import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.mod.AttributeMod;
 import org.apache.syncope.common.mod.MembershipMod;
 import org.apache.syncope.common.mod.StatusMod;
@@ -768,5 +769,103 @@ public class VirAttrTestITCase extends A
         
assertFalse(userTO.getVirAttrMap().get("virtualdata").getValues().isEmpty());
         assertEquals("syncope501_upda...@apache.org", 
userTO.getVirAttrMap().get("virtualdata").getValues().
                 get(0));
+
+        // ----------------------------------------------------------
+        
+        // PHASE 2: update only membership virtual attributes
+        // -------------------------------------------
+        // Update resource-db-virattr mapping adding new membership virtual 
schema mapping
+        // -------------------------------------------
+        ResourceTO resourceDBVirAttr = 
resourceService.read(RESOURCE_NAME_DBVIRATTR);
+        assertNotNull(resourceDBVirAttr);
+
+        final MappingTO resourceUMapping = resourceDBVirAttr.getUmapping();
+
+        MappingItemTO item = new MappingItemTO();
+        item.setIntAttrName("mvirtualdata");
+        item.setIntMappingType(IntMappingType.MembershipVirtualSchema);
+        item.setExtAttrName("EMAIL");
+        item.setPurpose(MappingPurpose.BOTH);
+
+        resourceUMapping.addItem(item);
+
+        resourceDBVirAttr.setUmapping(resourceUMapping);
+
+        resourceService.update(RESOURCE_NAME_DBVIRATTR, resourceDBVirAttr);
+        // -------------------------------------------
+
+        // -------------------------------------------
+        // Create a role ad-hoc
+        // -------------------------------------------
+        final String roleName = "issueSYNCOPE501-Role-" + getUUIDString();
+        RoleTO roleTO = new RoleTO();
+        roleTO.setName(roleName);
+        roleTO.setParent(2L);
+        roleTO.setInheritTemplates(true);
+        roleTO = createRole(roleTO);
+        // -------------------------------------------
+
+        // 1. add membership, with virtual attribute populated, to user
+        MembershipMod membershipMod = new MembershipMod();
+        membershipMod.setRole(roleTO.getId());
+        membershipMod.getVirAttrsToUpdate().add(attributeMod("mvirtualdata", 
"syncope501members...@test.org"));
+
+        userMod = new UserMod();
+        userMod.setId(userTO.getId());
+        userMod.getMembershipsToAdd().add(membershipMod);
+        userMod.setPwdPropRequest(statusMod);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+        assertEquals("syncope501members...@test.org",
+                
userTO.getMemberships().get(0).getVirAttrMap().get("mvirtualdata").getValues().get(0));
+
+        // 2. update only membership virtual attribute and propagate user
+        membershipMod = new MembershipMod();
+        membershipMod.setRole(roleTO.getId());
+        membershipMod.getVirAttrsToUpdate().add(attributeMod("mvirtualdata",
+                "syncope501membership_upda...@test.org"));
+        
membershipMod.getVirAttrsToRemove().add("syncope501members...@test.org");
+
+        userMod = new UserMod();
+        userMod.setId(userTO.getId());
+        userMod.getMembershipsToAdd().add(membershipMod);
+        
userMod.getMembershipsToRemove().add(userTO.getMemberships().iterator().next().getId());
+        userMod.setPwdPropRequest(statusMod);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+
+        // 3. check if change has been propagated
+        assertEquals("syncope501membership_upda...@test.org", 
userTO.getMemberships().get(0).getVirAttrMap().
+                get("mvirtualdata").getValues().get(0));
+
+        // 4. delete membership and check on resource attribute deletion
+        userMod = new UserMod();
+        userMod.setId(userTO.getId());
+        
userMod.getMembershipsToRemove().add(userTO.getMemberships().get(0).getId());
+        userMod.setPwdPropRequest(statusMod);
+
+        userTO = updateUser(userMod);
+        assertNotNull(userTO);
+        assertTrue(userTO.getMemberships().isEmpty());
+
+        // read attribute value directly on resource
+        final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+
+        final String emailValue = jdbcTemplate.queryForObject(
+                "SELECT EMAIL FROM testsync WHERE ID=?", String.class, 
userTO.getId());
+        assertTrue(StringUtils.isBlank(emailValue));
+        // ----------------------------------------
+
+        // -------------------------------------------
+        // Delete role ad-hoc and restore resource mapping
+        // -------------------------------------------
+        roleService.delete(roleTO.getId());
+
+        resourceUMapping.removeItem(item);
+        resourceDBVirAttr.setUmapping(resourceUMapping);
+        resourceService.update(RESOURCE_NAME_DBVIRATTR, resourceDBVirAttr);
+        // -------------------------------------------
     }
 }


Reply via email to