diff --git a/scim2-sdk-server/src/main/java/com/unboundid/scim2/server/utils/SchemaChecker.java b/scim2-sdk-server/src/main/java/com/unboundid/scim2/server/utils/SchemaChecker.java index daf75bef..0ce8364b 100644 --- a/scim2-sdk-server/src/main/java/com/unboundid/scim2/server/utils/SchemaChecker.java +++ b/scim2-sdk-server/src/main/java/com/unboundid/scim2/server/utils/SchemaChecker.java @@ -201,6 +201,15 @@ public enum Option * that are not defined by the definition of the parent attribute. */ ALLOW_UNDEFINED_SUB_ATTRIBUTES, + + /** + * Relax SCIM 2 standard schema requirements by allowing STRING-typed + * sub-attributes to contain JSON object values. + *
+ * Per RFC 7643, complex sub-attributes are not supported; this option
+ * relaxes that constraint for non-conformant resources.
+ */
+ ALLOW_OBJECT_VALUED_STRING_SUB_ATTRIBUTES
}
@NotNull
@@ -1040,6 +1049,19 @@ private void checkAttributeValue(
switch (attribute.getType())
{
case STRING:
+ if (!node.isString())
+ {
+ if (enabledOptions.contains(
+ Option.ALLOW_OBJECT_VALUED_STRING_SUB_ATTRIBUTES)
+ && node.isObject() && path.size() > 1)
+ {
+ return;
+ }
+ results.syntaxIssues.add(prefix + "Value for attribute " + path +
+ " must be a JSON string");
+ return;
+ }
+ break;
case DATETIME:
case REFERENCE:
if (!node.isString())
diff --git a/scim2-sdk-server/src/test/java/com/unboundid/scim2/server/utils/SchemaCheckerTestCase.java b/scim2-sdk-server/src/test/java/com/unboundid/scim2/server/utils/SchemaCheckerTestCase.java
index c943413b..48f23e7a 100644
--- a/scim2-sdk-server/src/test/java/com/unboundid/scim2/server/utils/SchemaCheckerTestCase.java
+++ b/scim2-sdk-server/src/test/java/com/unboundid/scim2/server/utils/SchemaCheckerTestCase.java
@@ -2268,6 +2268,133 @@ private Object[][] getResultData()
};
}
+ /**
+ * Test case for the allow object valued string sub-attributes option.
+ *
+ * @throws Exception if an error occurs.
+ */
+ @Test
+ public void testAllowObjectValuedStringSubAttributesOption()
+ throws Exception
+ {
+ // Build a schema with a complex parent attribute whose sub-attribute is
+ // STRING-typed but stores a JSON object value.
+ AttributeDefinition.Builder builder = new AttributeDefinition.Builder();
+ builder.setName("iamSvcUsrData");
+ builder.setType(AttributeDefinition.Type.STRING);
+ builder.setMultiValued(true);
+ AttributeDefinition iamSvcUsrData = builder.build();
+
+ builder = new AttributeDefinition.Builder();
+ builder.setName("iamServiceUser");
+ builder.setType(AttributeDefinition.Type.COMPLEX);
+ builder.addSubAttributes(iamSvcUsrData);
+ AttributeDefinition iamServiceUser = builder.build();
+
+ builder = new AttributeDefinition.Builder();
+ builder.setName("iamAdminUser");
+ builder.setType(AttributeDefinition.Type.STRING);
+ AttributeDefinition iamAdminUser = builder.build();
+
+ SchemaResource testSchema = new SchemaResource("urn:id:test", "test", "",
+ List.of(iamServiceUser, iamAdminUser));
+
+ ResourceTypeDefinition testResourceTypeDefinition =
+ new ResourceTypeDefinition.Builder("test", "/test").
+ setCoreSchema(testSchema).build();
+
+ // Define two schema checkers, one with the option to allow object valued
+ // string sub-attributes.
+ SchemaChecker refuseObjValStrSubAttributesChecker =
+ new SchemaChecker(testResourceTypeDefinition);
+
+ SchemaChecker allowObjValStrSubAttributesChecker =
+ new SchemaChecker(testResourceTypeDefinition);
+ allowObjValStrSubAttributesChecker.enable(
+ SchemaChecker.Option.ALLOW_OBJECT_VALUED_STRING_SUB_ATTRIBUTES);
+
+ // Construct JSON object value for the sub-attribute.
+ ObjectNode iamSvcUsrDataJsonObjectValue = JsonUtils.getJsonNodeFactory().objectNode();
+ iamSvcUsrDataJsonObjectValue.put("key", "initialValue");
+ iamSvcUsrDataJsonObjectValue.put("active", "true");
+
+ ArrayNode iamSvcUsrDataArray = JsonUtils.getJsonNodeFactory().
+ arrayNode().add(iamSvcUsrDataJsonObjectValue);
+
+ ObjectNode iamServiceUserNode = JsonUtils.getJsonNodeFactory().objectNode();
+ iamServiceUserNode.set("iamSvcUsrData", iamSvcUsrDataArray);
+
+ // checkCreate: JSON object value for a STRING sub-attribute should produce
+ // a syntax error by default but be accepted when
+ // ALLOW_OBJECT_VALUED_STRING_SUB_ATTRIBUTES is enabled.
+ ObjectNode iamServiceUserJsonObjectNode = JsonUtils.getJsonNodeFactory().objectNode();
+ iamServiceUserJsonObjectNode.putArray("schemas").add("urn:id:test");
+ iamServiceUserJsonObjectNode.set("iamServiceUser", iamServiceUserNode);
+
+ SchemaChecker.Results results = refuseObjValStrSubAttributesChecker.checkCreate(
+ iamServiceUserJsonObjectNode);
+ assertEquals(results.getSyntaxIssues().size(), 1,
+ results.getSyntaxIssues().toString());
+ assertTrue(containsIssueWith(results.getSyntaxIssues(),
+ "must be a JSON string"));
+
+ results = allowObjValStrSubAttributesChecker.checkCreate(
+ iamServiceUserJsonObjectNode);
+ assertTrue(results.getSyntaxIssues().isEmpty(),
+ results.getSyntaxIssues().toString());
+
+ // checkModify (PATCH replace on iamServiceUser).
+ ObjectNode currentResource = JsonUtils.getJsonNodeFactory().objectNode();
+ currentResource.putArray("schemas").add("urn:id:test");
+ currentResource.set("iamServiceUser", iamServiceUserNode);
+
+ List