You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2020/12/02 08:24:33 UTC
[syncope] branch master updated (492f280 -> 44db3ed)
This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git.
from 492f280 [SYNCOPE-1604] short date years represented by 4 digits (#228)
new e5f01ba Upgrading Swagger UI and Checkstyle
new 9588517 Upgrading gmavenplus-plugin
new 44db3ed [SYNCOPE-1605] Refactoring PropagationByResource (which drives Propagation) generation
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../java/data/AbstractAnyDataBinder.java | 104 +++++++++-------
.../java/data/AnyObjectDataBinderImpl.java | 105 +++-------------
.../java/data/GroupDataBinderImpl.java | 32 ++---
.../provisioning/java/data/UserDataBinderImpl.java | 136 ++++++---------------
.../AbstractPropagationTaskExecutor.java | 10 +-
.../syncope/fit/core/PropagationTaskITCase.java | 81 ++++++++++++
.../apache/syncope/fit/core/UserIssuesITCase.java | 6 +-
pom.xml | 6 +-
8 files changed, 212 insertions(+), 268 deletions(-)
[syncope] 02/03: Upgrading gmavenplus-plugin
Posted by il...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
commit 95885170678584e1fbde4de995861393a579ec08
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Tue Dec 1 18:10:07 2020 +0100
Upgrading gmavenplus-plugin
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 6583666..b8382b6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2482,7 +2482,7 @@ under the License.
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
- <version>1.11.0</version>
+ <version>1.11.1</version>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
[syncope] 01/03: Upgrading Swagger UI and Checkstyle
Posted by il...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
commit e5f01ba7631ce1fc8c2128dd18b48ed15203ecae
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Tue Dec 1 18:06:14 2020 +0100
Upgrading Swagger UI and Checkstyle
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 07b93cd..6583666 100644
--- a/pom.xml
+++ b/pom.xml
@@ -458,7 +458,7 @@ under the License.
<mockito.version>3.6.28</mockito.version>
<swagger-core.version>2.1.5</swagger-core.version>
- <swagger-ui.version>3.37.0</swagger-ui.version>
+ <swagger-ui.version>3.37.2</swagger-ui.version>
<jquery-slimscroll.version>1.3.8</jquery-slimscroll.version>
<jquery-cookie.version>1.4.1-1</jquery-cookie.version>
@@ -2328,7 +2328,7 @@ under the License.
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
- <version>8.37</version>
+ <version>8.38</version>
</dependency>
</dependencies>
<configuration>
[syncope] 03/03: [SYNCOPE-1605] Refactoring PropagationByResource
(which drives Propagation) generation
Posted by il...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
commit 44db3ed2b914b7b2cedff8c34c7f2ef817e432a1
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Tue Dec 1 18:05:05 2020 +0100
[SYNCOPE-1605] Refactoring PropagationByResource (which drives Propagation) generation
---
.../java/data/AbstractAnyDataBinder.java | 104 +++++++++-------
.../java/data/AnyObjectDataBinderImpl.java | 105 +++-------------
.../java/data/GroupDataBinderImpl.java | 32 ++---
.../provisioning/java/data/UserDataBinderImpl.java | 136 ++++++---------------
.../AbstractPropagationTaskExecutor.java | 10 +-
.../syncope/fit/core/PropagationTaskITCase.java | 81 ++++++++++++
.../apache/syncope/fit/core/UserIssuesITCase.java | 6 +-
7 files changed, 209 insertions(+), 265 deletions(-)
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 00de34e..ed17e68 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
@@ -28,6 +28,7 @@ import org.apache.syncope.common.lib.request.AnyUR;
import org.apache.syncope.common.lib.request.AttrPatch;
import org.apache.syncope.common.lib.request.StringPatchItem;
import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.ConnObjectTO;
import org.apache.syncope.common.lib.to.MembershipTO;
import org.apache.syncope.common.lib.to.RelationshipTO;
import org.apache.syncope.common.lib.types.AttrSchemaType;
@@ -40,7 +41,6 @@ import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
-import org.apache.syncope.core.persistence.api.dao.NotFoundException;
import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO;
import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
@@ -64,18 +64,20 @@ import org.apache.syncope.core.persistence.api.entity.Realm;
import org.apache.syncope.core.persistence.api.entity.VirSchema;
import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
-import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
import org.apache.syncope.core.provisioning.api.AccountGetter;
import org.apache.syncope.core.provisioning.api.DerAttrHandler;
import org.apache.syncope.core.provisioning.api.IntAttrName;
import org.apache.syncope.core.provisioning.api.MappingManager;
import org.apache.syncope.core.provisioning.api.PlainAttrGetter;
-import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.provisioning.api.VirAttrHandler;
import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
+import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.Uid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -153,6 +155,32 @@ abstract class AbstractAnyDataBinder {
}
}
+ protected Map<String, ConnObjectTO> onResources(
+ final Any<?> any,
+ final Collection<String> resources,
+ final String password,
+ final boolean changePwd) {
+
+ Map<String, ConnObjectTO> onResources = new HashMap<>();
+
+ resources.stream().map(resourceDAO::find).map(resource -> resource.getProvision(any.getType())).
+ filter(Optional::isPresent).map(Optional::get).
+ forEach(provision -> MappingUtils.getConnObjectKeyItem(provision).ifPresent(connObjectKeyItem -> {
+
+ Pair<String, Set<Attribute>> prepared = mappingManager.prepareAttrsFromAny(
+ any, password, changePwd, true, provision);
+
+ ConnObjectTO connObjectTO = ConnObjectUtils.getConnObjectTO(prepared.getRight());
+ connObjectTO.getAttrs().add(
+ new Attr.Builder(connObjectKeyItem.getExtAttrName()).value(prepared.getLeft()).build());
+ connObjectTO.getAttrs().add(new Attr.Builder(Uid.NAME).value(prepared.getLeft()).build());
+
+ onResources.put(provision.getResource().getKey(), connObjectTO);
+ }));
+
+ return onResources;
+ }
+
protected PlainSchema getPlainSchema(final String schemaName) {
PlainSchema schema = null;
if (StringUtils.isNotBlank(schemaName)) {
@@ -218,7 +246,8 @@ abstract class AbstractAnyDataBinder {
? ((PlainSchema) intAttrName.getSchema()).getType()
: AttrSchemaType.String;
- Pair<AttrSchemaType, List<PlainAttrValue>> intValues = mappingManager.getIntValues(provision,
+ Pair<AttrSchemaType, List<PlainAttrValue>> intValues = mappingManager.getIntValues(
+ provision,
mapItem,
intAttrName,
schemaType,
@@ -300,8 +329,6 @@ abstract class AbstractAnyDataBinder {
final PlainSchema schema,
final PlainAttr<?> attr,
final AnyUtils anyUtils,
- final Collection<ExternalResource> resources,
- final PropagationByResource<String> propByRes,
final SyncopeClientException invalidValues) {
switch (patch.getOperation()) {
@@ -338,31 +365,15 @@ abstract class AbstractAnyDataBinder {
any.remove(attr);
plainAttrDAO.delete(attr);
}
-
- resources.stream().
- filter(resource -> resource.getProvision(any.getType()).isPresent()
- && resource.getProvision(any.getType()).get().getMapping() != null).
- forEach(resource -> MappingUtils.getPropagationItems(
- resource.getProvision(any.getType()).get().getMapping().getItems().stream()).
- filter(item -> (schema.getKey().equals(item.getIntAttrName()))).
- forEach(item -> {
- propByRes.add(ResourceOperation.UPDATE, resource.getKey());
-
- if (item.isConnObjectKey() && !attr.getValuesAsStrings().isEmpty()) {
- propByRes.addOldConnObjectKey(resource.getKey(), attr.getValuesAsStrings().get(0));
- }
- }));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
- protected PropagationByResource<String> fill(
+ protected void fill(
final Any any,
final AnyUR anyUR,
final AnyUtils anyUtils,
final SyncopeClientCompositeException scce) {
- PropagationByResource<String> propByRes = new PropagationByResource<>();
-
// 1. anyTypeClasses
for (StringPatchItem patch : anyUR.getAuxClasses()) {
AnyTypeClass auxClass = anyTypeClassDAO.find(patch.getValue());
@@ -389,13 +400,11 @@ abstract class AbstractAnyDataBinder {
} else {
switch (patch.getOperation()) {
case ADD_REPLACE:
- propByRes.add(ResourceOperation.CREATE, resource.getKey());
any.add(resource);
break;
case DELETE:
default:
- propByRes.add(ResourceOperation.DELETE, resource.getKey());
any.getResources().remove(resource);
}
}
@@ -424,7 +433,7 @@ abstract class AbstractAnyDataBinder {
}
}
if (attr != null) {
- processAttrPatch(any, patch, schema, attr, anyUtils, resources, propByRes, invalidValues);
+ processAttrPatch(any, patch, schema, attr, anyUtils, invalidValues);
}
}
});
@@ -440,6 +449,30 @@ abstract class AbstractAnyDataBinder {
if (!requiredValuesMissing.isEmpty()) {
scce.addException(requiredValuesMissing);
}
+ }
+
+ protected PropagationByResource<String> propByRes(
+ final Map<String, ConnObjectTO> before,
+ final Map<String, ConnObjectTO> after) {
+
+ PropagationByResource<String> propByRes = new PropagationByResource<>();
+
+ after.forEach((resource, connObject) -> {
+ if (before.containsKey(resource)) {
+ ConnObjectTO beforeObject = before.get(resource);
+ if (!beforeObject.equals(connObject)) {
+ propByRes.add(ResourceOperation.UPDATE, resource);
+
+ beforeObject.getAttr(Uid.NAME).map(attr -> attr.getValues().get(0)).
+ ifPresent(value -> propByRes.addOldConnObjectKey(resource, value));
+ }
+ } else {
+ propByRes.add(ResourceOperation.CREATE, resource);
+ }
+ });
+ propByRes.addAll(
+ ResourceOperation.DELETE,
+ before.keySet().stream().filter(resource -> !after.containsKey(resource)).collect(Collectors.toSet()));
return propByRes;
}
@@ -607,23 +640,4 @@ abstract class AbstractAnyDataBinder {
return membershipTO;
}
-
- protected Map<String, String> getConnObjectKeys(final Any<?> any, final AnyUtils anyUtils) {
- Map<String, String> connObjectKeys = new HashMap<>();
-
- anyUtils.getAllResources(any).
- forEach(resource -> resource.getProvision(any.getType()).
- filter(provision -> provision.getMapping() != null).
- ifPresent(provision -> {
- MappingItem connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision).
- orElseThrow(() -> new NotFoundException(
- "ConnObjectKey mapping for " + any.getType().getKey() + ' ' + any.getKey()
- + " on resource '" + resource.getKey() + '\''));
-
- mappingManager.getConnObjectKeyValue(any, provision).
- ifPresent(value -> connObjectKeys.put(resource.getKey(), value));
- }));
-
- return connObjectKeys;
- }
}
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 a12f3db..6201586 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
@@ -18,9 +18,7 @@
*/
package org.apache.syncope.core.provisioning.java.data;
-import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -35,12 +33,13 @@ import org.apache.syncope.common.lib.request.AnyObjectCR;
import org.apache.syncope.common.lib.request.AnyObjectUR;
import org.apache.syncope.common.lib.request.AttrPatch;
import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.to.ConnObjectTO;
import org.apache.syncope.common.lib.to.MembershipTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.types.PatchOperation;
-import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
import org.apache.syncope.core.persistence.api.entity.AnyType;
import org.apache.syncope.core.persistence.api.entity.AnyUtils;
@@ -53,7 +52,6 @@ import org.apache.syncope.core.persistence.api.entity.anyobject.APlainAttr;
import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship;
import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
import org.apache.syncope.core.persistence.api.entity.group.Group;
-import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -266,27 +264,24 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
PropagationByResource<String> propByRes = new PropagationByResource<>();
+ // Save projection on Resources (before update)
+ Map<String, ConnObjectTO> beforeOnResources =
+ onResources(anyObject, anyObjectDAO.findAllResourceKeys(anyObject.getKey()), null, false);
+
SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
AnyUtils anyUtils = anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT);
- Collection<String> currentResources = anyObjectDAO.findAllResourceKeys(anyObject.getKey());
-
- // fetch connObjectKeys before update
- Map<String, String> oldConnObjectKeys = getConnObjectKeys(anyObject, anyUtils);
-
// realm
setRealm(anyObject, anyObjectUR);
// name
if (anyObjectUR.getName() != null && StringUtils.isNotBlank(anyObjectUR.getName().getValue())) {
- propByRes.addAll(ResourceOperation.UPDATE, anyObjectDAO.findAllResourceKeys(anyObject.getKey()));
-
anyObject.setName(anyObjectUR.getName().getValue());
}
// attributes and resources
- propByRes.merge(fill(anyObject, anyObjectUR, anyUtils, scce));
+ fill(anyObject, anyObjectUR, anyUtils, scce);
// relationships
Set<Pair<String, String>> relationships = new HashSet<>();
@@ -352,23 +347,6 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
}
});
- // prepare for membership-related resource management
- Collection<ExternalResource> resources = anyObjectDAO.findAllResources(anyObject);
-
- Map<String, Set<String>> reasons = new HashMap<>();
- anyObject.getResources().forEach(
- resource -> reasons.put(resource.getKey(), new HashSet<>(Set.of(anyObject.getKey()))));
- anyObjectDAO.findAllGroupKeys(anyObject).forEach(
- group -> groupDAO.findAllResourceKeys(group).forEach(resource -> {
- if (!reasons.containsKey(resource)) {
- reasons.put(resource, new HashSet<>());
- }
- reasons.get(resource).add(group);
- }));
-
- Set<String> toBeDeprovisioned = new HashSet<>();
- Set<String> toBeProvisioned = new HashSet<>();
-
SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues);
// memberships
@@ -385,12 +363,9 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
});
if (patch.getOperation() == PatchOperation.DELETE) {
- groupDAO.findAllResourceKeys(membership.getRightEnd().getKey()).stream().
- filter(reasons::containsKey).
- forEach(resource -> {
- reasons.get(resource).remove(membership.getRightEnd().getKey());
- toBeProvisioned.add(resource);
- });
+ propByRes.addAll(
+ ResourceOperation.UPDATE,
+ groupDAO.findAllResourceKeys((membership.getRightEnd().getKey())));
}
});
if (patch.getOperation() == PatchOperation.ADD_REPLACE) {
@@ -437,8 +412,6 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
schema,
newAttr,
anyUtils,
- resources,
- propByRes,
invalidValues);
}
}
@@ -447,7 +420,7 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
scce.addException(invalidValues);
}
- toBeProvisioned.addAll(groupDAO.findAllResourceKeys(group.getKey()));
+ propByRes.addAll(ResourceOperation.UPDATE, groupDAO.findAllResourceKeys(group.getKey()));
} else {
LOG.error("{} cannot be assigned to {}", group, anyObject);
@@ -459,50 +432,6 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
}
});
- // finalize resource management
- reasons.entrySet().stream().
- filter(entry -> entry.getValue().isEmpty()).
- forEach(entry -> toBeDeprovisioned.add(entry.getKey()));
-
- propByRes.addAll(ResourceOperation.DELETE, toBeDeprovisioned);
- propByRes.addAll(ResourceOperation.UPDATE, toBeProvisioned);
-
- // in case of new memberships all current resources need to be updated in order to propagate new group
- // attribute values.
- if (!toBeDeprovisioned.isEmpty() || !toBeProvisioned.isEmpty()) {
- currentResources.removeAll(toBeDeprovisioned);
- propByRes.addAll(ResourceOperation.UPDATE, currentResources);
- }
-
- // check if some connObjectKey was changed by the update above
- Map<String, String> newConnObjectKeys = getConnObjectKeys(anyObject, anyUtils);
- oldConnObjectKeys.entrySet().stream().
- filter(entry -> newConnObjectKeys.containsKey(entry.getKey())
- && !entry.getValue().equals(newConnObjectKeys.get(entry.getKey()))).
- forEach(entry -> {
- propByRes.addOldConnObjectKey(entry.getKey(), entry.getValue());
- propByRes.add(ResourceOperation.UPDATE, entry.getKey());
- });
-
- Pair<Set<String>, Set<String>> dynGroupMembs = anyObjectDAO.saveAndGetDynGroupMembs(anyObject);
-
- // finally check if any resource assignment is to be processed due to dynamic group membership change
- dynGroupMembs.getLeft().stream().
- filter(group -> !dynGroupMembs.getRight().contains(group)).
- forEach(delete -> groupDAO.find(delete).getResources().stream().
- filter(resource -> !propByRes.contains(resource.getKey())).
- forEach(resource -> propByRes.add(ResourceOperation.DELETE, resource.getKey())));
- dynGroupMembs.getLeft().stream().
- filter(group -> dynGroupMembs.getRight().contains(group)).
- forEach(update -> groupDAO.find(update).getResources().stream().
- filter(resource -> !propByRes.contains(resource.getKey())).
- forEach(resource -> propByRes.add(ResourceOperation.UPDATE, resource.getKey())));
- dynGroupMembs.getRight().stream().
- filter(group -> !dynGroupMembs.getLeft().contains(group)).
- forEach(create -> groupDAO.find(create).getResources().stream().
- filter(resource -> !propByRes.contains(resource.getKey())).
- forEach(resource -> propByRes.add(ResourceOperation.CREATE, resource.getKey())));
-
// Throw composite exception if there is at least one element set in the composing exceptions
if (scce.hasExceptions()) {
throw scce;
@@ -511,14 +440,10 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
// Re-merge any pending change from above
AnyObject saved = anyObjectDAO.save(anyObject);
- // ensure not to DELETE on External Resources that remain assigned
- Set<String> assigned = saved.getResources().stream().
- map(ExternalResource::getKey).collect(Collectors.toCollection(HashSet::new));
- assigned.addAll(saved.getMemberships().stream().
- flatMap(m -> m.getRightEnd().getResources().stream()).map(ExternalResource::getKey).
- collect(Collectors.toSet()));
- propByRes.removeAll(ResourceOperation.DELETE, assigned);
-
+ // Build final information for next stage (propagation)
+ propByRes.merge(propByRes(
+ beforeOnResources,
+ onResources(saved, anyObjectDAO.findAllResourceKeys(anyObject.getKey()), null, false)));
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 0554e65..cdca128 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
@@ -29,6 +29,7 @@ import org.apache.syncope.common.lib.SyncopeClientCompositeException;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.request.GroupCR;
import org.apache.syncope.common.lib.request.GroupUR;
+import org.apache.syncope.common.lib.to.ConnObjectTO;
import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.common.lib.to.TypeExtensionTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -44,7 +45,6 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.AnyType;
import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
-import org.apache.syncope.core.persistence.api.entity.AnyUtils;
import org.apache.syncope.core.persistence.api.entity.DerSchema;
import org.apache.syncope.core.persistence.api.entity.DynGroupMembership;
import org.apache.syncope.core.persistence.api.entity.Entity;
@@ -194,22 +194,17 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
// Re-merge any pending change from workflow tasks
Group group = groupDAO.save(toBeUpdated);
- PropagationByResource<String> propByRes = new PropagationByResource<>();
+ // Save projection on Resources (before update)
+ Map<String, ConnObjectTO> beforeOnResources =
+ onResources(group, groupDAO.findAllResourceKeys(group.getKey()), null, false);
SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
- AnyUtils anyUtils = anyUtilsFactory.getInstance(AnyTypeKind.GROUP);
-
- // fetch connObjectKeys before update
- Map<String, String> oldConnObjectKeys = getConnObjectKeys(group, anyUtils);
-
// realm
setRealm(group, groupUR);
// name
if (groupUR.getName() != null && StringUtils.isNotBlank(groupUR.getName().getValue())) {
- propByRes.addAll(ResourceOperation.UPDATE, groupDAO.findAllResourceKeys(group.getKey()));
-
group.setName(groupUR.getName().getValue());
}
@@ -226,17 +221,7 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
}
// attributes and resources
- propByRes.merge(fill(group, groupUR, anyUtils, scce));
-
- // check if some connObjectKey was changed by the update above
- Map<String, String> newConnObjectKeys = getConnObjectKeys(group, anyUtils);
- oldConnObjectKeys.entrySet().stream().
- filter(entry -> newConnObjectKeys.containsKey(entry.getKey())
- && !entry.getValue().equals(newConnObjectKeys.get(entry.getKey()))).
- forEach(entry -> {
- propByRes.addOldConnObjectKey(entry.getKey(), entry.getValue());
- propByRes.add(ResourceOperation.UPDATE, entry.getKey());
- });
+ fill(group, groupUR, anyUtilsFactory.getInstance(AnyTypeKind.GROUP), scce);
group = groupDAO.save(group);
@@ -311,8 +296,11 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
}
// Re-merge any pending change from above
- groupDAO.save(group);
- return propByRes;
+ group = groupDAO.save(group);
+
+ // Build final information for next stage (propagation)
+ return propByRes(
+ beforeOnResources, onResources(group, groupDAO.findAllResourceKeys(group.getKey()), null, false));
}
@Override
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 70ad56a..31a605d 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
@@ -18,9 +18,7 @@
*/
package org.apache.syncope.core.provisioning.java.data;
-import java.util.Collection;
import java.util.Date;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
@@ -40,6 +38,7 @@ import org.apache.syncope.common.lib.request.UserCR;
import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.request.AttrPatch;
import org.apache.syncope.common.lib.request.PasswordPatch;
+import org.apache.syncope.common.lib.to.ConnObjectTO;
import org.apache.syncope.common.lib.to.LinkedAccountTO;
import org.apache.syncope.common.lib.to.MembershipTO;
import org.apache.syncope.common.lib.to.UserTO;
@@ -381,29 +380,36 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
AnyUtils anyUtils = anyUtilsFactory.getInstance(AnyTypeKind.USER);
- Collection<String> currentResources = userDAO.findAllResourceKeys(user.getKey());
-
- // fetch connObjectKeys before update
- Map<String, String> oldConnObjectKeys = getConnObjectKeys(user, anyUtils);
-
// realm
setRealm(user, userUR);
// password
+ String password = null;
+ boolean changePwd = false;
if (userUR.getPassword() != null) {
if (userUR.getPassword().getOperation() == PatchOperation.DELETE) {
user.setEncodedPassword(null, null);
- propByRes.addAll(ResourceOperation.UPDATE, userUR.getPassword().getResources());
+
+ changePwd = true;
} else if (StringUtils.isNotBlank(userUR.getPassword().getValue())) {
if (userUR.getPassword().isOnSyncope()) {
setPassword(user, userUR.getPassword().getValue(), scce);
user.setChangePwdDate(new Date());
}
+ password = userUR.getPassword().getValue();
+ changePwd = true;
+ }
+
+ if (changePwd) {
propByRes.addAll(ResourceOperation.UPDATE, userUR.getPassword().getResources());
}
}
+ // Save projection on Resources (before update)
+ Map<String, ConnObjectTO> beforeOnResources =
+ onResources(user, userDAO.findAllResourceKeys(user.getKey()), password, changePwd);
+
// username
if (userUR.getUsername() != null && StringUtils.isNotBlank(userUR.getUsername().getValue())) {
String oldUsername = user.getUsername();
@@ -418,8 +424,6 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
accessToken.setOwner(userUR.getUsername().getValue());
accessTokenDAO.save(accessToken);
}
-
- propByRes.addAll(ResourceOperation.UPDATE, currentResources);
}
// security question / answer:
@@ -437,19 +441,6 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
}
}
- if (userUR.getMustChangePassword() != null) {
- user.setMustChangePassword(userUR.getMustChangePassword().getValue());
-
- propByRes.addAll(
- ResourceOperation.UPDATE,
- anyUtils.getAllResources(toBeUpdated).stream().
- filter(resource -> resource.getProvision(toBeUpdated.getType()).isPresent()).
- filter(resource -> mappingManager.hasMustChangePassword(
- resource.getProvision(toBeUpdated.getType()).get())).
- map(Entity::getKey).
- collect(Collectors.toSet()));
- }
-
// roles
for (StringPatchItem patch : userUR.getRoles()) {
Role role = roleDAO.find(patch.getValue());
@@ -469,7 +460,7 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
}
// attributes and resources
- propByRes.merge(fill(user, userUR, anyUtils, scce));
+ fill(user, userUR, anyUtils, scce);
// relationships
Set<Pair<String, String>> relationships = new HashSet<>();
@@ -522,22 +513,6 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
}
});
- // prepare for membership-related resource management
- Collection<ExternalResource> resources = userDAO.findAllResources(user);
-
- Map<String, Set<String>> reasons = new HashMap<>();
- user.getResources().forEach(
- resource -> reasons.put(resource.getKey(), new HashSet<>(Set.of(user.getKey()))));
- userDAO.findAllGroupKeys(user).forEach(group -> groupDAO.findAllResourceKeys(group).forEach(resource -> {
- if (!reasons.containsKey(resource)) {
- reasons.put(resource, new HashSet<>());
- }
- reasons.get(resource).add(group);
- }));
-
- Set<String> toBeDeprovisioned = new HashSet<>();
- Set<String> toBeProvisioned = new HashSet<>();
-
SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues);
// memberships
@@ -555,12 +530,9 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
});
if (patch.getOperation() == PatchOperation.DELETE) {
- groupDAO.findAllResourceKeys(membership.getRightEnd().getKey()).stream().
- filter(reasons::containsKey).
- forEach(resource -> {
- reasons.get(resource).remove(membership.getRightEnd().getKey());
- toBeProvisioned.add(resource);
- });
+ propByRes.addAll(
+ ResourceOperation.UPDATE,
+ groupDAO.findAllResourceKeys((membership.getRightEnd().getKey())));
}
});
if (patch.getOperation() == PatchOperation.ADD_REPLACE) {
@@ -606,8 +578,6 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
schema,
attr,
anyUtils,
- resources,
- propByRes,
invalidValues);
}
}
@@ -616,7 +586,7 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
scce.addException(invalidValues);
}
- toBeProvisioned.addAll(groupDAO.findAllResourceKeys(group.getKey()));
+ propByRes.addAll(ResourceOperation.UPDATE, groupDAO.findAllResourceKeys(group.getKey()));
// SYNCOPE-686: if password is invertible and we are adding resources with password mapping,
// ensure that they are counted for password propagation
@@ -675,50 +645,6 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
ResourceOperation.CREATE,
Pair.of(account.getResource().getKey(), account.getConnObjectKeyValue())));
- // finalize resource management
- reasons.entrySet().stream().
- filter(entry -> entry.getValue().isEmpty()).
- forEach(entry -> toBeDeprovisioned.add(entry.getKey()));
-
- propByRes.addAll(ResourceOperation.DELETE, toBeDeprovisioned);
- propByRes.addAll(ResourceOperation.UPDATE, toBeProvisioned);
-
- // in case of new memberships all current resources need to be updated in order to propagate new group
- // attribute values.
- if (!toBeDeprovisioned.isEmpty() || !toBeProvisioned.isEmpty()) {
- currentResources.removeAll(toBeDeprovisioned);
- propByRes.addAll(ResourceOperation.UPDATE, currentResources);
- }
-
- // check if some connObjectKey was changed by the update above
- Map<String, String> newConnObjectKeys = getConnObjectKeys(user, anyUtils);
- oldConnObjectKeys.entrySet().stream().
- filter(entry -> newConnObjectKeys.containsKey(entry.getKey())
- && !entry.getValue().equals(newConnObjectKeys.get(entry.getKey()))).
- forEach(entry -> {
- propByRes.addOldConnObjectKey(entry.getKey(), entry.getValue());
- propByRes.add(ResourceOperation.UPDATE, entry.getKey());
- });
-
- Pair<Set<String>, Set<String>> dynGroupMembs = userDAO.saveAndGetDynGroupMembs(user);
-
- // finally check if any resource assignment is to be processed due to dynamic group membership change
- dynGroupMembs.getLeft().stream().
- filter(group -> !dynGroupMembs.getRight().contains(group)).
- forEach(delete -> groupDAO.find(delete).getResources().stream().
- filter(resource -> !propByRes.contains(resource.getKey())).
- forEach(resource -> propByRes.add(ResourceOperation.DELETE, resource.getKey())));
- dynGroupMembs.getLeft().stream().
- filter(group -> dynGroupMembs.getRight().contains(group)).
- forEach(update -> groupDAO.find(update).getResources().stream().
- filter(resource -> !propByRes.contains(resource.getKey())).
- forEach(resource -> propByRes.add(ResourceOperation.UPDATE, resource.getKey())));
- dynGroupMembs.getRight().stream().
- filter(group -> !dynGroupMembs.getLeft().contains(group)).
- forEach(create -> groupDAO.find(create).getResources().stream().
- filter(resource -> !propByRes.contains(resource.getKey())).
- forEach(resource -> propByRes.add(ResourceOperation.CREATE, resource.getKey())));
-
// Throw composite exception if there is at least one element set in the composing exceptions
if (scce.hasExceptions()) {
throw scce;
@@ -727,13 +653,23 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
// Re-merge any pending change from above
User saved = userDAO.save(user);
- // ensure not to DELETE on External Resources that remain assigned
- Set<String> assigned = saved.getResources().stream().
- map(ExternalResource::getKey).collect(Collectors.toCollection(HashSet::new));
- assigned.addAll(saved.getMemberships().stream().
- flatMap(m -> m.getRightEnd().getResources().stream()).map(ExternalResource::getKey).
- collect(Collectors.toSet()));
- propByRes.removeAll(ResourceOperation.DELETE, assigned);
+ // Build final information for next stage (propagation)
+ Map<String, ConnObjectTO> afterOnResources =
+ onResources(user, userDAO.findAllResourceKeys(user.getKey()), password, changePwd);
+ propByRes.merge(propByRes(beforeOnResources, afterOnResources));
+
+ if (userUR.getMustChangePassword() != null) {
+ user.setMustChangePassword(userUR.getMustChangePassword().getValue());
+
+ propByRes.addAll(
+ ResourceOperation.UPDATE,
+ anyUtils.getAllResources(saved).stream().
+ map(resource -> resource.getProvision(saved.getType())).
+ filter(Optional::isPresent).map(Optional::get).
+ filter(mappingManager::hasMustChangePassword).
+ map(provision -> provision.getResource().getKey()).
+ collect(Collectors.toSet()));
+ }
return Pair.of(propByRes, propByLinkedAccount);
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index bc88e91..23b3b4c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -26,9 +26,9 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
-import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.to.ExecTO;
import org.apache.syncope.common.lib.types.AuditElements;
@@ -234,9 +234,9 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
collect(Collectors.toMap(attr -> attr.getName().toUpperCase(), Function.identity()));
// Only compare attribute from beforeObj that are also being updated
- Set<String> skipAttrNames = originalAttrMap.keySet();
+ Set<String> skipAttrNames = new HashSet<>(originalAttrMap.keySet());
skipAttrNames.removeAll(updateAttrMap.keySet());
- new HashSet<>(skipAttrNames).forEach(originalAttrMap::remove);
+ skipAttrNames.forEach(originalAttrMap::remove);
Set<Attribute> originalAttrs = new HashSet<>(originalAttrMap.values());
@@ -246,8 +246,8 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
} else {
LOG.debug("Attributes that would be updated {}", attributes);
- Set<Attribute> strictlyModified = new HashSet<>();
- attributes.stream().filter(attr -> (!originalAttrs.contains(attr))).forEach(strictlyModified::add);
+ Set<Attribute> strictlyModified =
+ attributes.stream().filter(attr -> !originalAttrs.contains(attr)).collect(Collectors.toSet());
// 3. provision entry
LOG.debug("Update {} on {}", strictlyModified, task.getResource().getKey());
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
index 7cf873b..ac13163 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
@@ -20,6 +20,7 @@ package org.apache.syncope.fit.core;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -49,6 +50,7 @@ import org.apache.syncope.common.lib.to.TaskTO;
import org.apache.syncope.common.lib.to.AnyObjectTO;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.request.GroupCR;
+import org.apache.syncope.common.lib.request.GroupUR;
import org.apache.syncope.common.lib.request.MembershipUR;
import org.apache.syncope.common.lib.request.ResourceDR;
import org.apache.syncope.common.lib.to.ConnObjectTO;
@@ -69,9 +71,11 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ImplementationEngine;
import org.apache.syncope.common.lib.types.AttrSchemaType;
import org.apache.syncope.common.lib.types.IdRepoImplementationType;
+import org.apache.syncope.common.lib.types.ExecStatus;
import org.apache.syncope.common.lib.types.MappingPurpose;
import org.apache.syncope.common.lib.types.PatchOperation;
import org.apache.syncope.common.lib.types.ResourceDeassociationAction;
+import org.apache.syncope.common.lib.types.ResourceOperation;
import org.apache.syncope.common.lib.types.SchemaType;
import org.apache.syncope.common.lib.types.TaskType;
import org.apache.syncope.common.rest.api.RESTHeaders;
@@ -84,6 +88,7 @@ import org.apache.syncope.fit.core.reference.DateToDateItemTransformer;
import org.apache.syncope.fit.core.reference.DateToLongItemTransformer;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.AttributeUtil;
+import org.identityconnectors.framework.common.objects.Name;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeAll;
@@ -632,4 +637,80 @@ public class PropagationTaskITCase extends AbstractTaskITCase {
}
}
}
+
+ @Test
+ public void issueSYNCOPE1605() throws ParseException {
+ ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
+ try {
+ // 1. clone the LDAP resource and add some sensible mappings
+ ProvisionTO provisionGroup =
+ SerializationUtils.clone(ldap.getProvision(AnyTypeKind.GROUP.name()).orElse(null));
+ assertNotNull(provisionGroup);
+ provisionGroup.getVirSchemas().clear();
+ provisionGroup.getMapping().getItems().clear();
+
+ ItemTO item = new ItemTO();
+ item.setConnObjectKey(true);
+ item.setIntAttrName("name");
+ item.setExtAttrName("description");
+ item.setPurpose(MappingPurpose.BOTH);
+
+ provisionGroup.getMapping().setConnObjectKeyItem(item);
+ provisionGroup.getMapping().setConnObjectLink("'cn=' + originalName + ',ou=groups,o=isp'");
+
+ ldap.getProvisions().clear();
+ ldap.getProvisions().add(provisionGroup);
+
+ ldap.setKey(RESOURCE_NAME_LDAP + "1605" + getUUIDString());
+ resourceService.create(ldap);
+
+ // 1. create group with the new resource assigned
+ String originalName = "grp1605-" + getUUIDString();
+
+ GroupCR groupCR = new GroupCR();
+ groupCR.setName("SYNCOPEGROUP1605-" + getUUIDString());
+ groupCR.setRealm("/");
+ groupCR.getResources().add(ldap.getKey());
+ groupCR.getPlainAttrs().add(new Attr.Builder("originalName").value(originalName).build());
+
+ GroupTO groupTO = createGroup(groupCR).getEntity();
+ assertNotNull(groupTO);
+
+ // 3. check attributes prepared for propagation
+ PagedResult<PropagationTaskTO> tasks = taskService.search(new TaskQuery.Builder(TaskType.PROPAGATION).
+ resource(groupTO.getResources().iterator().next()).
+ anyTypeKind(AnyTypeKind.GROUP).entityKey(groupTO.getKey()).build());
+ assertEquals(1, tasks.getSize());
+ assertEquals(ResourceOperation.CREATE, tasks.getResult().get(0).getOperation());
+ assertEquals(ExecStatus.SUCCESS.name(), tasks.getResult().get(0).getLatestExecStatus());
+
+ ConnObjectTO beforeConnObject =
+ resourceService.readConnObject(ldap.getKey(), AnyTypeKind.GROUP.name(), groupTO.getKey());
+
+ GroupUR groupUR = new GroupUR();
+ groupUR.setKey(groupTO.getKey());
+
+ groupUR.getPlainAttrs().add(attrAddReplacePatch("originalName", "new" + originalName));
+ groupTO = updateGroup(groupUR).getEntity();
+
+ tasks = taskService.search(new TaskQuery.Builder(TaskType.PROPAGATION).
+ resource(groupTO.getResources().iterator().next()).
+ anyTypeKind(AnyTypeKind.GROUP).entityKey(groupTO.getKey()).orderBy("start DESC").build());
+ assertEquals(2, tasks.getSize());
+ assertEquals(ResourceOperation.UPDATE, tasks.getResult().get(0).getOperation());
+ assertEquals(ExecStatus.SUCCESS.name(), tasks.getResult().get(0).getLatestExecStatus());
+
+ ConnObjectTO afterConnObject =
+ resourceService.readConnObject(ldap.getKey(), AnyTypeKind.GROUP.name(), groupTO.getKey());
+ assertNotEquals(afterConnObject.getAttr(Name.NAME).get().getValues().get(0),
+ beforeConnObject.getAttr(Name.NAME).get().getValues().get(0));
+ assertTrue(afterConnObject.getAttr(Name.NAME).get().getValues().get(0).contains("new" + originalName));
+ } finally {
+ try {
+ resourceService.delete(ldap.getKey());
+ } catch (Exception ignore) {
+ // ignore
+ }
+ }
+ }
}
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 5ca90bd..22541e9 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
@@ -604,7 +604,7 @@ public class UserIssuesITCase extends AbstractITCase {
ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
ldap.getProvision(AnyTypeKind.GROUP.name()).get().getMapping().getItems().stream().
filter(item -> ("description".equals(item.getExtAttrName()))).
- forEachOrdered(item -> item.setExtAttrName("uniqueMember"));
+ forEach(item -> item.setExtAttrName("uniqueMember"));
resourceService.update(ldap);
// 1. create group with LDAP resource
@@ -654,8 +654,8 @@ public class UserIssuesITCase extends AbstractITCase {
// 7. restore original resource-ldap group mapping
ldap.getProvision(AnyTypeKind.GROUP.name()).get().getMapping().getItems().stream().
- filter(item -> ("uniqueMember".equals(item.getExtAttrName()))).
- forEachOrdered(item -> item.setExtAttrName("description"));
+ filter(item -> "uniqueMember".equals(item.getExtAttrName())).
+ forEach(item -> item.setExtAttrName("description"));
resourceService.update(ldap);
}