package io.oasp.module.security.common.impl.accesscontrol; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashSet; import java.util.List; import java.util.Set; import net.sf.mmm.util.collection.base.NodeCycleException; import org.junit.Assert; import org.junit.Test; import org.springframework.core.io.ClassPathResource; import io.oasp.module.security.common.api.accesscontrol.AccessControl; import io.oasp.module.security.common.api.accesscontrol.AccessControlGroup; import io.oasp.module.security.common.api.accesscontrol.AccessControlPermission; import io.oasp.module.security.common.api.accesscontrol.AccessControlProvider; import io.oasp.module.security.common.api.accesscontrol.AccessControlSchema; import io.oasp.module.test.common.base.ModuleTest; /** * This is the test-case for {@link AccessControlSchema} and {@link AccessControlSchemaXmlMapper}. * */ public class AccessControlSchemaTest extends ModuleTest { /** The location of the reference configuration for regression tests. */ private static final String SCHEMA_XML = "config/app/security/access-control-schema.xml"; /** The location of the reference configuration with group type declaration */ private static final String SCHEMA_XML_GROUP_TYPES = "config/app/security/access-control-schema_groupTypes.xml"; /** The location of the configuration with a cyclic dependency. */ private static final String SCHEMA_XML_CYCLIC = "config/app/security/access-control-schema_cyclic.xml"; /** The location of the configuration that is syntactically corrupted (invalid group reference). */ private static final String SCHEMA_XML_CORRUPTED = "config/app/security/access-control-schema_corrupted.xml"; /** The location of the configuration that is syntactically corrupted (invalid group reference). */ private static final String SCHEMA_XML_ILLEGAL = "config/app/security/access-control-schema_illegal.xml"; /** * The constructor. */ public AccessControlSchemaTest() { super(); } /** * Regression test for {@link AccessControlSchemaXmlMapper#write(AccessControlSchema, java.io.OutputStream)}. * * @throws Exception if something goes wrong. */ @Test public void testWriteXml() throws Exception { // given AccessControlSchema conf = createSecurityConfiguration(); String expectedXml = readSecurityConfigurationXmlFile(); // when ByteArrayOutputStream baos = new ByteArrayOutputStream(); new AccessControlSchemaXmlMapper().write(conf, baos); String actualXml = baos.toString(); // then assertThat(expectedXml.replaceAll("\\r *|\\n *", "")).isEqualTo(actualXml); } /** * Regression test for {@link AccessControlSchemaXmlMapper#read(InputStream)}. * * @throws Exception if something goes wrong. */ @Test public void testReadXml() throws Exception { // given AccessControlSchema expectedConf = createSecurityConfiguration(); // when ClassPathResource resource = new ClassPathResource(SCHEMA_XML); AccessControlSchema actualConf; try (InputStream in = resource.getInputStream()) { actualConf = new AccessControlSchemaXmlMapper().read(in); } // then assertThat(expectedConf).isEqualTo(actualConf); } /** * Tests that {@link AccessControlProviderImpl} properly detects cyclic inheritance of {@link AccessControlGroup}s. */ @Test public void testProviderValid() { createProvider(SCHEMA_XML); } /** * Tests that {@link AccessControlProviderImpl} properly detects cyclic inheritance of {@link AccessControlGroup}s. */ @Test public void testProviderCyclic() { try { createProvider(SCHEMA_XML_CYCLIC); fail("Exception expected!"); } catch (NodeCycleException e) { assertThat(e).hasMessageContaining("[Cook-->Chief-->Barkeeper]"); } } /** Tests that {@link AccessControlProviderImpl} with corrupted XML (not well-formed). */ @Test public void testProviderCorrupted() { try { createProvider(SCHEMA_XML_CORRUPTED); fail("Exception expected!"); } catch (IllegalStateException e) { String message = e.getMessage(); assertThat(message).contains(SCHEMA_XML_CORRUPTED.toString()); String causeMessage = e.getCause().getMessage(); assertThat("Unmarshalling XML failed!").isEqualToIgnoringCase(causeMessage); } } /** Tests that {@link AccessControlProviderImpl} with illegal XML (undefined group reference). */ @Test public void testProviderIllegal() { try { createProvider(SCHEMA_XML_ILLEGAL); fail("Exception expected!"); } catch (IllegalStateException e) { String message = e.getMessage(); assertThat(message).contains(SCHEMA_XML_ILLEGAL.toString()); String causeMessage = e.getCause().getMessage(); assertThat(causeMessage).contains("Undefined ID \"Waiter\""); } } /** * Tests that {@link AccessControlProviderImpl} properly detects cyclic inheritance of {@link AccessControlGroup}s. */ public void testProvider() { AccessControlProvider provider = createProvider(SCHEMA_XML); Set<AccessControl> permissions = new HashSet<>(); boolean success; success = provider.collectAccessControls("", permissions); assertThat(success).isFalse(); assertThat(permissions.size()).isEqualTo(0); success = provider.collectAccessControls("Admin", permissions); assertThat(success).isTrue(); assertThat(permissions).contains(provider.getAccessControl("Customer_ReadCustomer")); assertThat(permissions).contains(provider.getAccessControl("Customer_CreateCustomer")); assertThat(permissions).contains(provider.getAccessControl("Customer_DeleteCustomer")); assertThat(permissions.size()).isEqualTo(24); success = provider.collectAccessControls("ReadOnly", permissions); assertThat(success).isTrue(); assertThat(permissions).contains(provider.getAccessControl("Contract_ReadContractAsset")); assertThat(permissions).contains(provider.getAccessControl("Contract_UpdateContractAsset")); assertThat(permissions).doesNotContain(provider.getAccessControl("System_DeleteUser")); assertThat(permissions.size()).isEqualTo(5); } /** Tests the correct extraction of group types */ @Test public void testGroupTypes() { ClassPathResource resource = new ClassPathResource(SCHEMA_XML_GROUP_TYPES); AccessControlSchemaProviderImpl accessControlSchemaProvider = new AccessControlSchemaProviderImpl(); accessControlSchemaProvider.setAccessControlSchema(resource); AccessControlSchema accessControlSchema = accessControlSchemaProvider.loadSchema(); List<AccessControlGroup> groups = accessControlSchema.getGroups(); Assert.assertNotNull(groups); Assert.assertEquals(3, groups.size()); for (AccessControlGroup group : groups) { if (group.getId().equals("Admin")) { Assert.assertEquals("role", group.getType()); } else if (group.getId().equals("ReadOnly") || group.getId().equals("ReadWrite")) { Assert.assertEquals("group", group.getType()); } } } private AccessControlProvider createProvider(String location) { ClassPathResource resource = new ClassPathResource(location); AccessControlProviderImpl accessControlProvider = new AccessControlProviderImpl(); AccessControlSchemaProviderImpl accessControlSchemaProvider = new AccessControlSchemaProviderImpl(); accessControlSchemaProvider.setAccessControlSchema(resource); accessControlProvider.setAccessControlSchemaProvider(accessControlSchemaProvider); accessControlProvider.initialize(); return accessControlProvider; } private String readSecurityConfigurationXmlFile() throws IOException, UnsupportedEncodingException { ClassPathResource resource = new ClassPathResource(SCHEMA_XML); byte[] data = Files.readAllBytes(Paths.get(resource.getURI())); String expectedXml = new String(data, "UTF-8"); return expectedXml; } private AccessControlSchema createSecurityConfiguration() { AccessControlSchema conf = new AccessControlSchema(); AccessControlGroup readOnly = new AccessControlGroup("ReadOnly"); readOnly.getPermissions().add(new AccessControlPermission("Customer_ReadCustomer")); readOnly.getPermissions().add(new AccessControlPermission("Customer_ReadProfile")); readOnly.getPermissions().add(new AccessControlPermission("Customer_ReadAddress")); readOnly.getPermissions().add(new AccessControlPermission("Contract_ReadContract")); readOnly.getPermissions().add(new AccessControlPermission("Contract_ReadContractAsset")); AccessControlGroup readWrite = new AccessControlGroup("ReadWrite"); readWrite.getInherits().add(readOnly); readWrite.getPermissions().add(new AccessControlPermission("Customer_CreateCustomer")); readWrite.getPermissions().add(new AccessControlPermission("Customer_CreateProfile")); readWrite.getPermissions().add(new AccessControlPermission("Customer_CreateAddress")); readWrite.getPermissions().add(new AccessControlPermission("Contract_CreateContract")); readWrite.getPermissions().add(new AccessControlPermission("Contract_CreateContractAsset")); readWrite.getPermissions().add(new AccessControlPermission("Customer_UpdateCustomer")); readWrite.getPermissions().add(new AccessControlPermission("Customer_UpdateProfile")); readWrite.getPermissions().add(new AccessControlPermission("Customer_UpdateAddress")); readWrite.getPermissions().add(new AccessControlPermission("Contract_UpdateContract")); readWrite.getPermissions().add(new AccessControlPermission("Contract_UpdateContractAsset")); AccessControlGroup customerAdmin = new AccessControlGroup("CustomerAdmin"); customerAdmin.getInherits().add(readWrite); customerAdmin.getPermissions().add(new AccessControlPermission("Customer_DeleteCustomer")); customerAdmin.getPermissions().add(new AccessControlPermission("Customer_DeleteProfile")); customerAdmin.getPermissions().add(new AccessControlPermission("Customer_DeleteAddress")); AccessControlGroup contractAdmin = new AccessControlGroup("ContractAdmin"); contractAdmin.getInherits().add(readWrite); contractAdmin.getPermissions().add(new AccessControlPermission("Contract_DeleteContract")); contractAdmin.getPermissions().add(new AccessControlPermission("Contract_DeleteContractAsset")); AccessControlGroup systemAdmin = new AccessControlGroup("SystemAdmin"); systemAdmin.getInherits().add(readWrite); systemAdmin.getPermissions().add(new AccessControlPermission("System_ReadUser")); systemAdmin.getPermissions().add(new AccessControlPermission("System_CreateUser")); systemAdmin.getPermissions().add(new AccessControlPermission("System_UpdateUser")); systemAdmin.getPermissions().add(new AccessControlPermission("System_DeleteUser")); AccessControlGroup admin = new AccessControlGroup("Admin"); admin.getInherits().add(customerAdmin); admin.getInherits().add(contractAdmin); admin.getInherits().add(systemAdmin); admin.getPermissions(); conf.getGroups().add(readOnly); conf.getGroups().add(readWrite); conf.getGroups().add(customerAdmin); conf.getGroups().add(contractAdmin); conf.getGroups().add(systemAdmin); conf.getGroups().add(admin); return conf; } }