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); + // ------------------------------------------- } }