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
The following commit(s) were added to refs/heads/master by this push:
new bb7f727dc7 [SYNCOPE-1957] Process auxClasses changes before attributes
during update
bb7f727dc7 is described below
commit bb7f727dc7effa80c186c51b754bd12ad654bafa
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Fri Mar 27 18:26:07 2026 +0100
[SYNCOPE-1957] Process auxClasses changes before attributes during update
---
.../core/provisioning/java/data/AnyDataBinder.java | 44 ++++----
.../java/data/AnyObjectDataBinderImpl.java | 2 +
.../java/data/GroupDataBinderImpl.java | 2 +
.../provisioning/java/data/UserDataBinderImpl.java | 2 +
.../core/flowable/FlowableWorkflowContext.java | 4 +-
.../apache/syncope/core/flowable/task/Update.java | 8 +-
.../apache/syncope/fit/core/AnyObjectITCase.java | 113 +++++++++++++++++++++
7 files changed, 144 insertions(+), 31 deletions(-)
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 328cb9b564..fd83a10bde 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
@@ -395,7 +395,25 @@ abstract class AnyDataBinder extends
AttributableDataBinder {
}
}
- @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected void fillAuxClasses(final Relatable<?, ?> any, final AnyUR
anyUR) {
+ for (StringPatchItem patch : anyUR.getAuxClasses()) {
+ anyTypeClassDAO.findById(patch.getValue()).ifPresentOrElse(
+ auxClass -> {
+ switch (patch.getOperation()) {
+ case ADD_REPLACE:
+ any.add(auxClass);
+ break;
+
+ case DELETE:
+ default:
+ any.getAuxClasses().remove(auxClass);
+ }
+ },
+ () -> LOG.debug("Invalid {} {}, ignoring...",
+ AnyTypeClass.class.getSimpleName(),
patch.getValue()));
+ }
+ }
+
protected void fill(
final AnyTO anyTO,
final Relatable<?, ?> any,
@@ -444,25 +462,7 @@ abstract class AnyDataBinder extends
AttributableDataBinder {
}
propByRes.merge(managerPropByRes);
- // 1. anyTypeClasses
- for (StringPatchItem patch : anyUR.getAuxClasses()) {
- anyTypeClassDAO.findById(patch.getValue()).ifPresentOrElse(
- auxClass -> {
- switch (patch.getOperation()) {
- case ADD_REPLACE:
- any.add(auxClass);
- break;
-
- case DELETE:
- default:
- any.getAuxClasses().remove(auxClass);
- }
- },
- () -> LOG.debug("Invalid {} {}, ignoring...",
- AnyTypeClass.class.getSimpleName(),
patch.getValue()));
- }
-
- // 2. relationships
+ // 1. relationships
Set<Pair<String, String>> relationships = new HashSet<>();
for (RelationshipUR patch : anyUR.getRelationships().stream().
filter(patch -> patch.getType() != null &&
patch.getOtherEndKey() != null).toList()) {
@@ -522,7 +522,7 @@ abstract class AnyDataBinder extends AttributableDataBinder
{
}
}
- // 3. resources
+ // 2. resources
for (StringPatchItem patch : anyUR.getResources()) {
resourceDAO.findById(patch.getValue()).ifPresentOrElse(
resource -> {
@@ -543,7 +543,7 @@ abstract class AnyDataBinder extends AttributableDataBinder
{
Set<ExternalResource> resources = anyUtils.getAllResources(any);
SyncopeClientException invalidValues =
SyncopeClientException.build(ClientExceptionType.InvalidValues);
- // 4. attributes
+ // 3. attributes
anyUR.getPlainAttrs().stream().filter(patch -> patch.getAttr() !=
null).
forEach(patch ->
getPlainSchema(patch.getAttr().getSchema()).ifPresentOrElse(
schema -> {
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 5b9aa4a478..a6215ca8dd 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
@@ -203,6 +203,8 @@ public class AnyObjectDataBinderImpl extends AnyDataBinder
implements AnyObjectD
@Override
public PropagationByResource<String> update(final AnyObject toBeUpdated,
final AnyObjectUR anyObjectUR) {
+ fillAuxClasses(toBeUpdated, anyObjectUR);
+
// Re-merge any pending change from workflow tasks
AnyObject anyObject = anyObjectDAO.save(toBeUpdated);
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 86fb42b166..f7d4c72187 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
@@ -169,6 +169,8 @@ public class GroupDataBinderImpl extends AnyDataBinder
implements GroupDataBinde
@Override
public PropagationByResource<String> update(final Group toBeUpdated, final
GroupUR groupUR) {
+ fillAuxClasses(toBeUpdated, groupUR);
+
// Re-merge any pending change from workflow tasks
Group group = groupDAO.save(toBeUpdated);
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 25f958512d..92ea5968df 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
@@ -342,6 +342,8 @@ public class UserDataBinderImpl extends AnyDataBinder
implements UserDataBinder
@Override
public UserWorkflowResult.PropagationInfo update(final User toBeUpdated,
final UserUR userUR) {
+ fillAuxClasses(toBeUpdated, userUR);
+
// Re-merge any pending change from workflow tasks
User user = userDAO.save(toBeUpdated);
diff --git
a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/FlowableWorkflowContext.java
b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/FlowableWorkflowContext.java
index b821143b37..1b1a643cfa 100644
---
a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/FlowableWorkflowContext.java
+++
b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/FlowableWorkflowContext.java
@@ -248,7 +248,7 @@ public class FlowableWorkflowContext {
@ConditionalOnMissingBean
@Bean
- public Update update(final UserDataBinder userDataBinder, final UserDAO
userDAO) {
- return new Update(userDataBinder, userDAO);
+ public Update update(final UserDataBinder userDataBinder) {
+ return new Update(userDataBinder);
}
}
diff --git
a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/task/Update.java
b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/task/Update.java
index 539d12b49b..2fda104efb 100644
---
a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/task/Update.java
+++
b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/task/Update.java
@@ -20,7 +20,6 @@ package org.apache.syncope.core.flowable.task;
import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.core.flowable.impl.FlowableRuntimeUtils;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.api.UserWorkflowResult;
import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
@@ -30,11 +29,8 @@ public class Update extends FlowableServiceTask {
protected final UserDataBinder dataBinder;
- protected final UserDAO userDAO;
-
- public Update(final UserDataBinder dataBinder, final UserDAO userDAO) {
+ public Update(final UserDataBinder dataBinder) {
this.dataBinder = dataBinder;
- this.userDAO = userDAO;
}
@Override
@@ -45,8 +41,6 @@ public class Update extends FlowableServiceTask {
} else {
User user = execution.getVariable(FlowableRuntimeUtils.USER,
User.class);
- user = userDAO.save(user);
-
UserWorkflowResult.PropagationInfo propInfo =
dataBinder.update(user, req);
// report updated user and propagation by resource as result
diff --git
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
index d011ca8189..4b7494d916 100644
---
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
+++
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AnyObjectITCase.java
@@ -25,22 +25,37 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import jakarta.ws.rs.core.Response;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
+import java.util.function.Consumer;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.request.AnyCR;
import org.apache.syncope.common.lib.request.AnyObjectCR;
import org.apache.syncope.common.lib.request.AnyObjectUR;
+import org.apache.syncope.common.lib.request.AnyUR;
+import org.apache.syncope.common.lib.request.GroupCR;
+import org.apache.syncope.common.lib.request.GroupUR;
import org.apache.syncope.common.lib.request.StringPatchItem;
+import org.apache.syncope.common.lib.request.UserCR;
+import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.AnyTypeClassTO;
import org.apache.syncope.common.lib.to.ConnObject;
+import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.PlainSchemaTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.types.PatchOperation;
import org.apache.syncope.common.lib.types.SchemaType;
import org.apache.syncope.common.rest.api.beans.AnyQuery;
+import org.apache.syncope.common.rest.api.service.AnyTypeClassService;
import org.apache.syncope.fit.AbstractITCase;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -211,4 +226,102 @@ public class AnyObjectITCase extends AbstractITCase {
assertFalse(printer.getResources().contains(RESOURCE_NAME_DBSCRIPTED),
"Should not contain removed resources");
assertFalse(printer.getAuxClasses().contains("csv"), "Should not
contain removed auxiliary classes");
}
+
+ @Test
+ public void issueSYNCOPE1957() {
+ // prepare
+ PlainSchemaTO schema1 = new PlainSchemaTO();
+ schema1.setKey("schema1" + getUUIDString());
+ schema1.setType(AttrSchemaType.Boolean);
+ createSchema(SchemaType.PLAIN, schema1);
+
+ PlainSchemaTO schema2 = new PlainSchemaTO();
+ schema2.setKey("schema2" + getUUIDString());
+ schema2.setType(AttrSchemaType.Boolean);
+ createSchema(SchemaType.PLAIN, schema2);
+
+ String class1Key = "class1" + getUUIDString();
+ AnyTypeClassTO class1 = new AnyTypeClassTO();
+ class1.setKey(class1Key);
+ class1.getPlainSchemas().add(schema1.getKey());
+ class1.getPlainSchemas().add(schema2.getKey());
+
+ Response response = ANY_TYPE_CLASS_SERVICE.create(class1);
+ assertEquals(Response.Status.CREATED.getStatusCode(),
response.getStatusInfo().getStatusCode());
+
+ class1 = getObject(response.getLocation(), AnyTypeClassService.class,
AnyTypeClassTO.class);
+
+ // 1. create user, group and printer with auxClass class1
+ Consumer<AnyCR> setupAnyCR = anyCR -> {
+ anyCR.getResources().clear();
+ anyCR.getAuxClasses().add(class1Key);
+ anyCR.getPlainAttrs().add(attr(schema1.getKey(), "true"));
+ anyCR.getPlainAttrs().add(attr(schema2.getKey(), "true"));
+ };
+ Consumer<AnyTO> checkAnyTO = anyTO -> {
+ assertTrue(anyTO.getPlainAttr(schema1.getKey()).isPresent());
+ assertTrue(anyTO.getPlainAttr(schema2.getKey()).isPresent());
+ };
+
+ UserCR userCR = UserITCase.getUniqueSample("[email protected]");
+ setupAnyCR.accept(userCR);
+
+ UserTO user = createUser(userCR).getEntity();
+ checkAnyTO.accept(user);
+
+ GroupCR groupCR = GroupITCase.getSample("syncope1957");
+ setupAnyCR.accept(groupCR);
+
+ GroupTO group = createGroup(groupCR).getEntity();
+ checkAnyTO.accept(group);
+
+ AnyObjectCR printerCR = getSample("syncope1957");
+ setupAnyCR.accept(printerCR);
+
+ AnyObjectTO printer = createAnyObject(printerCR).getEntity();
+ checkAnyTO.accept(printer);
+
+ // 2. create new anytypeclass and move schema there
+ class1.getPlainSchemas().remove(schema2.getKey());
+ ANY_TYPE_CLASS_SERVICE.update(class1);
+
+ class1 = ANY_TYPE_CLASS_SERVICE.read(class1.getKey());
+ assertEquals(List.of(schema1.getKey()), class1.getPlainSchemas());
+
+ String class2Key = "class2" + getUUIDString();
+ AnyTypeClassTO class2 = new AnyTypeClassTO();
+ class2.setKey(class2Key);
+ class2.getPlainSchemas().add(schema2.getKey());
+
+ response = ANY_TYPE_CLASS_SERVICE.create(class2);
+ assertEquals(Response.Status.CREATED.getStatusCode(),
response.getStatusInfo().getStatusCode());
+
+ class2 = getObject(response.getLocation(), AnyTypeClassService.class,
AnyTypeClassTO.class);
+ assertEquals(List.of(schema2.getKey()), class2.getPlainSchemas());
+
+ // 3. update user, group and printer by adding auxClass class2
+ Consumer<AnyUR> setupAnyUR = anyUR -> anyUR.getAuxClasses().
+ add(new StringPatchItem.Builder().value(class2Key).build());
+
+ UserUR userUR = new UserUR();
+ userUR.setKey(user.getKey());
+ setupAnyUR.accept(userUR);
+
+ user = updateUser(userUR).getEntity();
+ checkAnyTO.accept(user);
+
+ GroupUR groupUR = new GroupUR();
+ groupUR.setKey(group.getKey());
+ setupAnyUR.accept(groupUR);
+
+ group = updateGroup(groupUR).getEntity();
+ checkAnyTO.accept(group);
+
+ AnyObjectUR printerUR = new AnyObjectUR();
+ printerUR.setKey(printer.getKey());
+ setupAnyUR.accept(printerUR);
+
+ printer = updateAnyObject(printerUR).getEntity();
+ checkAnyTO.accept(printer);
+ }
}