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

andreapatricelli 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 4b13569785 SYNCOPE-1853 avoid unwanted user/any deprovision on group 
delete (#958) (#961)
4b13569785 is described below

commit 4b13569785933e6ab0e795ac2931374c4fd1e398
Author: Andrea Patricelli <[email protected]>
AuthorDate: Tue Jan 21 09:26:36 2025 +0100

    SYNCOPE-1853 avoid unwanted user/any deprovision on group delete (#958) 
(#961)
    
    * [SYNCOPE-1853] Avoid unwanted user/any object propagation on group delete
---
 .../java/data/GroupDataBinderImpl.java             | 12 +++--
 .../apache/syncope/fit/core/UserIssuesITCase.java  | 58 ++++++++++++++++++++++
 2 files changed, 67 insertions(+), 3 deletions(-)

diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
index 655bf6f449..2d3dc57590 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
@@ -48,13 +48,13 @@ import 
org.apache.syncope.core.persistence.api.dao.RealmSearchDAO;
 import org.apache.syncope.core.persistence.api.dao.RelationshipTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
-import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.DerSchema;
 import org.apache.syncope.core.persistence.api.entity.DynGroupMembership;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.GroupableRelatable;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.VirSchema;
 import 
org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership;
@@ -445,11 +445,17 @@ public class GroupDataBinderImpl extends 
AbstractAnyDataBinder implements GroupD
     }
 
     protected static void populateTransitiveResources(
-            final Group group, final Any<?> any, final Map<String, 
PropagationByResource<String>> result) {
+            final Group group,
+            final GroupableRelatable<?, ?, ?, ?, ?> any,
+            final Map<String, PropagationByResource<String>> result) {
 
         PropagationByResource<String> propByRes = new 
PropagationByResource<>();
         group.getResources().forEach(resource -> {
-            if (!any.getResources().contains(resource)) {
+            // exclude from propagation those objects that have that resource 
assigned by some other membership(s)
+            if (!any.getResources().contains(resource) && 
any.getMemberships().stream()
+                    .filter(otherGrpMemb -> 
!otherGrpMemb.getRightEnd().equals(group))
+                    .noneMatch(otherGrpMemb -> 
otherGrpMemb.getRightEnd().getResources().stream()
+                            .anyMatch(r -> 
resource.getKey().equals(r.getKey())))) {
                 propByRes.add(ResourceOperation.DELETE, resource.getKey());
             }
 
diff --git 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java
 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java
index 13eee4c164..466e417577 100644
--- 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java
+++ 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.fit.core;
 
+import static org.awaitility.Awaitility.await;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
@@ -40,6 +41,8 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 import javax.naming.NamingException;
 import org.apache.commons.lang3.tuple.Triple;
 import org.apache.cxf.helpers.IOUtils;
@@ -71,6 +74,7 @@ import org.apache.syncope.common.lib.to.Mapping;
 import org.apache.syncope.common.lib.to.MembershipTO;
 import org.apache.syncope.common.lib.to.PlainSchemaTO;
 import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.to.PushTaskTO;
 import org.apache.syncope.common.lib.to.RealmTO;
@@ -93,6 +97,7 @@ import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.common.lib.types.ResourceAssociationAction;
 import org.apache.syncope.common.lib.types.ResourceDeassociationAction;
+import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.syncope.common.lib.types.StatusRType;
 import org.apache.syncope.common.lib.types.TaskType;
@@ -100,6 +105,7 @@ import org.apache.syncope.common.lib.types.UnmatchingRule;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.common.rest.api.beans.RealmQuery;
 import org.apache.syncope.common.rest.api.beans.ReconQuery;
+import org.apache.syncope.common.rest.api.beans.TaskQuery;
 import org.apache.syncope.common.rest.api.service.UserService;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import 
org.apache.syncope.core.provisioning.java.propagation.DBPasswordPropagationActions;
@@ -1839,4 +1845,56 @@ public class UserIssuesITCase extends AbstractITCase {
             jdbcTemplate.update("DELETE FROM TESTPULL WHERE USERNAME = 
'rossini'");
         }
     }
+
+    @Test
+    void issueSYNCOPE1853() {
+        GroupTO cGroupForPropagation = createGroup(
+                new GroupCR.Builder(SyncopeConstants.ROOT_REALM, 
"cGroupForPropagation")
+                        .resource(RESOURCE_NAME_LDAP)
+                        .build()).getEntity();
+        GroupTO dGroupForPropagation = createGroup(
+                new GroupCR.Builder(SyncopeConstants.ROOT_REALM, 
"dGroupForPropagation")
+                        .resource(RESOURCE_NAME_LDAP)
+                        .build()).getEntity();
+        // 1. assign both groups cGroupForPropagation and dGroupForPropagation 
with resource-csv to bellini
+        updateUser(new 
UserUR.Builder("c9b2dec2-00a7-4855-97c0-d854842b4b24").memberships(
+                new 
MembershipUR.Builder(cGroupForPropagation.getKey()).build(),
+                new 
MembershipUR.Builder(dGroupForPropagation.getKey()).build()).build());
+        // 2. assign cGroupForPropagation also to vivaldi
+        updateUser(new 
UserUR.Builder("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee").membership(
+                new 
MembershipUR.Builder(dGroupForPropagation.getKey()).build()).build());
+        // 3. propagation tasks cleanup
+        TASK_SERVICE.search(
+                        new TaskQuery.Builder(TaskType.PROPAGATION)
+                                .anyTypeKind(AnyTypeKind.USER)
+                                .resource(RESOURCE_NAME_LDAP)
+                                
.entityKey("c9b2dec2-00a7-4855-97c0-d854842b4b24")
+                                .build()).getResult()
+                .forEach(pt -> TASK_SERVICE.delete(TaskType.PROPAGATION, 
pt.getKey()));
+        TASK_SERVICE.search(
+                        new TaskQuery.Builder(TaskType.PROPAGATION)
+                                .anyTypeKind(AnyTypeKind.USER)
+                                .resource(RESOURCE_NAME_LDAP)
+                                
.entityKey("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee")
+                                .build()).getResult()
+                .forEach(pt -> TASK_SERVICE.delete(TaskType.PROPAGATION, 
pt.getKey()));
+        // 4. delete group cGroupForPropagation: no deprovision should be 
fired on bellini, since there is already
+        // bGroupForPropagation, deprovision instead must be fired for vivaldi
+        GROUP_SERVICE.delete(cGroupForPropagation.getKey());
+        await().during(5, TimeUnit.SECONDS).atMost(10, 
TimeUnit.SECONDS).until(() -> TASK_SERVICE.search(
+                        new TaskQuery.Builder(TaskType.PROPAGATION)
+                                .anyTypeKind(AnyTypeKind.USER)
+                                .resource(RESOURCE_NAME_LDAP)
+                                
.entityKey("c9b2dec2-00a7-4855-97c0-d854842b4b24").build())
+                .getResult().stream().map(PropagationTaskTO.class::cast)
+                .collect(Collectors.toList()).stream().noneMatch(pt -> 
ResourceOperation.DELETE == pt.getOperation()));
+        GROUP_SERVICE.delete(dGroupForPropagation.getKey());
+        await().atMost(10, TimeUnit.SECONDS).until(() -> TASK_SERVICE.search(
+                        new TaskQuery.Builder(TaskType.PROPAGATION)
+                                .anyTypeKind(AnyTypeKind.USER)
+                                .resource(RESOURCE_NAME_LDAP)
+                                
.entityKey("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee").build())
+                .getResult().stream().map(PropagationTaskTO.class::cast)
+                .collect(Collectors.toList()).stream().anyMatch(pt -> 
ResourceOperation.DELETE == pt.getOperation()));
+    }
 }

Reply via email to