This is an automated email from the ASF dual-hosted git repository. andreapatricelli 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 93fc7802b8 [SYNCOPE-1894] adding support to attributes with dot on resource mapping (#1137) 93fc7802b8 is described below commit 93fc7802b81095235bc1dbfbfd6d4572a00a1a93 Author: Andrea Patricelli <andreapatrice...@apache.org> AuthorDate: Thu Jul 17 13:27:26 2025 +0200 [SYNCOPE-1894] adding support to attributes with dot on resource mapping (#1137) --- .../core/provisioning/api/IntAttrNameParser.java | 21 ++++++---- .../provisioning/api/IntAttrNameParserTest.java | 11 ++++- .../apache/syncope/fit/core/UserIssuesITCase.java | 47 ++++++++++++++++++++++ 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java index 745525f2f0..be93020834 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java @@ -22,6 +22,7 @@ import java.text.ParseException; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.SchemaType; @@ -36,23 +37,26 @@ import org.springframework.transaction.annotation.Transactional; @SuppressWarnings({ "squid:S4784", "squid:S3776" }) public class IntAttrNameParser { - protected static final String END_PATTERN = ")\\]\\.(.+)"; + protected static final String END_PATTERN = "\\]\\.(.+)"; protected static final Pattern ENCLOSING_GROUP_PATTERN = Pattern.compile( - "^groups\\[(" + Entity.ID_REGEX + END_PATTERN); + "^groups\\[(" + Entity.ID_REGEX + ")" + END_PATTERN); protected static final Pattern RELATED_USER_PATTERN = Pattern.compile( - "^users\\[(" + Entity.ID_REGEX + END_PATTERN); + "^users\\[(" + Entity.ID_REGEX + ")" + END_PATTERN); protected static final Pattern RELATED_ANY_OBJECT_PATTERN = Pattern.compile( - "^anyObjects\\[(" + Entity.ID_REGEX + END_PATTERN); + "^anyObjects\\[(" + Entity.ID_REGEX + ")" + END_PATTERN); protected static final Pattern MEMBERSHIP_PATTERN = Pattern.compile( - "^memberships\\[(" + Entity.ID_REGEX + END_PATTERN); + "^memberships\\[(" + Entity.ID_REGEX + ")" + END_PATTERN); protected static final Pattern RELATIONSHIP_PATTERN = Pattern.compile( "^relationships\\[(" + Entity.ID_REGEX + ")\\]" - + "\\[(" + Entity.ID_REGEX + END_PATTERN); + + "\\[(" + Entity.ID_REGEX + ")" + END_PATTERN); + + protected static final CharSequence[] RESERVED_WORDS = + { "groups", "users", "anyObjects", "memberships", "relationships" }; protected final PlainSchemaDAO plainSchemaDAO; @@ -103,11 +107,12 @@ public class IntAttrNameParser { public IntAttrName parse(final String intAttrName, final AnyTypeKind provisionAnyTypeKind) throws ParseException { IntAttrName result = new IntAttrName(); - if (intAttrName.indexOf('.') == -1) { + Matcher matcher = Pattern.compile(END_PATTERN).matcher(intAttrName); + if (!matcher.matches() && !StringUtils.containsAny(intAttrName, RESERVED_WORDS)) { result.setAnyTypeKind(provisionAnyTypeKind); setFieldOrSchemaName(intAttrName, result.getAnyTypeKind(), result); } else { - Matcher matcher = ENCLOSING_GROUP_PATTERN.matcher(intAttrName); + matcher = ENCLOSING_GROUP_PATTERN.matcher(intAttrName); if (matcher.matches()) { result.setAnyTypeKind(AnyTypeKind.GROUP); result.setEnclosingGroup(matcher.group(1)); diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java index 5823218fb3..fd7569e071 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java @@ -105,7 +105,7 @@ public class IntAttrNameParserTest extends AbstractTest { lenient().when(plainSchemaDAO.findById(anyString())).thenAnswer(ic -> { String schemaName = ic.getArgument(0); switch (schemaName) { - case "email", "firstname", "location", "index" -> { + case "email", "firstname", "location", "index", "user.valueWithDot" -> { PlainSchema schema = mock(PlainSchema.class); lenient().when(schema.getKey()).thenReturn(schemaName); lenient().when(schema.getType()).thenReturn(AttrSchemaType.String); @@ -374,4 +374,13 @@ public class IntAttrNameParserTest extends AbstractTest { assertNotNull(e); } } + + @Test + public void issueSYNCOPE1894() throws ParseException { + IntAttrName intAttrName = intAttrNameParser.parse("user.valueWithDot", AnyTypeKind.USER); + assertNotNull(intAttrName); + assertEquals(AnyTypeKind.USER, intAttrName.getAnyTypeKind()); + assertNull(intAttrName.getField()); + assertEquals("user.valueWithDot", intAttrName.getSchema().getKey()); + } } 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 a005e94de1..d4ccd0f758 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 @@ -72,12 +72,14 @@ import org.apache.syncope.common.lib.to.MembershipTO; import org.apache.syncope.common.lib.to.PlainSchemaTO; import org.apache.syncope.common.lib.to.PropagationStatus; import org.apache.syncope.common.lib.to.PropagationTaskTO; +import org.apache.syncope.common.lib.to.Provision; import org.apache.syncope.common.lib.to.ProvisioningResult; import org.apache.syncope.common.lib.to.PushTaskTO; import org.apache.syncope.common.lib.to.RealmTO; import org.apache.syncope.common.lib.to.ReconStatus; import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.syncope.common.lib.to.RoleTO; +import org.apache.syncope.common.lib.to.SchemaTO; import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.AttrSchemaType; @@ -1849,4 +1851,49 @@ public class UserIssuesITCase extends AbstractITCase { .toList().stream().anyMatch(pt -> ResourceOperation.DELETE == pt. getOperation())); } + + @Test + public void issueSYNCOPE1894() { + SchemaTO userWithDotSchema = new PlainSchemaTO(); + userWithDotSchema.setKey("user.testWithDot"); + userWithDotSchema.setAnyTypeClass("minimal user"); + SCHEMA_SERVICE.create(SchemaType.PLAIN, userWithDotSchema); + + ResourceTO ldap = RESOURCE_SERVICE.read(RESOURCE_NAME_LDAP); + ldap.setKey("ldapWithDot"); + + Provision provision = ldap.getProvision(AnyTypeKind.USER.name()).orElseThrow(); + provision.getMapping().getItems().removeIf(item -> "mail".equals(item.getIntAttrName())); + provision.getVirSchemas().clear(); + + ldap.getProvisions().clear(); + ldap.getProvisions().add(provision); + + Item item = new Item(); + item.setIntAttrName(userWithDotSchema.getKey()); + item.setExtAttrName("carLicense"); + item.setPurpose(MappingPurpose.PROPAGATION); + + provision.getMapping().add(item); + + ldap = createResource(ldap); + + try { + UserCR userCR = UserITCase.getUniqueSample("userwith...@syncope.apache.org"); + userCR.getPlainAttrs().add(attr("user.testWithDot", "someCarLicenseValue")); + userCR.getResources().add(ldap.getKey()); + + ProvisioningResult<UserTO> result = createUser(userCR); + assertEquals(1, result.getPropagationStatuses().size()); + assertNotNull(result.getPropagationStatuses().get(0).getAfterObj()); + + Attr carLicense = + result.getPropagationStatuses().get(0).getAfterObj().getAttr("carLicense").orElseThrow(); + assertEquals(1, carLicense.getValues().size()); + assertEquals("someCarLicenseValue", carLicense.getValues().get(0)); + } finally { + RESOURCE_SERVICE.delete(ldap.getKey()); + SCHEMA_SERVICE.delete(SchemaType.PLAIN, userWithDotSchema.getKey()); + } + } }