diff --git a/CHANGELOG.md b/CHANGELOG.md index e45c0005..cea1660e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and minimize the interactions with the ConnId framework and have much of that taken care of by a common API. # Change Log ++ **4.2.9** - FIN-12352 - Support attribute meta information for constraints (10/10/2024) + **4.2.8** - Revert ConnectionBrokenException thrown for 500+ - delegate to FaultProcessor handling case-by-case (10/01/2024) + **4.2.7** - Throw ConnectionBrokenException upon server error (09/26/2024) + **4.2.6** - FIN-12383 - Change return type of quickTest method (09/19/2024) diff --git a/gradle.properties b/gradle.properties index d134ff12..238978ce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -software_version=4.2.8 +software_version=4.2.9 test_connector_version=3.0.2 spring_boot_version=2.7.18 diff --git a/src/main/java/com/exclamationlabs/connid/base/connector/attribute/ConnectorAttribute.java b/src/main/java/com/exclamationlabs/connid/base/connector/attribute/ConnectorAttribute.java index cdf849f5..6bf4fc58 100644 --- a/src/main/java/com/exclamationlabs/connid/base/connector/attribute/ConnectorAttribute.java +++ b/src/main/java/com/exclamationlabs/connid/base/connector/attribute/ConnectorAttribute.java @@ -16,10 +16,7 @@ package com.exclamationlabs.connid.base.connector.attribute; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; +import java.util.*; import org.identityconnectors.framework.common.objects.AttributeInfo; /** @@ -38,6 +35,18 @@ public final class ConnectorAttribute { private Object value; + /** + * Provides JSON meta information about the connector attribute. In particular, this is used to + * store data constraint information for cases where data constraints (allowed characters, data + * length) need to be enforced. The actual enforcement is not handled by the base framework, and + * is instead handled by connector and onboarding implementations. Using the + * com.exclamationlabs.connid.base.connector.attribute.meta.AttributeMetaInfo structure is + * advised, but not enforced by the base framework. More Info: - Supplying this meta JSON is + * optional, and this value can be null. - The JSON supplied will be placed into the "subtype" for + * the ConnId AttributeInfo stored for this attribute (belonging to the schema/object class). + */ + private String metaInfoJson; + public ConnectorAttribute( String attributeName, ConnectorAttributeDataType attributeDataType, @@ -60,7 +69,15 @@ public ConnectorAttribute( String attributeName, ConnectorAttributeDataType attributeDataType, AttributeInfo.Flags... attributeFlags) { - this(attributeName, attributeName, attributeDataType, attributeFlags); + this(attributeName, attributeName, attributeDataType, null, attributeFlags); + } + + public ConnectorAttribute( + String attributeName, + ConnectorAttributeDataType attributeDataType, + String metaInfoJson, + AttributeInfo.Flags... attributeFlags) { + this(attributeName, attributeName, attributeDataType, metaInfoJson, attributeFlags); } public ConnectorAttribute( @@ -68,9 +85,19 @@ public ConnectorAttribute( String nativeNameInput, ConnectorAttributeDataType attributeDataType, AttributeInfo.Flags... attributeFlags) { + this(attributeName, nativeNameInput, attributeDataType, null, attributeFlags); + } + + public ConnectorAttribute( + String attributeName, + String nativeNameInput, + ConnectorAttributeDataType attributeDataType, + String metaInfoJsonInput, + AttributeInfo.Flags... attributeFlags) { name = attributeName; nativeName = nativeNameInput; dataType = attributeDataType; + metaInfoJson = metaInfoJsonInput; flags = new HashSet<>(); flags.addAll(Arrays.asList(attributeFlags)); } @@ -99,17 +126,20 @@ public String getNativeName() { return nativeName; } + public String getMetaInfoJson() { + return metaInfoJson; + } + @Override public String toString() { - return name - + "(" - + nativeName - + ") [" - + dataType - + "] {" - + (flags != null ? flags.toString() : "") - + "}: " - + (value != null ? value.toString() : ""); + return String.format( + "%s(%s) [%s] `%s` {%s}: %s", + name, + nativeName, + dataType, + metaInfoJson, + flags != null ? flags.toString() : "", + value != null ? value.toString() : ""); } @Override diff --git a/src/main/java/com/exclamationlabs/connid/base/connector/attribute/meta/AttributeConstraint.java b/src/main/java/com/exclamationlabs/connid/base/connector/attribute/meta/AttributeConstraint.java new file mode 100644 index 00000000..af5cc5b8 --- /dev/null +++ b/src/main/java/com/exclamationlabs/connid/base/connector/attribute/meta/AttributeConstraint.java @@ -0,0 +1,42 @@ +package com.exclamationlabs.connid.base.connector.attribute.meta; + +public class AttributeConstraint { + private Boolean outbound = Boolean.FALSE; + private Boolean inbound = Boolean.FALSE; + + private AttributeConstraintRule rule; + + private String ruleData; + + public Boolean getOutbound() { + return outbound; + } + + public void setOutbound(Boolean outbound) { + this.outbound = outbound; + } + + public Boolean getInbound() { + return inbound; + } + + public void setInbound(Boolean inbound) { + this.inbound = inbound; + } + + public AttributeConstraintRule getRule() { + return rule; + } + + public void setRule(AttributeConstraintRule rule) { + this.rule = rule; + } + + public String getRuleData() { + return ruleData; + } + + public void setRuleData(String ruleData) { + this.ruleData = ruleData; + } +} diff --git a/src/main/java/com/exclamationlabs/connid/base/connector/attribute/meta/AttributeConstraintRule.java b/src/main/java/com/exclamationlabs/connid/base/connector/attribute/meta/AttributeConstraintRule.java new file mode 100644 index 00000000..c0a0ce35 --- /dev/null +++ b/src/main/java/com/exclamationlabs/connid/base/connector/attribute/meta/AttributeConstraintRule.java @@ -0,0 +1,6 @@ +package com.exclamationlabs.connid.base.connector.attribute.meta; + +public enum AttributeConstraintRule { + MAX_LENGTH, + REGEX_MATCH +} diff --git a/src/main/java/com/exclamationlabs/connid/base/connector/attribute/meta/AttributeMetaInfo.java b/src/main/java/com/exclamationlabs/connid/base/connector/attribute/meta/AttributeMetaInfo.java new file mode 100644 index 00000000..8b291f74 --- /dev/null +++ b/src/main/java/com/exclamationlabs/connid/base/connector/attribute/meta/AttributeMetaInfo.java @@ -0,0 +1,20 @@ +package com.exclamationlabs.connid.base.connector.attribute.meta; + +import java.util.List; + +/** + * This class and others in this package are used to provide an advised structure for the optional + * attribute metaInfo. At this time, we're using this primarily to store attribute data + * constraint/validation info. + */ +public class AttributeMetaInfo { + List constraints; + + public List getConstraints() { + return constraints; + } + + public void setConstraints(List constraints) { + this.constraints = constraints; + } +} diff --git a/src/main/java/com/exclamationlabs/connid/base/connector/schema/DefaultConnectorSchemaBuilder.java b/src/main/java/com/exclamationlabs/connid/base/connector/schema/DefaultConnectorSchemaBuilder.java index 29e419ce..d9e6bc73 100644 --- a/src/main/java/com/exclamationlabs/connid/base/connector/schema/DefaultConnectorSchemaBuilder.java +++ b/src/main/java/com/exclamationlabs/connid/base/connector/schema/DefaultConnectorSchemaBuilder.java @@ -26,6 +26,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; import org.identityconnectors.framework.common.exceptions.ConfigurationException; import org.identityconnectors.framework.common.objects.*; import org.identityconnectors.framework.spi.operations.SearchOp; @@ -111,6 +112,8 @@ private static AttributeInfo buildAttributeInfo(ConnectorAttribute current) { return new AttributeInfoBuilder(current.getName()) .setNativeName(current.getNativeName()) .setType(current.getDataType().getClassType()) + .setSubtype( + StringUtils.isNotBlank(current.getMetaInfoJson()) ? current.getMetaInfoJson() : null) .setFlags(current.getFlags()) .build(); } diff --git a/src/test/java/com/exclamationlabs/connid/base/connector/StubConnectorTest.java b/src/test/java/com/exclamationlabs/connid/base/connector/StubConnectorTest.java index bd085f7c..4047edde 100644 --- a/src/test/java/com/exclamationlabs/connid/base/connector/StubConnectorTest.java +++ b/src/test/java/com/exclamationlabs/connid/base/connector/StubConnectorTest.java @@ -67,7 +67,27 @@ public void testTest() { @Test public void testSchema() { - assertNotNull(getConnectorFacade().schema()); + var schema = getConnectorFacade().schema(); + assertNotNull(schema); + var infos = schema.getObjectClassInfo(); + assertNotNull(infos); + var userInfoLookup = + infos.stream().filter(it -> StringUtils.equalsIgnoreCase("user", it.getType())).findFirst(); + assertTrue(userInfoLookup.isPresent()); + var userInfo = userInfoLookup.get(); + assertNotNull(userInfo.getAttributeInfo()); + var constrainedLookup = + userInfo.getAttributeInfo().stream() + .filter( + it -> + StringUtils.equalsIgnoreCase( + StubUserAttribute.USER_TEST_MAX_CONSTRAINT.name(), it.getName())) + .findFirst(); + assertTrue(constrainedLookup.isPresent()); + assertTrue( + StringUtils.equalsIgnoreCase( + "{\"constraints\":[{\"outbound\":true,\"inbound\":false,\"rule\":\"MAX_LENGTH\",\"ruleData\":\"12\"}]}", + constrainedLookup.get().getSubtype())); } @Test @@ -571,7 +591,7 @@ static void executeTestSchema( ObjectClassInfo userSchema = schemaResult.findObjectClassInfo("user"); assertNotNull(userSchema); assertNotNull(userSchema.getAttributeInfo()); - assertEquals(19, userSchema.getAttributeInfo().size()); + assertEquals(20, userSchema.getAttributeInfo().size()); ObjectClassInfo groupSchema = schemaResult.findObjectClassInfo("group"); assertNotNull(groupSchema); diff --git a/src/test/java/com/exclamationlabs/connid/base/connector/stub/adapter/StubUsersAdapter.java b/src/test/java/com/exclamationlabs/connid/base/connector/stub/adapter/StubUsersAdapter.java index ca78ae9f..dfe1d507 100644 --- a/src/test/java/com/exclamationlabs/connid/base/connector/stub/adapter/StubUsersAdapter.java +++ b/src/test/java/com/exclamationlabs/connid/base/connector/stub/adapter/StubUsersAdapter.java @@ -18,14 +18,18 @@ import static com.exclamationlabs.connid.base.connector.attribute.ConnectorAttributeDataType.*; import static com.exclamationlabs.connid.base.connector.stub.attribute.StubUserAttribute.*; -import static org.identityconnectors.framework.common.objects.AttributeInfo.Flags.MULTIVALUED; -import static org.identityconnectors.framework.common.objects.AttributeInfo.Flags.NOT_UPDATEABLE; +import static org.identityconnectors.framework.common.objects.AttributeInfo.Flags.*; import com.exclamationlabs.connid.base.connector.adapter.AdapterValueTypeConverter; import com.exclamationlabs.connid.base.connector.adapter.BaseAdapter; import com.exclamationlabs.connid.base.connector.attribute.ConnectorAttribute; +import com.exclamationlabs.connid.base.connector.attribute.meta.AttributeConstraint; +import com.exclamationlabs.connid.base.connector.attribute.meta.AttributeConstraintRule; +import com.exclamationlabs.connid.base.connector.attribute.meta.AttributeMetaInfo; import com.exclamationlabs.connid.base.connector.stub.configuration.StubConfiguration; import com.exclamationlabs.connid.base.connector.stub.model.StubUser; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import java.util.*; import org.identityconnectors.framework.common.objects.*; @@ -66,6 +70,10 @@ public Set getConnectorAttributes() { result.add(new ConnectorAttribute(USER_TEST_LONG.name(), LONG)); result.add(new ConnectorAttribute(USER_TEST_MAP.name(), MAP)); + result.add( + new ConnectorAttribute( + USER_TEST_MAX_CONSTRAINT.name(), STRING, getMetaJson(), NOT_CREATABLE, NOT_UPDATEABLE)); + return result; } @@ -123,4 +131,17 @@ protected Set constructAttributes(StubUser user) { public Set constructAttributesTestAccess(StubUser user) { return constructAttributes(user); } + + private String getMetaJson() { + GsonBuilder gsonBuilder = new GsonBuilder(); + AttributeConstraint constraint = new AttributeConstraint(); + constraint.setOutbound(true); + constraint.setRule(AttributeConstraintRule.MAX_LENGTH); + constraint.setRuleData("12"); + AttributeMetaInfo metaInfo = new AttributeMetaInfo(); + metaInfo.setConstraints(Collections.singletonList(constraint)); + + Gson gson = gsonBuilder.create(); + return gson.toJson(metaInfo); + } } diff --git a/src/test/java/com/exclamationlabs/connid/base/connector/stub/attribute/StubUserAttribute.java b/src/test/java/com/exclamationlabs/connid/base/connector/stub/attribute/StubUserAttribute.java index 68481f5a..09da997c 100644 --- a/src/test/java/com/exclamationlabs/connid/base/connector/stub/attribute/StubUserAttribute.java +++ b/src/test/java/com/exclamationlabs/connid/base/connector/stub/attribute/StubUserAttribute.java @@ -35,5 +35,7 @@ public enum StubUserAttribute { USER_TEST_GUARDED_STRING, USER_TEST_INTEGER, USER_TEST_LONG, - USER_TEST_MAP + USER_TEST_MAP, + + USER_TEST_MAX_CONSTRAINT }