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":"=<b>Change Log Column</b><br>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":"=<b>Change Log Column</b><br>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":"=<b>Change Log Column</b><br>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":"=<b>Change Log Column</b><br>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":"=<b>Change Log Column</b><br>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":"=<b>Change Log Column</b><br>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'");
+ }
+ }
}