-
Notifications
You must be signed in to change notification settings - Fork 76
Add a new schema checker option #301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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. | ||
| * <p> | ||
| * 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) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The node type is checked twice, which we can avoid (along with the copied code) by rewriting this with the following. I also think we can rename the option: case STRING:
if (node.isObject() && path.size() > 1
&& enabledOptions.contains(Option.ALLOW_OBJECT_AS_STRING_SUB_ATTR))
{
return;
}
// Fall through for string evaluation.
case DATETIME:
... |
||
| { | ||
| return; | ||
| } | ||
| results.syntaxIssues.add(prefix + "Value for attribute " + path + | ||
| " must be a JSON string"); | ||
| return; | ||
| } | ||
| break; | ||
| case DATETIME: | ||
| case REFERENCE: | ||
| if (!node.isString()) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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"); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The old tests don't do this much, but we should leverage the builder pattern on this: AttributeDefinition.Builder builder = new AttributeDefinition.Builder()
.setName(...)
.setType(...) |
||
| 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")); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New unit tests should use AssertJ's assertion statements instead of TestNG's. For example, this should be: assertThat(results.getSyntaxIssues()).hasSize(1);
assertThat(results.getSyntaxIssues().get(0))
.contains("must be a JSON string");For assertion statements that currently have a reason string, you can use |
||
|
|
||
| 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<PatchOperation> patchOps = Collections.singletonList( | ||
| PatchOperation.replace( | ||
| Path.root().attribute("iamServiceUser"), iamServiceUserNode)); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you want, this can use the string attribute directly since the method throws an exception. |
||
|
|
||
| results = refuseObjValStrSubAttributesChecker.checkModify( | ||
| patchOps, currentResource); | ||
| assertTrue(containsIssueWith(results.getSyntaxIssues(), | ||
| "must be a JSON string"), | ||
| results.getSyntaxIssues().toString()); | ||
|
|
||
| results = allowObjValStrSubAttributesChecker.checkModify( | ||
| patchOps, currentResource); | ||
| assertTrue(results.getSyntaxIssues().isEmpty(), | ||
| results.getSyntaxIssues().toString()); | ||
|
|
||
| // checkReplace (PUT): same expectation as checkCreate. | ||
| results = refuseObjValStrSubAttributesChecker.checkReplace( | ||
| iamServiceUserJsonObjectNode, null); | ||
| assertEquals(results.getSyntaxIssues().size(), 1, | ||
| results.getSyntaxIssues().toString()); | ||
| assertTrue(containsIssueWith(results.getSyntaxIssues(), | ||
| "must be a JSON string")); | ||
|
|
||
| results = allowObjValStrSubAttributesChecker.checkReplace( | ||
| iamServiceUserJsonObjectNode, null); | ||
| assertTrue(results.getSyntaxIssues().isEmpty(), | ||
| results.getSyntaxIssues().toString()); | ||
|
|
||
| // A top-level STRING attribute with a JSON object value must still be | ||
| // rejected even when ALLOW_OBJECT_VALUED_STRING_SUB_ATTRIBUTES is enabled, | ||
| // since the option only applies to sub-attributes (path.size() > 1). | ||
| ObjectNode iamAdminUserJsonObjectValue = | ||
| JsonUtils.getJsonNodeFactory().objectNode(); | ||
| iamAdminUserJsonObjectValue.put("key", "initialValue"); | ||
| iamAdminUserJsonObjectValue.put("active", "true"); | ||
|
|
||
| ObjectNode iamAdminUserNode = JsonUtils.getJsonNodeFactory().objectNode(); | ||
| iamAdminUserNode.putArray("schemas").add("urn:id:test"); | ||
| iamAdminUserNode.set("iamAdminUser", iamAdminUserJsonObjectValue); | ||
|
|
||
| results = allowObjValStrSubAttributesChecker.checkCreate(iamAdminUserNode); | ||
| assertEquals(results.getSyntaxIssues().size(), 1, | ||
| results.getSyntaxIssues().toString()); | ||
| assertTrue(containsIssueWith(results.getSyntaxIssues(), | ||
| "must be a JSON string")); | ||
| } | ||
|
|
||
| private SchemaChecker.Results getResults(List<String> syntaxIssues, | ||
| List<String> mutabilityIssues, List<String> pathIssues) throws Exception | ||
| { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should have a comma at the end.