You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by md...@apache.org on 2019/05/31 15:33:59 UTC

[syncope] branch master updated: [SYNCOPE-1473] conservative membership policy management

This is an automated email from the ASF dual-hosted git repository.

mdisabatino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/master by this push:
     new 3d6e0b6  [SYNCOPE-1473] conservative membership policy management
3d6e0b6 is described below

commit 3d6e0b6ba70bdfe0b0083215578b3def1fd0a396
Author: Marco Di Sabatino Di Diodoro <ma...@tirasa.net>
AuthorDate: Fri May 31 15:25:22 2019 +0200

    [SYNCOPE-1473] conservative membership policy management
---
 .../LDAPMembershipPropagationActions.java          |  56 +++++++++--
 .../syncope/fit/core/PropagationTaskITCase.java    | 107 +++++++++++++++++++++
 2 files changed, 153 insertions(+), 10 deletions(-)

diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java
index 58fc0d3..d1817ae 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java
@@ -23,6 +23,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
+import java.util.TreeSet;
 import org.apache.commons.jexl3.JexlContext;
 import org.apache.commons.jexl3.MapContext;
 import org.apache.commons.lang3.StringUtils;
@@ -34,6 +35,7 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
 import org.identityconnectors.framework.common.objects.Attribute;
@@ -87,16 +89,14 @@ public class LDAPMembershipPropagationActions implements PropagationActions {
                 userDAO.findAllGroupKeys(user).forEach(groupKey -> {
                     Group group = groupDAO.find(groupKey);
                     if (group != null && groupDAO.findAllResourceKeys(groupKey).contains(task.getResource().getKey())) {
-                        LOG.debug("Evaluating connObjectLink for {}", group);
-                        JexlContext jexlContext = new MapContext();
-                        JexlUtils.addFieldsToContext(group, jexlContext);
-                        JexlUtils.addPlainAttrsToContext(group.getPlainAttrs(), jexlContext);
-                        JexlUtils.addDerAttrsToContext(group, jexlContext);
-                        String groupConnObjectLinkLink =
-                                JexlUtils.evaluate(provision.get().getMapping().getConnObjectLink(), jexlContext);
-                        LOG.debug("ConnObjectLink for {} is '{}'", group, groupConnObjectLinkLink);
-                        if (StringUtils.isNotBlank(groupConnObjectLinkLink)) {
-                            groupConnObjectLinks.add(groupConnObjectLinkLink);
+
+                        String groupConnObjectLink = evaluateGroupConnObjectLink(
+                                provision.get().getMapping().getConnObjectLink(), group);
+
+                        LOG.debug("ConnObjectLink for {} is '{}'", group, groupConnObjectLink);
+                        if (StringUtils.isNotBlank(groupConnObjectLink)) {
+                            groupConnObjectLinks.add(groupConnObjectLink);
+
                         }
                     }
                 });
@@ -111,7 +111,24 @@ public class LDAPMembershipPropagationActions implements PropagationActions {
                         groups.add(obj.toString());
                     });
                     attributes.remove(ldapGroups);
+
+                    if (beforeObj != null && beforeObj.getAttributeByName(getGroupMembershipAttrName()) != null) {
+                        Set<String> connObjectLinks = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+                        buildManagedGroupConnObjectLinks(
+                                provision.get().getResource(),
+                                provision.get().getMapping().getConnObjectLink(),
+                                connObjectLinks);
+
+                        Attribute beforeLdapGroups = beforeObj.getAttributeByName(getGroupMembershipAttrName());
+                        LOG.debug("Memberships not managed by Syncope: {}", beforeLdapGroups);
+                        for (Object value : beforeLdapGroups.getValue()) {
+                            if (!connObjectLinks.contains(String.valueOf(value))) {
+                                groups.add(String.valueOf(value));
+                            }
+                        }
+                    }
                 }
+                LOG.debug("Add ldapGroups to attributes: {}" + groups);
                 attributes.add(AttributeBuilder.build(getGroupMembershipAttrName(), groups));
 
                 task.setAttributes(attributes);
@@ -120,4 +137,23 @@ public class LDAPMembershipPropagationActions implements PropagationActions {
             LOG.debug("Not about user, or group mapping missing for resource: not doing anything");
         }
     }
+
+    private String evaluateGroupConnObjectLink(final String connObjectLinkTemplate, final Group group) {
+        LOG.debug("Evaluating connObjectLink for {}", group);
+
+        JexlContext jexlContext = new MapContext();
+        JexlUtils.addFieldsToContext(group, jexlContext);
+        JexlUtils.addPlainAttrsToContext(group.getPlainAttrs(), jexlContext);
+        JexlUtils.addDerAttrsToContext(group, jexlContext);
+
+        return JexlUtils.evaluate(connObjectLinkTemplate, jexlContext);
+    }
+
+    private void buildManagedGroupConnObjectLinks(final ExternalResource externalResource,
+            final String connObjectLinkTemplate, final Set<String> connObjectLinks) {
+        List<Group> managedGroups = groupDAO.findByResource(externalResource);
+        managedGroups.forEach(group -> {
+            connObjectLinks.add(evaluateGroupConnObjectLink(connObjectLinkTemplate, group));
+        });
+    }
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
index 458e09f..63ec15e 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
@@ -46,12 +46,18 @@ import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.TaskTO;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.request.GroupCR;
+import org.apache.syncope.common.lib.request.MembershipUR;
+import org.apache.syncope.common.lib.request.ResourceDR;
 import org.apache.syncope.common.lib.to.ConnObjectTO;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.ExecTO;
 import org.apache.syncope.common.lib.to.ImplementationTO;
