Java tutorial
/* * Copyright 2015-2017 UnboundID Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License (GPLv2 only) * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see <http://www.gnu.org/licenses>. */ package com.unboundid.scim2.server.utils; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; import com.unboundid.scim2.common.Path; import com.unboundid.scim2.common.exceptions.BadRequestException; import com.unboundid.scim2.common.filters.Filter; import com.unboundid.scim2.common.messages.PatchOperation; import com.unboundid.scim2.common.types.AttributeDefinition; import com.unboundid.scim2.common.types.EnterpriseUserExtension; import com.unboundid.scim2.common.types.SchemaResource; import com.unboundid.scim2.common.types.UserResource; import com.unboundid.scim2.common.utils.JsonUtils; import com.unboundid.scim2.common.utils.SchemaUtils; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; /** * Tests for the SchemaEnforcer. */ public class SchemaCheckerTestCase { private SchemaResource coreSchema; private SchemaResource typeTestSchema; /** * Setup some basic schemas. * * @throws Exception If an error occurs. */ @BeforeClass public void setUp() throws Exception { coreSchema = SchemaUtils.getSchema(UserResource.class); List<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>(); // String AttributeDefinition.Builder builder = new AttributeDefinition.Builder(); builder.setName("string"); builder.setType(AttributeDefinition.Type.STRING); AttributeDefinition string = builder.build(); attributeDefinitions.add(string); // String with canonical values builder = new AttributeDefinition.Builder(); builder.setName("stringCanonical"); builder.setType(AttributeDefinition.Type.STRING); builder.addCanonicalValues("value1", "value2"); AttributeDefinition stringCanonical = builder.build(); attributeDefinitions.add(stringCanonical); // Datetime builder = new AttributeDefinition.Builder(); builder.setName("datetime"); builder.setType(AttributeDefinition.Type.DATETIME); AttributeDefinition datetime = builder.build(); attributeDefinitions.add(datetime); // Binary builder = new AttributeDefinition.Builder(); builder.setName("binary"); builder.setType(AttributeDefinition.Type.BINARY); AttributeDefinition binary = builder.build(); attributeDefinitions.add(binary); // Reference builder = new AttributeDefinition.Builder(); builder.setName("reference"); builder.setType(AttributeDefinition.Type.REFERENCE); AttributeDefinition reference = builder.build(); attributeDefinitions.add(reference); // Boolean builder = new AttributeDefinition.Builder(); builder.setName("boolean"); builder.setType(AttributeDefinition.Type.BOOLEAN); AttributeDefinition bool = builder.build(); attributeDefinitions.add(bool); // Decimal builder = new AttributeDefinition.Builder(); builder.setName("decimal"); builder.setType(AttributeDefinition.Type.DECIMAL); AttributeDefinition decimal = builder.build(); attributeDefinitions.add(decimal); // Integer builder = new AttributeDefinition.Builder(); builder.setName("integer"); builder.setType(AttributeDefinition.Type.INTEGER); AttributeDefinition integer = builder.build(); attributeDefinitions.add(integer); // Complex builder = new AttributeDefinition.Builder(); builder.setName("complex"); builder.setType(AttributeDefinition.Type.COMPLEX); builder.addSubAttributes(string); builder.addSubAttributes(stringCanonical); builder.addSubAttributes(datetime); builder.addSubAttributes(binary); builder.addSubAttributes(reference); builder.addSubAttributes(bool); builder.addSubAttributes(decimal); builder.addSubAttributes(integer); attributeDefinitions.add(builder.build()); // Multi-valued String builder = new AttributeDefinition.Builder(); builder.setName("mvstring"); builder.setType(AttributeDefinition.Type.STRING); builder.setMultiValued(true); attributeDefinitions.add(builder.build()); // Multi-valued String with canonical values builder = new AttributeDefinition.Builder(); builder.setName("mvstringCanonical"); builder.setType(AttributeDefinition.Type.STRING); builder.addCanonicalValues("value1", "value2"); builder.setMultiValued(true); attributeDefinitions.add(builder.build()); // Multi-valued Datetime builder = new AttributeDefinition.Builder(); builder.setName("mvdatetime"); builder.setType(AttributeDefinition.Type.DATETIME); builder.setMultiValued(true); attributeDefinitions.add(builder.build()); // Multi-valued Binary builder = new AttributeDefinition.Builder(); builder.setName("mvbinary"); builder.setType(AttributeDefinition.Type.BINARY); builder.setMultiValued(true); attributeDefinitions.add(builder.build()); // Multi-valued Reference builder = new AttributeDefinition.Builder(); builder.setName("mvreference"); builder.setType(AttributeDefinition.Type.REFERENCE); builder.setMultiValued(true); attributeDefinitions.add(builder.build()); // Multi-valued Boolean builder = new AttributeDefinition.Builder(); builder.setName("mvboolean"); builder.setType(AttributeDefinition.Type.BOOLEAN); builder.setMultiValued(true); attributeDefinitions.add(builder.build()); // Multi-valued Decimal builder = new AttributeDefinition.Builder(); builder.setName("mvdecimal"); builder.setType(AttributeDefinition.Type.DECIMAL); builder.setMultiValued(true); attributeDefinitions.add(builder.build()); // Multi-valued Integer builder = new AttributeDefinition.Builder(); builder.setName("mvinteger"); builder.setType(AttributeDefinition.Type.INTEGER); builder.setMultiValued(true); attributeDefinitions.add(builder.build()); // Multi-valued Complex builder = new AttributeDefinition.Builder(); builder.setName("mvcomplex"); builder.setType(AttributeDefinition.Type.COMPLEX); builder.setMultiValued(true); builder.addSubAttributes(string); builder.addSubAttributes(stringCanonical); builder.addSubAttributes(datetime); builder.addSubAttributes(binary); builder.addSubAttributes(reference); builder.addSubAttributes(bool); builder.addSubAttributes(decimal); builder.addSubAttributes(integer); attributeDefinitions.add(builder.build()); typeTestSchema = new SchemaResource("urn:id:test", "test", "", attributeDefinitions); } /** * Check the full Spec User representation against the Spec schemas. Shouldn't * be any issues. * * @throws Exception if an error occurs. */ @Test public void sanityCheck() throws Exception { String USER = "{ \n" + " \"schemas\":[ \n" + " \"urn:ietf:params:scim:schemas:core:2.0:User\",\n" + " \"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User\"\n" + " ],\n" + " \"id\":\"2819c223-7f76-453a-919d-413861904646\",\n" + " \"externalId\":\"701984\",\n" + " \"userName\":\"bjensen@example.com\",\n" + // Check for case insensitive behavior " \"nAme\":{ \n" + " \"formatted\":\"Ms. Barbara J Jensen III\",\n" + // Check for case insensitive behavior " \"FAMILYName\":\"Jensen\",\n" + " \"givenName\":\"Barbara\",\n" + " \"middleName\":\"Jane\",\n" + " \"honorificPrefix\":\"Ms.\",\n" + " \"honorificSuffix\":\"III\"\n" + " },\n" + " \"displayName\":\"Babs Jensen\",\n" + " \"nickName\":\"Babs\",\n" + " \"profileUrl\":\"https://login.example.com/bjensen\",\n" + " \"emails\":[ \n" + " { \n" + " \"value\":\"bjensen@example.com\",\n" + " \"type\":\"work\",\n" + // Check for case insensitive behavior " \"pRIMary\":true\n" + " },\n" + " { \n" + " \"value\":\"babs@jensen.org\",\n" + " \"type\":\"home\"\n" + " }\n" + " ],\n" + " \"addresses\":[ \n" + " { \n" + " \"streetAddress\":\"100 Universal City Plaza\",\n" + " \"locality\":\"Hollywood\",\n" + " \"region\":\"CA\",\n" + " \"postalCode\":\"91608\",\n" + " \"country\":\"USA\",\n" + " \"formatted\":\"100 Universal City Plaza\\nHollywood, " + "CA 91608 USA\",\n" + " \"type\":\"work\",\n" + " \"primary\":true\n" + " },\n" + " { \n" + " \"streetAddress\":\"456 Hollywood Blvd\",\n" + " \"locality\":\"Hollywood\",\n" + " \"region\":\"CA\",\n" + " \"postalCode\":\"91608\",\n" + " \"country\":\"USA\",\n" + " \"formatted\":\"456 Hollywood Blvd\\nHollywood, " + "CA 91608 USA\",\n" + " \"type\":\"home\"\n" + " }\n" + " ],\n" + " \"phoneNumbers\":[ \n" + " { \n" + " \"value\":\"555-555-5555\",\n" + " \"type\":\"work\"\n" + " },\n" + " { \n" + " \"value\":\"555-555-4444\",\n" + " \"type\":\"mobile\"\n" + " }\n" + " ],\n" + " \"ims\":[ \n" + " { \n" + " \"value\":\"someaimhandle\",\n" + " \"type\":\"aim\"\n" + " }\n" + " ],\n" + " \"photos\":[ \n" + " { \n" + " \"value\":\"https://photos.example.com/profilephoto/" + "72930000000Ccne/F\",\n" + " \"type\":\"photo\"\n" + " },\n" + " { \n" + " \"value\":\"https://photos.example.com/profilephoto/" + "72930000000Ccne/T\",\n" + " \"type\":\"thumbnail\"\n" + " }\n" + " ],\n" + " \"userType\":\"Employee\",\n" + " \"title\":\"Tour Guide\",\n" + " \"preferredLanguage\":\"en-US\",\n" + " \"locale\":\"en-US\",\n" + " \"timezone\":\"America/Los_Angeles\",\n" + " \"active\":true,\n" + " \"password\":\"t1meMa$heen\",\n" + " \"groups\":[ \n" + " { \n" + " \"value\":\"e9e30dba-f08f-4109-8486-d5c6a331660a\",\n" + " \"$ref\":\"../Groups/e9e30dba-f08f-4109-8486-d5c6a331660a\",\n" + " \"display\":\"Tour Guides\"\n" + " },\n" + " { \n" + " \"value\":\"fc348aa8-3835-40eb-a20b-c726e15c55b5\",\n" + " \"$ref\":\"../Groups/fc348aa8-3835-40eb-a20b-c726e15c55b5\",\n" + " \"display\":\"Employees\"\n" + " },\n" + " { \n" + " \"value\":\"71ddacd2-a8e7-49b8-a5db-ae50d0a5bfd7\",\n" + " \"$ref\":\"../Groups/71ddacd2-a8e7-49b8-a5db-ae50d0a5bfd7\",\n" + " \"display\":\"US Employees\"\n" + " }\n" + " ],\n" + " \"x509Certificates\":[ \n" + " { \n" + " \"value\":\"MIIDQzCCAqygAwIBAgICEAAwDQYJKoZIhvcNAQEFBQAwTj" + "ELMAkGA1UEBhMCVVMx" + " EzARBgNVBAgMCkNhbGlmb3JuaWExFDASBgNVBAoMC2V4YW1wbGUuY29t" + "MRQwEgYD" + " VQQDDAtleGFtcGxlLmNvbTAeFw0xMTEwMjIwNjI0MzFaFw0xMjEwMDQw" + "NjI0MzFa" + " MH8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYD" + "VQQKDAtl" + " eGFtcGxlLmNvbTEhMB8GA1UEAwwYTXMuIEJhcmJhcmEgSiBKZW5zZW4g" + "SUlJMSIw" + " IAYJKoZIhvcNAQkBFhNiamVuc2VuQGV4YW1wbGUuY29tMIIBIjANBgkq" + "hkiG9w0B" + " AQEFAAOCAQ8AMIIBCgKCAQEA7Kr+Dcds/JQ5GwejJFcBIP682X3xpjis" + "56AK02bc" + " 1FLgzdLI8auoR+cC9/Vrh5t66HkQIOdA4unHh0AaZ4xL5PhVbXIPMB5v" + "APKpzz5i" + " PSi8xO8SL7I7SDhcBVJhqVqr3HgllEG6UClDdHO7nkLuwXq8HcISKkbT" + "5WFTVfFZ" + " zidPl8HZ7DhXkZIRtJwBweq4bvm3hM1Os7UQH05ZS6cVDgweKNwdLLrT" + "51ikSQG3" + " DYrl+ft781UQRIqxgwqCfXEuDiinPh0kkvIi5jivVu1Z9QiwlYEdRbLJ" + "4zJQBmDr" + " SGTMYn4lRc2HgHO4DqB/bnMVorHB0CC6AV1QoFK4GPe1LwIDAQABo3sw" + "eTAJBgNV" + " HRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZ" + "XJ0aWZp" + " Y2F0ZTAdBgNVHQ4EFgQU8pD0U0vsZIsaA16lL8En8bx0F/gwHwYDVR0jB" + "BgwFoAU" + " dGeKitcaF7gnzsNwDx708kqaVt0wDQYJKoZIhvcNAQEFBQADgYEAA81Ss" + "FnOdYJt" + " Ng5Tcq+/ByEDrBgnusx0jloUhByPMEVkoMZ3J7j1ZgI8rAbOkNngX8+pK" + "fTiDz1R" + " C4+dx8oU6Za+4NJXUjlL5CvV6BEYb1+QAEJwitTVvxB/A67g42/vzgAto" + "RUeDov1" + " +GFiBZ+GNF/cAYKcMtGcrs2i97ZkJMo=\"\n" + " }\n" + " ],\n" + " \"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User\":{\n" + " \"employeeNumber\":\"701984\",\n" + " \"costCenter\":\"4130\",\n" + " \"organization\":\"Universal Studios\",\n" + " \"division\":\"Theme Park\",\n" + " \"department\":\"Tour Operations\",\n" + " \"manager\":{ \n" + " \"value\":\"26118915-6090-4610-87e4-49d8ca9f808d\",\n" + " \"$ref\":\"../Users/26118915-6090-4610-87e4-49d8ca9f808d\",\n" + " \"displayName\":\"John Smith\"\n" + " }\n" + " },\n" + " \"meta\":{ \n" + " \"resourceType\":\"User\",\n" + " \"created\":\"2010-01-23T04:56:22Z\",\n" + " \"lastModified\":\"2011-05-13T04:42:34Z\",\n" + " \"version\":\"W\\/\\\"3694e05e9dff591\\\"\",\n" + " \"location\":\"https://example.com/v2/Users/" + "2819c223-7f76-453a-919d-413861904646\"\n" + " }\n" + "}"; ObjectNode userResource = (ObjectNode) JsonUtils.getObjectReader().readTree(USER); SchemaResource coreSchema = SchemaUtils.getSchema(UserResource.class); SchemaResource enterpriseExtension = SchemaUtils.getSchema(EnterpriseUserExtension.class); ResourceTypeDefinition resourceTypeDefinition = new ResourceTypeDefinition.Builder("test", "/test") .setCoreSchema(coreSchema).addOptionalSchemaExtension(enterpriseExtension).build(); SchemaChecker checker = new SchemaChecker(resourceTypeDefinition); // Remove any read only attributes since they are suppose to be ignored // on create and replace. userResource = checker.removeReadOnlyAttributes(userResource); JsonNode copyUserResource = userResource.deepCopy(); // Check create SchemaChecker.Results results = checker.checkCreate(userResource); // Make sure there are no issues. assertTrue(results.getMutabilityIssues().isEmpty(), results.getMutabilityIssues().toString()); assertTrue(results.getPathIssues().isEmpty(), results.getPathIssues().toString()); assertTrue(results.getSyntaxIssues().isEmpty(), results.getSyntaxIssues().toString()); // Make sure the ObjectNode wasn't modified during the check. assertEquals(userResource, copyUserResource); // Check replace results = checker.checkReplace(userResource, null); // Make sure there are no issues. assertTrue(results.getMutabilityIssues().isEmpty(), results.getMutabilityIssues().toString()); assertTrue(results.getPathIssues().isEmpty(), results.getPathIssues().toString()); assertTrue(results.getSyntaxIssues().isEmpty(), results.getSyntaxIssues().toString()); // Make sure the ObjectNode wasn't modified during the check. assertEquals(userResource, copyUserResource); // Check modify String patchRequestStr = "{ \n" + " \"op\":\"add\",\n" + " \"value\":{ \n" + // Check for case insensitive behavior " \"passWORD\":\"password\",\n" + " \"name\":{ \n" + " \"givenName\":\"Barbara\",\n" + // Check for case insensitive behavior " \"FAMILYName\":\"Jensen\",\n" + " \"formatted\":\"Barbara Ann Jensen\"\n" + " },\n" + " \"emails\":[ \n" + " { \n" + // Check for case insensitive behavior " \"VALUE\":\"bjensen@example.com\",\n" + " \"type\":\"work\"\n" + " },\n" + " { \n" + " \"value\":\"babs@jensen.org\",\n" + " \"type\":\"home\"\n" + " }\n" + " ],\n" + " \"urn:ietf:params:scim:schemas:extension:" + "enterprise:2.0:User\":{ \n" + " \"employeeNumber\":\"701984\"\n" + " },\n" + " \"addresses\":[ \n" + " { \n" + " \"type\":\"work\",\n" + " \"streetAddress\":\"13809 Research Blvd\",\n" + " \"locality\":\"Austin\",\n" + " \"region\":\"TX\",\n" + " \"postalCode\":\"78750\",\n" + " \"country\":\"USA\",\n" + " \"formatted\":\"13809 Research Blvd\\n" + "Austin, TX 78750 USA\",\n" + " \"primary\":true\n" + " },\n" + " { \n" + " \"type\":\"home\",\n" + " \"streetAddress\":\"456 Hollywood Blvd\",\n" + " \"locality\":\"Hollywood\",\n" + " \"region\":\"CA\",\n" + " \"postalCode\":\"91608\",\n" + " \"country\":\"USA\",\n" + " \"formatted\":\"456 Hollywood Blvd\\n" + "Hollywood, CA 91608 USA\"\n" + " }\n" + " ]\n" + " }\n" + "}"; PatchOperation operation = JsonUtils.getObjectReader().forType(PatchOperation.class) .readValue(patchRequestStr); results = checker.checkModify(Collections.singleton(operation), userResource); // Make sure there are no issues. assertTrue(results.getMutabilityIssues().isEmpty(), results.getMutabilityIssues().toString()); assertTrue(results.getPathIssues().isEmpty(), results.getPathIssues().toString()); assertTrue(results.getSyntaxIssues().isEmpty(), results.getSyntaxIssues().toString()); // Make sure the patch operation wasn't modified during the check. assertEquals(operation, JsonUtils.getObjectReader().forType(PatchOperation.class).readValue(patchRequestStr)); // Make sure the ObjectNode wasn't modified during the check. assertEquals(userResource, copyUserResource); } /** * Provider for testSchemaExtension. * * @return The test data. */ @DataProvider public Object[][] schemaExtensionProvider() { // Create one schema extension with a required attribute. AttributeDefinition reqAttr = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.STRING).setRequired(true).build(); SchemaResource extWithReqAttr = new SchemaResource("urn:id:testExt", "testExt", "", Collections.singleton(reqAttr)); // Create one schema extension with a required attribute. AttributeDefinition optAttr = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.STRING).build(); SchemaResource extWithOptAttr = new SchemaResource("urn:id:testExt", "testExt", "", Collections.singleton(optAttr)); ObjectNode extNotIn = JsonUtils.getJsonNodeFactory().objectNode(); extNotIn.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User"); extNotIn.put("userName", "test"); ObjectNode extInSchemas = JsonUtils.getJsonNodeFactory().objectNode(); extInSchemas.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User").add("urn:id:testExt"); extInSchemas.put("userName", "test"); ObjectNode extNotInSchemas = JsonUtils.getJsonNodeFactory().objectNode(); extNotInSchemas.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User"); extNotInSchemas.put("userName", "test"); extNotInSchemas.putObject("urn:id:testExt").put("test", "test"); ObjectNode extIn = JsonUtils.getJsonNodeFactory().objectNode(); extIn.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User").add("urn:id:testExt"); extIn.put("userName", "test"); extIn.putObject("urn:id:testExt").put("test", "test"); ObjectNode undefinedInSchemas = JsonUtils.getJsonNodeFactory().objectNode(); undefinedInSchemas.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User") .add("urn:id:undefined"); undefinedInSchemas.put("userName", "test"); ObjectNode undefinedNotInSchemas = JsonUtils.getJsonNodeFactory().objectNode(); undefinedNotInSchemas.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User"); undefinedNotInSchemas.put("userName", "test"); undefinedNotInSchemas.putObject("urn:id:undefined").put("test", "test"); ObjectNode undefinedIn = JsonUtils.getJsonNodeFactory().objectNode(); undefinedIn.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User").add("urn:id:undefined"); undefinedIn.put("userName", "test"); undefinedIn.putObject("urn:id:undefined").put("test", "test"); ObjectNode notObject = JsonUtils.getJsonNodeFactory().objectNode(); notObject.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User").add("urn:id:testExt"); notObject.put("userName", "test"); notObject.putArray("urn:id:testExt").addObject().put("test", "test"); return new Object[][] { new Object[] { extWithReqAttr, true, extNotIn, 1, 0 }, new Object[] { extWithReqAttr, true, extInSchemas, 1, 0 }, new Object[] { extWithReqAttr, true, extNotInSchemas, 2, 0 }, new Object[] { extWithReqAttr, true, extIn, 0, 0 }, new Object[] { extWithReqAttr, false, extNotIn, 0, 0 }, new Object[] { extWithReqAttr, false, extInSchemas, 1, 0 }, new Object[] { extWithReqAttr, false, extNotInSchemas, 1, 0 }, new Object[] { extWithReqAttr, false, extIn, 0, 0 }, new Object[] { extWithReqAttr, false, undefinedInSchemas, 1, 1 }, new Object[] { extWithReqAttr, false, undefinedNotInSchemas, 1, 1 }, new Object[] { extWithReqAttr, false, undefinedIn, 1, 2 }, new Object[] { extWithReqAttr, false, notObject, 1, 1 }, new Object[] { extWithOptAttr, true, extNotIn, 1, 0 }, new Object[] { extWithOptAttr, true, extInSchemas, 0, 0 }, new Object[] { extWithOptAttr, true, extNotInSchemas, 2, 0 }, new Object[] { extWithOptAttr, true, extIn, 0, 0 }, new Object[] { extWithOptAttr, false, extNotIn, 0, 0 }, new Object[] { extWithOptAttr, false, extInSchemas, 0, 0 }, new Object[] { extWithOptAttr, false, extNotInSchemas, 1, 0 }, new Object[] { extWithOptAttr, false, extIn, 0, 0 }, new Object[] { extWithOptAttr, false, undefinedInSchemas, 1, 1 }, new Object[] { extWithOptAttr, false, undefinedNotInSchemas, 1, 1 }, new Object[] { extWithOptAttr, false, undefinedIn, 1, 2 }, new Object[] { extWithOptAttr, false, notObject, 1, 1 }, }; } /** * Test to ensure schema extensions are checked correctly. * * @param extension The schema extension. * @param required Whether it is required. * @param node The object node to check. * @param expectedErrorOnCreate Expected errors on create. * @param expectedErrorOnPatch Expected errors on patch. * @throws Exception if an error occurs. */ @Test(dataProvider = "schemaExtensionProvider") public void testSchemaExtension(SchemaResource extension, boolean required, ObjectNode node, int expectedErrorOnCreate, int expectedErrorOnPatch) throws Exception { ResourceTypeDefinition resourceTypeDefinition = required ? new ResourceTypeDefinition.Builder("test", "/test").setCoreSchema(coreSchema) .addRequiredSchemaExtension(extension).build() : new ResourceTypeDefinition.Builder("test", "/test").setCoreSchema(coreSchema) .addOptionalSchemaExtension(extension).build(); SchemaChecker checker = new SchemaChecker(resourceTypeDefinition); SchemaChecker.Results results = checker.checkCreate(node); assertEquals(results.getSyntaxIssues().size(), expectedErrorOnCreate, results.getSyntaxIssues().toString()); // Partial patch results = checker.checkModify(Collections.singleton(PatchOperation.add(node)), null); assertEquals(results.getSyntaxIssues().size(), expectedErrorOnPatch, results.getSyntaxIssues().toString()); results = checker.checkModify(Collections.singleton(PatchOperation.replace(node)), null); assertEquals(results.getSyntaxIssues().size(), expectedErrorOnPatch, results.getSyntaxIssues().toString()); } /** * Test to ensure not including the core schema in the schemas attribute * should result in syntax error. * * @throws Exception if an error occurs. */ @Test public void testCoreSchema() throws Exception { // Create one schema extension with a required attribute. AttributeDefinition reqAttr = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.STRING).setRequired(true).build(); SchemaResource extWithReqAttr = new SchemaResource("urn:id:testExt", "testExt", "", Collections.singleton(reqAttr)); // Not including the core schema should be an error. ObjectNode resource = JsonUtils.getJsonNodeFactory().objectNode(); resource.putArray("schemas").add("urn:id:testExt"); resource.put("userName", "test"); resource.putObject("urn:id:testExt").put("test", "test"); ResourceTypeDefinition resourceTypeDefinition = new ResourceTypeDefinition.Builder("test", "/test") .setCoreSchema(coreSchema).addRequiredSchemaExtension(extWithReqAttr).build(); SchemaChecker checker = new SchemaChecker(resourceTypeDefinition); SchemaChecker.Results results = checker.checkCreate(resource); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); } /** * Test to ensure modifications using patch operations on the schemas * attribute are checked correctly. * * @throws Exception if an error occurs. */ @Test public void testSchemasModify() throws Exception { // Create one schema extension with a required attribute. AttributeDefinition reqAttr = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.STRING).setRequired(true).build(); SchemaResource extWithReqAttr = new SchemaResource("urn:id:extWithReqAttr", "extWithReqAttr", "", Collections.singleton(reqAttr)); // Create one schema extension with a required attribute. AttributeDefinition optAttr = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.STRING).build(); SchemaResource extWithOptAttr = new SchemaResource("urn:id:extWithOptAttr", "extWithOptAttr", "", Collections.singleton(optAttr)); ResourceTypeDefinition resourceTypeDefinition = new ResourceTypeDefinition.Builder("test", "/test") .setCoreSchema(coreSchema).addRequiredSchemaExtension(extWithReqAttr) .addOptionalSchemaExtension(extWithOptAttr).build(); SchemaChecker checker = new SchemaChecker(resourceTypeDefinition); ObjectNode resource = JsonUtils.getJsonNodeFactory().objectNode(); resource.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User").add("urn:id:extWithReqAttr"); resource.put("userName", "test"); resource.putObject("urn:id:extWithReqAttr").put("test", "test"); // Shouldn't be able to remove the core schema List<PatchOperation> patchOps = new LinkedList<PatchOperation>(); patchOps.add(PatchOperation.remove(Path.root().attribute("schemas", Filter.eq("value", "urn:ietf:params:scim:schemas:core:2.0:User")))); SchemaChecker.Results results = checker.checkModify(patchOps, resource); assertEquals(results.getSyntaxIssues().size(), 2, results.getSyntaxIssues().toString()); // Shouldn't be able to remove a required schema extension patchOps = new LinkedList<PatchOperation>(); patchOps.add(PatchOperation .remove(Path.root().attribute("schemas", Filter.eq("value", "urn:id:extWithReqAttr")))); results = checker.checkModify(patchOps, resource); assertEquals(results.getSyntaxIssues().size(), 3, results.getSyntaxIssues().toString()); // Shouldn't be able to replace the core schema patchOps = new LinkedList<PatchOperation>(); patchOps.add(PatchOperation.replace( Path.root().attribute("schemas", Filter.eq("value", "urn:ietf:params:scim:schemas:core:2.0:User")), TextNode.valueOf("urn:id:extWithOptAttr"))); results = checker.checkModify(patchOps, resource); assertEquals(results.getSyntaxIssues().size(), 2, results.getSyntaxIssues().toString()); // Shouldn't be able to replace a required schema extension patchOps = new LinkedList<PatchOperation>(); patchOps.add(PatchOperation.replace( Path.root().attribute("schemas", Filter.eq("value", "urn:id:extWithReqAttr")), TextNode.valueOf("urn:id:extWithOptAttr"))); results = checker.checkModify(patchOps, resource); assertEquals(results.getSyntaxIssues().size(), 3, results.getSyntaxIssues().toString()); // Shouldn't be able to add an undefined schema extension patchOps = new LinkedList<PatchOperation>(); patchOps.add(PatchOperation.add(Path.root().attribute("schemas"), JsonUtils.getJsonNodeFactory().arrayNode().add("urn:id:undefined"))); results = checker.checkModify(patchOps, resource); assertEquals(results.getSyntaxIssues().size(), 2, results.getSyntaxIssues().toString()); } /** * Provider for testRequiredAttributes. * * @return The test data. */ @DataProvider public Object[][] requiredAttributesProvider() { // Optional attribute AttributeDefinition optAttr = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.STRING).setRequired(false).build(); // Required attribute AttributeDefinition reqAttr = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.STRING).setRequired(true).build(); // Optional attribute, optional sub-attribute AttributeDefinition optAttrOptSub = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.COMPLEX).setRequired(false).addSubAttributes(optAttr).build(); // Optional attribute, required sub-attribute AttributeDefinition optAttrReqSub = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.COMPLEX).setRequired(false).addSubAttributes(reqAttr).build(); // Required attribute, required sub-attribute AttributeDefinition reqAttrReqSub = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.COMPLEX).setRequired(true).addSubAttributes(reqAttr).build(); // Optional multi-valeud attribute AttributeDefinition optMVAttr = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.STRING).setRequired(false).setMultiValued(true).build(); // Required multi-valued attribute AttributeDefinition reqMVAttr = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.STRING).setRequired(true).setMultiValued(true).build(); // Optional multi-valued attribute, optional sub-attribute AttributeDefinition optMVAttrOptSub = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.COMPLEX).setRequired(false).addSubAttributes(optAttr) .setMultiValued(true).build(); // Optional multi-valued attribute, required sub-attribute AttributeDefinition optMVAttrReqSub = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.COMPLEX).setRequired(false).addSubAttributes(reqAttr) .setMultiValued(true).build(); // Required multi-valued attribute, required sub-attribute AttributeDefinition reqMVAttrReqSub = new AttributeDefinition.Builder().setName("test") .setType(AttributeDefinition.Type.COMPLEX).setRequired(true).addSubAttributes(reqAttr) .setMultiValued(true).build(); // Attribute not present ObjectNode notPresent = JsonUtils.getJsonNodeFactory().objectNode(); notPresent.putArray("schemas").add("urn:id:test"); // Attribute with null value ObjectNode nullValue = JsonUtils.getJsonNodeFactory().objectNode(); nullValue.putArray("schemas").add("urn:id:test"); nullValue.putNull("test"); // Attribute with not present sub-attribute ObjectNode subNotPresent = JsonUtils.getJsonNodeFactory().objectNode(); subNotPresent.putArray("schemas").add("urn:id:test"); subNotPresent.putObject("test"); // Attribute with null value sub-attribute ObjectNode subNullValue = JsonUtils.getJsonNodeFactory().objectNode(); subNullValue.putArray("schemas").add("urn:id:test"); subNullValue.putObject("test").putNull("test"); // Attribute with empty array ObjectNode emptyArray = JsonUtils.getJsonNodeFactory().objectNode(); emptyArray.putArray("schemas").add("urn:id:test"); emptyArray.putArray("test"); // Attribute with one element not present sub-attribute ObjectNode arrayNotPresent = JsonUtils.getJsonNodeFactory().objectNode(); arrayNotPresent.putArray("schemas").add("urn:id:test"); arrayNotPresent.putArray("test").addObject(); // Attribute with one element null value sub-attribute ObjectNode arrayNullValue = JsonUtils.getJsonNodeFactory().objectNode(); arrayNullValue.putArray("schemas").add("urn:id:test"); arrayNullValue.putArray("test").addObject().putNull("test"); return new Object[][] { new Object[] { optAttr, notPresent, 0 }, new Object[] { optAttr, nullValue, 0 }, new Object[] { reqAttr, notPresent, 1 }, new Object[] { reqAttr, nullValue, 1 }, new Object[] { optAttrOptSub, notPresent, 0 }, new Object[] { optAttrOptSub, nullValue, 0 }, new Object[] { optAttrOptSub, subNotPresent, 0 }, new Object[] { optAttrOptSub, subNullValue, 0 }, new Object[] { optAttrReqSub, notPresent, 0 }, new Object[] { optAttrReqSub, nullValue, 0 }, new Object[] { optAttrReqSub, subNotPresent, 1 }, new Object[] { optAttrReqSub, subNullValue, 1 }, new Object[] { reqAttrReqSub, notPresent, 1 }, new Object[] { reqAttrReqSub, nullValue, 1 }, new Object[] { reqAttrReqSub, subNotPresent, 1 }, new Object[] { reqAttrReqSub, subNullValue, 1 }, new Object[] { optMVAttr, notPresent, 0 }, new Object[] { optMVAttr, emptyArray, 0 }, new Object[] { reqMVAttr, notPresent, 1 }, new Object[] { reqMVAttr, emptyArray, 1 }, new Object[] { optMVAttrOptSub, notPresent, 0 }, new Object[] { optMVAttrOptSub, emptyArray, 0 }, new Object[] { optMVAttrOptSub, arrayNotPresent, 0 }, new Object[] { optMVAttrOptSub, arrayNullValue, 0 }, new Object[] { optMVAttrReqSub, notPresent, 0 }, new Object[] { optMVAttrReqSub, emptyArray, 0 }, new Object[] { optMVAttrReqSub, arrayNotPresent, 1 }, new Object[] { optMVAttrReqSub, arrayNullValue, 1 }, new Object[] { reqMVAttrReqSub, notPresent, 1 }, new Object[] { reqMVAttrReqSub, emptyArray, 1 }, new Object[] { reqMVAttrReqSub, arrayNotPresent, 1 }, new Object[] { reqMVAttrReqSub, arrayNullValue, 1 }, }; } /** * Test to ensure required attributes are checked correctly. * * @param attributeDefinition The attribute definition. * @param node The object node to check. * @param expectError Whether an error is expected. * @throws Exception if an error occurs. */ @Test(dataProvider = "requiredAttributesProvider") public void testRequiredAttributes(AttributeDefinition attributeDefinition, ObjectNode node, int expectError) throws Exception { SchemaResource schema = new SchemaResource("urn:id:test", "test", "", Collections.singleton(attributeDefinition)); ResourceTypeDefinition resourceTypeDefinition = new ResourceTypeDefinition.Builder("test", "/test") .setCoreSchema(schema).build(); SchemaChecker checker = new SchemaChecker(resourceTypeDefinition); SchemaChecker.Results results = checker.checkCreate(node); assertEquals(results.getSyntaxIssues().size(), expectError, results.getSyntaxIssues().toString()); // Can't remove required attributes in patch if (attributeDefinition.isRequired()) { // Check for case insensitive behavior results = checker.checkModify(Collections.singleton(PatchOperation.remove(Path.fromString("TEST"))), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); } if (attributeDefinition.getSubAttributes() != null && attributeDefinition.getSubAttributes().iterator().next().isRequired()) { results = checker.checkModify( Collections.singleton(PatchOperation.remove(Path.root().attribute("test").attribute("test"))), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); } } /** * Test that undefined attributes are detected correctly. * * @throws Exception if an error occurs. */ @Test public void testUndefinedAttributes() throws Exception { SchemaResource coreSchema = SchemaUtils.getSchema(UserResource.class); SchemaResource enterpriseExtension = SchemaUtils.getSchema(EnterpriseUserExtension.class); ResourceTypeDefinition resourceTypeDefinition = new ResourceTypeDefinition.Builder("test", "/test") .setCoreSchema(coreSchema).addOptionalSchemaExtension(enterpriseExtension).build(); SchemaChecker checker = new SchemaChecker(resourceTypeDefinition); // Core attribute is undefined ObjectNode coreUndefined = JsonUtils.getJsonNodeFactory().objectNode(); coreUndefined.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User") .add("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"); coreUndefined.put("userName", "test"); coreUndefined.put("undefined", "value"); SchemaChecker.Results results = checker.checkCreate(coreUndefined); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "is undefined for schema")); results = checker.checkModify(Collections.singleton( PatchOperation.add(Path.root().attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = checker.checkModify( Collections.singleton( PatchOperation.replace(Path.root().attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = checker.checkModify( Collections.singleton(PatchOperation.remove(Path.root().attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = checker.checkSearch(Filter.fromString("undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 1, results.getFilterIssues().toString()); assertTrue(containsIssueWith(results.getFilterIssues(), "is undefined")); // Core sub-attribute is undefined ObjectNode coreSubUndefined = JsonUtils.getJsonNodeFactory().objectNode(); coreSubUndefined.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User") .add("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"); coreSubUndefined.put("userName", "test"); coreSubUndefined.putObject("name").put("undefined", "value"); results = checker.checkCreate(coreSubUndefined); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "is undefined for attribute")); results = checker .checkModify( Collections.singleton(PatchOperation.add( Path.root().attribute("name").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = checker .checkModify( Collections.singleton(PatchOperation.replace( Path.root().attribute("name").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = checker.checkModify( Collections.singleton(PatchOperation.remove(Path.root().attribute("name").attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = checker.checkSearch(Filter.fromString("name.undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 1, results.getFilterIssues().toString()); assertTrue(containsIssueWith(results.getFilterIssues(), "is undefined")); // Extended attribute is undefined ObjectNode extendedUndefined = JsonUtils.getJsonNodeFactory().objectNode(); extendedUndefined.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User") .add("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"); extendedUndefined.put("userName", "test"); extendedUndefined.putObject("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User").put("undefined", "value"); results = checker.checkCreate(extendedUndefined); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "is undefined for schema")); results = checker.checkModify(Collections.singleton(PatchOperation.add( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = checker.checkModify(Collections.singleton(PatchOperation.replace( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = checker.checkModify(Collections.singleton(PatchOperation.remove( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User").attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = checker.checkSearch(Filter .fromString("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 1, results.getFilterIssues().toString()); assertTrue(containsIssueWith(results.getFilterIssues(), "is undefined")); // Extended sub-attribute is undefined ObjectNode extendedSubUndefined = JsonUtils.getJsonNodeFactory().objectNode(); extendedSubUndefined.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User") .add("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"); extendedSubUndefined.put("userName", "test"); extendedSubUndefined.putObject("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User") .putObject("manager").put("$ref", "https://value").put("value", "value").put("undefined", "value"); results = checker.checkCreate(extendedSubUndefined); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "is undefined for attribute")); results = checker .checkModify( Collections .singleton(PatchOperation.add( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User") .attribute("manager").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = checker .checkModify( Collections .singleton(PatchOperation.replace( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User") .attribute("manager").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = checker.checkModify(Collections.singleton( PatchOperation.remove(Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User") .attribute("manager").attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = checker.checkSearch(Filter.fromString( "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager.undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 1, results.getFilterIssues().toString()); assertTrue(containsIssueWith(results.getFilterIssues(), "is undefined")); } /** * Test case for the allow undefined attribute options. * * @throws Exception if an error occurs. */ @Test public void testAllowUndefinedAttributeOptions() throws Exception { SchemaResource coreSchema = SchemaUtils.getSchema(UserResource.class); SchemaResource enterpriseExtension = SchemaUtils.getSchema(EnterpriseUserExtension.class); ResourceTypeDefinition resourceTypeDefinition = new ResourceTypeDefinition.Builder("test", "/test") .setCoreSchema(coreSchema).addOptionalSchemaExtension(enterpriseExtension).build(); SchemaChecker undefinedAttributesChecker = new SchemaChecker(resourceTypeDefinition); undefinedAttributesChecker.enable(SchemaChecker.Option.ALLOW_UNDEFINED_ATTRIBUTES); SchemaChecker undefinedSubAttributesChecker = new SchemaChecker(resourceTypeDefinition); undefinedSubAttributesChecker.enable(SchemaChecker.Option.ALLOW_UNDEFINED_SUB_ATTRIBUTES); // Core attribute is undefined ObjectNode coreUndefined = JsonUtils.getJsonNodeFactory().objectNode(); coreUndefined.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User") .add("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"); coreUndefined.put("userName", "test"); coreUndefined.put("undefined", "value"); SchemaChecker.Results results = undefinedAttributesChecker.checkCreate(coreUndefined); assertEquals(results.getSyntaxIssues().size(), 0, results.getSyntaxIssues().toString()); results = undefinedSubAttributesChecker.checkCreate(coreUndefined); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "is undefined for schema")); results = undefinedAttributesChecker.checkModify(Collections.singleton( PatchOperation.add(Path.root().attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedSubAttributesChecker.checkModify(Collections.singleton( PatchOperation.add(Path.root().attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedAttributesChecker.checkModify( Collections.singleton( PatchOperation.replace(Path.root().attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedSubAttributesChecker.checkModify( Collections.singleton( PatchOperation.replace(Path.root().attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedAttributesChecker.checkModify( Collections.singleton(PatchOperation.remove(Path.root().attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedSubAttributesChecker.checkModify( Collections.singleton(PatchOperation.remove(Path.root().attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedAttributesChecker.checkSearch(Filter.fromString("undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 0, results.getFilterIssues().toString()); results = undefinedSubAttributesChecker.checkSearch(Filter.fromString("undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 1, results.getFilterIssues().toString()); assertTrue(containsIssueWith(results.getFilterIssues(), "is undefined")); // Core sub-attribute is undefined ObjectNode coreSubUndefined = JsonUtils.getJsonNodeFactory().objectNode(); coreSubUndefined.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User") .add("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"); coreSubUndefined.put("userName", "test"); coreSubUndefined.putObject("name").put("undefined", "value"); results = undefinedAttributesChecker.checkCreate(coreSubUndefined); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "is undefined for attribute")); results = undefinedSubAttributesChecker.checkCreate(coreSubUndefined); assertEquals(results.getSyntaxIssues().size(), 0, results.getSyntaxIssues().toString()); results = undefinedAttributesChecker .checkModify( Collections.singleton(PatchOperation.add( Path.root().attribute("name").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedSubAttributesChecker .checkModify( Collections.singleton(PatchOperation.add( Path.root().attribute("name").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedAttributesChecker .checkModify( Collections.singleton(PatchOperation.replace( Path.root().attribute("name").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedSubAttributesChecker .checkModify( Collections.singleton(PatchOperation.replace( Path.root().attribute("name").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedAttributesChecker.checkModify( Collections.singleton(PatchOperation.remove(Path.root().attribute("name").attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedSubAttributesChecker.checkModify( Collections.singleton(PatchOperation.remove(Path.root().attribute("name").attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedAttributesChecker.checkSearch(Filter.fromString("name.undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 1, results.getFilterIssues().toString()); assertTrue(containsIssueWith(results.getFilterIssues(), "is undefined")); results = undefinedSubAttributesChecker.checkSearch(Filter.fromString("name.undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 0, results.getFilterIssues().toString()); // Extended attribute namespace is undefined ObjectNode extendedUndefined = JsonUtils.getJsonNodeFactory().objectNode(); extendedUndefined.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User"); extendedUndefined.put("userName", "test"); extendedUndefined.putObject("urn:undefined").put("undefined", "value"); // This should still be an error since all namespaces must be defined in the // schemas attribute. results = undefinedAttributesChecker.checkCreate(extendedUndefined); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); extendedUndefined = JsonUtils.getJsonNodeFactory().objectNode(); extendedUndefined.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User") .add("urn:undefined"); extendedUndefined.put("userName", "test"); extendedUndefined.putObject("urn:undefined").put("undefined", "value"); results = undefinedSubAttributesChecker.checkCreate(extendedUndefined); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "is undefined")); results = undefinedAttributesChecker.checkModify(Collections.singleton( PatchOperation.add(Path.root("urn:undefined").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedSubAttributesChecker.checkModify(Collections.singleton( PatchOperation.add(Path.root("urn:undefined").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedAttributesChecker .checkModify( Collections.singleton(PatchOperation.replace( Path.root("urn:undefined").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedSubAttributesChecker .checkModify( Collections.singleton(PatchOperation.replace( Path.root("urn:undefined").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedAttributesChecker.checkModify( Collections.singleton(PatchOperation.remove(Path.root("urn:undefined").attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedSubAttributesChecker.checkModify( Collections.singleton(PatchOperation.remove(Path.root("urn:undefined").attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedAttributesChecker.checkSearch(Filter.fromString("urn:undefined:undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 0, results.getFilterIssues().toString()); results = undefinedSubAttributesChecker .checkSearch(Filter.fromString("urn:undefined:undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 1, results.getFilterIssues().toString()); assertTrue(containsIssueWith(results.getFilterIssues(), "is undefined")); // Extended attribute is undefined extendedUndefined = JsonUtils.getJsonNodeFactory().objectNode(); extendedUndefined.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User") .add("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"); extendedUndefined.put("userName", "test"); extendedUndefined.putObject("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User").put("undefined", "value"); results = undefinedAttributesChecker.checkCreate(extendedUndefined); assertEquals(results.getSyntaxIssues().size(), 0, results.getSyntaxIssues().toString()); results = undefinedSubAttributesChecker.checkCreate(extendedUndefined); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "is undefined for schema")); results = undefinedAttributesChecker.checkModify(Collections.singleton(PatchOperation.add( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedSubAttributesChecker.checkModify(Collections.singleton(PatchOperation.add( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedAttributesChecker.checkModify(Collections.singleton(PatchOperation.replace( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedSubAttributesChecker.checkModify(Collections.singleton(PatchOperation.replace( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedAttributesChecker.checkModify(Collections.singleton(PatchOperation.remove( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User").attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedSubAttributesChecker.checkModify(Collections.singleton(PatchOperation.remove( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User").attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedAttributesChecker.checkSearch(Filter .fromString("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 0, results.getFilterIssues().toString()); results = undefinedSubAttributesChecker.checkSearch(Filter .fromString("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 1, results.getFilterIssues().toString()); assertTrue(containsIssueWith(results.getFilterIssues(), "is undefined")); // Extended sub-attribute is undefined ObjectNode extendedSubUndefined = JsonUtils.getJsonNodeFactory().objectNode(); extendedSubUndefined.putArray("schemas").add("urn:ietf:params:scim:schemas:core:2.0:User") .add("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"); extendedSubUndefined.put("userName", "test"); extendedSubUndefined.putObject("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User") .putObject("manager").put("$ref", "https://value").put("value", "value").put("undefined", "value"); results = undefinedAttributesChecker.checkCreate(extendedSubUndefined); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "is undefined for attribute")); results = undefinedSubAttributesChecker.checkCreate(extendedSubUndefined); assertEquals(results.getSyntaxIssues().size(), 0, results.getSyntaxIssues().toString()); results = undefinedAttributesChecker .checkModify( Collections .singleton(PatchOperation.add( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User") .attribute("manager").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedSubAttributesChecker .checkModify( Collections .singleton(PatchOperation.add( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User") .attribute("manager").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedAttributesChecker .checkModify( Collections .singleton(PatchOperation.replace( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User") .attribute("manager").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedSubAttributesChecker .checkModify( Collections .singleton(PatchOperation.replace( Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User") .attribute("manager").attribute("undefined"), TextNode.valueOf("value"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedAttributesChecker.checkModify(Collections.singleton( PatchOperation.remove(Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User") .attribute("manager").attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 1, results.getPathIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "is undefined")); results = undefinedSubAttributesChecker.checkModify(Collections.singleton( PatchOperation.remove(Path.root("urn:ietf:params:scim:schemas:extension:enterprise:2.0:User") .attribute("manager").attribute("undefined"))), null); assertEquals(results.getPathIssues().size(), 0, results.getPathIssues().toString()); results = undefinedAttributesChecker.checkSearch(Filter.fromString( "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager.undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 1, results.getFilterIssues().toString()); assertTrue(containsIssueWith(results.getFilterIssues(), "is undefined")); results = undefinedSubAttributesChecker.checkSearch(Filter.fromString( "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager.undefined eq \"value\"")); assertEquals(results.getFilterIssues().size(), 0, results.getFilterIssues().toString()); } /** * Provider for testAttributeValueType. * * @return The test data. */ @DataProvider public Object[][] attributeValueTypeProvider() { return new Object[][] { // Wrong attribute value types new Object[] { "string", JsonUtils.getJsonNodeFactory().numberNode(1) }, new Object[] { "string", JsonUtils.getJsonNodeFactory().booleanNode(true) }, new Object[] { "string", JsonUtils.getJsonNodeFactory().objectNode() }, new Object[] { "string", JsonUtils.getJsonNodeFactory().arrayNode() }, new Object[] { "stringCanonical", JsonUtils.getJsonNodeFactory().textNode("value3") }, new Object[] { "datetime", JsonUtils.getJsonNodeFactory().textNode("notdatetime") }, new Object[] { "datetime", JsonUtils.getJsonNodeFactory().numberNode(1) }, new Object[] { "datetime", JsonUtils.getJsonNodeFactory().booleanNode(true) }, new Object[] { "datetime", JsonUtils.getJsonNodeFactory().objectNode() }, new Object[] { "datetime", JsonUtils.getJsonNodeFactory().arrayNode() }, new Object[] { "binary", JsonUtils.getJsonNodeFactory().textNode("()$#@_@") }, new Object[] { "binary", JsonUtils.getJsonNodeFactory().numberNode(1) }, new Object[] { "binary", JsonUtils.getJsonNodeFactory().booleanNode(true) }, new Object[] { "binary", JsonUtils.getJsonNodeFactory().objectNode() }, new Object[] { "binary", JsonUtils.getJsonNodeFactory().arrayNode() }, new Object[] { "reference", JsonUtils.getJsonNodeFactory().textNode("rtp:\\") }, new Object[] { "reference", JsonUtils.getJsonNodeFactory().numberNode(1) }, new Object[] { "reference", JsonUtils.getJsonNodeFactory().booleanNode(true) }, new Object[] { "reference", JsonUtils.getJsonNodeFactory().objectNode() }, new Object[] { "reference", JsonUtils.getJsonNodeFactory().arrayNode() }, new Object[] { "boolean", JsonUtils.getJsonNodeFactory().textNode("string") }, new Object[] { "boolean", JsonUtils.getJsonNodeFactory().numberNode(1) }, new Object[] { "boolean", JsonUtils.getJsonNodeFactory().objectNode() }, new Object[] { "boolean", JsonUtils.getJsonNodeFactory().arrayNode() }, new Object[] { "decimal", JsonUtils.getJsonNodeFactory().textNode("string") }, new Object[] { "decimal", JsonUtils.getJsonNodeFactory().booleanNode(true) }, new Object[] { "decimal", JsonUtils.getJsonNodeFactory().objectNode() }, new Object[] { "decimal", JsonUtils.getJsonNodeFactory().arrayNode() }, new Object[] { "integer", JsonUtils.getJsonNodeFactory().textNode("string") }, new Object[] { "integer", JsonUtils.getJsonNodeFactory().booleanNode(true) }, new Object[] { "integer", JsonUtils.getJsonNodeFactory().objectNode() }, new Object[] { "integer", JsonUtils.getJsonNodeFactory().arrayNode() }, new Object[] { "integer", JsonUtils.getJsonNodeFactory().numberNode(1.1) }, new Object[] { "complex", JsonUtils.getJsonNodeFactory().textNode("string") }, new Object[] { "complex", JsonUtils.getJsonNodeFactory().numberNode(1) }, new Object[] { "complex", JsonUtils.getJsonNodeFactory().booleanNode(true) }, }; } /** * Test to ensure attribute values are checked for the correct type. * * @param field The attribute name to test. * @param node The value node to test. * @throws Exception if an error occurs. */ @Test(dataProvider = "attributeValueTypeProvider") public void testAttributeValueType(String field, JsonNode node) throws Exception { ResourceTypeDefinition resourceTypeDefinition = new ResourceTypeDefinition.Builder("test", "/test") .setCoreSchema(typeTestSchema).build(); SchemaChecker checker = new SchemaChecker(resourceTypeDefinition); // First test as an attribute ObjectNode o = JsonUtils.getJsonNodeFactory().objectNode(); o.putArray("schemas").add("urn:id:test"); o.set(field, node); SchemaChecker.Results results = checker.checkCreate(o); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); if (!((node.isArray() || node.isObject()) && node.size() == 0)) { // Partial patch results = checker.checkModify(Collections.singleton(PatchOperation.add(o)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); results = checker.checkModify(Collections.singleton(PatchOperation.replace(o)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); // Path'ed patch results = checker.checkModify( Collections.singleton(PatchOperation.add(Path.root().attribute(field), node)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); results = checker.checkModify( Collections.singleton(PatchOperation.replace(Path.root().attribute(field), node)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); results = checker.checkModify( Collections.singleton( PatchOperation.replace(Path.root().attribute(field, Filter.eq("test", "test")), node)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getPathIssues(), "not multi-valued")); } // Then test a an sub-attribute if (!field.equals("complex")) { o = JsonUtils.getJsonNodeFactory().objectNode(); o.putArray("schemas").add("urn:id:test"); o.putObject("complex").set(field, node); results = checker.checkCreate(o); assertFalse(results.getSyntaxIssues().isEmpty(), results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); if (!((node.isArray() || node.isObject()) && node.size() == 0)) { // Partial patch results = checker.checkModify(Collections.singleton(PatchOperation.add(o)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); results = checker.checkModify(Collections.singleton(PatchOperation.replace(o)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); // Path'ed patch results = checker.checkModify(Collections.singleton( PatchOperation.add(Path.root().attribute("complex").attribute(field), node)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); results = checker.checkModify( Collections.singleton( PatchOperation.replace(Path.root().attribute("complex").attribute(field), node)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); } } // Then test as a single-value for multi-valued attributes if (!node.isArray()) { o = JsonUtils.getJsonNodeFactory().objectNode(); o.putArray("schemas").add("urn:id:test"); o.set("mv" + field, node); results = checker.checkCreate(o); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); if (!((node.isArray() || node.isObject()) && node.size() == 0)) { // Partial patch results = checker.checkModify(Collections.singleton(PatchOperation.add(o)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); results = checker.checkModify(Collections.singleton(PatchOperation.replace(o)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); // Path'ed patch results = checker.checkModify( Collections.singleton(PatchOperation.add(Path.root().attribute("mv" + field), node)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); results = checker.checkModify( Collections.singleton(PatchOperation.replace(Path.root().attribute("mv" + field), node)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); } } // Then test as a multi-valued attribute o = JsonUtils.getJsonNodeFactory().objectNode(); o.putArray("schemas").add("urn:id:test"); o.putArray("mv" + field).add(node); results = checker.checkCreate(o); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); if (!((node.isArray() || node.isObject()) && node.size() == 0)) { // Partial patch results = checker.checkModify(Collections.singleton(PatchOperation.add(o)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); results = checker.checkModify(Collections.singleton(PatchOperation.replace(o)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); // Path'ed patch results = checker .checkModify(Collections.singleton(PatchOperation.add(Path.root().attribute("mv" + field), JsonUtils.getJsonNodeFactory().arrayNode().add(node))), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); results = checker .checkModify(Collections.singleton(PatchOperation.replace(Path.root().attribute("mv" + field), JsonUtils.getJsonNodeFactory().arrayNode().add(node))), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); } // Finally test as a sub-attribute of a multi-valued attribute if (!field.equals("complex")) { o = JsonUtils.getJsonNodeFactory().objectNode(); o.putArray("schemas").add("urn:id:test"); o.putArray("mvcomplex").addObject().set(field, node); results = checker.checkCreate(o); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); if (!((node.isArray() || node.isObject()) && node.size() == 0)) { // Partial patch results = checker.checkModify(Collections.singleton(PatchOperation.add(o)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); results = checker.checkModify(Collections.singleton(PatchOperation.replace(o)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); // Path'ed patch results = checker.checkModify( Collections.singleton( PatchOperation.add(Path.root().attribute("mvcomplex").attribute(field), node)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); results = checker.checkModify( Collections.singleton( PatchOperation.replace(Path.root().attribute("mvcomplex").attribute(field), node)), null); assertEquals(results.getSyntaxIssues().size(), 1, results.getSyntaxIssues().toString()); assertTrue(containsIssueWith(results.getSyntaxIssues(), "Value")); } } } /** * Test the attribute mutability constratins are checked correctly. * * @throws Exception if an error occurs. */ @Test public void testAttributeMutability() throws Exception { List<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>(); // Read-only attribute AttributeDefinition readOnly = new AttributeDefinition.Builder().setName("readOnly") .setType(AttributeDefinition.Type.STRING).setMutability(AttributeDefinition.Mutability.READ_ONLY) .build(); // Immutable attribute AttributeDefinition immutable = new AttributeDefinition.Builder().setName("immutable") .setType(AttributeDefinition.Type.STRING).setMutability(AttributeDefinition.Mutability.IMMUTABLE) .build(); attributeDefinitions.add(readOnly); attributeDefinitions.add(immutable); SchemaResource schema = new SchemaResource("urn:id:test", "test", "", attributeDefinitions); ResourceTypeDefinition resourceTypeDefinition = new ResourceTypeDefinition.Builder("test", "/test") .setCoreSchema(schema).build(); SchemaChecker checker = new SchemaChecker(resourceTypeDefinition); // Can not create read-only ObjectNode o = JsonUtils.getJsonNodeFactory().objectNode(); o.putArray("schemas").add("urn:id:test"); o.put("readOnly", "value"); SchemaChecker.Results results = checker.checkCreate(o); assertEquals(results.getMutabilityIssues().size(), 1, results.getMutabilityIssues().toString()); assertTrue(containsIssueWith(results.getMutabilityIssues(), "read-only")); // Can not replace read-only results = checker.checkReplace(o, null); assertEquals(results.getMutabilityIssues().size(), 1, results.getMutabilityIssues().toString()); assertTrue(containsIssueWith(results.getMutabilityIssues(), "read-only")); // Can not add read-only in patch results = checker.checkModify(Collections .singleton(PatchOperation.add(Path.root().attribute("readOnly"), TextNode.valueOf("value"))), null); assertEquals(results.getMutabilityIssues().size(), 1, results.getMutabilityIssues().toString()); assertTrue(containsIssueWith(results.getMutabilityIssues(), "read-only")); // Can not remove read-only in patch results = checker .checkModify(Collections.singleton(PatchOperation.remove(Path.root().attribute("readOnly"))), null); assertEquals(results.getMutabilityIssues().size(), 1, results.getMutabilityIssues().toString()); assertTrue(containsIssueWith(results.getMutabilityIssues(), "read-only")); // Can not replace read-only in patch results = checker.checkModify( Collections.singleton( PatchOperation.replace(Path.root().attribute("readOnly"), TextNode.valueOf("value"))), null); assertEquals(results.getMutabilityIssues().size(), 1, results.getMutabilityIssues().toString()); assertTrue(containsIssueWith(results.getMutabilityIssues(), "read-only")); // Can create immutable o = JsonUtils.getJsonNodeFactory().objectNode(); o.putArray("schemas").add("urn:id:test"); o.put("immutable", "value"); results = checker.checkCreate(o); assertTrue(results.getMutabilityIssues().isEmpty(), results.getMutabilityIssues().toString()); // Can replace immutable if not already present o = JsonUtils.getJsonNodeFactory().objectNode(); o.putArray("schemas").add("urn:id:test"); o.put("immutable", "value"); results = checker.checkReplace(o, null); assertTrue(results.getMutabilityIssues().isEmpty(), results.getMutabilityIssues().toString()); // Can replace if it is the same o = JsonUtils.getJsonNodeFactory().objectNode(); o.putArray("schemas").add("urn:id:test"); o.put("immutable", "value"); results = checker.checkReplace(o, o); assertTrue(results.getMutabilityIssues().isEmpty(), results.getMutabilityIssues().toString()); // Can not replace if value already present and different o = JsonUtils.getJsonNodeFactory().objectNode(); o.putArray("schemas").add("urn:id:test"); o.put("immutable", "value"); results = checker.checkReplace(o.deepCopy().put("immutable", "newValue"), o); assertEquals(results.getMutabilityIssues().size(), 1, results.getMutabilityIssues().toString()); assertTrue(containsIssueWith(results.getMutabilityIssues(), "immutable")); // Can add immutable in patch if not already present results = checker.checkModify(Collections.singleton( PatchOperation.add(Path.root().attribute("immutable"), TextNode.valueOf("value"))), null); assertTrue(results.getMutabilityIssues().isEmpty(), results.getMutabilityIssues().toString()); // Can not replace immutable in patch if not already present results = checker.checkModify( Collections.singleton( PatchOperation.replace(Path.root().attribute("immutable"), TextNode.valueOf("value"))), null); assertEquals(results.getMutabilityIssues().size(), 1, results.getMutabilityIssues().toString()); assertTrue(containsIssueWith(results.getMutabilityIssues(), "immutable")); // Can not add immutable in patch if it is the same results = checker.checkModify(Collections .singleton(PatchOperation.add(Path.root().attribute("immutable"), TextNode.valueOf("value"))), o); assertEquals(results.getMutabilityIssues().size(), 1, results.getMutabilityIssues().toString()); assertTrue(containsIssueWith(results.getMutabilityIssues(), "immutable")); // Can not replace immutable in patch if it is the same results = checker.checkModify(Collections.singleton( PatchOperation.replace(Path.root().attribute("immutable"), TextNode.valueOf("value"))), o); assertEquals(results.getMutabilityIssues().size(), 1, results.getMutabilityIssues().toString()); assertTrue(containsIssueWith(results.getMutabilityIssues(), "immutable")); // Can not add immutable in patch if already present and different results = checker.checkModify(Collections.singleton( PatchOperation.add(Path.root().attribute("immutable"), TextNode.valueOf("newValue"))), o); assertEquals(results.getMutabilityIssues().size(), 2, results.getMutabilityIssues().toString()); assertTrue(containsIssueWith(results.getMutabilityIssues(), "immutable")); // Can not replace immutable in patch if already present and different results = checker.checkModify( Collections.singleton( PatchOperation.replace(Path.root().attribute("immutable"), TextNode.valueOf("newValue"))), o); assertEquals(results.getMutabilityIssues().size(), 2, results.getMutabilityIssues().toString()); assertTrue(containsIssueWith(results.getMutabilityIssues(), "immutable")); // Can not remove immutable results = checker.checkModify( Collections.singleton(PatchOperation.remove(Path.root().attribute("immutable"))), null); assertEquals(results.getMutabilityIssues().size(), 1, results.getMutabilityIssues().toString()); assertTrue(containsIssueWith(results.getMutabilityIssues(), "immutable")); } /** * Test that the proper exceptions are thrown if errors are found during * schema checking. This test uses a data provider for its data, and uses * reflection to set private fields of the results to make the test simpler. * * @param expectedMsg The expected exception message. * @param syntaxIssues A list of syntax issues. * @param mutabilityIssues A list of mutability issues. * @param pathIssues A list of path issues. * @throws Exception throw in case of error. */ @Test(dataProvider = "schemaResultsProvider") public void testSchemaResultExceptions(String expectedMsg, List<String> syntaxIssues, List<String> mutabilityIssues, List<String> pathIssues) throws Exception { BadRequestException caughtException = null; SchemaChecker.Results results = getResults(syntaxIssues, mutabilityIssues, pathIssues); try { results.throwSchemaExceptions(); } catch (BadRequestException ex) { caughtException = ex; Assert.assertEquals(caughtException.getMessage(), expectedMsg); } if (!syntaxIssues.isEmpty()) { Assert.assertNotNull(caughtException); Assert.assertEquals(caughtException.getScimError().getScimType(), BadRequestException.INVALID_SYNTAX); } else if (!mutabilityIssues.isEmpty()) { Assert.assertNotNull(caughtException); Assert.assertEquals(caughtException.getScimError().getScimType(), BadRequestException.MUTABILITY); } else if (!pathIssues.isEmpty()) { Assert.assertNotNull(caughtException); Assert.assertEquals(caughtException.getScimError().getScimType(), BadRequestException.INVALID_PATH); } else { Assert.assertNull(caughtException, "Bad exception thrown"); } } @DataProvider(name = "schemaResultsProvider") private Object[][] getResultData() { return new Object[][] { { "syntaxIssueOne, syntaxIssueTwo", Arrays.asList("syntaxIssueOne", "syntaxIssueTwo"), Collections.emptyList(), Collections.emptyList() }, { "mutabilityIssueOne, mutabilityIssueTwo", Collections.emptyList(), Arrays.asList("mutabilityIssueOne", "mutabilityIssueTwo"), Collections.emptyList() }, { "pathIssueOne, pathIssueTwo", Collections.emptyList(), Collections.emptyList(), Arrays.asList("pathIssueOne", "pathIssueTwo") }, { "syntaxIssueOne, syntaxIssueTwo", Arrays.asList("syntaxIssueOne", "syntaxIssueTwo"), Arrays.asList("mutabilityIssueOne", "mutabilityIssueTwo"), Arrays.asList("pathIssueOne", "pathIssueTwo") }, { "mutabilityIssueOne", Collections.emptyList(), Arrays.asList("mutabilityIssueOne"), Arrays.asList("pathIssueOne", "pathIssueTwo") }, { null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList() } }; } private SchemaChecker.Results getResults(List<String> syntaxIssues, List<String> mutabilityIssues, List<String> pathIssues) throws Exception { SchemaChecker.Results results = new SchemaChecker.Results(); Field syntaxField = SchemaChecker.Results.class.getDeclaredField("syntaxIssues"); syntaxField.setAccessible(true); syntaxField.set(results, syntaxIssues); Field pathField = SchemaChecker.Results.class.getDeclaredField("pathIssues"); pathField.setAccessible(true); pathField.set(results, pathIssues); Field mutabilityField = SchemaChecker.Results.class.getDeclaredField("mutabilityIssues"); mutabilityField.setAccessible(true); mutabilityField.set(results, mutabilityIssues); return results; } private boolean containsIssueWith(Collection<String> issues, String issueText) { for (String issue : issues) { if (issue.contains(issueText)) { return true; } } return false; } }