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 23f2e24906 [SYNCOPE-1818] fixes wrong status propagation while pulling 
a status update (#703)
23f2e24906 is described below

commit 23f2e24906967955737b2ee0b3c72682d78cff14
Author: Andrea Patricelli <[email protected]>
AuthorDate: Sat May 4 08:38:26 2024 +0200

    [SYNCOPE-1818] fixes wrong status propagation while pulling a status update 
(#703)
    
    ---------
    
    Co-authored-by: Francesco Chicchiriccò <[email protected]>
---
 .../src/test/resources/domains/MasterContent.xml   |   4 +-
 .../src/test/resources/domains/MasterContent.xml   |   4 +-
 .../src/test/resources/domains/MasterContent.xml   |   4 +-
 .../java/DefaultUserProvisioningManager.java       |  31 +-----
 .../pushpull/DefaultUserPullResultHandler.java     |   2 +-
 .../pushpull/DefaultUserPushResultHandler.java     |   2 +-
 .../core/workflow/api/UserWorkflowAdapter.java     |   3 +-
 .../workflow/java/AbstractUserWorkflowAdapter.java |  24 ++++-
 fit/build-tools/src/main/resources/testdb.sql      |   1 +
 .../apache/syncope/fit/core/PullTaskITCase.java    |  12 +--
 .../apache/syncope/fit/core/UserIssuesITCase.java  | 112 +++++++++++++++++++++
 11 files changed, 156 insertions(+), 43 deletions(-)

diff --git 
a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml 
b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
index df1b52f71e..a2702c8923 100644
--- a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
@@ -530,7 +530,7 @@ under the License.
                 
location="connid://${testconnectorserver.key}@localhost:${testconnectorserver.port}"
                 
connectorName="net.tirasa.connid.bundles.db.table.DatabaseTableConnector" 
                 displayName="H2-testpull" version="${connid.db.version}"
-                
jsonConf='[{"schema":{"name":"changeLogColumn","displayName":"Change Log Column 
(Sync)","helpMessage":"=&lt;b&gt;Change Log Column&lt;/b&gt;&lt;br&gt;The 
change log column store the latest change time. Providing this value the Pull 
capabilities are 
activated.","type":"java.lang.String","required":false,"order":21,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"nativeTimestamps","displayName":"Native
 Timestamps ","helpMessage" [...]
+                
jsonConf='[{"schema":{"name":"changeLogColumn","displayName":"Change Log Column 
(Sync)","helpMessage":"=&lt;b&gt;Change Log Column&lt;/b&gt;&lt;br&gt;The 
change log column store the latest change time. Providing this value the Pull 
capabilities are 
activated.","type":"java.lang.String","required":false,"order":21,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"nativeTimestamps","displayName":"Native
 Timestamps ","helpMessage" [...]
                 capabilities='["CREATE","UPDATE","DELETE","SEARCH"]'/>
   
   <ConnInstance id="a6d017fd-a705-4507-bb7c-6ab6a6745997" 
bundleName="net.tirasa.connid.bundles.db" 
@@ -738,7 +738,7 @@ under the License.
             destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" 
performCreate="1" performUpdate="1" performDelete="0" syncStatus="1" 
pullMode="FULL_RECONCILIATION"
             unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" 
jobDelegate_id="PullJobDelegate"/>
   <PullTask remediation="0" id="7c2242f4-14af-4ab5-af31-cdae23783655" 
name="TestDB Pull Task" resource_id="resource-db-pull"
-            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" 
pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" 
performUpdate="1" syncStatus="0"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" 
pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" 
performUpdate="1" syncStatus="1"
             unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" 
jobDelegate_id="PullJobDelegate"/>
   <PullTask remediation="0" id="1e419ca4-ea81-4493-a14f-28b90113686d" 
name="LDAP Pull Task" resource_id="resource-ldap"
             destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" 
pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" 
performUpdate="1" syncStatus="0"
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml 
b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index e2d418881f..f84831c39e 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -616,7 +616,7 @@ under the License.
                 
location="connid://${testconnectorserver.key}@localhost:${testconnectorserver.port}"
                 
connectorName="net.tirasa.connid.bundles.db.table.DatabaseTableConnector" 
                 displayName="H2-testpull" version="${connid.db.version}"
-                
jsonConf='[{"schema":{"name":"changeLogColumn","displayName":"Change Log Column 
(Sync)","helpMessage":"=&lt;b&gt;Change Log Column&lt;/b&gt;&lt;br&gt;The 
change log column store the latest change time. Providing this value the Pull 
capabilities are 
activated.","type":"java.lang.String","required":false,"order":21,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"nativeTimestamps","displayName":"Native
 Timestamps ","helpMessage" [...]
+                
jsonConf='[{"schema":{"name":"changeLogColumn","displayName":"Change Log Column 
(Sync)","helpMessage":"=&lt;b&gt;Change Log Column&lt;/b&gt;&lt;br&gt;The 
change log column store the latest change time. Providing this value the Pull 
capabilities are 
activated.","type":"java.lang.String","required":false,"order":21,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"nativeTimestamps","displayName":"Native
 Timestamps ","helpMessage" [...]
                 capabilities='["CREATE","UPDATE","DELETE","SEARCH"]'/>
   
   <ConnInstance id="a6d017fd-a705-4507-bb7c-6ab6a6745997" 
bundleName="net.tirasa.connid.bundles.db" 
@@ -824,7 +824,7 @@ under the License.
             destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" 
performCreate="1" performUpdate="1" performDelete="0" syncStatus="1" 
pullMode="FULL_RECONCILIATION"
             unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" 
jobDelegate_id="PullJobDelegate"/>
   <PullTask remediation="0" id="7c2242f4-14af-4ab5-af31-cdae23783655" 
name="TestDB Pull Task" resource_id="resource-db-pull"
-            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" 
pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" 
performUpdate="1" syncStatus="0"
+            destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" 
pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" 
performUpdate="1" syncStatus="1"
             unmatchingRule="PROVISION" matchingRule="UPDATE" active="1" 
jobDelegate_id="PullJobDelegate"/>
   <PullTask remediation="0" id="1e419ca4-ea81-4493-a14f-28b90113686d" 
name="LDAP Pull Task" resource_id="resource-ldap"
             destinationRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28" 
pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" 
performUpdate="1" syncStatus="0"
diff --git 
a/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml 
b/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml
index d24afb9f01..42680a05fc 100644
--- a/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml
@@ -544,7 +544,7 @@ under the License.
                 
location="connid://${testconnectorserver.key}@localhost:${testconnectorserver.port}"
                 
connectorName="net.tirasa.connid.bundles.db.table.DatabaseTableConnector" 
                 displayName="H2-testpull" version="${connid.db.version}"
-                
jsonConf='[{"schema":{"name":"changeLogColumn","displayName":"Change Log Column 
(Sync)","helpMessage":"=&lt;b&gt;Change Log Column&lt;/b&gt;&lt;br&gt;The 
change log column store the latest change time. Providing this value the Pull 
capabilities are 
activated.","type":"java.lang.String","required":false,"order":21,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"nativeTimestamps","displayName":"Native
 Timestamps ","helpMessage" [...]
+                
jsonConf='[{"schema":{"name":"changeLogColumn","displayName":"Change Log Column 
(Sync)","helpMessage":"=&lt;b&gt;Change Log Column&lt;/b&gt;&lt;br&gt;The 
change log column store the latest change time. Providing this value the Pull 
capabilities are 
activated.","type":"java.lang.String","required":false,"order":21,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"nativeTimestamps","displayName":"Native
 Timestamps ","helpMessage" [...]
                 capabilities='["CREATE","UPDATE","DELETE","SEARCH"]'/>
   <ConnInstance_Realm left="be24b061-019d-4e3e-baf0-0a6d0a45cb9c" 
right="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"/>
   
@@ -772,7 +772,7 @@ under the License.
   <PullTask_Realm left="81d88f73-d474-4450-9031-605daa4e313f" 
right="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"/>
   <PullTask_Implementation type="SCHED_TASK_JOB_DELEGATE" 
left="81d88f73-d474-4450-9031-605daa4e313f" right="PullJobDelegate"/>
   <PullTask remediation="0" id="7c2242f4-14af-4ab5-af31-cdae23783655" 
name="TestDB Pull Task"
-            pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" 
performUpdate="1" syncStatus="0"
+            pullMode="FULL_RECONCILIATION" performCreate="1" performDelete="1" 
performUpdate="1" syncStatus="1"
             unmatchingRule="PROVISION" matchingRule="UPDATE" active="1"/>
   <PullTask_ExternalResource left="7c2242f4-14af-4ab5-af31-cdae23783655" 
right="resource-db-pull"/>
   <PullTask_Realm left="7c2242f4-14af-4ab5-af31-cdae23783655" 
right="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"/>
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
index 63653b2424..e3c17c14f5 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
@@ -38,7 +38,6 @@ import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.StatusRType;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
 import org.apache.syncope.core.provisioning.api.UserWorkflowResult;
@@ -128,7 +127,7 @@ public class DefaultUserProvisioningManager implements 
UserProvisioningManager {
                 null,
                 Set.of());
 
-        UserWorkflowResult<Pair<UserUR, Boolean>> updated = 
uwfAdapter.update(userUR, updater, context);
+        UserWorkflowResult<Pair<UserUR, Boolean>> updated = 
uwfAdapter.update(userUR, null, updater, context);
 
         List<PropagationTaskInfo> taskInfos = 
propagationManager.setAttributeDeltas(
                 propagationManager.getUserUpdateTasks(updated),
@@ -171,7 +170,7 @@ public class DefaultUserProvisioningManager implements 
UserProvisioningManager {
 
         UserWorkflowResult<Pair<UserUR, Boolean>> updated;
         try {
-            updated = uwfAdapter.update(userUR, updater, context);
+            updated = uwfAdapter.update(userUR, enabled, updater, context);
         } catch (Exception e) {
             LOG.error("Update of user {} failed, trying to pull its status 
anyway (if configured)",
                     userUR.getKey(), e);
@@ -186,28 +185,6 @@ public class DefaultUserProvisioningManager implements 
UserProvisioningManager {
                     new HashSet<>());
         }
 
-        if (enabled != null) {
-            User user = userDAO.findById(userUR.getKey()).
-                    orElseThrow(() -> new IllegalStateException("Could not 
find the AnyObject just updated"));
-
-            UserWorkflowResult<String> enableUpdate = null;
-            if (user.isSuspended() == null) {
-                enableUpdate = uwfAdapter.activate(userUR.getKey(), null, 
updater, context);
-            } else if (enabled && user.isSuspended()) {
-                enableUpdate = uwfAdapter.reactivate(userUR.getKey(), updater, 
context);
-            } else if (!enabled && !user.isSuspended()) {
-                enableUpdate = uwfAdapter.suspend(userUR.getKey(), updater, 
context);
-            }
-
-            if (enableUpdate != null) {
-                if (enableUpdate.getPropByRes() != null) {
-                    updated.getPropByRes().merge(enableUpdate.getPropByRes());
-                    updated.getPropByRes().purge();
-                }
-                
updated.getPerformedTasks().addAll(enableUpdate.getPerformedTasks());
-            }
-        }
-
         List<PropagationTaskInfo> taskInfos = 
propagationManager.setAttributeDeltas(
                 propagationManager.getUserUpdateTasks(
                         updated,
@@ -264,13 +241,13 @@ public class DefaultUserProvisioningManager implements 
UserProvisioningManager {
 
     @Override
     public String unlink(final UserUR userUR, final String updater, final 
String context) {
-        UserWorkflowResult<Pair<UserUR, Boolean>> updated = 
uwfAdapter.update(userUR, updater, context);
+        UserWorkflowResult<Pair<UserUR, Boolean>> updated = 
uwfAdapter.update(userUR, null, updater, context);
         return updated.getResult().getLeft().getKey();
     }
 
     @Override
     public String link(final UserUR userUR, final String updater, final String 
context) {
-        return uwfAdapter.update(userUR, updater, 
context).getResult().getLeft().getKey();
+        return uwfAdapter.update(userUR, null, updater, 
context).getResult().getLeft().getKey();
     }
 
     @Override
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
index f1aaeb70d8..48995d3c97 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
@@ -95,7 +95,7 @@ public class DefaultUserPullResultHandler extends 
AbstractPullResultHandler impl
     @Override
     protected WorkflowResult<? extends AnyUR> update(final AnyUR req) {
         WorkflowResult<Pair<UserUR, Boolean>> update =
-                uwfAdapter.update((UserUR) req, profile.getExecutor(), 
getContext());
+                uwfAdapter.update((UserUR) req, null, profile.getExecutor(), 
getContext());
         return new WorkflowResult<>(update.getResult().getLeft(), 
update.getPropByRes(), update.getPerformedTasks());
     }
 
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
index 7c4e09c8ea..c676153fed 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
@@ -174,7 +174,7 @@ public class DefaultUserPushResultHandler extends 
AbstractPushResultHandler impl
     @Override
     protected WorkflowResult<? extends AnyUR> update(final AnyUR req) {
         WorkflowResult<Pair<UserUR, Boolean>> update =
-                uwfAdapter.update((UserUR) req, profile.getExecutor(), 
getContext());
+                uwfAdapter.update((UserUR) req, null, profile.getExecutor(), 
getContext());
         return new WorkflowResult<>(update.getResult().getLeft(), 
update.getPropByRes(), update.getPerformedTasks());
     }
 
diff --git 
a/core/workflow-api/src/main/java/org/apache/syncope/core/workflow/api/UserWorkflowAdapter.java
 
b/core/workflow-api/src/main/java/org/apache/syncope/core/workflow/api/UserWorkflowAdapter.java
index 6dfcbeb938..13a9d3ebf7 100644
--- 
a/core/workflow-api/src/main/java/org/apache/syncope/core/workflow/api/UserWorkflowAdapter.java
+++ 
b/core/workflow-api/src/main/java/org/apache/syncope/core/workflow/api/UserWorkflowAdapter.java
@@ -66,11 +66,12 @@ public interface UserWorkflowAdapter extends 
WorkflowAdapter {
      * Update an user.
      *
      * @param userUR modification set to be performed
+     * @param enabled whether status shall be changed or not
      * @param updater username that requested this operation
      * @param context context information
      * @return user just updated and propagations to be performed
      */
-    UserWorkflowResult<Pair<UserUR, Boolean>> update(UserUR userUR, String 
updater, String context);
+    UserWorkflowResult<Pair<UserUR, Boolean>> update(UserUR userUR, Boolean 
enabled, String updater, String context);
 
     /**
      * Suspend an user.
diff --git 
a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
 
b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
index cbb5c05b34..077056d85c 100644
--- 
a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
+++ 
b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
@@ -269,7 +269,7 @@ public abstract class AbstractUserWorkflowAdapter extends 
AbstractWorkflowAdapte
 
     @Override
     public UserWorkflowResult<Pair<UserUR, Boolean>> update(
-            final UserUR userUR, final String updater, final String context) {
+            final UserUR userUR, final Boolean enabled, final String updater, 
final String context) {
 
         User user = Optional.ofNullable(userDAO.authFind(userUR.getKey())).
                 orElseThrow(() -> new IllegalStateException("Could not find 
the User to update"));
@@ -306,6 +306,28 @@ public abstract class AbstractUserWorkflowAdapter extends 
AbstractWorkflowAdapte
                 
Optional.ofNullable(userUR.getPassword()).map(PasswordPatch::getValue).orElse(null));
         user = userDAO.save(user);
 
+        if (enabled != null) {
+            UserWorkflowResult<String> enableUpdate = null;
+            if (user.isSuspended() == null) {
+                enableUpdate = activate(userUR.getKey(), null, updater, 
context);
+                result.setResult(Pair.of(result.getResult().getLeft(), true));
+            } else if (enabled && user.isSuspended()) {
+                enableUpdate = reactivate(userUR.getKey(), updater, context);
+                result.setResult(Pair.of(result.getResult().getLeft(), true));
+            } else if (!enabled && !user.isSuspended()) {
+                enableUpdate = suspend(userUR.getKey(), updater, context);
+                result.setResult(Pair.of(result.getResult().getLeft(), false));
+            }
+
+            Optional.ofNullable(enableUpdate).ifPresent(eu -> {
+                Optional.ofNullable(eu.getPropByRes()).ifPresent(eupbr -> {
+                    result.getPropByRes().merge(eupbr);
+                    result.getPropByRes().purge();
+                });
+                result.getPerformedTasks().addAll(eu.getPerformedTasks());
+            });
+        }
+
         if (!AuthContextUtils.getUsername().equals(user.getUsername())) {
             // ensure that requester's administration rights are still valid
             Set<String> authRealms = new HashSet<>();
diff --git a/fit/build-tools/src/main/resources/testdb.sql 
b/fit/build-tools/src/main/resources/testdb.sql
index 5c772d06bc..9765192356 100644
--- a/fit/build-tools/src/main/resources/testdb.sql
+++ b/fit/build-tools/src/main/resources/testdb.sql
@@ -41,6 +41,7 @@ username VARCHAR(80),
 surname VARCHAR(80),
 mustChangePassword BOOLEAN,
 email VARCHAR(80),
+status VARCHAR(5) DEFAULT 'true',
 lastModification TIMESTAMP);
 
 DROP TABLE testPRINTER IF EXISTS;
diff --git 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
index 96f564cc46..4669ef0de9 100644
--- 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
+++ 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
@@ -667,9 +667,9 @@ public class PullTaskITCase extends AbstractTaskITCase {
         try {
             // 1. create 2 users on testpull
             jdbcTemplate.execute("INSERT INTO testpull VALUES ("
-                    + '\'' + user1OnTestPull + "', 'user1', 'Doe', false, 
'[email protected]', NULL)");
+                    + '\'' + user1OnTestPull + "', 'user1', 'Doe', false, 
'[email protected]', 'true', NULL)");
             jdbcTemplate.execute("INSERT INTO testpull VALUES ("
-                    + '\'' + user2OnTestPull + "', 'user2', 'Rossi', false, 
'[email protected]', NULL)");
+                    + '\'' + user2OnTestPull + "', 'user2', 'Rossi', false, 
'[email protected]', 'true', NULL)");
 
             // 2. create new pull task for test-db, with reconciliation filter 
(surname 'Rossi') 
             ImplementationTO reconFilterBuilder = new ImplementationTO();
@@ -771,10 +771,10 @@ public class PullTaskITCase extends AbstractTaskITCase {
             jdbcTemplate.execute("DELETE FROM testpull");
             jdbcTemplate.execute("INSERT INTO testpull VALUES "
                     + "(1040, 'syncTokenWithErrors1', 'Surname1', "
-                    + "false, '[email protected]', 
'2014-05-23 13:53:24.293')");
+                    + "false, '[email protected]', 
'true', '2014-05-23 13:53:24.293')");
             jdbcTemplate.execute("INSERT INTO testpull VALUES "
                     + "(1041, 'syncTokenWithErrors2', 'Surname2', "
-                    + "false, '[email protected]', 
'2015-05-23 13:53:24.293')");
+                    + "false, '[email protected]', 
'true', '2015-05-23 13:53:24.293')");
 
             ExecTO exec = execSchedTask(TASK_SERVICE, TaskType.PULL, 
pullTask.getKey(), MAX_WAIT_SECONDS, false);
             assertEquals(ExecStatus.SUCCESS, 
ExecStatus.valueOf(exec.getStatus()));
@@ -1096,8 +1096,8 @@ public class PullTaskITCase extends AbstractTaskITCase {
         String id = "a54b3794-b231-47be-b24a-11e1a42949f6";
 
         // 1. populate the external table
-        jdbcTemplate.execute("INSERT INTO testpull VALUES"
-                + "('" + id + "', 'issuesyncope230', 'Surname230', false, 
'[email protected]', NULL)");
+        jdbcTemplate.execute("INSERT INTO testpull VALUES" + "('" + id
+                + "', 'issuesyncope230', 'Surname230', false, 
'[email protected]', 'true', NULL)");
 
         // 2. execute PullTask for resource-db-pull (table TESTPULL on 
external H2)
         execSchedTask(
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 bd0a0544d1..01fc04db21 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
@@ -56,12 +56,14 @@ import org.apache.syncope.common.lib.request.MembershipUR;
 import org.apache.syncope.common.lib.request.PasswordPatch;
 import org.apache.syncope.common.lib.request.ResourceAR;
 import org.apache.syncope.common.lib.request.ResourceDR;
+import org.apache.syncope.common.lib.request.StatusR;
 import org.apache.syncope.common.lib.request.StringPatchItem;
 import org.apache.syncope.common.lib.request.StringReplacePatchItem;
 import org.apache.syncope.common.lib.request.UserCR;
 import org.apache.syncope.common.lib.request.UserUR;
 import org.apache.syncope.common.lib.to.AnyTypeClassTO;
 import org.apache.syncope.common.lib.to.ConnObject;
+import org.apache.syncope.common.lib.to.ExecTO;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.ImplementationTO;
 import org.apache.syncope.common.lib.to.Item;
@@ -70,7 +72,9 @@ 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.ProvisioningResult;
+import org.apache.syncope.common.lib.to.PushTaskTO;
 import org.apache.syncope.common.lib.to.RealmTO;
+import org.apache.syncope.common.lib.to.ReconStatus;
 import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.to.RoleTO;
 import org.apache.syncope.common.lib.to.UserTO;
@@ -84,13 +88,18 @@ import 
org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.syncope.common.lib.types.IdRepoImplementationType;
 import org.apache.syncope.common.lib.types.ImplementationEngine;
 import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.MatchingRule;
 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.SchemaType;
+import org.apache.syncope.common.lib.types.StatusRType;
+import org.apache.syncope.common.lib.types.TaskType;
+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.service.UserService;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import 
org.apache.syncope.core.provisioning.java.propagation.DBPasswordPropagationActions;
@@ -1708,4 +1717,107 @@ public class UserIssuesITCase extends AbstractITCase {
             SCHEMA_SERVICE.delete(SchemaType.PLAIN, 
externalKeySchemaTO.getKey());
         }
     }
+
+    @Test
+    void issueSYNCOPE1818() {
+        UserTO rossini = USER_SERVICE.read("rossini");
+        try {
+            // 1. provision rossini on resource-db-pull
+            updateUser(new UserUR.Builder(rossini.getKey()).
+                    plainAttr(attrAddReplacePatch("email", 
"[email protected]")).
+                    resource(new 
StringPatchItem.Builder().value(RESOURCE_NAME_DBPULL).build()).
+                    build());
+
+            // 2. pull users from resource-db-pull
+            ExecTO execution = AbstractTaskITCase.execSchedTask(TASK_SERVICE,
+                    TaskType.PULL,
+                    "7c2242f4-14af-4ab5-af31-cdae23783655",
+                    MAX_WAIT_SECONDS,
+                    false);
+            assertEquals("SUCCESS", execution.getStatus());
+            assertFalse(rossini.isSuspended());
+            assertEquals("active", rossini.getStatus());
+
+            // 3. push rossini on LDAP
+            PushTaskTO pushTaskTO = new PushTaskTO();
+            pushTaskTO.setSourceRealm(SyncopeConstants.ROOT_REALM);
+            pushTaskTO.setMatchingRule(MatchingRule.UPDATE);
+            pushTaskTO.setUnmatchingRule(UnmatchingRule.ASSIGN);
+            pushTaskTO.setPerformCreate(true);
+            pushTaskTO.setPerformUpdate(true);
+            pushTaskTO.setSyncStatus(true);
+            RECONCILIATION_SERVICE.push(new 
ReconQuery.Builder(AnyTypeKind.USER.name(), RESOURCE_NAME_LDAP).anyKey(
+                    rossini.getKey()).build(), pushTaskTO);
+
+            // 4. disable rossini on resource-db-pull to fire a propagation 
towards resource-ldap
+            JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+            jdbcTemplate.update("UPDATE TESTPULL SET EMAIL 
='[email protected]', STATUS = "
+                    + "'false' WHERE USERNAME = 'rossini'");
+
+            // 5. pull again rossini from resource-db-pull
+            execution = AbstractTaskITCase.execSchedTask(
+                    TASK_SERVICE,
+                    TaskType.PULL,
+                    "7c2242f4-14af-4ab5-af31-cdae23783655",
+                    MAX_WAIT_SECONDS,
+                    false);
+            assertEquals("SUCCESS", execution.getStatus());
+
+            rossini = USER_SERVICE.read("rossini");
+            assertTrue(rossini.isSuspended());
+            assertEquals("suspended", rossini.getStatus());
+            
assertTrue(rossini.getPlainAttr("email").get().getValues().contains("[email protected]"));
+
+            ReconStatus onLDAP = RECONCILIATION_SERVICE.status(new 
ReconQuery.Builder(AnyTypeKind.USER.name(),
+                    RESOURCE_NAME_LDAP).anyKey(rossini.getKey()).build());
+            Attr enableAttr = 
onLDAP.getOnResource().getAttr(OperationalAttributes.ENABLE_NAME).orElseThrow();
+            assertFalse(Boolean.parseBoolean(enableAttr.getValues().get(0)));
+
+            // 6. re-enable on resource-db-pull and restore old values to fire 
a propagation towards resource-ldap
+            jdbcTemplate.update("UPDATE TESTPULL SET EMAIL = 
'[email protected]', STATUS = "
+                    + "'true' WHERE USERNAME = 'rossini'");
+
+            // 7. pull again rossini from resource-db-pull
+            execution = AbstractTaskITCase.execSchedTask(
+                    TASK_SERVICE,
+                    TaskType.PULL,
+                    "7c2242f4-14af-4ab5-af31-cdae23783655",
+                    MAX_WAIT_SECONDS,
+                    false);
+            assertEquals("SUCCESS", execution.getStatus());
+
+            rossini = USER_SERVICE.read("rossini");
+            assertFalse(rossini.isSuspended());
+            assertEquals("active", rossini.getStatus());
+
+            if (!IS_FLOWABLE_ENABLED) {
+                // we can check update only if on default workflow since 
flowable test workflow definition does not 
+                // support update of suspended users
+                
assertTrue(rossini.getPlainAttr("email").get().getValues().contains("[email protected]"));
+
+                onLDAP = RECONCILIATION_SERVICE.status(new ReconQuery.Builder(
+                        AnyTypeKind.USER.name(), 
RESOURCE_NAME_LDAP).anyKey(rossini.getKey()).build());
+                enableAttr = 
onLDAP.getOnResource().getAttr(OperationalAttributes.ENABLE_NAME).orElseThrow();
+                
assertTrue(Boolean.parseBoolean(enableAttr.getValues().get(0)));
+            }
+        } finally {
+            // restore attributes and (if needed) status
+            updateUser(new UserUR.Builder(rossini.getKey()).
+                    plainAttrs(
+                            attrAddReplacePatch("surname", "Rossini"),
+                            new AttrPatch.Builder(
+                                    new 
Attr.Builder("email").build()).operation(PatchOperation.DELETE).build()).
+                    resource(new StringPatchItem.Builder().
+                            
value(RESOURCE_NAME_DBPULL).operation(PatchOperation.DELETE).build()).
+                    build());
+
+            if (USER_SERVICE.read("rossini").isSuspended()) {
+                USER_SERVICE.status(new StatusR.Builder(rossini.getKey(), 
StatusRType.REACTIVATE).onSyncope(true)
+                        .resources(rossini.getResources()).build());
+            }
+
+            JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
+            jdbcTemplate.update("DELETE FROM TESTPULL WHERE USERNAME = 
'rossini'");
+        }
+    }
 }

Reply via email to