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 c0cfc09be0 [SYNCOPE-1864] Unwanted password propagation after update
on pull
c0cfc09be0 is described below
commit c0cfc09be01f96551b60fa64149f8ab01fdb3eb7
Author: Marco Di Sabatino Di Diodoro <[email protected]>
AuthorDate: Thu Feb 6 12:11:51 2025 +0100
[SYNCOPE-1864] Unwanted password propagation after update on pull
---
.../api/propagation/PropagationManager.java | 13 ++--
.../java/DefaultAnyObjectProvisioningManager.java | 6 +-
.../java/DefaultGroupProvisioningManager.java | 6 +-
.../java/DefaultUserProvisioningManager.java | 13 ++--
.../java/data/AbstractAnyDataBinder.java | 4 +-
.../java/data/AnyObjectDataBinderImpl.java | 4 +-
.../java/data/GroupDataBinderImpl.java | 5 +-
.../provisioning/java/data/UserDataBinderImpl.java | 12 +--
.../propagation/DefaultPropagationManager.java | 50 +++++++-----
.../java/pushpull/AbstractPushResultHandler.java | 5 +-
.../pushpull/DefaultUserPushResultHandler.java | 2 +-
.../apache/syncope/fit/core/PullTaskITCase.java | 89 ++++++++++++++++++++++
.../test/resources/AddResourcePullActions.groovy | 69 +++++++++++++++++
13 files changed, 226 insertions(+), 52 deletions(-)
diff --git
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
index c0e2b99860..f9ac05cca2 100644
---
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
+++
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
@@ -100,7 +100,7 @@ public interface PropagationManager {
* @param anyUR update request
* @param kind any type kind
* @param key any key
- * @param changePwd whether password should be included for propagation
attributes or not
+ * @param changePwdRes the resources in which the password must be
included in the propagation attributes
* @param enable whether any should be enabled or not, may be null to
leave unchanged
* @param propByRes operation to be performed per resource
* @param propByLinkedAccount operation to be performed for linked accounts
@@ -112,7 +112,7 @@ public interface PropagationManager {
AnyUR anyUR,
AnyTypeKind kind,
String key,
- boolean changePwd,
+ List<String> changePwdRes,
Boolean enable,
PropagationByResource<String> propByRes,
PropagationByResource<Pair<String, String>> propByLinkedAccount,
@@ -123,13 +123,13 @@ public interface PropagationManager {
* Create the update tasks for the user on each resource associated,
unless in {@code excludedResources}.
*
* @param wfResult user to be propagated (and info associated), as per
result from workflow
- * @param changePwd whether password should be included for propagation
attributes or not
+ * @param changePwdRes the resources in which the password must be
included in the propagation attributes
* @param excludedResources external resources not to be considered for
propagation
* @return list of propagation tasks
*/
List<PropagationTaskInfo> getUserUpdateTasks(
UserWorkflowResult<Pair<UserUR, Boolean>> wfResult,
- boolean changePwd,
+ List<String> changePwdRes,
Collection<String> excludedResources);
/**
@@ -186,7 +186,8 @@ public interface PropagationManager {
* @param kind any type kind
* @param key any key
* @param password to be set (for users)
- * @param changePwd whether password should be included for propagation
attributes or not (for users)
+ * @param changePwdRes the resources in which the password must be
included in the propagation attributes (for
+ * users)
* @param enable whether any should be enabled or not, may be null to
leave unchanged
* @param excludedResources external resource keys not to be considered
for propagation
* @return map with prepared attributes per External Resource
@@ -195,7 +196,7 @@ public interface PropagationManager {
AnyTypeKind kind,
String key,
String password,
- boolean changePwd,
+ List<String> changePwdRes,
Boolean enable,
Collection<String> excludedResources);
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
index 7462b204ea..78528fd098 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
@@ -114,7 +114,7 @@ public class DefaultAnyObjectProvisioningManager implements
AnyObjectProvisionin
AnyTypeKind.ANY_OBJECT,
anyObjectUR.getKey(),
null,
- false,
+ List.of(),
null,
excludedResources);
@@ -125,7 +125,7 @@ public class DefaultAnyObjectProvisioningManager implements
AnyObjectProvisionin
updated.getResult(),
AnyTypeKind.ANY_OBJECT,
updated.getResult().getKey(),
- false,
+ List.of(),
null,
updated.getPropByRes(),
null,
@@ -198,7 +198,7 @@ public class DefaultAnyObjectProvisioningManager implements
AnyObjectProvisionin
null,
AnyTypeKind.ANY_OBJECT,
key,
- false,
+ List.of(),
null,
propByRes,
null,
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
index 6f6a85cb7d..ebdfc79a0d 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultGroupProvisioningManager.java
@@ -134,7 +134,7 @@ public class DefaultGroupProvisioningManager implements
GroupProvisioningManager
AnyTypeKind.GROUP,
groupUR.getKey(),
null,
- false,
+ List.of(),
null,
excludedResources);
@@ -145,7 +145,7 @@ public class DefaultGroupProvisioningManager implements
GroupProvisioningManager
updated.getResult(),
AnyTypeKind.GROUP,
updated.getResult().getKey(),
- false,
+ List.of(),
null,
updated.getPropByRes(),
null,
@@ -233,7 +233,7 @@ public class DefaultGroupProvisioningManager implements
GroupProvisioningManager
null,
AnyTypeKind.GROUP,
key,
- false,
+ List.of(),
null,
propByRes,
null,
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 837187360d..b20ce8178c 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
@@ -123,7 +123,7 @@ public class DefaultUserProvisioningManager implements
UserProvisioningManager {
AnyTypeKind.USER,
userUR.getKey(),
Optional.ofNullable(userUR.getPassword()).map(PasswordPatch::getValue).orElse(null),
- userUR.getPassword() != null,
+ userUR.getPassword() != null ?
userUR.getPassword().getResources() : List.of(),
null,
Set.of());
@@ -163,7 +163,7 @@ public class DefaultUserProvisioningManager implements
UserProvisioningManager {
AnyTypeKind.USER,
userUR.getKey(),
Optional.ofNullable(userUR.getPassword()).map(PasswordPatch::getValue).orElse(null),
- userUR.getPassword() != null,
+ userUR.getPassword() != null ?
userUR.getPassword().getResources() : List.of(),
enabled,
excludedResources);
@@ -187,7 +187,9 @@ public class DefaultUserProvisioningManager implements
UserProvisioningManager {
List<PropagationTaskInfo> taskInfos =
propagationManager.setAttributeDeltas(
propagationManager.getUserUpdateTasks(
updated,
- updated.getResult().getLeft().getPassword() != null,
+ updated.getResult().getLeft().getPassword() != null
+ ?
updated.getResult().getLeft().getPassword().getResources()
+ : List.of(),
excludedResources),
beforeAttrs);
PropagationReporter propagationReporter =
taskExecutor.execute(taskInfos, nullPriorityAsync, updater);
@@ -290,7 +292,7 @@ public class DefaultUserProvisioningManager implements
UserProvisioningManager {
null,
AnyTypeKind.USER,
statusR.getKey(),
- false,
+ List.of(),
statusR.getType() != StatusRType.SUSPEND,
propByRes,
null,
@@ -348,7 +350,8 @@ public class DefaultUserProvisioningManager implements
UserProvisioningManager {
UserWorkflowResult<Pair<UserUR, Boolean>> wfResult = new
UserWorkflowResult<>(
Pair.of(userUR, (Boolean) null), propByRes, null, "update");
- List<PropagationTaskInfo> taskInfos =
propagationManager.getUserUpdateTasks(wfResult, changePwd, null);
+ List<PropagationTaskInfo> taskInfos =
propagationManager.getUserUpdateTasks(wfResult,
+ userUR.getPassword().getResources(), null);
PropagationReporter propagationReporter =
taskExecutor.execute(taskInfos, nullPriorityAsync, executor);
return propagationReporter.getStatuses();
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
index 392fed66b4..b845dac959 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
@@ -251,7 +251,7 @@ abstract class AbstractAnyDataBinder {
final Any<?> any,
final Collection<String> resources,
final String password,
- final boolean changePwd) {
+ final Set<String> changePwdRes) {
Map<String, ConnObject> onResources = new HashMap<>();
@@ -260,7 +260,7 @@ abstract class AbstractAnyDataBinder {
ifPresent(provision ->
MappingUtils.getConnObjectKeyItem(provision).ifPresent(keyItem -> {
Pair<String, Set<Attribute>> prepared =
mappingManager.prepareAttrsFromAny(
- any, password, changePwd, true, resource, provision);
+ any, password, changePwdRes.contains(resource.getKey()),
true, resource, provision);
ConnObject connObjectTO;
if (StringUtils.isBlank(prepared.getLeft())) {
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
index a6b719905a..230d40a19f 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
@@ -316,7 +316,7 @@ public class AnyObjectDataBinderImpl extends
AbstractAnyDataBinder implements An
// Save projection on Resources (before update)
Map<String, ConnObject> beforeOnResources =
- onResources(anyObject,
anyObjectDAO.findAllResourceKeys(anyObject.getKey()), null, false);
+ onResources(anyObject,
anyObjectDAO.findAllResourceKeys(anyObject.getKey()), null, Set.of());
SyncopeClientCompositeException scce =
SyncopeClientException.buildComposite();
@@ -486,7 +486,7 @@ public class AnyObjectDataBinderImpl extends
AbstractAnyDataBinder implements An
// Build final information for next stage (propagation)
propByRes.merge(propByRes(
beforeOnResources,
- onResources(saved,
anyObjectDAO.findAllResourceKeys(anyObject.getKey()), null, false)));
+ onResources(saved,
anyObjectDAO.findAllResourceKeys(anyObject.getKey()), null, Set.of())));
return propByRes;
}
}
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 1207d072a3..5a07349136 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
@@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.AnyOperations;
import org.apache.syncope.common.lib.EntityTOUtils;
@@ -235,7 +236,7 @@ public class GroupDataBinderImpl extends
AbstractAnyDataBinder implements GroupD
// Save projection on Resources (before update)
Map<String, ConnObject> beforeOnResources =
- onResources(group,
groupDAO.findAllResourceKeys(group.getKey()), null, false);
+ onResources(group,
groupDAO.findAllResourceKeys(group.getKey()), null, Set.of());
SyncopeClientCompositeException scce =
SyncopeClientException.buildComposite();
@@ -366,7 +367,7 @@ public class GroupDataBinderImpl extends
AbstractAnyDataBinder implements GroupD
// Build final information for next stage (propagation)
PropagationByResource<String> propByRes = propByRes(
- beforeOnResources, onResources(group,
groupDAO.findAllResourceKeys(group.getKey()), null, false));
+ beforeOnResources, onResources(group,
groupDAO.findAllResourceKeys(group.getKey()), null, Set.of()));
propByRes.merge(ownerPropByRes);
return propByRes;
}
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index f42c994f1f..6190b7e1ad 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@ -455,12 +455,12 @@ public class UserDataBinderImpl extends
AbstractAnyDataBinder implements UserDat
// password
String password = null;
- boolean changePwd = false;
+ Set<String> changePwdRes = new HashSet<>();
if (userUR.getPassword() != null) {
if (userUR.getPassword().getOperation() == PatchOperation.DELETE) {
user.setEncodedPassword(null, null);
- changePwd = true;
+ changePwdRes.addAll(userUR.getPassword().getResources());
} else if
(StringUtils.isNotBlank(userUR.getPassword().getValue())) {
if (userUR.getPassword().isOnSyncope()) {
setPassword(user, userUR.getPassword().getValue(), scce);
@@ -468,17 +468,17 @@ public class UserDataBinderImpl extends
AbstractAnyDataBinder implements UserDat
}
password = userUR.getPassword().getValue();
- changePwd = true;
+ changePwdRes.addAll(userUR.getPassword().getResources());
}
- if (changePwd) {
+ if (!changePwdRes.isEmpty()) {
propByRes.addAll(ResourceOperation.UPDATE,
userUR.getPassword().getResources());
}
}
// Save projection on Resources (before update)
Map<String, ConnObject> beforeOnResources =
- onResources(user, userDAO.findAllResourceKeys(user.getKey()),
password, changePwd);
+ onResources(user, userDAO.findAllResourceKeys(user.getKey()),
password, changePwdRes);
// realm
setRealm(user, userUR);
@@ -724,7 +724,7 @@ public class UserDataBinderImpl extends
AbstractAnyDataBinder implements UserDat
// Build final information for next stage (propagation)
Map<String, ConnObject> afterOnResources =
- onResources(user, userDAO.findAllResourceKeys(user.getKey()),
password, changePwd);
+ onResources(user, userDAO.findAllResourceKeys(user.getKey()),
password, changePwdRes);
propByRes.merge(propByRes(beforeOnResources, afterOnResources));
if (userUR.getMustChangePassword() != null) {
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationManager.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationManager.java
index fc3a03145a..6c0b59d05b 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationManager.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationManager.java
@@ -169,6 +169,7 @@ public class DefaultPropagationManager implements
PropagationManager {
return List.of();
}
+ List<String> changePwdRes = new ArrayList<>();
if (excludedResources != null) {
if (propByRes != null) {
propByRes.get(ResourceOperation.CREATE).removeAll(excludedResources);
@@ -180,7 +181,13 @@ public class DefaultPropagationManager implements
PropagationManager {
}
}
- return createTasks(any, password, true, enable, propByRes,
propByLinkedAccount, vAttrs);
+ if (propByRes != null) {
+ propByRes.asMap().forEach((resource, resourceOperation) ->
changePwdRes.add(resource));
+ }
+ if (propByLinkedAccount != null) {
+ propByLinkedAccount.asMap().forEach((resource, resourceOperation)
-> changePwdRes.add(resource.getKey()));
+ }
+ return createTasks(any, password, changePwdRes, enable, propByRes,
propByLinkedAccount, vAttrs);
}
@Override
@@ -188,7 +195,7 @@ public class DefaultPropagationManager implements
PropagationManager {
final AnyUR anyUR,
final AnyTypeKind kind,
final String key,
- final boolean changePwd,
+ final List<String> changePwdRes,
final Boolean enable,
final PropagationByResource<String> propByRes,
final PropagationByResource<Pair<String, String>>
propByLinkedAccount,
@@ -199,7 +206,7 @@ public class DefaultPropagationManager implements
PropagationManager {
anyUR,
anyUtilsFactory.getInstance(kind).dao().authFind(key),
null,
- changePwd,
+ changePwdRes,
enable,
propByRes,
propByLinkedAccount,
@@ -210,7 +217,7 @@ public class DefaultPropagationManager implements
PropagationManager {
@Override
public List<PropagationTaskInfo> getUserUpdateTasks(
final UserWorkflowResult<Pair<UserUR, Boolean>> wfResult,
- final boolean changePwd,
+ final List<String> changePwdRes,
final Collection<String> excludedResources) {
return getUpdateTasks(
@@ -218,7 +225,7 @@ public class DefaultPropagationManager implements
PropagationManager {
anyUtilsFactory.getInstance(AnyTypeKind.USER).dao().authFind(wfResult.getResult().getLeft().getKey()),
Optional.ofNullable(wfResult.getResult().getLeft().getPassword()).
map(PasswordPatch::getValue).orElse(null),
- changePwd,
+ changePwdRes,
wfResult.getResult().getRight(),
wfResult.getPropByRes(),
wfResult.getPropByLinkedAccount(),
@@ -234,7 +241,7 @@ public class DefaultPropagationManager implements
PropagationManager {
List<PropagationTaskInfo> tasks;
if (userUR.getPassword() == null) {
// a. no specific password propagation request: generate
propagation tasks for any resource associated
- tasks = getUserUpdateTasks(wfResult, false, null);
+ tasks = getUserUpdateTasks(wfResult, List.of(), null);
} else {
tasks = new ArrayList<>();
@@ -264,7 +271,7 @@ public class DefaultPropagationManager implements
PropagationManager {
map(AbstractPatchItem::getValue).toList());
toBeExcluded.removeAll(pwdResourceNames);
- tasks.addAll(getUserUpdateTasks(pwdWFResult, true,
toBeExcluded));
+ tasks.addAll(getUserUpdateTasks(pwdWFResult, new
ArrayList<>(pwdResourceNames), toBeExcluded));
}
UserWorkflowResult<Pair<UserUR, Boolean>> noPwdWFResult = new
UserWorkflowResult<>(
@@ -277,7 +284,7 @@ public class DefaultPropagationManager implements
PropagationManager {
noPwdWFResult.getPropByRes().removeAll(pwdResourceNames);
noPwdWFResult.getPropByRes().purge();
if (!noPwdWFResult.getPropByRes().isEmpty()) {
- tasks.addAll(getUserUpdateTasks(noPwdWFResult, false,
pwdResourceNames));
+ tasks.addAll(getUserUpdateTasks(noPwdWFResult, List.of(),
pwdResourceNames));
}
tasks = tasks.stream().distinct().toList();
@@ -291,7 +298,7 @@ public class DefaultPropagationManager implements
PropagationManager {
final AnyUR anyUR,
final Any<?> any,
final String password,
- final boolean changePwd,
+ final List<String> changePwdRes,
final Boolean enable,
final PropagationByResource<String> propByRes,
final PropagationByResource<Pair<String, String>>
propByLinkedAccount,
@@ -316,7 +323,7 @@ public class DefaultPropagationManager implements
PropagationManager {
List<PropagationTaskInfo> tasks = createTasks(
any,
password,
- changePwd,
+ changePwdRes,
enable,
Optional.ofNullable(propByRes).orElseGet(PropagationByResource::new),
propByLinkedAccount,
@@ -367,7 +374,7 @@ public class DefaultPropagationManager implements
PropagationManager {
}
}
- return createTasks(any, null, false, false, localPropByRes,
propByLinkedAccount, null);
+ return createTasks(any, null, List.of(), false, localPropByRes,
propByLinkedAccount, null);
}
@Override
@@ -420,7 +427,7 @@ public class DefaultPropagationManager implements
PropagationManager {
*
* @param any to be provisioned
* @param password clear text password to be provisioned
- * @param changePwd whether password should be included for propagation
attributes or not
+ * @param changePwdRes the resources in which the password must be
included in the propagation attributes
* @param enable whether user must be enabled or not
* @param propByRes operation to be performed per resource
* @param propByLinkedAccount operation to be performed on linked accounts
@@ -430,7 +437,7 @@ public class DefaultPropagationManager implements
PropagationManager {
protected List<PropagationTaskInfo> createTasks(
final Any<?> any,
final String password,
- final boolean changePwd,
+ final List<String> changePwdRes,
final Boolean enable,
final PropagationByResource<String> propByRes,
final PropagationByResource<Pair<String, String>>
propByLinkedAccount,
@@ -497,7 +504,8 @@ public class DefaultPropagationManager implements
PropagationManager {
any.getType(), resource);
} else {
Pair<String, Set<Attribute>> preparedAttrs =
- mappingManager.prepareAttrsFromAny(any, password,
changePwd, enable, resource, provision);
+ mappingManager.prepareAttrsFromAny(any, password,
changePwdRes.contains(resourceKey),
+ enable, resource, provision);
if (vAttrMap.containsKey(resourceKey)) {
preparedAttrs.getRight().addAll(vAttrMap.get(resourceKey));
}
@@ -556,7 +564,9 @@ public class DefaultPropagationManager implements
PropagationManager {
mappingItems,
Pair.of(account.getConnObjectKeyValue(),
mappingManager.prepareAttrsFromLinkedAccount(
- user, account, password, true,
provision)));
+ user, account, password,
+
changePwdRes.contains(account.getResource().getKey()),
+ provision)));
tasks.add(accountTask);
LOG.debug("PropagationTask created for Linked Account {}:
{}",
@@ -621,13 +631,14 @@ public class DefaultPropagationManager implements
PropagationManager {
return tasks;
}
- @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+ @Transactional(readOnly = true,
+ propagation = Propagation.REQUIRES_NEW)
@Override
public Map<Pair<String, String>, Set<Attribute>> prepareAttrs(
final AnyTypeKind kind,
final String key,
final String password,
- final boolean changePwd,
+ final List<String> changePwdRes,
final Boolean enable,
final Collection<String> excludedResources) {
@@ -645,7 +656,7 @@ public class DefaultPropagationManager implements
PropagationManager {
Pair<String, Set<Attribute>> preparedAttrs =
mappingManager.prepareAttrsFromAny(
any,
password,
- changePwd,
+ changePwdRes.contains(resource.getKey()),
enable,
resource,
resource.getProvisionByAnyType(any.getType().getKey()).get());
@@ -676,7 +687,8 @@ public class DefaultPropagationManager implements
PropagationManager {
return attrs;
}
- @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+ @Transactional(readOnly = true,
+ propagation = Propagation.REQUIRES_NEW)
@Override
public Map<Pair<String, String>, Set<Attribute>> prepareAttrs(final Realm
realm) {
Map<Pair<String, String>, Set<Attribute>> attrs = new HashMap<>();
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
index 57d30d0175..2bc0e280b4 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
@@ -135,8 +135,7 @@ public abstract class AbstractPushResultHandler extends
AbstractSyncopeResultHan
final Boolean enable,
final ConnectorObject beforeObj,
final ProvisioningReport result) {
-
- boolean changepwd = any instanceof User;
+
List<String> ownedResources =
getAnyUtils().getAllResources(any).stream().
map(ExternalResource::getKey).toList();
@@ -151,7 +150,7 @@ public abstract class AbstractPushResultHandler extends
AbstractSyncopeResultHan
null,
any.getType().getKind(),
any.getKey(),
- changepwd,
+ any instanceof User ?
List.of(profile.getTask().getResource().getKey()) : List.of(),
enable,
propByRes,
null,
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 0e34ebc67d..c491c7d369 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
@@ -127,7 +127,7 @@ public class DefaultUserPushResultHandler extends
AbstractPushResultHandler impl
null,
any.getType().getKind(),
any.getKey(),
- true,
+ List.of(profile.getTask().getResource().getKey()),
enable,
propByRes,
propByLinkedAccount,
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 73d990d33b..c3ad5def63 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
@@ -81,6 +81,7 @@ import org.apache.syncope.common.lib.to.ImplementationTO;
import org.apache.syncope.common.lib.to.Item;
import org.apache.syncope.common.lib.to.MembershipTO;
import org.apache.syncope.common.lib.to.PagedResult;
+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;
@@ -113,10 +114,13 @@ import org.apache.syncope.common.rest.api.beans.TaskQuery;
import org.apache.syncope.common.rest.api.service.ConnectorService;
import org.apache.syncope.common.rest.api.service.TaskService;
import org.apache.syncope.common.rest.api.service.UserService;
+import org.apache.syncope.core.persistence.api.entity.task.PropagationData;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
import
org.apache.syncope.core.provisioning.java.pushpull.DBPasswordPullActions;
import
org.apache.syncope.core.provisioning.java.pushpull.LDAPPasswordPullActions;
import org.apache.syncope.core.spring.security.Encryptor;
import org.apache.syncope.fit.core.reference.TestInboundActions;
+import org.identityconnectors.framework.common.objects.AttributeUtil;
import org.identityconnectors.framework.common.objects.Name;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -1641,4 +1645,89 @@ public class PullTaskITCase extends AbstractTaskITCase {
Optional.ofNullable(pullFromLDAP4issue1656).ifPresent(u ->
USER_SERVICE.delete(u.getKey()));
}
}
+
+ @Test
+ public void issueSYNCOPE1864() throws Exception {
+ // First of all, clear any potential conflict with existing user /
group
+ ldapCleanup();
+
+ UserTO user = null;
+ PullTaskTO pullTask = null;
+ ConnInstanceTO resourceConnector = null;
+ ConnConfProperty property = null;
+ try {
+ // 1. create user in LDAP
+ String oldCleanPassword = "security123";
+ UserCR userCR =
UserITCase.getUniqueSample("[email protected]");
+ userCR.setPassword(oldCleanPassword);
+ userCR.getResources().add(RESOURCE_NAME_LDAP);
+ userCR.getResources().add(RESOURCE_NAME_DBPULL);
+ user = createUser(userCR).getEntity();
+ assertNotNull(user);
+ assertFalse(user.getResources().isEmpty());
+
+ // 2. Pull the user from the resource
+ ImplementationTO pullActions;
+ try {
+ pullActions = IMPLEMENTATION_SERVICE.read(
+ IdMImplementationType.INBOUND_ACTIONS,
"AddResourcePullActions");
+ } catch (SyncopeClientException e) {
+ pullActions = new ImplementationTO();
+ pullActions.setKey("AddResourcePullActions");
+ pullActions.setEngine(ImplementationEngine.GROOVY);
+ pullActions.setType(IdMImplementationType.INBOUND_ACTIONS);
+ pullActions.setBody(org.apache.commons.io.IOUtils.toString(
+
getClass().getResourceAsStream("/AddResourcePullActions.groovy"),
StandardCharsets.UTF_8));
+ Response response = IMPLEMENTATION_SERVICE.create(pullActions);
+ pullActions = IMPLEMENTATION_SERVICE.read(
+ pullActions.getType(),
response.getHeaderString(RESTHeaders.RESOURCE_KEY));
+ }
+ assertNotNull(pullActions);
+
+ pullTask = new PullTaskTO();
+ pullTask.setDestinationRealm(SyncopeConstants.ROOT_REALM);
+ pullTask.setName(getUUIDString());
+ pullTask.setActive(true);
+ pullTask.setPerformCreate(true);
+ pullTask.setPerformUpdate(true);
+ pullTask.setPullMode(PullMode.FULL_RECONCILIATION);
+ pullTask.setResource(RESOURCE_NAME_LDAP);
+ pullTask.getActions().add(pullActions.getKey());
+ Response taskResponse = TASK_SERVICE.create(TaskType.PULL,
pullTask);
+
+ pullTask = getObject(taskResponse.getLocation(),
TaskService.class, PullTaskTO.class);
+ assertNotNull(pullTask);
+
+ ExecTO execution = execSchedTask(TASK_SERVICE, TaskType.PULL,
pullTask.getKey(), MAX_WAIT_SECONDS, false);
+ assertEquals(ExecStatus.SUCCESS,
ExecStatus.valueOf(execution.getStatus()));
+
+ // 3. Test if password is not present in the propagation task for
DB
+ PagedResult<PropagationTaskTO> propagationTasks =
TASK_SERVICE.search(
+ new TaskQuery.Builder(TaskType.PROPAGATION).
+ resource(RESOURCE_NAME_TESTDB2).
+
anyTypeKind(AnyTypeKind.USER).entityKey(user.getKey()).build());
+
assertTrue(propagationTasks.getResult().stream().anyMatch(propagationTask -> {
+ Set<org.identityconnectors.framework.common.objects.Attribute>
attributes =
+ POJOHelper.deserialize(
+ propagationTask.getPropagationData(),
PropagationData.class).getAttributes();
+ return
ResourceOperation.UPDATE.equals(propagationTask.getOperation())
+ && AttributeUtil.getPasswordValue(attributes) != null;
+ }));
+
+ propagationTasks = TASK_SERVICE.search(
+ new TaskQuery.Builder(TaskType.PROPAGATION).
+ resource(RESOURCE_NAME_DBPULL).
+
anyTypeKind(AnyTypeKind.USER).entityKey(user.getKey()).build());
+
assertTrue(propagationTasks.getResult().stream().anyMatch(propagationTask -> {
+ Set<org.identityconnectors.framework.common.objects.Attribute>
attributes =
+ POJOHelper.deserialize(
+ propagationTask.getPropagationData(),
PropagationData.class).getAttributes();
+ return
ResourceOperation.UPDATE.equals(propagationTask.getOperation())
+ && AttributeUtil.getPasswordValue(attributes) == null;
+ }));
+ } finally {
+ // remove test entity
+ deleteUser(user.getKey());
+ }
+ }
}
diff --git
a/fit/core-reference/src/test/resources/AddResourcePullActions.groovy
b/fit/core-reference/src/test/resources/AddResourcePullActions.groovy
new file mode 100644
index 0000000000..e0e5533724
--- /dev/null
+++ b/fit/core-reference/src/test/resources/AddResourcePullActions.groovy
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import groovy.transform.CompileStatic
+
+import org.apache.syncope.core.provisioning.api.pushpull.InboundActions;
+import org.apache.syncope.common.lib.request.AnyUR;
+import org.apache.syncope.common.lib.to.EntityTO;
+import org.apache.syncope.core.provisioning.api.job.JobExecutionException;
+import org.apache.syncope.common.lib.request.UserUR;
+import org.apache.syncope.common.lib.request.PasswordPatch;
+import org.apache.syncope.common.lib.request.StringPatchItem;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
+import org.apache.syncope.common.lib.types.PatchOperation;
+import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.request.AttrPatch;
+import org.identityconnectors.framework.common.objects.LiveSyncDelta;
+
+/**
+ * Class for integration tests: add new resource and put a password only for
it.
+ */
+@CompileStatic
+class AddResourcePullActions implements InboundActions {
+
+ void beforeUpdate(
+ final ProvisioningProfile<?, ?> profile,
+ final LiveSyncDelta delta,
+ final EntityTO entity,
+ final AnyUR anyUR) throws JobExecutionException {
+
+ if (anyUR instanceof UserUR) {
+ UserUR userUR = (UserUR) anyUR;
+ Attr attr = new Attr();
+ attr.setSchema("surname");
+ attr.getValues().add("surname2");
+ AttrPatch attrPatch = new AttrPatch();
+ attrPatch.setAttr(attr);
+ attrPatch.setOperation(PatchOperation.ADD_REPLACE);
+ userUR.getPlainAttrs().add(attrPatch);
+
+ PasswordPatch patch = new PasswordPatch();
+ patch.setOnSyncope(false);
+ patch.setValue("Password123");
+ patch.setOperation(PatchOperation.ADD_REPLACE);
+ patch.getResources().add("resource-testdb2");
+ userUR.setPassword(patch);
+
+ StringPatchItem resPatchItem = new StringPatchItem();
+ resPatchItem.setValue("resource-testdb2");
+ resPatchItem.setOperation(PatchOperation.ADD_REPLACE);
+ userUR.getResources().add(resPatchItem);
+ }
+ }
+}
\ No newline at end of file