package org.cloudfoundry.identity.uaa.mock.zones; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import org.cloudfoundry.identity.uaa.approval.Approval; import org.cloudfoundry.identity.uaa.approval.ApprovalStore; import org.cloudfoundry.identity.uaa.audit.AuditEventType; import org.cloudfoundry.identity.uaa.audit.event.AbstractUaaEvent; import org.cloudfoundry.identity.uaa.audit.event.EntityDeletedEvent; import org.cloudfoundry.identity.uaa.client.event.ClientCreateEvent; import org.cloudfoundry.identity.uaa.client.event.ClientDeleteEvent; import org.cloudfoundry.identity.uaa.mock.InjectedMockContextTest; import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils; import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.IdentityZoneCreationResult; import org.cloudfoundry.identity.uaa.oauth.client.ClientConstants; import org.cloudfoundry.identity.uaa.provider.IdentityProvider; import org.cloudfoundry.identity.uaa.provider.IdentityProviderProvisioning; import org.cloudfoundry.identity.uaa.provider.JdbcIdentityProviderProvisioning; import org.cloudfoundry.identity.uaa.scim.*; import org.cloudfoundry.identity.uaa.scim.event.GroupModifiedEvent; import org.cloudfoundry.identity.uaa.scim.event.UserModifiedEvent; import org.cloudfoundry.identity.uaa.scim.exception.ScimResourceNotFoundException; import org.cloudfoundry.identity.uaa.scim.jdbc.JdbcScimGroupProvisioning; import org.cloudfoundry.identity.uaa.test.TestApplicationEventListener; import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.cloudfoundry.identity.uaa.util.KeyWithCertTest; import org.cloudfoundry.identity.uaa.util.SetServerNameRequestPostProcessor; import org.cloudfoundry.identity.uaa.zone.*; import org.cloudfoundry.identity.uaa.zone.event.IdentityZoneModifiedEvent; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.http.HttpStatus; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.security.oauth2.common.util.RandomValueStringGenerator; import org.springframework.security.oauth2.provider.client.BaseClientDetails; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import java.util.*; import java.util.stream.Collectors; import static org.cloudfoundry.identity.uaa.constants.OriginKeys.LOGIN_SERVER; import static org.cloudfoundry.identity.uaa.constants.OriginKeys.UAA; import static org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils.CookieCsrfPostProcessor.cookieCsrf; import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.TokenFormat.JWT; import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.TokenFormat.OPAQUE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.junit.Assert.*; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.http.MediaType.TEXT_HTML_VALUE; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.util.StringUtils.hasText; public class IdentityZoneEndpointsMockMvcTests extends InjectedMockContextTest { public static final List<String> BASE_URLS = Arrays.asList("/identity-zones", "/identity-zones/"); private final String serviceProviderKey = "-----BEGIN RSA PRIVATE KEY-----\n" + "MIICXQIBAAKBgQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5\n" + "L39WqS9u0hnA+O7MCA/KlrAR4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vA\n" + "fpOwznoD66DDCnQVpbCjtDYWX+x6imxn8HCYxhMol6ZnTbSsFW6VZjFMjQIDAQAB\n" + "AoGAVOj2Yvuigi6wJD99AO2fgF64sYCm/BKkX3dFEw0vxTPIh58kiRP554Xt5ges\n" + "7ZCqL9QpqrChUikO4kJ+nB8Uq2AvaZHbpCEUmbip06IlgdA440o0r0CPo1mgNxGu\n" + "lhiWRN43Lruzfh9qKPhleg2dvyFGQxy5Gk6KW/t8IS4x4r0CQQD/dceBA+Ndj3Xp\n" + "ubHfxqNz4GTOxndc/AXAowPGpge2zpgIc7f50t8OHhG6XhsfJ0wyQEEvodDhZPYX\n" + "kKBnXNHzAkEAyCA76vAwuxqAd3MObhiebniAU3SnPf2u4fdL1EOm92dyFs1JxyyL\n" + "gu/DsjPjx6tRtn4YAalxCzmAMXFSb1qHfwJBAM3qx3z0gGKbUEWtPHcP7BNsrnWK\n" + "vw6By7VC8bk/ffpaP2yYspS66Le9fzbFwoDzMVVUO/dELVZyBnhqSRHoXQcCQQCe\n" + "A2WL8S5o7Vn19rC0GVgu3ZJlUrwiZEVLQdlrticFPXaFrn3Md82ICww3jmURaKHS\n" + "N+l4lnMda79eSp3OMmq9AkA0p79BvYsLshUJJnvbk76pCjR28PK4dV1gSDUEqQMB\n" + "qy45ptdwJLqLJCeNoR0JUcDNIRhOCuOPND7pcMtX6hI/\n" + "-----END RSA PRIVATE KEY-----"; private final String serviceProviderKeyPassword = "password"; private final String serviceProviderCertificate = "-----BEGIN CERTIFICATE-----\n" + "MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEO\n" + "MAwGA1UECBMFYXJ1YmExDjAMBgNVBAoTBWFydWJhMQ4wDAYDVQQHEwVhcnViYTEO\n" + "MAwGA1UECxMFYXJ1YmExDjAMBgNVBAMTBWFydWJhMR0wGwYJKoZIhvcNAQkBFg5h\n" + "cnViYUBhcnViYS5hcjAeFw0xNTExMjAyMjI2MjdaFw0xNjExMTkyMjI2MjdaMHwx\n" + "CzAJBgNVBAYTAmF3MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAM\n" + "BgNVBAcTBWFydWJhMQ4wDAYDVQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAb\n" + "BgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GN\n" + "ADCBiQKBgQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5L39W\n" + "qS9u0hnA+O7MCA/KlrAR4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vAfpOw\n" + "znoD66DDCnQVpbCjtDYWX+x6imxn8HCYxhMol6ZnTbSsFW6VZjFMjQIDAQABo4Ha\n" + "MIHXMB0GA1UdDgQWBBTx0lDzjH/iOBnOSQaSEWQLx1syGDCBpwYDVR0jBIGfMIGc\n" + "gBTx0lDzjH/iOBnOSQaSEWQLx1syGKGBgKR+MHwxCzAJBgNVBAYTAmF3MQ4wDAYD\n" + "VQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYD\n" + "VQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAbBgkqhkiG9w0BCQEWDmFydWJh\n" + "QGFydWJhLmFyggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYvBJ\n" + "0HOZbbHClXmGUjGs+GS+xC1FO/am2suCSYqNB9dyMXfOWiJ1+TLJk+o/YZt8vuxC\n" + "KdcZYgl4l/L6PxJ982SRhc83ZW2dkAZI4M0/Ud3oePe84k8jm3A7EvH5wi5hvCkK\n" + "RpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0=\n" + "-----END CERTIFICATE-----\n"; private String identityClientToken = null; private String identityClientZonesReadToken = null; private String identityClientZonesWriteToken = null; private String adminToken = null; private MockMvcUtils mockMvcUtils = MockMvcUtils.utils(); private RandomValueStringGenerator generator = new RandomValueStringGenerator(); private TestApplicationEventListener<IdentityZoneModifiedEvent> zoneModifiedEventListener; private TestApplicationEventListener<ClientCreateEvent> clientCreateEventListener; private TestApplicationEventListener<ClientDeleteEvent> clientDeleteEventListener; private TestApplicationEventListener<GroupModifiedEvent> groupModifiedEventListener; private TestApplicationEventListener<UserModifiedEvent> userModifiedEventListener; private TestApplicationEventListener<AbstractUaaEvent> uaaEventListener; private String lowPriviledgeToken; private JdbcIdentityZoneProvisioning provisioning; @Before public void setUp() throws Exception { zoneModifiedEventListener = mockMvcUtils.addEventListener(getWebApplicationContext(), IdentityZoneModifiedEvent.class); clientCreateEventListener = mockMvcUtils.addEventListener(getWebApplicationContext(), ClientCreateEvent.class); clientDeleteEventListener = mockMvcUtils.addEventListener(getWebApplicationContext(), ClientDeleteEvent.class); groupModifiedEventListener = mockMvcUtils.addEventListener(getWebApplicationContext(), GroupModifiedEvent.class); userModifiedEventListener = mockMvcUtils.addEventListener(getWebApplicationContext(), UserModifiedEvent.class); uaaEventListener = mockMvcUtils.addEventListener(getWebApplicationContext(), AbstractUaaEvent.class); JdbcTemplate jdbcTemplate = getWebApplicationContext().getBean(JdbcTemplate.class); provisioning = new JdbcIdentityZoneProvisioning(jdbcTemplate); identityClientToken = testClient.getClientCredentialsOAuthAccessToken( "identity", "identitysecret", "zones.read,zones.write,scim.zones"); identityClientZonesReadToken = testClient.getClientCredentialsOAuthAccessToken( "identity", "identitysecret", "zones.read"); identityClientZonesWriteToken = testClient.getClientCredentialsOAuthAccessToken( "identity", "identitysecret", "zones.write"); adminToken = testClient.getClientCredentialsOAuthAccessToken( "admin", "adminsecret", ""); lowPriviledgeToken = testClient.getClientCredentialsOAuthAccessToken( "admin", "adminsecret", "scim.read"); IdentityZoneHolder.clear(); zoneModifiedEventListener.clearEvents(); clientCreateEventListener.clearEvents(); clientDeleteEventListener.clearEvents(); groupModifiedEventListener.clearEvents(); userModifiedEventListener.clearEvents(); } @After public void after() throws Exception { IdentityZoneHolder.clear(); mockMvcUtils.removeEventListener(getWebApplicationContext(), zoneModifiedEventListener); mockMvcUtils.removeEventListener(getWebApplicationContext(), clientCreateEventListener); mockMvcUtils.removeEventListener(getWebApplicationContext(), clientDeleteEventListener); mockMvcUtils.removeEventListener(getWebApplicationContext(), groupModifiedEventListener); mockMvcUtils.removeEventListener(getWebApplicationContext(), userModifiedEventListener); } private ScimUser createUser(String token, String subdomain) throws Exception { ScimUser user = getScimUser(); byte[] requestBody = JsonUtils.writeValueAsBytes(user); MockHttpServletRequestBuilder post = post("/Users") .header("Authorization", "Bearer " + token) .contentType(APPLICATION_JSON) .content(requestBody); if (subdomain != null && !subdomain.equals("")) post.with(new SetServerNameRequestPostProcessor(subdomain + ".localhost")); MvcResult result = getMockMvc().perform(post) .andExpect(status().isCreated()) .andExpect(header().string("ETag", "\"0\"")) .andExpect(jsonPath("$.userName").value(user.getUserName())) .andExpect(jsonPath("$.emails[0].value").value(user.getUserName())) .andExpect(jsonPath("$.name.familyName").value(user.getFamilyName())) .andExpect(jsonPath("$.name.givenName").value(user.getGivenName())) .andReturn(); return JsonUtils.readValue(result.getResponse().getContentAsString(), ScimUser.class); } private ScimUser getScimUser() { String email = "joe@" + generator.generate().toLowerCase() + ".com"; ScimUser user = new ScimUser(); user.setPassword("password"); user.setUserName(email); user.setName(new ScimUser.Name("Joe", "User")); user.addEmail(email); return user; } @Test public void readWithoutTokenShouldFail() throws Exception { for (String url : BASE_URLS) { getMockMvc().perform(get(url)) .andExpect(status().isUnauthorized()); } } @Test public void readWith_Write_TokenShouldNotFail() throws Exception { for (String url : BASE_URLS) { getMockMvc().perform( get(url) .header("Authorization", "Bearer " + identityClientZonesWriteToken)) .andExpect(status().isOk()); } } @Test public void readWith_Read_TokenShouldSucceed() throws Exception { for (String url : BASE_URLS) { getMockMvc().perform( get(url) .header("Authorization", "Bearer " + identityClientZonesReadToken)) .andExpect(status().isOk()); } } @Test public void create_zone_no_links() throws Exception { String id = generator.generate().toLowerCase(); IdentityZoneConfiguration zoneConfiguration = new IdentityZoneConfiguration(); IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken, zoneConfiguration); assertNull(created.getConfig().getLinks().getHomeRedirect()); assertNull(created.getConfig().getLinks().getSelfService().getSignup()); assertNull(created.getConfig().getLinks().getSelfService().getPasswd()); IdentityZone retrieved = getIdentityZone(id, HttpStatus.OK, identityClientToken); assertNull(retrieved.getConfig().getLinks().getHomeRedirect()); assertNull(retrieved.getConfig().getLinks().getSelfService().getSignup()); assertNull(retrieved.getConfig().getLinks().getSelfService().getPasswd()); } @Test public void create_and_update_with_links() throws Exception { String id = generator.generate().toLowerCase(); IdentityZoneConfiguration zoneConfiguration = new IdentityZoneConfiguration(); zoneConfiguration.getLinks().setHomeRedirect("/home"); zoneConfiguration.getLinks().getSelfService().setSignup("/signup"); zoneConfiguration.getLinks().getSelfService().setPasswd("/passwd"); IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken, zoneConfiguration); assertEquals("/home", created.getConfig().getLinks().getHomeRedirect()); assertEquals("/signup", created.getConfig().getLinks().getSelfService().getSignup()); assertEquals("/passwd", created.getConfig().getLinks().getSelfService().getPasswd()); IdentityZone retrieved = getIdentityZone(id, HttpStatus.OK, identityClientToken); assertEquals("/home", retrieved.getConfig().getLinks().getHomeRedirect()); assertEquals("/signup", retrieved.getConfig().getLinks().getSelfService().getSignup()); assertEquals("/passwd", retrieved.getConfig().getLinks().getSelfService().getPasswd()); zoneConfiguration = created.getConfig(); zoneConfiguration.getLinks().setHomeRedirect(null); zoneConfiguration.getLinks().getSelfService().setSignup(null); zoneConfiguration.getLinks().getSelfService().setPasswd(null); IdentityZone updated = updateZone(created, HttpStatus.OK, identityClientToken); assertNull(updated.getConfig().getLinks().getHomeRedirect()); assertNull(updated.getConfig().getLinks().getSelfService().getSignup()); assertNull(updated.getConfig().getLinks().getSelfService().getPasswd()); } @Test public void testGetZoneAsIdentityClient() throws Exception { String id = generator.generate(); IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); IdentityZone retrieved = getIdentityZone(id, HttpStatus.OK, identityClientToken); assertEquals(created.getId(), retrieved.getId()); assertEquals(created.getName(), retrieved.getName()); assertEquals(created.getSubdomain(), retrieved.getSubdomain()); assertEquals(created.getDescription(), retrieved.getDescription()); } @Test public void test_bootstrapped_system_scopes() throws Exception { String id = generator.generate(); createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); List<String> groups = getWebApplicationContext().getBean(JdbcScimGroupProvisioning.class) .retrieveAll(id).stream().map(g -> g.getDisplayName()).collect(Collectors.toList()); ZoneManagementScopes.getSystemScopes() .stream() .forEach( scope -> assertTrue("Scope:" + scope + " should have been bootstrapped into the new zone", groups.contains(scope)) ); } @Test public void testGetZonesAsIdentityClient() throws Exception { String id = generator.generate(); IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); getMockMvc().perform( get("/identity-zones/") .header("Authorization", "Bearer " + lowPriviledgeToken)) .andExpect(status().isForbidden()); MvcResult result = getMockMvc().perform( get("/identity-zones/") .header("Authorization", "Bearer " + identityClientToken)) .andExpect(status().isOk()) .andReturn(); List<IdentityZone> zones = JsonUtils.readValue(result.getResponse().getContentAsString(), new TypeReference<List<IdentityZone>>() { }); IdentityZone retrieved = null; for (IdentityZone identityZone : zones) { if (identityZone.getId().equals(id)) { retrieved = identityZone; } } assertEquals(created.getId(), retrieved.getId()); assertEquals(created.getName(), retrieved.getName()); assertEquals(created.getSubdomain(), retrieved.getSubdomain()); assertEquals(created.getDescription(), retrieved.getDescription()); } @Test public void testGetZoneThatDoesntExist() throws Exception { String id = generator.generate(); getIdentityZone(id, HttpStatus.NOT_FOUND, identityClientToken); } @Test public void testCreateZone() throws Exception { String id = generator.generate(); IdentityZone zone = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); assertEquals(id, zone.getId()); assertEquals(id.toLowerCase(), zone.getSubdomain()); assertFalse(zone.getConfig().getTokenPolicy().isRefreshTokenUnique()); assertEquals(JWT.getStringValue(),zone.getConfig().getTokenPolicy().getRefreshTokenFormat()); checkAuditEventListener(1, AuditEventType.IdentityZoneCreatedEvent, zoneModifiedEventListener, IdentityZone.getUaa().getId(), "http://localhost:8080/uaa/oauth/token", "identity"); } @Test public void createZoneWithNoNameFailsWithUnprocessableEntity() throws Exception { String id = generator.generate(); IdentityZone zone = this.getIdentityZone(id); zone.setName(null); getMockMvc().perform( post("/identity-zones") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(zone))) .andExpect(status().isUnprocessableEntity()) .andExpect(jsonPath("$.error").value("invalid_identity_zone")) .andExpect(jsonPath("$.error_description").value("The identity zone must be given a name.")); assertEquals(0, zoneModifiedEventListener.getEventCount()); } @Test public void createZoneWithNoSubdomainFailsWithUnprocessableEntity() throws Exception { String id = generator.generate(); IdentityZone zone = this.getIdentityZone(id); zone.setSubdomain(null); getMockMvc().perform( post("/identity-zones") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(zone))) .andExpect(status().isUnprocessableEntity()) .andExpect(jsonPath("$.error").value("invalid_identity_zone")) .andExpect(jsonPath("$.error_description").value("The subdomain must be provided.")); assertEquals(0, zoneModifiedEventListener.getEventCount()); } @Test public void testCreateZoneInsufficientScope() throws Exception { String id = new RandomValueStringGenerator().generate(); createZone(id, HttpStatus.FORBIDDEN, lowPriviledgeToken, new IdentityZoneConfiguration()); assertEquals(0, zoneModifiedEventListener.getEventCount()); } @Test public void testCreateZoneNoToken() throws Exception { String id = new RandomValueStringGenerator().generate(); createZone(id, HttpStatus.UNAUTHORIZED, "", new IdentityZoneConfiguration()); assertEquals(0, zoneModifiedEventListener.getEventCount()); } @Test public void testCreateZoneWithoutID() throws Exception { IdentityZone zone = createZone("", HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); assertTrue(hasText(zone.getId())); checkZoneAuditEventInUaa(1, AuditEventType.IdentityZoneCreatedEvent); } @Test public void testUpdateNonExistentReturns403() throws Exception { String id = new RandomValueStringGenerator().generate(); IdentityZone identityZone = getIdentityZone(id); //zone doesn't exist and we don't have the token scope updateZone(identityZone, HttpStatus.FORBIDDEN, lowPriviledgeToken); assertEquals(0, zoneModifiedEventListener.getEventCount()); } @Test public void testUpdateUaaIsForbidden() throws Exception { updateZone(IdentityZone.getUaa(), HttpStatus.FORBIDDEN, identityClientToken); assertEquals(0, zoneModifiedEventListener.getEventCount()); } @Test public void testUpdateNonExistentReturns404() throws Exception { String id = generator.generate(); IdentityZone identityZone = getIdentityZone(id); updateZone(identityZone, HttpStatus.NOT_FOUND, identityClientToken); assertEquals(0, zoneModifiedEventListener.getEventCount()); } @Test public void testUpdateWithSameDataReturns200() throws Exception { String id = generator.generate(); IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); checkZoneAuditEventInUaa(1, AuditEventType.IdentityZoneCreatedEvent); updateZone(created, HttpStatus.OK, identityClientToken); checkZoneAuditEventInUaa(2, AuditEventType.IdentityZoneModifiedEvent); } @Test public void testUpdateWithDifferentDataReturns200() throws Exception { String id = generator.generate(); IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); checkZoneAuditEventInUaa(1, AuditEventType.IdentityZoneCreatedEvent); created.setDescription("updated description"); IdentityZoneConfiguration definition = new IdentityZoneConfiguration(new TokenPolicy(3600, 7200)); created.setConfig(definition); IdentityZone updated = updateZone(created, HttpStatus.OK, identityClientToken); assertEquals("updated description", updated.getDescription()); assertEquals(JsonUtils.writeValueAsString(definition), JsonUtils.writeValueAsString(updated.getConfig())); checkZoneAuditEventInUaa(2, AuditEventType.IdentityZoneModifiedEvent); } @Test public void testCreateAndUpdateDoesNotReturnKeys() throws Exception { String id = generator.generate(); IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); assertEquals(Collections.EMPTY_MAP, created.getConfig().getTokenPolicy().getKeys()); assertEquals("kid", created.getConfig().getTokenPolicy().getActiveKeyId()); assertNull(created.getConfig().getSamlConfig().getPrivateKey()); assertNull(created.getConfig().getSamlConfig().getPrivateKeyPassword()); assertNotNull(created.getConfig().getSamlConfig().getCertificate()); checkZoneAuditEventInUaa(1, AuditEventType.IdentityZoneCreatedEvent); created.setDescription("updated description"); TokenPolicy tokenPolicy = new TokenPolicy(3600, 7200); HashMap<String, String> keys = new HashMap<>(); keys.put("key1","value1"); tokenPolicy.setKeys(keys); tokenPolicy.setActiveKeyId("key1"); SamlConfig samlConfig = new SamlConfig(); samlConfig.setCertificate(serviceProviderCertificate); samlConfig.setPrivateKey(serviceProviderKey); samlConfig.setPrivateKeyPassword(serviceProviderKeyPassword); IdentityZoneConfiguration definition = new IdentityZoneConfiguration(tokenPolicy); definition.setSamlConfig(samlConfig); created.setConfig(definition); IdentityZone updated = updateZone(created, HttpStatus.OK, identityClientToken); assertEquals("updated description", updated.getDescription()); assertEquals(Collections.EMPTY_MAP, updated.getConfig().getTokenPolicy().getKeys()); assertEquals("key1", updated.getConfig().getTokenPolicy().getActiveKeyId()); assertNull(updated.getConfig().getSamlConfig().getPrivateKey()); assertNull(updated.getConfig().getSamlConfig().getPrivateKeyPassword()); assertEquals(serviceProviderCertificate, updated.getConfig().getSamlConfig().getCertificate()); } @Test public void testUpdateIgnoresKeysWhenNotPresentInPayload() throws Exception { String id = generator.generate(); IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); IdentityZone retrieve = provisioning.retrieve(created.getId()); Map<String, String> keys = new HashMap<>(); keys.put("kid", "key"); assertEquals(keys.toString(), retrieve.getConfig().getTokenPolicy().getKeys().toString()); created.setDescription("updated description"); created.getConfig().getTokenPolicy().setKeys(null); IdentityZone updated = updateZone(created, HttpStatus.OK, identityClientToken); retrieve = provisioning.retrieve(created.getId()); assertEquals(keys.toString(), retrieve.getConfig().getTokenPolicy().getKeys().toString()); } @Test public void testUpdateWithInvalidSamlKeyCertPair() throws Exception { String id = generator.generate(); IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); String samlPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n" + "Proc-Type: 4,ENCRYPTED\n" + "DEK-Info: DES-EDE3-CBC,5771044F3450A262\n" + "\n" + "VfRgIdzq/TUFdIwTOxochDs02sSQXA/Z6mRnffYTQMwXpQ5f5nRuqcY8zECGMaDe\n" + "aLrndpWzGbxiePKgN5AxuIDYNnKMrDRgyCzaaPx66rb87oMwtuq1HM18qqs+yN5v\n" + "CdsoS2uz57fCDI24BuJkIDSIeumLXc5MdN0HUeaxOVzmpbpsbBXjRYa24gW38mUh\n" + "DzmOAsNDxfoSTox02Cj+GV024e+PiWR6AMA7RKhsKPf9F4ctWwozvEHrV8fzTy5B\n" + "+KM361P7XwJYueiV/gMZW2DXSujNRBEVfC1CLaxDV3eVsFX5iIiUbc4JQYOM6oQ3\n" + "KxGPImcRQPY0asKgEDIaWtysUuBoDSbfQ/FxGWeqwR6P/Vth4dXzVGheYLu1V1CU\n" + "o6M+EXC/VUhERKwi13EgqXLKrDI352/HgEKG60EhM6xIJy9hLHy0UGjdHDcA+cF6\n" + "NEl6E3CivddMHIPQWil5x4AMaevGa3v/gcZI0DN8t7L1g4fgjtSPYzvwmOxoxHGi\n" + "7V7PdzaD4GWV75fv99sBlq2e0KK9crNUzs7vbFA/m6tgNA628SGhU1uAc/5xOskI\n" + "0Ez6kjgHoh4U7t/fu7ey1MbFQt6byHY9lk27nW1ub/QMAaRJ+EDnrReB/NN6q5Vu\n" + "h9eQNniNOeQfflzFyPB9omLNsVJkENn+lZNNrrlbn8OmJ0pT58Iaetfh79rDZPw9\n" + "zmHVqmMynmecTWAcA9ATf7+lh+xV88JDjQkLcG/3WEXNH7HXKO00pUa8+JtyxbAb\n" + "dAwGgrjJkbbk1qLLScOqY4mA5WXa5+80LMkCYO44vVTp2VKmnxj8Mw==\n" + "-----END RSA PRIVATE KEY-----\n"; String samlKeyPassphrase = "password"; String samlCertificate = "-----BEGIN CERTIFICATE-----\n" + "MIIEbzCCA1egAwIBAgIQCTPRC15ZcpIxJwdwiMVDSjANBgkqhkiG9w0BAQUFADA2\n" + "MQswCQYDVQQGEwJOTDEPMA0GA1UEChMGVEVSRU5BMRYwFAYDVQQDEw1URVJFTkEg\n" + "U1NMIENBMB4XDTEzMDczMDAwMDAwMFoXDTE2MDcyOTIzNTk1OVowPzEhMB8GA1UE\n" + "CxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMRowGAYDVQQDExFlZHVyb2FtLmJi\n" + "ay5hYy51azCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANrSBWTl56O2\n" + "VJbahURgPznums43Nnn/smJ6cGywPu4mtJHUHSmONlBDTAWFS1fLkh8YHIQmdwYg\n" + "FY4pHjZmKVtJ6ZOFhDNN1R2VMka4ZtREWn3XX8pUacol5KjEIh6U/FvMHyRv7sV5\n" + "9J6JUK+n5R7ZsSu7XRi6TrT3xhfu0KoWo8RM/salKo2theIcyqLPHiFLEtA7ISLV\n" + "q7I49uj9h9Hni/iCpBey+Gn5yDub4nrv81aDfD6zDoW/vXIOrcXFYRK3lXWOOFi4\n" + "cfmu4SQQwMV1jBOer8JgfsQ3EQMgwauSMLUR31wPM83eMbOC72HhW9SJUtFDj42c\n" + "PIEWd+rTA8ECAwEAAaOCAW4wggFqMB8GA1UdIwQYMBaAFAy9k2gM896ro0lrKzdX\n" + "R+qQ47ntMB0GA1UdDgQWBBQgoU+Pbgk2MthczZt7TviUiIWyrjAOBgNVHQ8BAf8E\n" + "BAMCBaAwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH\n" + "AwIwIgYDVR0gBBswGTANBgsrBgEEAbIxAQICHTAIBgZngQwBAgEwOgYDVR0fBDMw\n" + "MTAvoC2gK4YpaHR0cDovL2NybC50Y3MudGVyZW5hLm9yZy9URVJFTkFTU0xDQS5j\n" + "cmwwbQYIKwYBBQUHAQEEYTBfMDUGCCsGAQUFBzAChilodHRwOi8vY3J0LnRjcy50\n" + "ZXJlbmEub3JnL1RFUkVOQVNTTENBLmNydDAmBggrBgEFBQcwAYYaaHR0cDovL29j\n" + "c3AudGNzLnRlcmVuYS5vcmcwHAYDVR0RBBUwE4IRZWR1cm9hbS5iYmsuYWMudWsw\n" + "DQYJKoZIhvcNAQEFBQADggEBAHTw5b1lrTBqnx/QSO50Mww+OPYgV4b4NSu2rqxG\n" + "I2hHLiD4l7Sk3WOdXPAQMmTlo6N10Lt6p8gLLxKsOAw+nK+z9aLcgKk9/kYoe4C8\n" + "jHzwTy6eO+sCKnJfTqEX8p3b8l736lUWwPgMjjEN+d49ZegqCwH6SEz7h0+DwGmF\n" + "LLfFM8J1SozgPVXgmfCv0XHpFyYQPhXligeWk39FouC2DfhXDTDOgc0n/UQjETNl\n" + "r2Jawuw1VG6/+EFf4qjwr0/hIrxc/0XEd9+qLHKef1rMjb9pcZA7Dti+DoKHsxWi\n" + "yl3DnNZlj0tFP0SBcwjg/66VAekmFtJxsLx3hKxtYpO3m8c=\n" + "-----END CERTIFICATE-----\n"; SamlConfig samlConfig = created.getConfig().getSamlConfig(); samlConfig.setPrivateKey(samlPrivateKey); samlConfig.setPrivateKeyPassword(samlKeyPassphrase); samlConfig.setCertificate(samlCertificate); updateZone(created, HttpStatus.UNPROCESSABLE_ENTITY, identityClientToken); } @Test public void testUpdateWithPartialSamlKeyCertPair() throws Exception { String id = generator.generate(); IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); SamlConfig samlConfig = created.getConfig().getSamlConfig(); samlConfig.setPrivateKey(serviceProviderKey); samlConfig.setPrivateKeyPassword(null); samlConfig.setCertificate(serviceProviderCertificate); updateZone(created, HttpStatus.UNPROCESSABLE_ENTITY, identityClientToken); samlConfig = created.getConfig().getSamlConfig(); samlConfig.setPrivateKey(null); samlConfig.setPrivateKeyPassword(serviceProviderKeyPassword); samlConfig.setCertificate(serviceProviderCertificate); updateZone(created, HttpStatus.UNPROCESSABLE_ENTITY, identityClientToken); } @Test public void testUpdateWithEmptySamlKeyCertPairRetainsCurrentValue() throws Exception { String id = generator.generate(); IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); created.getConfig().getTokenPolicy().setKeys(new HashMap<>(Collections.singletonMap("kid", "key"))); SamlConfig samlConfig = created.getConfig().getSamlConfig(); samlConfig.setAssertionTimeToLiveSeconds(77); samlConfig.setPrivateKey(null); samlConfig.setPrivateKeyPassword(null); updateZone(created, HttpStatus.OK, identityClientToken); IdentityZone updated = provisioning.retrieve(created.getId()); SamlConfig updatedSamlConfig = updated.getConfig().getSamlConfig(); assertEquals(77, samlConfig.getAssertionTimeToLiveSeconds()); assertEquals(serviceProviderCertificate, updatedSamlConfig.getCertificate()); assertEquals(serviceProviderKey, updatedSamlConfig.getPrivateKey()); assertEquals(serviceProviderKeyPassword, updatedSamlConfig.getPrivateKeyPassword()); } @Test public void testUpdateWithNewSamlCertNoKeyIsUnprocessableEntity() throws Exception { String id = generator.generate(); IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); SamlConfig samlConfig = created.getConfig().getSamlConfig(); samlConfig.setAssertionTimeToLiveSeconds(77); samlConfig.setCertificate(KeyWithCertTest.cert); samlConfig.setPrivateKey(null); samlConfig.setPrivateKeyPassword(null); updateZone(created, HttpStatus.UNPROCESSABLE_ENTITY, identityClientToken); } @Test public void testUpdateWithNewKeyNoCertIsUnprocessableEntity() throws Exception { String id = generator.generate(); IdentityZone created = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); SamlConfig samlConfig = created.getConfig().getSamlConfig(); samlConfig.setAssertionTimeToLiveSeconds(77); samlConfig.setCertificate(null); samlConfig.setPrivateKey(serviceProviderKey); samlConfig.setPrivateKeyPassword(serviceProviderKeyPassword); updateZone(created, HttpStatus.UNPROCESSABLE_ENTITY, identityClientToken); } @Test public void testUpdateZoneWithExistingSubdomain() throws Exception { String id1 = generator.generate(); IdentityZone created1 = createZone(id1, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); checkZoneAuditEventInUaa(1, AuditEventType.IdentityZoneCreatedEvent); String id2 = generator.generate(); IdentityZone created2 = createZone(id2, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); checkZoneAuditEventInUaa(2, AuditEventType.IdentityZoneCreatedEvent); created1.setSubdomain(created2.getSubdomain()); updateZone(created1, HttpStatus.CONFLICT, identityClientToken); checkZoneAuditEventInUaa(2, AuditEventType.IdentityZoneCreatedEvent); } @Test public void testUpdateZoneNoToken() throws Exception { String id = new RandomValueStringGenerator().generate(); IdentityZone identityZone = getIdentityZone(id); updateZone(identityZone, HttpStatus.UNAUTHORIZED, ""); assertEquals(0, zoneModifiedEventListener.getEventCount()); } @Test public void testUpdateZoneInsufficientScope() throws Exception { String id = new RandomValueStringGenerator().generate(); IdentityZone identityZone = getIdentityZone(id); updateZone(identityZone, HttpStatus.FORBIDDEN, lowPriviledgeToken); assertEquals(0, zoneModifiedEventListener.getEventCount()); } @Test public void testCreateDuplicateZoneReturns409() throws Exception { String id = generator.generate(); createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); checkZoneAuditEventInUaa(1, AuditEventType.IdentityZoneCreatedEvent); createZone(id, HttpStatus.CONFLICT, identityClientToken, new IdentityZoneConfiguration()); assertEquals(1, zoneModifiedEventListener.getEventCount()); } @Test public void testCreateZoneAndIdentityProvider() throws Exception { String id = UUID.randomUUID().toString(); IdentityZone identityZone = getIdentityZone(id); TokenPolicy tokenPolicy = new TokenPolicy(3600, 7200); Map<String, String> jwtKeys = new HashMap<>(); jwtKeys.put("key_id_1", "secret_key_1"); jwtKeys.put("key_id_2", "secret_key_2"); tokenPolicy.setKeys(jwtKeys); tokenPolicy.setActiveKeyId("key_id_1"); SamlConfig samlConfig = new SamlConfig(); String samlPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n" + "MIICXAIBAAKBgQCpnqPQiDCfJY1hVaQUZG6Rs1Wd3FmP1EStN71hXeXOLog5nvpa\n" + "H45P3v79EGpaO06vH5qSu/xr6kQRBOA4h9OqXGS72BGQBH8jMNCoHqgJrIADQTHX\n" + "H85RYF38bH6Ycp18jch0KVmYwKeiaLNfMDngnAv6wMDONJz761GBtrG1/wIDAQAB\n" + "AoGAPjYeNSzOUICwcyO7E3Omji/tVgHso3EiYznPbvfGgrHUavXhMs7iHm9WrLCp\n" + "oUChYl/ADNOACICayHc2WeWPfxJ26BF0ahTzOX1fJsg++JDweCYCNN2WrrYcyA9o\n" + "XDU18IFh2dY2CvPL8G7ex5WEq9nYTASQzRfC899nTvUSTyECQQDZddRhqF9g6Zc9\n" + "vuSjwQf+dMztsvhLVPAPaSdgE4LMa4nE2iNC/sLq1uUEwrrrOKGaFB9IXeIU7hPW\n" + "2QmgJewxAkEAx65IjpesMEq+zE5qRPYkfxjdaa0gNBCfATEBGI4bTx37cKskf49W\n" + "2qFlombE9m9t/beYXVC++2W40i53ov+pLwJALRp0X4EFr1sjxGnIkHJkDxH4w0CA\n" + "oVdPp1KfGR1S3sVbQNohwC6JDR5fR/p/vHP1iLituFvInaC3urMvfOkAsQJBAJg9\n" + "0gYdr+O16Vi95JoljNf2bkG3BJmNnp167ln5ZurgcieJ5K7464CPk3zJnBxEAvlx\n" + "dFKZULM98DcXxJFbGXMCQC2ZkPFgzMlRwYu4gake2ruOQR9N3HzLoau1jqDrgh6U\n" + "Ow3ylw8RWPq4zmLkDPn83DFMBquYsg3yzBPi7PANBO4=\n" + "-----END RSA PRIVATE KEY-----\n"; String samlKeyPassphrase = "password"; String samlCertificate = "-----BEGIN CERTIFICATE-----\n" + "MIID4zCCA0ygAwIBAgIJAJdmwmBdhEydMA0GCSqGSIb3DQEBBQUAMIGoMQswCQYD\n" + "VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xJzAl\n" + "BgNVBAoTHkNsb3VkIEZvdW5kcnkgRm91bmRhdGlvbiwgSW5jLjEMMAoGA1UECxMD\n" + "VUFBMRIwEAYDVQQDEwlsb2NhbGhvc3QxKTAnBgkqhkiG9w0BCQEWGmNmLWlkZW50\n" + "aXR5LWVuZ0BwaXZvdGFsLmlvMB4XDTE2MDIxNjIyMTMzN1oXDTE2MDMxNzIyMTMz\n" + "N1owgagxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZy\n" + "YW5jaXNjbzEnMCUGA1UEChMeQ2xvdWQgRm91bmRyeSBGb3VuZGF0aW9uLCBJbmMu\n" + "MQwwCgYDVQQLEwNVQUExEjAQBgNVBAMTCWxvY2FsaG9zdDEpMCcGCSqGSIb3DQEJ\n" + "ARYaY2YtaWRlbnRpdHktZW5nQHBpdm90YWwuaW8wgZ8wDQYJKoZIhvcNAQEBBQAD\n" + "gY0AMIGJAoGBAKmeo9CIMJ8ljWFVpBRkbpGzVZ3cWY/URK03vWFd5c4uiDme+lof\n" + "jk/e/v0Qalo7Tq8fmpK7/GvqRBEE4DiH06pcZLvYEZAEfyMw0KgeqAmsgANBMdcf\n" + "zlFgXfxsfphynXyNyHQpWZjAp6Jos18wOeCcC/rAwM40nPvrUYG2sbX/AgMBAAGj\n" + "ggERMIIBDTAdBgNVHQ4EFgQUdiixDfiZ61ljk7J/uUYcay26n5swgd0GA1UdIwSB\n" + "1TCB0oAUdiixDfiZ61ljk7J/uUYcay26n5uhga6kgaswgagxCzAJBgNVBAYTAlVT\n" + "MQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEnMCUGA1UEChMe\n" + "Q2xvdWQgRm91bmRyeSBGb3VuZGF0aW9uLCBJbmMuMQwwCgYDVQQLEwNVQUExEjAQ\n" + "BgNVBAMTCWxvY2FsaG9zdDEpMCcGCSqGSIb3DQEJARYaY2YtaWRlbnRpdHktZW5n\n" + "QHBpdm90YWwuaW+CCQCXZsJgXYRMnTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB\n" + "BQUAA4GBAAPf/SPl/LuVYrl0HDUU8YDR3N7Fi4OjhF3+n+uBYRhO+9IbQ/t1sC1p\n" + "enWhiAfyZtgFv2OmjvtFyty9YqHhIPAg9Ceod37Q7HNSG04vbYHNJ6XhGUzacMj8\n" + "hQ1ZzQBv+CaKWZarBIql/TsxtpvvXhaE4QqR4NvUDnESHtxefriv\n" + "-----END CERTIFICATE-----\n"; samlConfig.setCertificate(samlCertificate); samlConfig.setPrivateKey(samlPrivateKey); samlConfig.setPrivateKeyPassword(samlKeyPassphrase); IdentityZoneConfiguration definition = new IdentityZoneConfiguration(tokenPolicy); identityZone.setConfig(definition.setSamlConfig(samlConfig)); for (String url : BASE_URLS) { getMockMvc().perform( post(url) .header("Authorization", "Bearer " + identityClientZonesReadToken) .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(identityZone))) .andExpect(status().isForbidden()); } getMockMvc().perform( post("/identity-zones") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(identityZone))) .andExpect(status().isCreated()); checkZoneAuditEventInUaa(1, AuditEventType.IdentityZoneCreatedEvent); IdentityProviderProvisioning idpp = (IdentityProviderProvisioning) getWebApplicationContext().getBean("identityProviderProvisioning"); IdentityProvider idp1 = idpp.retrieveByOrigin(UAA, identityZone.getId()); IdentityProvider idp2 = idpp.retrieveByOrigin(UAA, IdentityZone.getUaa().getId()); assertNotEquals(idp1, idp2); IdentityZoneProvisioning identityZoneProvisioning = (IdentityZoneProvisioning) getWebApplicationContext().getBean("identityZoneProvisioning"); IdentityZone createdZone = identityZoneProvisioning.retrieve(id); assertEquals(JsonUtils.writeValueAsString(definition), JsonUtils.writeValueAsString(createdZone.getConfig())); assertEquals(samlCertificate, createdZone.getConfig().getSamlConfig().getCertificate()); assertEquals(samlPrivateKey, createdZone.getConfig().getSamlConfig().getPrivateKey()); } @Test public void testCreateZoneWithInvalidPrimarySigningKeyId() throws Exception { String id = UUID.randomUUID().toString(); IdentityZone identityZone = getIdentityZone(id); TokenPolicy tokenPolicy = identityZone.getConfig().getTokenPolicy(); Map<String, String> jwtKeys = new HashMap<>(); jwtKeys.put("key_id_1", "secret_key_1"); jwtKeys.put("key_id_2", "secret_key_2"); tokenPolicy.setKeys(jwtKeys); tokenPolicy.setActiveKeyId("nonexistent_key"); getMockMvc().perform( post("/identity-zones") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(identityZone))) .andExpect(status().isUnprocessableEntity()); } @Test public void testCreateZoneWithNoActiveKeyId() throws Exception { String id = UUID.randomUUID().toString(); IdentityZone identityZone = getIdentityZone(id); TokenPolicy tokenPolicy = identityZone.getConfig().getTokenPolicy(); Map<String, String> jwtKeys = new HashMap<>(); jwtKeys.put("key_id_1", "secret_key_1"); jwtKeys.put("key_id_2", "secret_key_2"); jwtKeys.put("key_id_3", "secret_key_3"); tokenPolicy.setKeys(jwtKeys); getMockMvc().perform( post("/identity-zones") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(identityZone))) .andExpect(status().isCreated()); } @Test public void testCreateZoneWithRefreshTokenConfig() throws Exception { String id = UUID.randomUUID().toString(); IdentityZone identityZone = getIdentityZone(id); TokenPolicy tokenPolicy = identityZone.getConfig().getTokenPolicy(); tokenPolicy.setRefreshTokenFormat(OPAQUE.getStringValue().toUpperCase()); tokenPolicy.setRefreshTokenUnique(true); getMockMvc().perform( post("/identity-zones") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(identityZone))) .andExpect(status().isCreated()) .andExpect(jsonPath("$.config.tokenPolicy.refreshTokenUnique").value(true)) .andExpect(jsonPath("$.config.tokenPolicy.refreshTokenFormat").value(OPAQUE.getStringValue())); IdentityZone createdZone = provisioning.retrieve(id); assertEquals(OPAQUE.getStringValue(), createdZone.getConfig().getTokenPolicy().getRefreshTokenFormat()); assertTrue(createdZone.getConfig().getTokenPolicy().isRefreshTokenUnique()); } @Test public void testCreateZoneWithInvalidSamlKeyCertPair() throws Exception { String id = UUID.randomUUID().toString(); IdentityZone identityZone = getIdentityZone(id); TokenPolicy tokenPolicy = new TokenPolicy(3600, 7200); Map<String, String> jwtKeys = new HashMap<>(); jwtKeys.put("key_id_1", "secret_key_1"); jwtKeys.put("key_id_2", "secret_key_2"); tokenPolicy.setKeys(jwtKeys); tokenPolicy.setActiveKeyId("key_id_1"); SamlConfig samlConfig = new SamlConfig(); String samlPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n" + "Proc-Type: 4,ENCRYPTED\n" + "DEK-Info: DES-EDE3-CBC,5771044F3450A262\n" + "\n" + "VfRgIdzq/TUFdIwTOxochDs02sSQXA/Z6mRnffYTQMwXpQ5f5nRuqcY8zECGMaDe\n" + "aLrndpWzGbxiePKgN5AxuIDYNnKMrDRgyCzaaPx66rb87oMwtuq1HM18qqs+yN5v\n" + "CdsoS2uz57fCDI24BuJkIDSIeumLXc5MdN0HUeaxOVzmpbpsbBXjRYa24gW38mUh\n" + "DzmOAsNDxfoSTox02Cj+GV024e+PiWR6AMA7RKhsKPf9F4ctWwozvEHrV8fzTy5B\n" + "+KM361P7XwJYueiV/gMZW2DXSujNRBEVfC1CLaxDV3eVsFX5iIiUbc4JQYOM6oQ3\n" + "KxGPImcRQPY0asKgEDIaWtysUuBoDSbfQ/FxGWeqwR6P/Vth4dXzVGheYLu1V1CU\n" + "o6M+EXC/VUhERKwi13EgqXLKrDI352/HgEKG60EhM6xIJy9hLHy0UGjdHDcA+cF6\n" + "NEl6E3CivddMHIPQWil5x4AMaevGa3v/gcZI0DN8t7L1g4fgjtSPYzvwmOxoxHGi\n" + "7V7PdzaD4GWV75fv99sBlq2e0KK9crNUzs7vbFA/m6tgNA628SGhU1uAc/5xOskI\n" + "0Ez6kjgHoh4U7t/fu7ey1MbFQt6byHY9lk27nW1ub/QMAaRJ+EDnrReB/NN6q5Vu\n" + "h9eQNniNOeQfflzFyPB9omLNsVJkENn+lZNNrrlbn8OmJ0pT58Iaetfh79rDZPw9\n" + "zmHVqmMynmecTWAcA9ATf7+lh+xV88JDjQkLcG/3WEXNH7HXKO00pUa8+JtyxbAb\n" + "dAwGgrjJkbbk1qLLScOqY4mA5WXa5+80LMkCYO44vVTp2VKmnxj8Mw==\n" + "-----END RSA PRIVATE KEY-----\n"; String samlKeyPassphrase = "password"; String samlCertificate = "-----BEGIN CERTIFICATE-----\n" + "MIIEbzCCA1egAwIBAgIQCTPRC15ZcpIxJwdwiMVDSjANBgkqhkiG9w0BAQUFADA2\n" + "MQswCQYDVQQGEwJOTDEPMA0GA1UEChMGVEVSRU5BMRYwFAYDVQQDEw1URVJFTkEg\n" + "U1NMIENBMB4XDTEzMDczMDAwMDAwMFoXDTE2MDcyOTIzNTk1OVowPzEhMB8GA1UE\n" + "CxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMRowGAYDVQQDExFlZHVyb2FtLmJi\n" + "ay5hYy51azCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANrSBWTl56O2\n" + "VJbahURgPznums43Nnn/smJ6cGywPu4mtJHUHSmONlBDTAWFS1fLkh8YHIQmdwYg\n" + "FY4pHjZmKVtJ6ZOFhDNN1R2VMka4ZtREWn3XX8pUacol5KjEIh6U/FvMHyRv7sV5\n" + "9J6JUK+n5R7ZsSu7XRi6TrT3xhfu0KoWo8RM/salKo2theIcyqLPHiFLEtA7ISLV\n" + "q7I49uj9h9Hni/iCpBey+Gn5yDub4nrv81aDfD6zDoW/vXIOrcXFYRK3lXWOOFi4\n" + "cfmu4SQQwMV1jBOer8JgfsQ3EQMgwauSMLUR31wPM83eMbOC72HhW9SJUtFDj42c\n" + "PIEWd+rTA8ECAwEAAaOCAW4wggFqMB8GA1UdIwQYMBaAFAy9k2gM896ro0lrKzdX\n" + "R+qQ47ntMB0GA1UdDgQWBBQgoU+Pbgk2MthczZt7TviUiIWyrjAOBgNVHQ8BAf8E\n" + "BAMCBaAwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH\n" + "AwIwIgYDVR0gBBswGTANBgsrBgEEAbIxAQICHTAIBgZngQwBAgEwOgYDVR0fBDMw\n" + "MTAvoC2gK4YpaHR0cDovL2NybC50Y3MudGVyZW5hLm9yZy9URVJFTkFTU0xDQS5j\n" + "cmwwbQYIKwYBBQUHAQEEYTBfMDUGCCsGAQUFBzAChilodHRwOi8vY3J0LnRjcy50\n" + "ZXJlbmEub3JnL1RFUkVOQVNTTENBLmNydDAmBggrBgEFBQcwAYYaaHR0cDovL29j\n" + "c3AudGNzLnRlcmVuYS5vcmcwHAYDVR0RBBUwE4IRZWR1cm9hbS5iYmsuYWMudWsw\n" + "DQYJKoZIhvcNAQEFBQADggEBAHTw5b1lrTBqnx/QSO50Mww+OPYgV4b4NSu2rqxG\n" + "I2hHLiD4l7Sk3WOdXPAQMmTlo6N10Lt6p8gLLxKsOAw+nK+z9aLcgKk9/kYoe4C8\n" + "jHzwTy6eO+sCKnJfTqEX8p3b8l736lUWwPgMjjEN+d49ZegqCwH6SEz7h0+DwGmF\n" + "LLfFM8J1SozgPVXgmfCv0XHpFyYQPhXligeWk39FouC2DfhXDTDOgc0n/UQjETNl\n" + "r2Jawuw1VG6/+EFf4qjwr0/hIrxc/0XEd9+qLHKef1rMjb9pcZA7Dti+DoKHsxWi\n" + "yl3DnNZlj0tFP0SBcwjg/66VAekmFtJxsLx3hKxtYpO3m8c=\n" + "-----END CERTIFICATE-----\n"; samlConfig.setCertificate(samlCertificate); samlConfig.setPrivateKey(samlPrivateKey); samlConfig.setPrivateKeyPassword(samlKeyPassphrase); IdentityZoneConfiguration definition = new IdentityZoneConfiguration(tokenPolicy); identityZone.setConfig(definition.setSamlConfig(samlConfig)); getMockMvc().perform( post("/identity-zones") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(identityZone))) .andExpect(status().isUnprocessableEntity()); } @Test public void test_delete_zone_cleans_db() throws Exception { IdentityProviderProvisioning idpp = getWebApplicationContext().getBean(JdbcIdentityProviderProvisioning.class); ScimGroupProvisioning groupProvisioning = getWebApplicationContext().getBean(ScimGroupProvisioning.class); ScimUserProvisioning userProvisioning = getWebApplicationContext().getBean(ScimUserProvisioning.class); ScimGroupMembershipManager membershipManager = getWebApplicationContext().getBean(ScimGroupMembershipManager.class); ScimGroupExternalMembershipManager externalMembershipManager = getWebApplicationContext().getBean(ScimGroupExternalMembershipManager.class); ApprovalStore approvalStore = getWebApplicationContext().getBean(ApprovalStore.class); JdbcTemplate template = getWebApplicationContext().getBean(JdbcTemplate.class); String id = generator.generate(); IdentityZone zone = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); //create zone and clients BaseClientDetails client = new BaseClientDetails("limited-client", null, "openid", "authorization_code", "uaa.resource"); client.setClientSecret("secret"); client.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, Collections.singletonList(UAA)); client.addAdditionalInformation("foo", "bar"); for (String url : Arrays.asList("", "/")) { getMockMvc().perform( post("/identity-zones/" + zone.getId() + "/clients" + url) .header("Authorization", "Bearer " + identityClientZonesReadToken) .contentType(APPLICATION_JSON) .accept(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(client))) .andExpect(status().isForbidden()); } MvcResult result = getMockMvc().perform( post("/identity-zones/" + zone.getId() + "/clients") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .accept(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(client))) .andExpect(status().isCreated()).andReturn(); BaseClientDetails created = JsonUtils.readValue(result.getResponse().getContentAsString(), BaseClientDetails.class); assertNull(created.getClientSecret()); assertEquals("zones.write", created.getAdditionalInformation().get(ClientConstants.CREATED_WITH)); assertEquals(Collections.singletonList(UAA), created.getAdditionalInformation().get(ClientConstants.ALLOWED_PROVIDERS)); assertEquals("bar", created.getAdditionalInformation().get("foo")); //ensure that UAA provider is there assertNotNull(idpp.retrieveByOrigin(UAA, zone.getId())); assertEquals(UAA, idpp.retrieveByOrigin(UAA, zone.getId()).getOriginKey()); //create login-server provider IdentityProvider provider = new IdentityProvider() .setOriginKey(LOGIN_SERVER) .setActive(true) .setIdentityZoneId(zone.getId()) .setName("Delete Test") .setType(LOGIN_SERVER); IdentityZoneHolder.set(zone); provider = idpp.create(provider); assertNotNull(idpp.retrieveByOrigin(LOGIN_SERVER, zone.getId())); assertEquals(provider.getId(), idpp.retrieveByOrigin(LOGIN_SERVER, zone.getId()).getId()); //create user and add user to group ScimUser user = getScimUser(); user.setOrigin(LOGIN_SERVER); user = userProvisioning.createUser(user, ""); assertNotNull(userProvisioning.retrieve(user.getId())); assertEquals(zone.getId(), user.getZoneId()); //create group ScimGroup group = new ScimGroup("Delete Test Group"); group.setZoneId(zone.getId()); group = groupProvisioning.create(group); membershipManager.addMember(group.getId(), new ScimGroupMember(user.getId(), ScimGroupMember.Type.USER, Arrays.asList(ScimGroupMember.Role.MEMBER))); assertEquals(zone.getId(), group.getZoneId()); assertNotNull(groupProvisioning.retrieve(group.getId())); assertEquals("Delete Test Group", groupProvisioning.retrieve(group.getId()).getDisplayName()); assertEquals(1, membershipManager.getMembers(group.getId(), null, false).size()); //failed authenticated user getMockMvc().perform( post("/login.do") .header("Host", zone.getSubdomain() + ".localhost") .with(cookieCsrf()) .accept(TEXT_HTML_VALUE) .param("username", user.getUserName()) .param("password", "adasda") ) .andExpect(status().isFound()); //ensure we have some audit records //this doesn't work yet //assertThat(template.queryForObject("select count(*) from sec_audit where identity_zone_id=?", new Object[] {user.getZoneId()}, Integer.class), greaterThan(0)); //create an external group map IdentityZoneHolder.set(zone); ScimGroupExternalMember externalMember = externalMembershipManager.mapExternalGroup(group.getId(), "externalDeleteGroup", LOGIN_SERVER); assertEquals(1, externalMembershipManager.getExternalGroupMapsByGroupId(group.getId(), LOGIN_SERVER).size()); assertThat(template.queryForObject("select count(*) from external_group_mapping where origin=?", new Object[]{LOGIN_SERVER}, Integer.class), is(1)); //add user approvals approvalStore.addApproval( new Approval() .setClientId(client.getClientId()) .setScope("openid") .setStatus(Approval.ApprovalStatus.APPROVED) .setUserId(user.getId()) ); assertEquals(1, approvalStore.getApprovals(user.getId(), client.getClientId()).size()); //perform zone delete getMockMvc().perform( delete("/identity-zones/{id}", zone.getId()) .header("Authorization", "Bearer " + identityClientToken) .accept(APPLICATION_JSON)) .andExpect(status().isOk()); getMockMvc().perform( delete("/identity-zones/{id}", zone.getId()) .header("Authorization", "Bearer " + identityClientToken) .accept(APPLICATION_JSON)) .andExpect(status().isNotFound()); assertThat(template.queryForObject("select count(*) from identity_zone where id=?", new Object[]{zone.getId()}, Integer.class), is(0)); assertThat(template.queryForObject("select count(*) from oauth_client_details where identity_zone_id=?", new Object[]{zone.getId()}, Integer.class), is(0)); assertThat(template.queryForObject("select count(*) from groups where identity_zone_id=?", new Object[]{zone.getId()}, Integer.class), is(0)); assertThat(template.queryForObject("select count(*) from sec_audit where identity_zone_id=?", new Object[]{zone.getId()}, Integer.class), is(0)); assertThat(template.queryForObject("select count(*) from users where identity_zone_id=?", new Object[]{zone.getId()}, Integer.class), is(0)); assertThat(template.queryForObject("select count(*) from external_group_mapping where origin=?", new Object[]{LOGIN_SERVER}, Integer.class), is(0)); try { externalMembershipManager.getExternalGroupMapsByGroupId(group.getId(), LOGIN_SERVER); fail("no external groups should be found"); } catch (ScimResourceNotFoundException e) { } assertThat(template.queryForObject("select count(*) from authz_approvals where user_id=?", new Object[]{user.getId()}, Integer.class), is(0)); assertEquals(0, approvalStore.getApprovals(user.getId(), client.getClientId()).size()); } @Test public void testDeleteZonePublishesEvent() throws Exception { String id = generator.generate(); IdentityZone zone = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); uaaEventListener.clearEvents(); ResultActions result = getMockMvc().perform( delete("/identity-zones/{id}", zone.getId()) .header("Authorization", "Bearer " + identityClientToken) .accept(APPLICATION_JSON)) .andExpect(status().isOk()); IdentityZone deletedZone = JsonUtils.readValue(result.andReturn().getResponse().getContentAsString(), IdentityZone.class); assertEquals(Collections.EMPTY_MAP, deletedZone.getConfig().getTokenPolicy().getKeys()); assertNull(deletedZone.getConfig().getSamlConfig().getPrivateKey()); assertNull(deletedZone.getConfig().getSamlConfig().getPrivateKeyPassword()); assertEquals(serviceProviderCertificate, deletedZone.getConfig().getSamlConfig().getCertificate()); assertThat(uaaEventListener.getEventCount(), is(1)); AbstractUaaEvent event = uaaEventListener.getLatestEvent(); assertThat(event, instanceOf(EntityDeletedEvent.class)); EntityDeletedEvent deletedEvent = (EntityDeletedEvent) event; assertThat(deletedEvent.getDeleted(), instanceOf(IdentityZone.class)); deletedZone = (IdentityZone) deletedEvent.getDeleted(); assertThat(deletedZone.getId(), is(id)); assertThat(deletedEvent.getIdentityZone().getId(), is(id)); String auditedIdentityZone = deletedEvent.getAuditEvent().getData(); assertThat(auditedIdentityZone, containsString(id)); } @Test public void testCreateAndDeleteLimitedClientInNewZoneUsingZoneEndpoint() throws Exception { String id = generator.generate(); IdentityZone zone = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); BaseClientDetails client = new BaseClientDetails("limited-client", null, "openid", "authorization_code", "uaa.resource"); client.setClientSecret("secret"); client.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, Collections.singletonList(UAA)); client.addAdditionalInformation("foo", "bar"); for (String url : Arrays.asList("", "/")) { getMockMvc().perform( post("/identity-zones/" + zone.getId() + "/clients" + url) .header("Authorization", "Bearer " + identityClientZonesReadToken) .contentType(APPLICATION_JSON) .accept(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(client))) .andExpect(status().isForbidden()); } MvcResult result = getMockMvc().perform( post("/identity-zones/" + zone.getId() + "/clients") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .accept(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(client))) .andExpect(status().isCreated()).andReturn(); BaseClientDetails created = JsonUtils.readValue(result.getResponse().getContentAsString(), BaseClientDetails.class); assertNull(created.getClientSecret()); assertEquals("zones.write", created.getAdditionalInformation().get(ClientConstants.CREATED_WITH)); assertEquals(Collections.singletonList(UAA), created.getAdditionalInformation().get(ClientConstants.ALLOWED_PROVIDERS)); assertEquals("bar", created.getAdditionalInformation().get("foo")); checkAuditEventListener(1, AuditEventType.ClientCreateSuccess, clientCreateEventListener, id, "http://localhost:8080/uaa/oauth/token", "identity"); for (String url : Arrays.asList("", "/")) { getMockMvc().perform( delete("/identity-zones/" + zone.getId() + "/clients/" + created.getClientId(), IdentityZone.getUaa().getId() + url) .header("Authorization", "Bearer " + identityClientZonesReadToken) .accept(APPLICATION_JSON)) .andExpect(status().isForbidden()); } getMockMvc().perform( delete("/identity-zones/" + zone.getId() + "/clients/" + created.getClientId(), IdentityZone.getUaa().getId()) .header("Authorization", "Bearer " + identityClientToken) .accept(APPLICATION_JSON)) .andExpect(status().isOk()); checkAuditEventListener(1, AuditEventType.ClientDeleteSuccess, clientDeleteEventListener, id, "http://localhost:8080/uaa/oauth/token", "identity"); } @Test public void testCreateAndDeleteLimitedClientInUAAZoneReturns403() throws Exception { BaseClientDetails client = new BaseClientDetails("limited-client", null, "openid", "authorization_code", "uaa.resource"); client.setClientSecret("secret"); client.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, Collections.singletonList(UAA)); getMockMvc().perform( post("/identity-zones/uaa/clients") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .accept(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(client))) .andExpect(status().isForbidden()); assertEquals(0, clientCreateEventListener.getEventCount()); getMockMvc().perform( delete("/identity-zones/uaa/clients/admin") .header("Authorization", "Bearer " + identityClientToken) .accept(APPLICATION_JSON)) .andExpect(status().isForbidden()); assertEquals(0, clientDeleteEventListener.getEventCount()); } @Test public void testCreateAdminClientInNewZoneUsingZoneEndpointReturns400() throws Exception { String id = generator.generate(); IdentityZone zone = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); BaseClientDetails client = new BaseClientDetails("admin-client", null, null, "client_credentials", "clients.write"); client.setClientSecret("secret"); getMockMvc().perform( post("/identity-zones/" + zone.getId() + "/clients") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .accept(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(client))) .andExpect(status().isBadRequest()); } @Test public void testCreatesZonesWithDuplicateSubdomains() throws Exception { String subdomain = UUID.randomUUID().toString(); String id1 = UUID.randomUUID().toString(); String id2 = UUID.randomUUID().toString(); IdentityZone identityZone1 = MultitenancyFixture.identityZone(id1, subdomain); IdentityZone identityZone2 = MultitenancyFixture.identityZone(id2, subdomain); getMockMvc().perform( post("/identity-zones") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .accept(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(identityZone1))) .andExpect(status().isCreated()); checkZoneAuditEventInUaa(1, AuditEventType.IdentityZoneCreatedEvent); getMockMvc().perform( post("/identity-zones") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .accept(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(identityZone2))) .andExpect(status().isConflict()); assertEquals(1, zoneModifiedEventListener.getEventCount()); } @Test public void testZoneAdminTokenAgainstZoneEndpoints() throws Exception { String zone1 = generator.generate().toLowerCase(); String zone2 = generator.generate().toLowerCase(); IdentityZoneCreationResult result1 = MockMvcUtils.utils().createOtherIdentityZoneAndReturnResult(zone1, getMockMvc(), getWebApplicationContext(), null); IdentityZoneCreationResult result2 = MockMvcUtils.utils().createOtherIdentityZoneAndReturnResult(zone2, getMockMvc(), getWebApplicationContext(), null); MvcResult result = getMockMvc().perform( get("/identity-zones") .header("Authorization", "Bearer " + result1.getZoneAdminToken()) .header(IdentityZoneSwitchingFilter.HEADER, result1.getIdentityZone().getId()) .accept(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); //test read your own zone only List<IdentityZone> zones = JsonUtils.readValue(result.getResponse().getContentAsString(), new TypeReference<List<IdentityZone>>() { }); assertEquals(1, zones.size()); assertEquals(zone1, zones.get(0).getSubdomain()); //test write your own getMockMvc().perform( put("/identity-zones/" + result1.getIdentityZone().getId()) .header("Authorization", "Bearer " + result1.getZoneAdminToken()) .header(IdentityZoneSwitchingFilter.HEADER, result1.getIdentityZone().getId()) .contentType(APPLICATION_JSON) .accept(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(result1.getIdentityZone()))) .andExpect(status().isOk()); //test write someone elses getMockMvc().perform( put("/identity-zones/" + result2.getIdentityZone().getId()) .header("Authorization", "Bearer " + result1.getZoneAdminToken()) .header(IdentityZoneSwitchingFilter.HEADER, result1.getIdentityZone().getId()) .contentType(APPLICATION_JSON) .accept(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(result2.getIdentityZone()))) .andExpect(status().isForbidden()); //test create as zone admin getMockMvc().perform( post("/identity-zones") .header("Authorization", "Bearer " + result1.getZoneAdminToken()) .header(IdentityZoneSwitchingFilter.HEADER, result1.getIdentityZone().getId()) .contentType(APPLICATION_JSON) .accept(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(result2.getIdentityZone()))) .andExpect(status().isForbidden()); } @Test public void testSuccessfulUserManagementInZoneUsingAdminClient() throws Exception { String subdomain = generator.generate().toLowerCase(); BaseClientDetails adminClient = new BaseClientDetails("admin", null, null, "client_credentials", "scim.read,scim.write"); adminClient.setClientSecret("admin-secret"); IdentityZoneCreationResult creationResult = mockMvcUtils.createOtherIdentityZoneAndReturnResult(subdomain, getMockMvc(), getWebApplicationContext(), adminClient); IdentityZone identityZone = creationResult.getIdentityZone(); checkZoneAuditEventInUaa(1, AuditEventType.IdentityZoneCreatedEvent); checkAuditEventListener(1, AuditEventType.GroupCreatedEvent, groupModifiedEventListener, IdentityZone.getUaa().getId(), "http://localhost:8080/uaa/oauth/token", "identity"); checkAuditEventListener(1, AuditEventType.ClientCreateSuccess, clientCreateEventListener, identityZone.getId(), "http://localhost:8080/uaa/oauth/token", creationResult.getZoneAdminUser().getId()); String scimAdminToken = testClient.getClientCredentialsOAuthAccessToken("admin", "admin-secret", "scim.write,scim.read", subdomain); ScimUser user = createUser(scimAdminToken, subdomain); checkAuditEventListener(1, AuditEventType.UserCreatedEvent, userModifiedEventListener, identityZone.getId(), "http://" + subdomain + ".localhost:8080/uaa/oauth/token", "admin"); user.setUserName("updated-username@test.com"); MockHttpServletRequestBuilder put = put("/Users/" + user.getId()) .header("Authorization", "Bearer " + scimAdminToken) .header("If-Match", "\"" + user.getVersion() + "\"") .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(user)) .with(new SetServerNameRequestPostProcessor(subdomain + ".localhost")); MvcResult result = getMockMvc().perform(put) .andExpect(status().isOk()) .andExpect(jsonPath("$.userName").value(user.getUserName())) .andReturn(); checkAuditEventListener(2, AuditEventType.UserModifiedEvent, userModifiedEventListener, identityZone.getId(), "http://" + subdomain + ".localhost:8080/uaa/oauth/token", "admin"); user = JsonUtils.readValue(result.getResponse().getContentAsString(), ScimUser.class); List<ScimUser> users = getUsersInZone(subdomain, scimAdminToken); assertTrue(users.contains(user)); assertEquals(1, users.size()); MockHttpServletRequestBuilder delete = delete("/Users/" + user.getId()) .header("Authorization", "Bearer " + scimAdminToken) .header("If-Match", "\"" + user.getVersion() + "\"") .contentType(APPLICATION_JSON) .with(new SetServerNameRequestPostProcessor(subdomain + ".localhost")); getMockMvc().perform(delete) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(user.getId())) .andReturn(); checkAuditEventListener(3, AuditEventType.UserDeletedEvent, userModifiedEventListener, identityZone.getId(), "http://" + subdomain + ".localhost:8080/uaa/oauth/token", "admin"); users = getUsersInZone(subdomain, scimAdminToken); assertEquals(0, users.size()); } @Test public void testCreateAndListUsersInOtherZoneIsUnauthorized() throws Exception { String subdomain = generator.generate(); mockMvcUtils.createOtherIdentityZone(subdomain, getMockMvc(), getWebApplicationContext()); checkZoneAuditEventInUaa(1, AuditEventType.IdentityZoneCreatedEvent); String defaultZoneAdminToken = testClient.getClientCredentialsOAuthAccessToken("admin", "adminsecret", "scim.write,scim.read"); ScimUser user = getScimUser(); byte[] requestBody = JsonUtils.writeValueAsBytes(user); MockHttpServletRequestBuilder post = post("/Users") .with(new SetServerNameRequestPostProcessor(subdomain + ".localhost")) .header("Authorization", "Bearer " + defaultZoneAdminToken) .contentType(APPLICATION_JSON) .content(requestBody); getMockMvc().perform(post).andExpect(status().isUnauthorized()); MockHttpServletRequestBuilder get = get("/Users").header("Authorization", "Bearer " + defaultZoneAdminToken); if (subdomain != null && !subdomain.equals("")) get.with(new SetServerNameRequestPostProcessor(subdomain + ".localhost")); getMockMvc().perform(get).andExpect(status().isUnauthorized()).andReturn(); } @Test public void testModifyandDeleteUserInOtherZoneIsUnauthorized() throws Exception { String scimWriteToken = testClient.getClientCredentialsOAuthAccessToken("admin", "adminsecret", "scim.write"); ScimUser user = createUser(scimWriteToken, null); String subdomain = generator.generate(); mockMvcUtils.createOtherIdentityZone(subdomain, getMockMvc(), getWebApplicationContext()); checkZoneAuditEventInUaa(1, AuditEventType.IdentityZoneCreatedEvent); String scimAdminToken = testClient.getClientCredentialsOAuthAccessToken("admin", "admin-secret", "scim.write,scim.read", subdomain); user.setUserName("updated-user@defaultzone.com"); MockHttpServletRequestBuilder put = put("/Users/" + user.getId()) .header("Authorization", "Bearer " + scimAdminToken) .header("If-Match", "\"" + user.getVersion() + "\"") .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(user)); getMockMvc().perform(put) .andExpect(status().isUnauthorized()) .andReturn(); MockHttpServletRequestBuilder delete = delete("/Users/" + user.getId()) .header("Authorization", "Bearer " + scimAdminToken) .header("If-Match", "\"" + user.getVersion() + "\"") .contentType(APPLICATION_JSON); getMockMvc().perform(delete) .andExpect(status().isUnauthorized()) .andReturn(); } @Test public void userCanReadAZone_withZoneZoneIdReadToken() throws Exception { String scimWriteToken = testClient.getClientCredentialsOAuthAccessToken("admin", "adminsecret", "scim.write"); ScimUser user = createUser(scimWriteToken, null); String id = generator.generate().toLowerCase(); IdentityZone identityZone = createZone(id, HttpStatus.CREATED, identityClientToken, new IdentityZoneConfiguration()); for (String displayName : Arrays.asList("read", "admin")) { ScimGroup group = new ScimGroup(); String zoneReadScope = "zones." + identityZone.getId() + "." + displayName; group.setDisplayName(zoneReadScope); group.setMembers(Collections.singletonList(new ScimGroupMember(user.getId()))); getMockMvc().perform( post("/Groups/zones") .header("Authorization", "Bearer " + identityClientToken) .contentType(APPLICATION_JSON) .accept(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(group))) .andExpect(status().isCreated()); } String userAccessToken = mockMvcUtils.getUserOAuthAccessTokenAuthCode(getMockMvc(), "identity", "identitysecret", user.getId(), user.getUserName(), user.getPassword(), "zones." + identityZone.getId() + ".read"); MvcResult result = getMockMvc().perform( get("/identity-zones/" + identityZone.getId()) .header("Authorization", "Bearer " + userAccessToken) .header(IdentityZoneSwitchingFilter.HEADER, identityZone.getId()) .accept(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); IdentityZone zoneResult = JsonUtils.readValue(result.getResponse().getContentAsString(), new TypeReference<IdentityZone>() { }); assertEquals(identityZone, zoneResult); assertNull(zoneResult.getConfig().getSamlConfig().getPrivateKey()); assertEquals(Collections.EMPTY_MAP, zoneResult.getConfig().getTokenPolicy().getKeys()); String userAccessTokenReadAndAdmin = mockMvcUtils.getUserOAuthAccessTokenAuthCode(getMockMvc(), "identity", "identitysecret", user.getId(), user.getUserName(), user.getPassword(), "zones." + identityZone.getId() + ".read " + "zones." + identityZone.getId() + ".admin "); result = getMockMvc().perform( get("/identity-zones/" + identityZone.getId()) .header("Authorization", "Bearer " + userAccessTokenReadAndAdmin) .header(IdentityZoneSwitchingFilter.HEADER, identityZone.getId()) .accept(APPLICATION_JSON)) .andExpect(status().isOk()) .andReturn(); zoneResult = JsonUtils.readValue(result.getResponse().getContentAsString(), new TypeReference<IdentityZone>() { }); assertEquals(identityZone, zoneResult); assertNull(zoneResult.getConfig().getSamlConfig().getPrivateKey()); assertEquals(serviceProviderCertificate, zoneResult.getConfig().getSamlConfig().getCertificate()); assertNull(zoneResult.getConfig().getSamlConfig().getPrivateKeyPassword()); assertEquals(Collections.EMPTY_MAP, zoneResult.getConfig().getTokenPolicy().getKeys()); assertEquals("kid", zoneResult.getConfig().getTokenPolicy().getActiveKeyId()); } private IdentityZone getIdentityZone(String id, HttpStatus expect, String token) throws Exception { MvcResult result = getMockMvc().perform( get("/identity-zones/" + id) .header("Authorization", "Bearer " + token)) .andExpect(status().is(expect.value())) .andReturn(); if (expect.is2xxSuccessful()) { return JsonUtils.readValue(result.getResponse().getContentAsString(), IdentityZone.class); } return null; } private IdentityZone createZone(String id, HttpStatus expect, String token, IdentityZoneConfiguration zoneConfiguration) throws Exception { IdentityZone identityZone = getIdentityZone(id); identityZone.setConfig(zoneConfiguration); identityZone.getConfig().getSamlConfig().setPrivateKey(serviceProviderKey); identityZone.getConfig().getSamlConfig().setPrivateKeyPassword(serviceProviderKeyPassword); identityZone.getConfig().getSamlConfig().setCertificate(serviceProviderCertificate); Map<String, String> keys = new HashMap<>(); keys.put("kid", "key"); identityZone.getConfig().getTokenPolicy().setKeys(keys); identityZone.getConfig().getTokenPolicy().setActiveKeyId("kid"); MvcResult result = getMockMvc().perform( post("/identity-zones") .header("Authorization", "Bearer " + token) .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(identityZone))) .andExpect(status().is(expect.value())) .andReturn(); if (expect.is2xxSuccessful()) { return JsonUtils.readValue(result.getResponse().getContentAsString(), IdentityZone.class); } return null; } private IdentityZone updateZone(IdentityZone identityZone, HttpStatus expect, String token) throws Exception { MvcResult result = getMockMvc().perform( put("/identity-zones/" + identityZone.getId()) .header("Authorization", "Bearer " + token) .contentType(APPLICATION_JSON) .content(JsonUtils.writeValueAsString(identityZone))) .andExpect(status().is(expect.value())) .andReturn(); if (expect.is2xxSuccessful()) { return JsonUtils.readValue(result.getResponse().getContentAsString(), IdentityZone.class); } return null; } private <T extends AbstractUaaEvent> void checkZoneAuditEventInUaa(int eventCount, AuditEventType eventType) { checkAuditEventListener(eventCount, eventType, zoneModifiedEventListener, IdentityZone.getUaa().getId(), "http://localhost:8080/uaa/oauth/token", "identity"); } private <T extends AbstractUaaEvent> void checkAuditEventListener(int eventCount, AuditEventType eventType, TestApplicationEventListener<T> eventListener, String identityZoneId, String issuer, String subject) { T event = eventListener.getLatestEvent(); assertEquals(eventCount, eventListener.getEventCount()); if (eventCount > 0) { assertEquals(eventType, event.getAuditEvent().getType()); assertEquals(identityZoneId, event.getAuditEvent().getIdentityZoneId()); String origin = event.getAuditEvent().getOrigin(); if (hasText(origin) && !origin.contains("opaque-token=present")) { assertTrue(origin.contains("iss=" + issuer)); assertTrue(origin.contains("sub=" + subject)); } } } private IdentityZone getIdentityZone(String id) { IdentityZone identityZone = new IdentityZone(); identityZone.setId(id); identityZone.setSubdomain(hasText(id) ? id : new RandomValueStringGenerator().generate()); identityZone.setName("The Twiglet Zone"); identityZone.setDescription("Like the Twilight Zone but tastier."); return identityZone; } private List<ScimUser> getUsersInZone(String subdomain, String token) throws Exception { MockHttpServletRequestBuilder get = get("/Users").header("Authorization", "Bearer " + token); if (subdomain != null && !subdomain.equals("")) get.with(new SetServerNameRequestPostProcessor(subdomain + ".localhost")); MvcResult mvcResult = getMockMvc().perform(get).andExpect(status().isOk()).andReturn(); JsonNode root = JsonUtils.readTree(mvcResult.getResponse().getContentAsString()); return JsonUtils.readValue(root.get("resources").toString(), new TypeReference<List<ScimUser>>() { }); } }