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

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

commit aff77803fd44dad101318674cc5fc213c35e7d86
Author: Andrea Patricelli <[email protected]>
AuthorDate: Mon Apr 27 09:00:43 2026 +0200

    [SYNCOPE-1965] managed incremental setting of the user/group manager to 
avoid unwanted removal (#1361)
---
 .../apache/syncope/common/lib/AnyOperations.java   | 29 ++++++++++--
 .../apache/syncope/fit/core/UserIssuesITCase.java  | 52 ++++++++++++++++++++++
 2 files changed, 77 insertions(+), 4 deletions(-)

diff --git 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/AnyOperations.java
 
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/AnyOperations.java
index 5fb557e829..19362b4ce6 100644
--- 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/AnyOperations.java
+++ 
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/AnyOperations.java
@@ -128,10 +128,31 @@ public final class AnyOperations {
         result.setRealm(replacePatchItem(updated.getRealm(), 
original.getRealm(), new StringReplacePatchItem()));
 
         // 2. manager
-        result.setUManager(
-                replacePatchItem(updated.getUManager(), 
original.getUManager(), new StringReplacePatchItem()));
-        result.setGManager(
-                replacePatchItem(updated.getGManager(), 
original.getGManager(), new StringReplacePatchItem()));
+        if (updated.getUManager() != null || original.getUManager() != null) {
+            StringReplacePatchItem uManager = new StringReplacePatchItem();
+            if (updated.getUManager() == null) {
+                if (!incremental) {
+                    uManager.setOperation(PatchOperation.DELETE);
+                    result.setUManager(uManager);
+                }
+            } else if (!updated.getUManager().equals(original.getUManager())) {
+                uManager.setValue(updated.getUManager());
+                result.setUManager(uManager);
+            }
+        }
+
+        if (updated.getGManager() != null || original.getGManager() != null) {
+            StringReplacePatchItem gManager = new StringReplacePatchItem();
+            if (updated.getGManager() == null) {
+                if (!incremental) {
+                    gManager.setOperation(PatchOperation.DELETE);
+                    result.setGManager(gManager);
+                }
+            } else if (!updated.getGManager().equals(original.getGManager())) {
+                gManager.setValue(updated.getGManager());
+                result.setGManager(gManager);
+            }
+        }
 
         // 3. auxiliary classes
         result.getAuxClasses().clear();
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 e0321ed187..e74f20bc5d 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
@@ -72,6 +72,7 @@ import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.Provision;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.PullTaskTO;
 import org.apache.syncope.common.lib.to.PushTaskTO;
 import org.apache.syncope.common.lib.to.RealmTO;
 import org.apache.syncope.common.lib.to.ReconStatus;
@@ -1896,4 +1897,55 @@ public class UserIssuesITCase extends AbstractITCase {
                     .build()).getResult().forEach(u -> deleteUser(u.getKey()));
         }
     }
+
+    @Test
+    public void issueSYNCOPE1965() {
+        // 1. create a sample user with a manager and propagate on LDAP
+        UserCR userCR = 
UserITCase.getUniqueSample("[email protected]");
+        userCR.getResources().add(RESOURCE_NAME_TESTDB);
+        userCR.setPassword("Password123!");
+        userCR.setUManager(USER_SERVICE.read("puccini").getKey());
+        ProvisioningResult<UserTO> pr = createUser(userCR);
+        assertEquals(ExecStatus.SUCCESS, 
pr.getPropagationStatuses().getFirst().getStatus());
+        assertNotNull(pr.getEntity().getUManager());
+        // 2. pull from resource-testdb
+        PullTaskTO pullTaskTO = new PullTaskTO();
+        pullTaskTO.setPerformCreate(true);
+        pullTaskTO.setPerformUpdate(true);
+        pullTaskTO.setDestinationRealm(SyncopeConstants.ROOT_REALM);
+        pullTaskTO.setMatchingRule(MatchingRule.UPDATE);
+        pullTaskTO.setUnmatchingRule(UnmatchingRule.ASSIGN);
+        RECONCILIATION_SERVICE.pull(
+                new ReconQuery.Builder(AnyTypeKind.USER.name(), 
RESOURCE_NAME_TESTDB).anyKey(pr.getEntity().getKey())
+                        .build(), pullTaskTO);
+        // user manager should be kept
+        UserTO updatedUser = USER_SERVICE.read(pr.getEntity().getKey());
+        assertNotNull(updatedUser.getUManager());
+        assertEquals(USER_SERVICE.read("puccini").getKey(), 
updatedUser.getUManager());
+
+        // perform the same check on a group
+        GroupCR groupCR = GroupITCase.getSample("issue1965grp");
+        groupCR.getResources().add(RESOURCE_NAME_LDAP);
+        groupCR.setGManager(GROUP_SERVICE.read("managingDirector").getKey());
+        ProvisioningResult<GroupTO> prGrp = createGroup(groupCR);
+        assertEquals(ExecStatus.SUCCESS, 
prGrp.getPropagationStatuses().getFirst().getStatus());
+        assertNotNull(prGrp.getEntity().getGManager());
+        assertEquals(GROUP_SERVICE.read("managingDirector").getKey(), 
prGrp.getEntity().getGManager());
+
+        // 2. pull from resource-ldap
+        pullTaskTO = new PullTaskTO();
+        pullTaskTO.setPerformCreate(true);
+        pullTaskTO.setPerformUpdate(true);
+        pullTaskTO.getActions().add("LDAPMembershipPullActions");
+        pullTaskTO.setDestinationRealm(SyncopeConstants.ROOT_REALM);
+        pullTaskTO.setMatchingRule(MatchingRule.UPDATE);
+        pullTaskTO.setUnmatchingRule(UnmatchingRule.ASSIGN);
+        RECONCILIATION_SERVICE.pull(
+                new ReconQuery.Builder(AnyTypeKind.USER.name(), 
RESOURCE_NAME_LDAP).anyKey(pr.getEntity().getKey())
+                        .build(), pullTaskTO);
+        // group manager should be kept
+        GroupTO updatedGrp = GROUP_SERVICE.read(prGrp.getEntity().getKey());
+        assertNotNull(updatedGrp.getGManager());
+        assertEquals(GROUP_SERVICE.read("managingDirector").getKey(), 
updatedGrp.getGManager());
+    }
 }

Reply via email to