/******************************************************************************* * Cloud Foundry * Copyright (c) [2009-2016] Pivotal Software, Inc. All Rights Reserved. * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. * * This product includes a number of subcomponents with * separate copyright notices and license terms. Your use of these * subcomponents is subject to the terms and conditions of the * subcomponent's license, as noted in the LICENSE file. *******************************************************************************/ package org.cloudfoundry.identity.uaa.scim.endpoints; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cloudfoundry.identity.uaa.constants.OriginKeys; import org.cloudfoundry.identity.uaa.resources.SearchResults; import org.cloudfoundry.identity.uaa.resources.jdbc.JdbcPagingListFactory; import org.cloudfoundry.identity.uaa.resources.jdbc.LimitSqlAdapterFactory; import org.cloudfoundry.identity.uaa.scim.ScimGroup; import org.cloudfoundry.identity.uaa.scim.ScimGroupExternalMember; import org.cloudfoundry.identity.uaa.scim.ScimGroupMember; import org.cloudfoundry.identity.uaa.scim.ScimUser; import org.cloudfoundry.identity.uaa.scim.bootstrap.ScimExternalGroupBootstrap; import org.cloudfoundry.identity.uaa.scim.exception.InvalidScimResourceException; import org.cloudfoundry.identity.uaa.scim.exception.ScimException; import org.cloudfoundry.identity.uaa.scim.exception.ScimResourceAlreadyExistsException; import org.cloudfoundry.identity.uaa.scim.exception.ScimResourceNotFoundException; import org.cloudfoundry.identity.uaa.scim.jdbc.JdbcScimGroupExternalMembershipManager; import org.cloudfoundry.identity.uaa.scim.jdbc.JdbcScimGroupMembershipManager; import org.cloudfoundry.identity.uaa.scim.jdbc.JdbcScimGroupProvisioning; import org.cloudfoundry.identity.uaa.scim.jdbc.JdbcScimUserProvisioning; import org.cloudfoundry.identity.uaa.scim.test.TestUtils; import org.cloudfoundry.identity.uaa.scim.validate.PasswordValidator; import org.cloudfoundry.identity.uaa.security.SecurityContextAccessor; import org.cloudfoundry.identity.uaa.test.JdbcTestBase; import org.cloudfoundry.identity.uaa.web.ExceptionReportHttpMessageConverter; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; import org.springframework.http.converter.HttpMessageConversionException; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.web.HttpMediaTypeException; import org.springframework.web.servlet.View; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class ScimGroupEndpointsTests extends JdbcTestBase { Log logger = LogFactory.getLog(getClass()); private volatile JdbcScimGroupProvisioning dao; private volatile JdbcScimUserProvisioning udao; private volatile JdbcScimGroupMembershipManager mm; private volatile JdbcScimGroupExternalMembershipManager em; private volatile ScimExternalGroupBootstrap externalGroupBootstrap; private volatile ScimGroupEndpoints endpoints; private volatile ScimUserEndpoints userEndpoints; private volatile List<String> groupIds; private volatile List<String> userIds; private static final String SQL_INJECTION_FIELDS = "displayName,version,created,lastModified"; @Rule public ExpectedException expectedEx = ExpectedException.none(); @Before public void initScimGroupEndpointsTests() throws Exception { TestUtils.deleteFrom(dataSource, "users", "groups", "group_membership"); JdbcTemplate template = jdbcTemplate; JdbcPagingListFactory pagingListFactory = new JdbcPagingListFactory(template, LimitSqlAdapterFactory.getLimitSqlAdapter()); dao = new JdbcScimGroupProvisioning(template, pagingListFactory); udao = new JdbcScimUserProvisioning(template, pagingListFactory); mm = new JdbcScimGroupMembershipManager(template, pagingListFactory); mm.setScimGroupProvisioning(dao); mm.setScimUserProvisioning(udao); mm.setDefaultUserGroups(Collections.singleton("uaa.user")); em = new JdbcScimGroupExternalMembershipManager(template, pagingListFactory); em.setScimGroupProvisioning(dao); endpoints = new ScimGroupEndpoints(dao, mm); endpoints.setExternalMembershipManager(em); userEndpoints = new ScimUserEndpoints(); userEndpoints.setScimUserProvisioning(udao); userEndpoints.setScimGroupMembershipManager(mm); userEndpoints.setPasswordValidator(mock(PasswordValidator.class)); groupIds = new ArrayList<String>(); userIds = new ArrayList<String>(); groupIds.add(addGroup("uaa.resource", Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN), createMember(ScimGroupMember.Type.GROUP, ScimGroupMember.GROUP_MEMBER), createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))) ); groupIds.add(addGroup("uaa.admin", Collections.<ScimGroupMember> emptyList())); groupIds.add(addGroup("uaa.none", Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_MEMBER), createMember(ScimGroupMember.Type.GROUP, ScimGroupMember.GROUP_ADMIN))) ); externalGroupBootstrap = new ScimExternalGroupBootstrap(dao, em); externalGroupBootstrap.setAddNonExistingGroups(true); Map<String, Map<String, List>> externalGroups = new HashMap<>(); Map<String, List> externalToInternalMap = new HashMap<>(); externalToInternalMap.put("cn=test_org,ou=people,o=springsource,o=org", Collections.singletonList("organizations.acme")); externalToInternalMap.put("cn=developers,ou=scopes,dc=test,dc=com", Collections.singletonList("internal.read")); externalToInternalMap.put("cn=operators,ou=scopes,dc=test,dc=com", Collections.singletonList("internal.write")); externalToInternalMap.put("cn=superusers,ou=scopes,dc=test,dc=com", Arrays.asList("internal.everything", "internal.superuser")); externalGroups.put(OriginKeys.LDAP, externalToInternalMap); externalGroupBootstrap.setExternalGroupMaps(externalGroups); externalGroupBootstrap.afterPropertiesSet(); } private String addGroup(String name, List<ScimGroupMember> m) { ScimGroup g = new ScimGroup(null, name, IdentityZoneHolder.get().getId()); g = dao.create(g); for (ScimGroupMember member : m) { mm.addMember(g.getId(), member); } return g.getId(); } private ScimGroupMember createMember(ScimGroupMember.Type t, List<ScimGroupMember.Role> a) { String id = UUID.randomUUID().toString(); if (t == ScimGroupMember.Type.USER) { id = userEndpoints.createUser(TestUtils.scimUserInstance(id), new MockHttpServletRequest(), new MockHttpServletResponse()).getId(); userIds.add(id); } else { id = dao.create(new ScimGroup(null, id, IdentityZoneHolder.get().getId())).getId(); groupIds.add(id); } return new ScimGroupMember(id, t, a); } private void deleteGroup(String name) { for (ScimGroup g : dao.query("displayName eq \"" + name + "\"")) { dao.delete(g.getId(), g.getVersion()); mm.removeMembersByGroupId(g.getId()); } } private void validateSearchResults(SearchResults<?> results, int expectedSize) { assertNotNull(results); assertNotNull(results.getResources()); assertEquals(expectedSize, results.getResources().size()); } private void validateGroup(ScimGroup g, String expectedName, int expectedMemberCount) { assertNotNull(g); assertNotNull(g.getId()); assertNotNull(g.getVersion()); assertEquals(expectedName, g.getDisplayName()); assertNotNull(g.getMembers()); assertEquals(expectedMemberCount, g.getMembers().size()); } private void validateUserGroups(String id, String... gnm) { ScimUser user = userEndpoints.getUser(id, new MockHttpServletResponse()); Set<String> expectedAuthorities = new HashSet<String>(); expectedAuthorities.addAll(Arrays.asList(gnm)); expectedAuthorities.add("uaa.user"); assertNotNull(user.getGroups()); logger.debug("user's groups: " + user.getGroups() + ", expecting: " + expectedAuthorities); assertEquals(expectedAuthorities.size(), user.getGroups().size()); for (ScimUser.Group g : user.getGroups()) { assertTrue(expectedAuthorities.contains(g.getDisplay())); } } private SecurityContextAccessor mockSecurityContextAccessor(String userId) { SecurityContextAccessor sca = mock(SecurityContextAccessor.class); when(sca.getUserId()).thenReturn(userId); when(sca.isUser()).thenReturn(true); return sca; } @Test public void testListGroups() throws Exception { validateSearchResults(endpoints.listGroups("id,displayName", "id pr", "created", "ascending", 1, 100), 11); } @Test public void testListGroups_Without_Description() throws Exception { validateSearchResults(endpoints.listGroups("id,displayName,description", "id pr", "created", "ascending", 1, 100), 11); validateSearchResults(endpoints.listGroups("id,displayName,meta.lastModified", "id pr", "created", "ascending", 1, 100), 11); validateSearchResults(endpoints.listGroups("id,displayName,zoneId", "id pr", "created", "ascending", 1, 100), 11); } @Test public void testListExternalGroups() throws Exception { validateSearchResults(endpoints.getExternalGroups(1, 100, ""), 5); } @Test public void testListExternalGroupsInvalidFilter() throws Exception { try { endpoints.getExternalGroups(1, 100, "dasda dasdas dasdas"); }catch (ScimException x) { assertTrue(x.getMessage().startsWith("Invalid filter")); } } @Test public void mapExternalGroup_truncatesLeadingAndTrailingSpaces_InExternalGroupName() throws Exception { ScimGroupExternalMember member = getScimGroupExternalMember(); assertEquals("external_group_id", member.getExternalGroup()); } @Test public void unmapExternalGroup_truncatesLeadingAndTrailingSpaces_InExternalGroupName() throws Exception { ScimGroupExternalMember member = getScimGroupExternalMember(); member = endpoints.unmapExternalGroup(member.getGroupId(), " \nexternal_group_id\n", OriginKeys.LDAP); assertEquals("external_group_id", member.getExternalGroup()); } @Test public void unmapExternalGroupUsingName_truncatesLeadingAndTrailingSpaces_InExternalGroupName() throws Exception { ScimGroupExternalMember member = getScimGroupExternalMember(); member = endpoints.unmapExternalGroupUsingName(member.getDisplayName(), " \nexternal_group_id\n"); assertEquals("external_group_id", member.getExternalGroup()); } private ScimGroupExternalMember getScimGroupExternalMember() { ScimGroupExternalMember member = new ScimGroupExternalMember(groupIds.get(0), " external_group_id "); member = endpoints.mapExternalGroup(member); return member; } @Test public void testFindPageOfIds() { SearchResults<?> results = endpoints.listGroups("id", "id pr", null, "ascending", 1, 1); assertEquals(11, results.getTotalResults()); assertEquals(1, results.getResources().size()); } @Test public void testFindMultiplePagesOfIds() { int pageSize = dao.getPageSize(); dao.setPageSize(1); try { SearchResults<?> results = endpoints.listGroups("id", "id pr", null, "ascending", 1, 100); assertEquals(11, results.getTotalResults()); assertEquals(11, results.getResources().size()); } finally { dao.setPageSize(pageSize); } } @Test public void testListGroupsWithNameEqFilter() { validateSearchResults(endpoints.listGroups("id,displayName", "displayName eq \"uaa.user\"", "created", "ascending", 1, 100), 1); } @Test public void testListGroupsWithNameCoFilter() { validateSearchResults(endpoints.listGroups("id,displayName", "displayName co \"admin\"", "created", "ascending", 1, 100), 1); } @Test public void testListGroupsWithInvalidFilterFails() { expectedEx.expect(ScimException.class); expectedEx.expectMessage("Invalid filter expression"); endpoints.listGroups("id,displayName", "displayName cr \"admin\"", "created", "ascending", 1, 100); } @Test public void testListGroupsWithInvalidAttributes() { validateSearchResults(endpoints.listGroups("id,displayNameee", "displayName co \"admin\"", "created", "ascending", 1, 100), 1); } @Test public void testListGroupsWithNullAttributes() { validateSearchResults(endpoints.listGroups(null, "displayName co \"admin\"", "created", "ascending", 1, 100), 1); } @Test public void testSqlInjectionAttackFailsCorrectly() { expectedEx.expect(ScimException.class); expectedEx.expectMessage("Invalid filter expression"); endpoints.listGroups("id,display", "displayName='something'; select " + SQL_INJECTION_FIELDS + " from groups where displayName='something'", "created", "ascending", 1, 100); } @Test public void legacyTestListGroupsWithNameEqFilter() { validateSearchResults(endpoints.listGroups("id,displayName", "displayName eq 'uaa.user'", "created", "ascending", 1, 100), 1); } @Test public void legacyTestListGroupsWithNameCoFilter() { validateSearchResults(endpoints.listGroups("id,displayName", "displayName co 'admin'", "created", "ascending", 1, 100), 1); } @Test public void legacyTestListGroupsWithInvalidFilterFails() { expectedEx.expect(ScimException.class); expectedEx.expectMessage("Invalid filter expression"); endpoints.listGroups("id,displayName", "displayName cr 'admin'", "created", "ascending", 1, 100); } @Test public void legacyTestListGroupsWithInvalidAttributes() { validateSearchResults(endpoints.listGroups("id,displayNameee", "displayName co 'admin'", "created", "ascending", 1, 100), 1); } @Test public void legacyTestListGroupsWithNullAttributes() { validateSearchResults(endpoints.listGroups(null, "displayName co 'admin'", "created", "ascending", 1, 100), 1); } @Test public void testGetGroup() throws Exception { MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); ScimGroup g = endpoints.getGroup(groupIds.get(groupIds.size() - 1), httpServletResponse); validateGroup(g, "uaa.none", 2); assertEquals("\"0\"", httpServletResponse.getHeader("ETag")); } @Test public void testGetNonExistentGroupFails() { expectedEx.expect(ScimResourceNotFoundException.class); endpoints.getGroup("wrongid", new MockHttpServletResponse()); } @Test public void testCreateGroup() throws Exception { ScimGroup g = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); ScimGroup g1 = endpoints.createGroup(g, httpServletResponse); assertEquals("\"0\"", httpServletResponse.getHeader("ETag")); validateGroup(g1, "clients.read", 1); validateUserGroups(g.getMembers().get(0).getMemberId(), "clients.read"); deleteGroup("clients.read"); } @Test public void testCreateExistingGroupFails() { ScimGroup g = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); endpoints.createGroup(g, new MockHttpServletResponse()); try { endpoints.createGroup(g, new MockHttpServletResponse()); fail("must have thrown exception"); } catch (ScimResourceAlreadyExistsException ex) { validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.read\"", "id", "ASC", 1, 100), 1); } deleteGroup("clients.read"); } @Test public void testCreateGroupWithInvalidMemberFails() { ScimGroup g = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g.setMembers(Arrays.asList(new ScimGroupMember("non-existent id", ScimGroupMember.Type.USER,ScimGroupMember.GROUP_ADMIN))); try { endpoints.createGroup(g, new MockHttpServletResponse()); fail("must have thrown exception"); } catch (InvalidScimResourceException ex) { // ensure that the group was not created validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.read\"", "id", "ASC", 1, 100), 0); } } @Test public void testUpdateGroup() throws Exception { ScimGroup g = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g = endpoints.createGroup(g, new MockHttpServletResponse()); validateUserGroups(g.getMembers().get(0).getMemberId(), "clients.read"); g.setDisplayName("superadmin"); g.getMembers().get(0).setRoles(ScimGroupMember.GROUP_MEMBER); MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); ScimGroup g1 = endpoints.updateGroup(g, g.getId(), "*", httpServletResponse); assertEquals("\"1\"", httpServletResponse.getHeader("ETag")); validateGroup(g1, "superadmin", 1); assertEquals(ScimGroupMember.GROUP_MEMBER, g1.getMembers().get(0).getRoles()); validateUserGroups(g.getMembers().get(0).getMemberId(), "superadmin"); } @Test public void testUpdateGroupQuotedEtag() throws Exception { ScimGroup g = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g = endpoints.createGroup(g, new MockHttpServletResponse()); validateUserGroups(g.getMembers().get(0).getMemberId(), "clients.read"); g.setDisplayName("superadmin"); g.getMembers().get(0).setRoles(ScimGroupMember.GROUP_MEMBER); MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); ScimGroup g1 = endpoints.updateGroup(g, g.getId(), "\"*\"", httpServletResponse); assertEquals("\"1\"", httpServletResponse.getHeader("ETag")); validateGroup(g1, "superadmin", 1); assertEquals(ScimGroupMember.GROUP_MEMBER, g1.getMembers().get(0).getRoles()); validateUserGroups(g.getMembers().get(0).getMemberId(), "superadmin"); } @Test public void testUpdateGroupRemoveMembers() throws Exception { ScimGroup g = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g = endpoints.createGroup(g, new MockHttpServletResponse()); validateUserGroups(g.getMembers().get(0).getMemberId(), "clients.read"); g.setDisplayName("superadmin"); g.setMembers(new ArrayList<ScimGroupMember>()); MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); ScimGroup g1 = endpoints.updateGroup(g, g.getId(), "*", httpServletResponse); assertEquals("\"1\"", httpServletResponse.getHeader("ETag")); validateGroup(g1, "superadmin", 0); } @Test(expected = ScimException.class) public void testUpdateGroupNullEtag() throws Exception { ScimGroup g = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g = endpoints.createGroup(g, new MockHttpServletResponse()); validateUserGroups(g.getMembers().get(0).getMemberId(), "clients.read"); g.setDisplayName("superadmin"); g.getMembers().get(0).setRoles(ScimGroupMember.GROUP_MEMBER); MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); endpoints.updateGroup(g, g.getId(), null, httpServletResponse); } @Test(expected = ScimException.class) public void testUpdateGroupNoEtag() throws Exception { ScimGroup g = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g = endpoints.createGroup(g, new MockHttpServletResponse()); validateUserGroups(g.getMembers().get(0).getMemberId(), "clients.read"); g.setDisplayName("superadmin"); g.getMembers().get(0).setRoles(ScimGroupMember.GROUP_MEMBER); MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); endpoints.updateGroup(g, g.getId(), "", httpServletResponse); } @Test(expected = ScimException.class) public void testUpdateGroupInvalidEtag() throws Exception { ScimGroup g = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g = endpoints.createGroup(g, new MockHttpServletResponse()); validateUserGroups(g.getMembers().get(0).getMemberId(), "clients.read"); g.setDisplayName("superadmin"); g.getMembers().get(0).setRoles(ScimGroupMember.GROUP_MEMBER); MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); endpoints.updateGroup(g, g.getId(), "abc", httpServletResponse); } @Test public void testUpdateNonUniqueDisplayNameFails() { ScimGroup g1 = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g1.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g1 = endpoints.createGroup(g1, new MockHttpServletResponse()); ScimGroup g2 = new ScimGroup(null, "clients.write", IdentityZoneHolder.get().getId()); g2.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g2 = endpoints.createGroup(g2, new MockHttpServletResponse()); g1.setDisplayName("clients.write"); try { endpoints.updateGroup(g1, g1.getId(), "*", new MockHttpServletResponse()); fail("must have thrown exception"); } catch (InvalidScimResourceException ex) { validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.write\"", "id", "ASC", 1, 100), 1); validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.read\"", "id", "ASC", 1, 100), 1); } deleteGroup("clients.read"); deleteGroup("clients.write"); } @Test public void testUpdateWithInvalidMemberFails() { ScimGroup g1 = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g1.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g1 = endpoints.createGroup(g1, new MockHttpServletResponse()); g1.setMembers( Arrays.asList( new ScimGroupMember("non-existent id", ScimGroupMember.Type.USER,ScimGroupMember.GROUP_ADMIN) ) ); g1.setDisplayName("clients.write"); try { endpoints.updateGroup(g1, g1.getId(), "*", new MockHttpServletResponse()); fail("must have thrown exception"); } catch (ScimException ex) { // ensure that displayName was not updated g1 = endpoints.getGroup(g1.getId(), new MockHttpServletResponse()); validateGroup(g1, "clients.read", 0); validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.write\"", "id", "ASC", 1, 100), 0); } deleteGroup("clients.read"); } @Test public void testUpdateInvalidVersionFails() { ScimGroup g1 = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g1.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g1 = endpoints.createGroup(g1, new MockHttpServletResponse()); g1.setDisplayName("clients.write"); try { endpoints.updateGroup(g1, g1.getId(), "version", new MockHttpServletResponse()); } catch (ScimException ex) { assertTrue("Wrong exception message", ex.getMessage().contains("Invalid version")); validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.write\"", "id", "ASC", 1, 100), 0); validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.read\"", "id", "ASC", 1, 100), 1); } deleteGroup("clients.read"); } @Test public void testUpdateGroupWithNullEtagFails() { ScimGroup g1 = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g1.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g1 = endpoints.createGroup(g1, new MockHttpServletResponse()); g1.setDisplayName("clients.write"); try { endpoints.updateGroup(g1, g1.getId(), null, new MockHttpServletResponse()); } catch (ScimException ex) { assertTrue("Wrong exception message", ex.getMessage().contains("Missing If-Match")); validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.write\"", "id", "ASC", 1, 100), 0); validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.read\"", "id", "ASC", 1, 100), 1); } deleteGroup("clients.read"); } @Test public void testUpdateWithQuotedVersionSucceeds() { ScimGroup g1 = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g1.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g1 = endpoints.createGroup(g1, new MockHttpServletResponse()); g1.setDisplayName("clients.write"); endpoints.updateGroup(g1, g1.getId(), "\"*", new MockHttpServletResponse()); endpoints.updateGroup(g1, g1.getId(), "*\"", new MockHttpServletResponse()); validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.write\"", "id", "ASC", 1, 100), 1); validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.read\"", "id", "ASC", 1, 100), 0); deleteGroup("clients.write"); } @Test public void testUpdateWrongVersionFails() { ScimGroup g1 = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g1.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g1 = endpoints.createGroup(g1, new MockHttpServletResponse()); g1.setDisplayName("clients.write"); try { endpoints.updateGroup(g1, g1.getId(), String.valueOf(g1.getVersion() + 23), new MockHttpServletResponse()); } catch (ScimException ex) { validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.write\"", "id", "ASC", 1, 100), 0); validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.read\"", "id", "ASC", 1, 100), 1); } deleteGroup("clients.read"); } @Test public void testUpdateGroupWithNoMembers() { ScimGroup g = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g = endpoints.createGroup(g, new MockHttpServletResponse()); validateUserGroups(g.getMembers().get(0).getMemberId(), "clients.read"); g.setDisplayName("someadmin"); g.setMembers(null); ScimGroup g1 = endpoints.updateGroup(g, g.getId(), "*", new MockHttpServletResponse()); validateGroup(g1, "someadmin", 0); deleteGroup("clients.read"); } @Test public void testDeleteGroup() throws Exception { ScimGroup g = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g = endpoints.createGroup(g, new MockHttpServletResponse()); validateUserGroups(g.getMembers().get(0).getMemberId(), "clients.read"); g = endpoints.deleteGroup(g.getId(), "*", new MockHttpServletResponse()); try { endpoints.getGroup(g.getId(), new MockHttpServletResponse()); fail("group should not exist"); } catch (ScimResourceNotFoundException ex) { } validateUserGroups(g.getMembers().get(0).getMemberId(), "uaa.user"); } @Test public void testDeleteWrongVersionFails() { ScimGroup g = new ScimGroup(null, "clients.read", IdentityZoneHolder.get().getId()); g.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); g = endpoints.createGroup(g, new MockHttpServletResponse()); try { endpoints.deleteGroup(g.getId(), String.valueOf(g.getVersion() + 3), new MockHttpServletResponse()); } catch (ScimException ex) { validateSearchResults(endpoints.listGroups("id", "displayName eq \"clients.read\"", "id", "ASC", 1, 100), 1); } deleteGroup("clients.read"); } @Test public void testDeleteNonExistentGroupFails() { expectedEx.expect(ScimResourceNotFoundException.class); endpoints.deleteGroup("some id", "*", new MockHttpServletResponse()); } @Test public void testExceptionHandler() { Map<Class<? extends Exception>, HttpStatus> map = new HashMap<Class<? extends Exception>, HttpStatus>(); map.put(IllegalArgumentException.class, HttpStatus.BAD_REQUEST); map.put(UnsupportedOperationException.class, HttpStatus.BAD_REQUEST); map.put(BadSqlGrammarException.class, HttpStatus.BAD_REQUEST); map.put(DataIntegrityViolationException.class, HttpStatus.BAD_REQUEST); map.put(HttpMessageConversionException.class, HttpStatus.BAD_REQUEST); map.put(HttpMediaTypeException.class, HttpStatus.BAD_REQUEST); endpoints.setStatuses(map); endpoints.setMessageConverters(new HttpMessageConverter<?>[] { new ExceptionReportHttpMessageConverter() }); MockHttpServletRequest request = new MockHttpServletRequest(); validateView(endpoints.handleException(new ScimResourceNotFoundException(""), request), HttpStatus.NOT_FOUND); validateView(endpoints.handleException(new UnsupportedOperationException(""), request), HttpStatus.BAD_REQUEST); validateView(endpoints.handleException(new BadSqlGrammarException("", "", null), request), HttpStatus.BAD_REQUEST); validateView(endpoints.handleException(new IllegalArgumentException(""), request), HttpStatus.BAD_REQUEST); validateView(endpoints.handleException(new DataIntegrityViolationException(""), request), HttpStatus.BAD_REQUEST); } private void validateView(View view, HttpStatus status) { MockHttpServletResponse response = new MockHttpServletResponse(); try { view.render(new HashMap<String, Object>(), new MockHttpServletRequest(), response); assertNotNull(response.getContentAsString()); } catch (Exception e) { fail("view should render correct status and body"); } assertEquals(status.value(), response.getStatus()); } @Test public void testPatch() { ScimGroup g1 = new ScimGroup(null, "name", IdentityZoneHolder.get().getId()); g1.setDescription("description"); g1 = dao.create(g1); ScimGroup patch = new ScimGroup("NewName"); patch.setId(g1.getId()); patch = endpoints.patchGroup(patch, patch.getId(), Integer.toString(g1.getVersion()), new MockHttpServletResponse()); assertEquals("NewName", patch.getDisplayName()); assertEquals(g1.getDescription(), patch.getDescription()); } @Test(expected=ScimException.class) public void testPatchInvalidResourceFails() { ScimGroup g1 = new ScimGroup(null, "name", IdentityZoneHolder.get().getId()); g1.setDescription("description"); ScimGroup patch = endpoints.patchGroup(g1, "id", "0", new MockHttpServletResponse()); } @Test public void testPatchAddMembers(){ ScimGroup g1 = new ScimGroup(null, "name", IdentityZoneHolder.get().getId()); g1.setDescription("description"); g1 = dao.create(g1); ScimGroup patch = new ScimGroup(); assertEquals(null, g1.getMembers()); assertEquals(null, patch.getMembers()); patch.setMembers(Arrays.asList(createMember(ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN))); assertEquals(1, patch.getMembers().size()); patch = endpoints.patchGroup(patch, g1.getId(), "0", new MockHttpServletResponse()); assertEquals(1, patch.getMembers().size()); ScimGroupMember member = patch.getMembers().get(0); assertEquals(ScimGroupMember.Type.USER, member.getType()); assertEquals(ScimGroupMember.GROUP_ADMIN, member.getRoles()); } @Test(expected = ScimException.class) public void testPatchIncorrectEtagFails() { ScimGroup g1 = new ScimGroup(null, "name", IdentityZoneHolder.get().getId()); g1.setDescription("description"); g1 = dao.create(g1); ScimGroup patch = new ScimGroup("NewName"); patch.setId(g1.getId()); patch = endpoints.patchGroup(patch, patch.getId(), Integer.toString(g1.getVersion() +1), new MockHttpServletResponse()); } }