This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch 4_0_X
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/4_0_X by this push:
new 95fa928ffe [SYNCOPE-1944] Fixing derived attribute management for
memberships
95fa928ffe is described below
commit 95fa928ffe08e016c1b9b566770093ff6f49a852
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Thu Jan 15 16:38:38 2026 +0100
[SYNCOPE-1944] Fixing derived attribute management for memberships
---
.../core/provisioning/api/DerAttrHandler.java | 44 ++++++-------
.../provisioning/api/jexl/JexlContextBuilder.java | 5 +-
.../core/provisioning/api/jexl/JexlUtilsTest.java | 10 +--
.../provisioning/java/DefaultDerAttrHandler.java | 75 +++++++++++-----------
.../provisioning/java/DefaultMappingManager.java | 8 ++-
.../core/provisioning/java/data/AnyDataBinder.java | 9 ++-
.../java/data/RealmDataBinderImpl.java | 2 +-
.../java/DefaultMappingManagerTest.java | 9 ++-
.../apache/syncope/fit/core/MembershipITCase.java | 27 ++++++++
9 files changed, 110 insertions(+), 79 deletions(-)
diff --git
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/DerAttrHandler.java
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/DerAttrHandler.java
index f9863946a9..c1f82867a8 100644
---
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/DerAttrHandler.java
+++
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/DerAttrHandler.java
@@ -28,30 +28,21 @@ import org.apache.syncope.core.persistence.api.entity.Realm;
public interface DerAttrHandler {
/**
- * Calculates derived attribute value associated to the given realm, for
the given derived schema.
+ * Calculates derived attributes values associated to the given realm.
*
* @param realm realm
- * @param schema derived schema
- * @return derived attribute value
+ * @return derived attribute values
*/
- String getValue(Realm realm, DerSchema schema);
+ Map<String, String> getValues(Realm realm);
/**
- * Calculates derived attribute value associated to the given any, for the
given derived schema.
+ * Calculates derived attribute value associated to the given realm, for
the given derived schema.
*
- * @param any any object
+ * @param realm realm
* @param schema derived schema
* @return derived attribute value
*/
- String getValue(Any any, DerSchema schema);
-
- /**
- * Calculates derived attributes values associated to the given realm.
- *
- * @param realm realm
- * @return derived attribute values
- */
- Map<DerSchema, String> getValues(Realm realm);
+ String getValue(Realm realm, DerSchema schema);
/**
* Calculates derived attributes values associated to the given any.
@@ -59,25 +50,34 @@ public interface DerAttrHandler {
* @param any any object
* @return derived attribute values
*/
- Map<DerSchema, String> getValues(Any any);
+ Map<String, String> getValues(Any any);
/**
- * Calculates derived attribute value associated to the given any, for the
given membership and
- * derived schema.
+ * Calculates derived attribute value associated to the given any, for the
given derived schema.
*
* @param any any object
- * @param membership membership
* @param schema derived schema
* @return derived attribute value
*/
- String getValue(Any any, Membership<?> membership, DerSchema schema);
+ String getValue(Any any, DerSchema schema);
/**
* Calculates derived attributes values associated to the given any, for
the given membership.
*
- * @param any any object
+ * @param groupable user or any object
* @param membership membership
* @return derived attribute values
*/
- Map<DerSchema, String> getValues(Groupable<?, ?, ?> any, Membership<?>
membership);
+ Map<String, String> getValues(Groupable<?, ?, ?> groupable, Membership<?>
membership);
+
+ /**
+ * Calculates derived attribute value associated to the given any, for the
given membership and
+ * derived schema.
+ *
+ * @param groupable user or any object
+ * @param membership membership
+ * @param schema derived schema
+ * @return derived attribute value
+ */
+ String getValue(Groupable<?, ?, ?> groupable, Membership<?> membership,
DerSchema schema);
}
diff --git
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlContextBuilder.java
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlContextBuilder.java
index 269ec784e8..bed2130497 100644
---
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlContextBuilder.java
+++
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlContextBuilder.java
@@ -41,7 +41,6 @@ import org.apache.syncope.common.lib.to.AnyTO;
import org.apache.syncope.common.lib.to.RealmTO;
import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.Attributable;
-import org.apache.syncope.core.persistence.api.entity.DerSchema;
import org.apache.syncope.core.persistence.api.entity.PlainAttr;
import org.apache.syncope.core.persistence.api.entity.Realm;
import org.apache.syncope.core.persistence.api.utils.FormatUtils;
@@ -187,13 +186,13 @@ public class JexlContextBuilder {
}
public JexlContextBuilder derAttrs(final Attributable attributable, final
DerAttrHandler derAttrHandler) {
- Map<DerSchema, String> derAttrs = attributable instanceof Realm realm
+ Map<String, String> derAttrs = attributable instanceof Realm realm
? derAttrHandler.getValues(realm)
: attributable instanceof Any any
? derAttrHandler.getValues(any)
: Map.of();
- derAttrs.forEach((schema, value) -> jexlContext.set(schema.getKey(),
value));
+ derAttrs.forEach((schema, value) -> jexlContext.set(schema, value));
return this;
}
diff --git
a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java
b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java
index 5ccaad0c27..39cae5985f 100644
---
a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java
+++
b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java
@@ -147,8 +147,8 @@ public class JexlUtilsTest extends AbstractTest {
String expression = null;
- Map<DerSchema, String> derAttrs = new HashMap<>();
- derAttrs.put(derSchema, expression);
+ Map<String, String> derAttrs = new HashMap<>();
+ derAttrs.put("derSchema", expression);
when(derAttrHandler.getValues(any(Any.class))).thenReturn(derAttrs);
@@ -156,7 +156,7 @@ public class JexlUtilsTest extends AbstractTest {
ReflectionTestUtils.setField(builder, "jexlContext", context);
builder.derAttrs(any, derAttrHandler);
- verify(context).set(derAttrs.get(derSchema), expression);
+ verify(context).set("derSchema", expression);
}
@Test
@@ -168,8 +168,8 @@ public class JexlUtilsTest extends AbstractTest {
String expression = null;
- Map<DerSchema, String> derAttrs = new HashMap<>();
- derAttrs.put(derSchema, expression);
+ Map<String, String> derAttrs = new HashMap<>();
+ derAttrs.put("derSchema", expression);
when(any.getPlainAttrs()).thenReturn(new ArrayList<>());
when(derAttrHandler.getValues(any(Any.class))).thenReturn(derAttrs);
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java
index edd822dc7a..5497ceb3a7 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java
@@ -52,11 +52,11 @@ public class DefaultDerAttrHandler implements
DerAttrHandler {
this.jexlTools = jexlTools;
}
- protected Map<DerSchema, String> getValues(
+ protected Map<String, String> getValues(
final Attributable attributable,
final Collection<? extends DerSchema> schemas) {
- Map<DerSchema, String> result = new HashMap<>(schemas.size());
+ Map<String, String> result = new HashMap<>(schemas.size());
schemas.forEach(schema -> {
JexlContext jexlContext = new JexlContextBuilder().
@@ -64,12 +64,20 @@ public class DefaultDerAttrHandler implements
DerAttrHandler {
fields(attributable).
build();
- result.put(schema,
jexlTools.evaluateExpression(schema.getExpression(), jexlContext).toString());
+ result.put(schema.getKey(),
jexlTools.evaluateExpression(schema.getExpression(), jexlContext).toString());
});
return result;
}
+ @Override
+ public Map<String, String> getValues(final Realm realm) {
+ return getValues(
+ realm,
+ realm.getAnyTypeClasses().stream().
+ flatMap(atc ->
atc.getDerSchemas().stream()).collect(Collectors.toSet()));
+ }
+
@Override
public String getValue(final Realm realm, final DerSchema schema) {
if (realm.getAnyTypeClasses().stream().flatMap(atc ->
atc.getDerSchemas().stream()).anyMatch(schema::equals)) {
@@ -77,50 +85,32 @@ public class DefaultDerAttrHandler implements
DerAttrHandler {
return null;
}
- return getValues(realm, Set.of(schema)).get(schema);
+ return getValues(realm, Set.of(schema)).get(schema.getKey());
}
@Override
- public String getValue(final Any any, final DerSchema schema) {
- if (!anyUtilsFactory.getInstance(any).dao().findAllowedSchemas(any,
DerSchema.class).forSelfContains(schema)) {
- LOG.debug("{} not allowed for {}", schema, any);
- return null;
- }
-
- return getValues(any, Set.of(schema)).get(schema);
+ public Map<String, String> getValues(final Any any) {
+ return getValues(
+ any,
+ anyUtilsFactory.getInstance(any).dao().findAllowedSchemas(any,
DerSchema.class).getForSelf());
}
@Override
- public String getValue(final Any any, final Membership<?> membership,
final DerSchema schema) {
- if (!anyUtilsFactory.getInstance(any).dao().
- findAllowedSchemas(any,
DerSchema.class).getForMembership(membership.getRightEnd()).contains(schema)) {
-
+ public String getValue(final Any any, final DerSchema schema) {
+ if (!anyUtilsFactory.getInstance(any).dao().findAllowedSchemas(any,
DerSchema.class).forSelfContains(schema)) {
LOG.debug("{} not allowed for {}", schema, any);
return null;
}
- return getValues(any, Set.of(schema)).get(schema);
+ return getValues(any, Set.of(schema)).get(schema.getKey());
}
- @Override
- public Map<DerSchema, String> getValues(final Realm realm) {
- return getValues(
- realm,
- realm.getAnyTypeClasses().stream().
- flatMap(atc ->
atc.getDerSchemas().stream()).collect(Collectors.toSet()));
- }
+ protected Map<String, String> getValues(
+ final Groupable<?, ?, ?> groupable,
+ final Membership<?> membership,
+ final Set<DerSchema> schemas) {
- @Override
- public Map<DerSchema, String> getValues(final Any any) {
- return getValues(
- any,
- anyUtilsFactory.getInstance(any).dao().findAllowedSchemas(any,
DerSchema.class).getForSelf());
- }
-
- protected Map<DerSchema, String> getValues(
- final Groupable<?, ?, ?> groupable, final Membership<?>
membership, final Set<DerSchema> schemas) {
-
- Map<DerSchema, String> result = new HashMap<>(schemas.size());
+ Map<String, String> result = new HashMap<>(schemas.size());
schemas.forEach(schema -> {
JexlContext jexlContext = new JexlContextBuilder().
@@ -128,18 +118,31 @@ public class DefaultDerAttrHandler implements
DerAttrHandler {
fields(groupable).
build();
- result.put(schema,
jexlTools.evaluateExpression(schema.getExpression(), jexlContext).toString());
+ result.put(schema.getKey(),
jexlTools.evaluateExpression(schema.getExpression(), jexlContext).toString());
});
return result;
}
@Override
- public Map<DerSchema, String> getValues(final Groupable<?, ?, ?> any,
final Membership<?> membership) {
+ public Map<String, String> getValues(final Groupable<?, ?, ?> any, final
Membership<?> membership) {
return getValues(
any,
membership,
anyUtilsFactory.getInstance(any).dao().
findAllowedSchemas(any,
DerSchema.class).getForMembership(membership.getRightEnd()));
}
+
+ @Override
+ public String getValue(final Groupable<?, ?, ?> groupable, final
Membership<?> membership, final DerSchema schema) {
+ if (!anyUtilsFactory.getInstance(groupable).dao().
+ findAllowedSchemas(groupable, DerSchema.class).
+ getForMembership(membership.getRightEnd()).contains(schema)) {
+
+ LOG.debug("{} not allowed for {}", schema, groupable);
+ return null;
+ }
+
+ return getValues(groupable, membership,
Set.of(schema)).get(schema.getKey());
+ }
}
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
index 76b88c6baf..33b3b878ea 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
@@ -278,7 +278,11 @@ public class DefaultMappingManager implements
MappingManager {
String connObjectLink = orgUnit.getConnObjectLink();
String evalConnObjectLink = null;
if (StringUtils.isNotBlank(connObjectLink)) {
- JexlContext jexlContext = new
JexlContextBuilder().fields(realm).build();
+ JexlContext jexlContext = new JexlContextBuilder().
+ fields(realm).
+ plainAttrs(realm.getPlainAttrs()).
+ derAttrs(realm, derAttrHandler).
+ build();
evalConnObjectLink = jexlTools.evaluateExpression(connObjectLink,
jexlContext).toString();
}
@@ -848,7 +852,7 @@ public class DefaultMappingManager implements
MappingManager {
DerSchema derSchema = (DerSchema)
intAttrName.getSchema();
String derValue = membership == null
? derAttrHandler.getValue(ref, derSchema)
- : derAttrHandler.getValue(ref, membership,
derSchema);
+ : derAttrHandler.getValue((Groupable<?, ?, ?>)
ref, membership, derSchema);
if (derValue != null) {
PlainAttrValue attrValue = new PlainAttrValue();
attrValue.setStringValue(derValue);
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyDataBinder.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyDataBinder.java
index c14fcec373..dfe01d7f4f 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyDataBinder.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyDataBinder.java
@@ -63,7 +63,6 @@ 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.AnyUtilsFactory;
-import org.apache.syncope.core.persistence.api.entity.DerSchema;
import org.apache.syncope.core.persistence.api.entity.EntityFactory;
import org.apache.syncope.core.persistence.api.entity.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.Groupable;
@@ -97,7 +96,7 @@ abstract class AnyDataBinder extends AttributableDataBinder {
final String realmFullPath,
final Collection<? extends AnyTypeClass> auxClasses,
final Collection<PlainAttr> plainAttrs,
- final Map<DerSchema, String> derAttrs,
+ final Map<String, String> derAttrs,
final Collection<? extends ExternalResource> resources) {
anyTO.setRealm(realmFullPath);
@@ -108,7 +107,7 @@ abstract class AnyDataBinder extends AttributableDataBinder
{
add(new
Attr.Builder(plainAttr.getSchema()).values(plainAttr.getValuesAsStrings()).build()));
derAttrs.forEach((schema, value) -> anyTO.getDerAttrs().
- add(new Attr.Builder(schema.getKey()).value(value).build()));
+ add(new Attr.Builder(schema).value(value).build()));
anyTO.getResources().addAll(resources.stream().map(ExternalResource::getKey).collect(Collectors.toSet()));
}
@@ -131,7 +130,7 @@ abstract class AnyDataBinder extends AttributableDataBinder
{
protected static MembershipTO getMembershipTO(
final Collection<PlainAttr> plainAttrs,
- final Map<DerSchema, String> derAttrs,
+ final Map<String, String> derAttrs,
final Membership<? extends Any> membership) {
MembershipTO membershipTO = new
MembershipTO.Builder(membership.getRightEnd().getKey()).
@@ -141,7 +140,7 @@ abstract class AnyDataBinder extends AttributableDataBinder
{
add(new
Attr.Builder(plainAttr.getSchema()).values(plainAttr.getValuesAsStrings()).build()));
derAttrs.forEach((schema, value) -> membershipTO.getDerAttrs().
- add(new Attr.Builder(schema.getKey()).value(value).build()));
+ add(new Attr.Builder(schema).value(value).build()));
return membershipTO;
}
diff --git
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
index ab9572e14c..aa99277b7a 100644
---
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
+++
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
@@ -359,7 +359,7 @@ public class RealmDataBinderImpl extends
AttributableDataBinder implements Realm
add(new
Attr.Builder(plainAttr.getSchema()).values(plainAttr.getValuesAsStrings()).build()));
derAttrHandler.getValues(realm).forEach((schema, value) ->
realmTO.getDerAttrs().
- add(new Attr.Builder(schema.getKey()).value(value).build()));
+ add(new Attr.Builder(schema).value(value).build()));
if (admin) {
Optional.ofNullable(realm.getAccountPolicy()).map(AccountPolicy::getKey).
diff --git
a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DefaultMappingManagerTest.java
b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DefaultMappingManagerTest.java
index 57d692715d..95561f9e71 100644
---
a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DefaultMappingManagerTest.java
+++
b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DefaultMappingManagerTest.java
@@ -83,7 +83,7 @@ public class DefaultMappingManagerTest extends AbstractTest {
public void prepareAttrsForUser() {
User bellini = userDAO.findByUsername("bellini").orElseThrow();
ExternalResource ldap =
resourceDAO.findById("resource-ldap").orElseThrow();
- Provision provision =
ldap.getProvisionByAnyType(AnyTypeKind.USER.name()).get();
+ Provision provision =
ldap.getProvisionByAnyType(AnyTypeKind.USER.name()).orElseThrow();
assertNotEquals(CipherAlgorithm.AES, bellini.getCipherAlgorithm());
@@ -161,7 +161,7 @@ public class DefaultMappingManagerTest extends AbstractTest
{
public void prepareAttrsForLinkedAccount() {
User vivaldi = userDAO.findByUsername("vivaldi").orElseThrow();
ExternalResource ldap =
resourceDAO.findById("resource-ldap").orElseThrow();
- Provision provision =
ldap.getProvisionByAnyType(AnyTypeKind.USER.name()).get();
+ Provision provision =
ldap.getProvisionByAnyType(AnyTypeKind.USER.name()).orElseThrow();
LinkedAccount account = entityFactory.newEntity(LinkedAccount.class);
account.setConnObjectKeyValue("admin");
@@ -264,13 +264,12 @@ public class DefaultMappingManagerTest extends
AbstractTest {
entityManager.flush();
- // 2. verify that dynamic membership is in place
+ // 2. verify that dynamic membership is effective
assertTrue(userDAO.findAllGroupKeys(user).contains(group.getKey()));
// 3. check propagation attrs
ExternalResource csv =
resourceDAO.findById("resource-csv").orElseThrow();
- Provision provision =
csv.getProvisionByAnyType(AnyTypeKind.USER.name()).get();
- assertNotNull(provision);
+ Provision provision =
csv.getProvisionByAnyType(AnyTypeKind.USER.name()).orElseThrow();
MappingManager.PreparedAttrs attrs =
mappingManager.prepareAttrsFromAny(
user,
diff --git
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java
index 02d8256d99..e7c8306a39 100644
---
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java
+++
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MembershipITCase.java
@@ -342,4 +342,31 @@ public class MembershipITCase extends AbstractITCase {
assertEquals(ClientExceptionType.InvalidMembership, e.getType());
}
}
+
+ @Test
+ public void issueSYNCOPE1944() {
+ TypeExtensionTO typeExt = new TypeExtensionTO();
+ typeExt.setAnyType(AnyTypeKind.USER.name());
+ typeExt.getAuxClasses().add("minimal user");
+
+ GroupCR gcr = GroupITCase.getBasicSample("syncope1944");
+ gcr.getTypeExtensions().add(typeExt);
+
+ GroupTO group = createGroup(gcr).getEntity();
+ assertNotNull(group.getKey());
+
+ UserCR ucr =
UserITCase.getUniqueSample("[email protected]");
+ ucr.getMemberships().add(new MembershipTO.Builder(group.getKey()).
+ plainAttr(attr("fullname", ucr.getUsername())).
+ plainAttr(attr("userId", ucr.getUsername())).
+ plainAttr(attr("firstname", "Galileo")).
+ plainAttr(attr("surname", "Galilei")).build());
+
+ UserTO user = createUser(ucr).getEntity();
+ assertNotNull(user.getKey());
+
+ assertEquals(
+ "Galilei, Galileo",
+
user.getMembership(group.getKey()).orElseThrow().getDerAttr("cn").orElseThrow().getValues().getFirst());
+ }
}