+import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.ItemTO;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.PlainSchemaTO;
 import org.apache.syncope.common.lib.to.ProvisionTO;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.to.ResourceTO;
@@ -59,8 +65,11 @@ import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.IdMImplementationType;
 import org.apache.syncope.common.lib.types.ImplementationEngine;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.common.lib.types.PatchOperation;
+import org.apache.syncope.common.lib.types.ResourceDeassociationAction;
+import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.common.rest.api.beans.ExecuteQuery;
@@ -462,4 +471,102 @@ public class PropagationTaskITCase extends AbstractTaskITCase {
             }
         }
     }
+
+    @Test
+    public void issueSYNCOPE1473() throws ParseException {
+        // create a new group schema
+        PlainSchemaTO schemaTO = new PlainSchemaTO();
+        schemaTO.setKey("ldapGroups" + getUUIDString());
+        schemaTO.setType(AttrSchemaType.String);
+        schemaTO.setMultivalue(true);
+        schemaTO.setReadonly(true);
+        schemaTO.setAnyTypeClass("minimal user");
+
+        schemaTO = createSchema(SchemaType.PLAIN, schemaTO);
+        assertNotNull(schemaTO);
+
+        ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
+        try {
+            // 1. clone the LDAP resource and add some sensible mappings
+            ProvisionTO provisionGroup =
+                    SerializationUtils.clone(ldap.getProvision(AnyTypeKind.GROUP.name()).orElse(null));
+            assertNotNull(provisionGroup);
+            provisionGroup.getVirSchemas().clear();
+
+            ProvisionTO provisionUser =
+                    SerializationUtils.clone(ldap.getProvision(AnyTypeKind.USER.name()).orElse(null));
+            assertNotNull(provisionUser);
+            provisionUser.getMapping().getItems().removeIf(item -> "mail".equals(item.getExtAttrName()));
+            provisionUser.getVirSchemas().clear();
+
+            ItemTO ldapGroups = new ItemTO();
+            ldapGroups.setPurpose(MappingPurpose.PROPAGATION);
+            ldapGroups.setIntAttrName(schemaTO.getKey());
+            ldapGroups.setExtAttrName("ldapGroups");
+            provisionUser.getMapping().add(ldapGroups);
+
+            ldap.getProvisions().clear();
+            ldap.getProvisions().add(provisionUser);
+            ldap.getProvisions().add(provisionGroup);
+            ldap.setKey(RESOURCE_NAME_LDAP + "1473" + getUUIDString());
+            resourceService.create(ldap);
+
+            // 1. create group with the new resource assigned
+            GroupCR groupCR = new GroupCR();
+            groupCR.setName("SYNCOPEGROUP1473-" + getUUIDString());
+            groupCR.setRealm("/");
+            groupCR.getResources().add(ldap.getKey());
+
+            GroupTO groupTO = createGroup(groupCR).getEntity();
+            assertNotNull(groupCR);
+
+            // 2. create user with the new resource assigned
+            UserCR userCR = UserITCase.getUniqueSample("syncope1473@syncope.apache.org");
+            userCR.getResources().clear();
+            userCR.getResources().add(ldap.getKey());
+            userCR.getMemberships().add(new MembershipTO.Builder(groupTO.getKey()).build());
+
+            UserTO userTO = createUser(userCR).getEntity();
+            assertNotNull(userTO);
+
+            // 3. check attributes prepared for propagation
+            PagedResult<PropagationTaskTO> tasks = taskService.search(new TaskQuery.Builder(TaskType.PROPAGATION).
+                    resource(userTO.getResources().iterator().next()).
+                    anyTypeKind(AnyTypeKind.USER).entityKey(userTO.getKey()).build());
+            assertEquals(1, tasks.getSize());
+
+            ResourceDR resourceDR = new ResourceDR.Builder().key(groupTO.getKey()).
+                    action(ResourceDeassociationAction.UNLINK).resource(ldap.getKey()).build();
+
+            groupService.deassociate(resourceDR);
+            groupService.delete(groupTO.getKey());
+
+            GroupCR newGroupCR = new GroupCR();
+            newGroupCR.setName("NEWSYNCOPEGROUP1473-" + getUUIDString());
+            newGroupCR.setRealm("/");
+            newGroupCR.getResources().add(ldap.getKey());
+
+            GroupTO newGroupTO = createGroup(newGroupCR).getEntity();
+            assertNotNull(newGroupTO);
+
+            UserUR userUR = new UserUR();
+            userUR.setKey(userTO.getKey());
+            userUR.getMemberships().add(
+                    new MembershipUR.Builder(newGroupTO.getKey()).operation(PatchOperation.ADD_REPLACE).build());
+            userService.update(userUR);
+
+            ConnObjectTO connObject =
+                    resourceService.readConnObject(ldap.getKey(), AnyTypeKind.USER.name(), userTO.getKey());
+            assertNotNull(connObject);
+            assertNotNull(connObject.getAttr("ldapGroups"));
+            assertTrue(connObject.getAttr("ldapGroups").get().getValues().size() == 2);
+
+        } finally {
+            try {
+                resourceService.delete(ldap.getKey());
+            } catch (Exception ignore) {
+                // ignore
+            }
+        }
+    }
 }