/******************************************************************************* * 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.jdbc; import org.cloudfoundry.identity.uaa.audit.event.EntityDeletedEvent; import org.cloudfoundry.identity.uaa.constants.OriginKeys; import org.cloudfoundry.identity.uaa.provider.IdentityProvider; import org.cloudfoundry.identity.uaa.resources.jdbc.JdbcPagingListFactory; import org.cloudfoundry.identity.uaa.scim.ScimGroup; import org.cloudfoundry.identity.uaa.scim.ScimGroupMember; import org.cloudfoundry.identity.uaa.scim.exception.InvalidScimResourceException; import org.cloudfoundry.identity.uaa.scim.exception.MemberNotFoundException; import org.cloudfoundry.identity.uaa.scim.exception.ScimResourceNotFoundException; import org.cloudfoundry.identity.uaa.scim.test.TestUtils; import org.cloudfoundry.identity.uaa.test.JdbcTestBase; import org.cloudfoundry.identity.uaa.user.UaaUser; import org.cloudfoundry.identity.uaa.user.UaaUserPrototype; import org.cloudfoundry.identity.uaa.zone.IdentityZone; import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder; import org.cloudfoundry.identity.uaa.zone.MultitenancyFixture; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.util.RandomValueStringGenerator; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; import static org.cloudfoundry.identity.uaa.constants.OriginKeys.LDAP; import static org.cloudfoundry.identity.uaa.constants.OriginKeys.LOGIN_SERVER; import static org.cloudfoundry.identity.uaa.constants.OriginKeys.UAA; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; public class JdbcScimGroupMembershipManagerTests extends JdbcTestBase { private JdbcScimGroupProvisioning gdao; private JdbcScimUserProvisioning udao; private JdbcScimGroupMembershipManager dao; private static final String addUserSqlFormat = "insert into users (id, username, password, email, givenName, familyName, phoneNumber, authorities ,identity_zone_id) values ('%s','%s','%s','%s','%s','%s','%s','%s','%s')"; private static final String addGroupSqlFormat = "insert into groups (id, displayName, identity_zone_id) values ('%s','%s','%s')"; private static final String addMemberSqlFormat = "insert into group_membership (group_id, member_id, member_type, authorities, origin, identity_zone_id) values ('%s', '%s', '%s', '%s', '%s', 'uaa')"; private static final String addExternalMapSql = "insert into external_group_mapping (group_id, external_group, added, origin, identity_zone_id) values (?, ?, ?, ?, 'uaa')"; private RandomValueStringGenerator generator = new RandomValueStringGenerator(); private IdentityZone zone = MultitenancyFixture.identityZone(generator.generate(), generator.generate()); private JdbcScimGroupExternalMembershipManager egdao; @Before public void initJdbcScimGroupMembershipManagerTests() { JdbcTemplate template = new JdbcTemplate(dataSource); JdbcPagingListFactory pagingListFactory = new JdbcPagingListFactory(template, limitSqlAdapter); udao = new JdbcScimUserProvisioning(template, pagingListFactory); gdao = new JdbcScimGroupProvisioning(template, pagingListFactory); dao = new JdbcScimGroupMembershipManager(template, pagingListFactory); dao.setScimGroupProvisioning(gdao); dao.setScimUserProvisioning(udao); dao.setDefaultUserGroups(Collections.singleton("uaa.user")); egdao = new JdbcScimGroupExternalMembershipManager(jdbcTemplate, pagingListFactory); for (String id : Arrays.asList(zone.getId(), IdentityZone.getUaa().getId())) { String g1 = id.equals(zone.getId()) ? zone.getId()+"-"+"g1" : "g1"; String g2 = id.equals(zone.getId()) ? zone.getId()+"-"+"g2" : "g2"; String g3 = id.equals(zone.getId()) ? zone.getId()+"-"+"g3" : "g3"; String m1 = id.equals(zone.getId()) ? zone.getId()+"-"+"m1" : "m1"; String m2 = id.equals(zone.getId()) ? zone.getId()+"-"+"m2" : "m2"; String m3 = id.equals(zone.getId()) ? zone.getId()+"-"+"m3" : "m3"; addGroup(g1, "test1", id); addGroup(g2, "test2", id); addGroup(g3, "test3", id); addUser(m1, "test", id); addUser(m2, "test", id); addUser(m3, "test", id); mapExternalGroup(g1, g1+"-external", UAA); mapExternalGroup(g2, g2+"-external", LOGIN_SERVER); mapExternalGroup(g3, g3+"-external", UAA); } validateCount(0); } private void mapExternalGroup(String gId, String external, String origin) { Timestamp now = new Timestamp(System.currentTimeMillis()); jdbcTemplate.update(addExternalMapSql, gId, external, now, origin); } private void addMember(String gId, String mId, String mType, String authorities) { addMember(gId,mId,mType,authorities, OriginKeys.UAA); } private void addMember(String gId, String mId, String mType, String authorities, String origin) { gId = IdentityZoneHolder.isUaa() ? gId : IdentityZoneHolder.get().getId()+"-"+gId; mId = IdentityZoneHolder.isUaa() ? mId : IdentityZoneHolder.get().getId()+"-"+mId; jdbcTemplate.execute(String.format(addMemberSqlFormat, gId, mId, mType, authorities, origin)); } private void addGroup(String id, String name, String zoneId) { TestUtils.assertNoSuchUser(jdbcTemplate, "id", id); jdbcTemplate.execute(String.format(addGroupSqlFormat, id, name, zoneId)); } private void addUser(String id, String password, String zoneId) { TestUtils.assertNoSuchUser(jdbcTemplate, "id", id); jdbcTemplate.execute(String.format(addUserSqlFormat, id, id, password, id, id, id, id, "", zoneId)); } private void validateCount(int expected) { validateCount(expected, "No message given."); } private void validateCount(int expected, String msg) { int existingMemberCount = jdbcTemplate.queryForObject("select count(*) from groups g, group_membership gm where g.identity_zone_id=? and gm.group_id=g.id", new Object[] {IdentityZoneHolder.get().getId()}, Integer.class); assertEquals(msg, expected, existingMemberCount); } private void validateUserGroups(String id, String... gNm) { Set<ScimGroup> directGroups = dao.getGroupsWithMember(id, false); assertNotNull(directGroups); Set<ScimGroup> indirectGroups = dao.getGroupsWithMember(id, true); indirectGroups.removeAll(directGroups); assertNotNull(indirectGroups); Set<String> expectedAuthorities = Collections.<String> emptySet(); if (gNm != null) { expectedAuthorities = new HashSet<>(Arrays.asList(gNm)); } expectedAuthorities.add("uaa.user"); assertEquals(expectedAuthorities.size(), directGroups.size() + indirectGroups.size()); for (ScimGroup group : directGroups) { assertTrue(expectedAuthorities.contains(group.getDisplayName())); } for (ScimGroup group : indirectGroups) { assertTrue(expectedAuthorities.contains(group.getDisplayName() + ".i")); } } @After public void cleanupDataSource() throws Exception { IdentityZoneHolder.clear(); } @Test public void canQuery_Filter_Has_ZoneIn_Effect() throws Exception { addMembers(); validateCount(4); String id = generator.generate(); IdentityZone zone = MultitenancyFixture.identityZone(id,id); IdentityZoneHolder.set(zone); assertEquals(0,dao.query("origin eq \"" + OriginKeys.UAA + "\"").size()); IdentityZoneHolder.clear(); assertEquals(4,dao.query("origin eq \"" + OriginKeys.UAA + "\"").size()); assertEquals(4,dao.query("origin eq \"" + OriginKeys.UAA + "\"", "member_id", true).size()); assertEquals(4,dao.query("origin eq \"" + OriginKeys.UAA + "\"", "1,2", true).size()); assertEquals(4,dao.query("origin eq \"" + OriginKeys.UAA + "\"", "origin", true).size()); } @Test(expected = IllegalArgumentException.class) public void cannotQuery_Filter_Has_Unknown_Sort() throws Exception { dao.query("origin eq \"" + OriginKeys.UAA + "\"", "unknown,origin", true); } @Test public void canDeleteWithFilter1() throws Exception { addMembers(); validateCount(4); dao.delete("origin eq \"" + OriginKeys.UAA + "\""); validateCount(0); } @Test public void canDeleteWithFilter2() throws Exception { addMembers(); validateCount(4); dao.delete("origin eq \""+ OriginKeys.ORIGIN +"\""); validateCount(4); } @Test public void canDeleteWithFilter3() throws Exception { addMembers(); validateCount(4); dao.delete("member_id eq \"m3\" and origin eq \""+ OriginKeys.UAA +"\""); validateCount(2); } @Test public void canDeleteWithFilter4() throws Exception { addMembers(); validateCount(4); dao.delete("member_id sw \"m\" and origin eq \""+ OriginKeys.UAA +"\""); validateCount(1); } @Test public void canDeleteWithFilter5() throws Exception { addMembers(); validateCount(4); dao.delete("member_id sw \"m\" and origin eq \""+ OriginKeys.LDAP +"\""); validateCount(4); } @Test public void cannot_Delete_With_Filter_Outside_Zone() throws Exception { String id = generator.generate(); addMembers(); validateCount(4); IdentityZone zone = MultitenancyFixture.identityZone(id,id); IdentityZoneHolder.set(zone); dao.delete("member_id eq \"m3\" and origin eq \"" + OriginKeys.UAA + "\""); IdentityZoneHolder.clear(); validateCount(4); } @Test public void canGetGroupsForMember() { addMembers(); Set<ScimGroup> groups = dao.getGroupsWithMember("g2", false); assertNotNull(groups); assertEquals(1, groups.size()); groups = dao.getGroupsWithMember("m3", true); assertNotNull(groups); assertEquals(3, groups.size()); } private void addMembers(String origin) { addMember("g1", "m3", "USER", "READER", origin); addMember("g1", "g2", "GROUP", "READER", origin); addMember("g3", "m2", "USER", "READER,WRITER", origin); addMember("g2", "m3", "USER", "READER", origin); } private void addMembers() { addMembers(OriginKeys.UAA); } @Test public void user_delete_clears_memberships() throws Exception { UaaUserPrototype prototype = new UaaUserPrototype() .withUsername("username") .withEmail("test@test.com"); for (IdentityZone zone : Arrays.asList(this.zone, IdentityZone.getUaa())) { String userId = this.zone.getId().equals(zone.getId()) ? zone.getId()+"-"+"m3" : "m3"; UaaUser user = new UaaUser(prototype.withId(userId).withZoneId(zone.getId())); IdentityZoneHolder.set(zone); addMembers(OriginKeys.LDAP); validateCount(4); IdentityZoneHolder.clear(); gdao.onApplicationEvent(new EntityDeletedEvent<>(user, mock(Authentication.class))); IdentityZoneHolder.set(zone); validateCount(2, "ZoneID: "+zone.getId()); } } @Test public void zone_delete_clears_memberships() throws Exception { for (IdentityZone zone : Arrays.asList(this.zone, IdentityZone.getUaa())) { IdentityZoneHolder.set(zone); addMembers(OriginKeys.LDAP); validateCount(4); IdentityZoneHolder.clear(); gdao.onApplicationEvent(new EntityDeletedEvent<>(zone, mock(Authentication.class))); validateCount(Objects.equals(zone, IdentityZone.getUaa()) ? 4 : 0, "ZoneID: "+zone.getId()); } } @Test public void provider_delete_clears_memberships() throws Exception { for (IdentityZone zone : Arrays.asList(this.zone, IdentityZone.getUaa())) { IdentityZoneHolder.set(zone); addMembers(OriginKeys.LDAP); validateCount(4, "ZoneID: "+zone.getId()); IdentityZoneHolder.clear(); IdentityProvider provider = new IdentityProvider() .setId("ldap-id") .setOriginKey(LDAP) .setIdentityZoneId(zone.getId()); gdao.onApplicationEvent(new EntityDeletedEvent<>(provider, mock(Authentication.class))); IdentityZoneHolder.set(zone); validateCount(0, "ZoneID: "+zone.getId()); } } @Test public void test_zone_deleted() { String zoneAdminId = generator.generate(); addGroup(zoneAdminId, "zones." + zone.getId() + ".admin", IdentityZone.getUaa().getId()); addMember(zoneAdminId, "m1", "USER", "MEMBER", OriginKeys.UAA); IdentityZoneHolder.set(zone); addMembers(); assertThat(jdbcTemplate.queryForObject("select count(*) from group_membership where group_id in (select id from groups where identity_zone_id=?)", new Object[]{IdentityZoneHolder.get().getId()}, Integer.class), is(4)); assertThat(jdbcTemplate.queryForObject("select count(*) from groups where identity_zone_id=?", new Object[]{IdentityZoneHolder.get().getId()}, Integer.class), is(3)); assertThat(jdbcTemplate.queryForObject("select count(*) from external_group_mapping where group_id in (select id from groups where identity_zone_id=?)", new Object[]{IdentityZoneHolder.get().getId()}, Integer.class), is(3)); assertThat(jdbcTemplate.queryForObject("select count(*) from group_membership where group_id in (select id from groups where identity_zone_id=? and displayName like ?)", new Object[]{IdentityZone.getUaa().getId(), "zones." + IdentityZoneHolder.get().getId() + ".%"}, Integer.class), is(1)); assertThat(jdbcTemplate.queryForObject("select count(*) from groups where identity_zone_id=? and displayName like ?", new Object[]{IdentityZone.getUaa().getId(), "zones." + IdentityZoneHolder.get().getId() + ".%"}, Integer.class), is(1)); gdao.onApplicationEvent(new EntityDeletedEvent<>(zone, null)); assertThat(jdbcTemplate.queryForObject("select count(*) from group_membership where group_id in (select id from groups where identity_zone_id=?)", new Object[]{IdentityZoneHolder.get().getId()}, Integer.class), is(0)); assertThat(jdbcTemplate.queryForObject("select count(*) from groups where identity_zone_id=?", new Object[]{IdentityZoneHolder.get().getId()}, Integer.class), is(0)); assertThat(jdbcTemplate.queryForObject("select count(*) from external_group_mapping where group_id in (select id from groups where identity_zone_id=?)", new Object[]{IdentityZoneHolder.get().getId()}, Integer.class), is(0)); assertThat(jdbcTemplate.queryForObject("select count(*) from group_membership where group_id in (select id from groups where identity_zone_id=? and displayName like ?)", new Object[]{IdentityZone.getUaa().getId(), "zones." + IdentityZoneHolder.get().getId() + ".%"}, Integer.class), is(0)); assertThat(jdbcTemplate.queryForObject("select count(*) from groups where identity_zone_id=? and displayName like ?", new Object[]{IdentityZone.getUaa().getId(), "zones." + IdentityZoneHolder.get().getId() + ".%"}, Integer.class), is(0)); } @Test public void test_provider_deleted() { IdentityZoneHolder.set(zone); addMembers(LOGIN_SERVER); assertThat(jdbcTemplate.queryForObject("select count(*) from group_membership where group_id in (select id from groups where identity_zone_id=?)", new Object[] {IdentityZoneHolder.get().getId()}, Integer.class), is(4)); assertThat(jdbcTemplate.queryForObject("select count(*) from groups where identity_zone_id=?", new Object[] {IdentityZoneHolder.get().getId()}, Integer.class), is(3)); assertThat(jdbcTemplate.queryForObject("select count(*) from external_group_mapping where origin = ? and group_id in (select id from groups where identity_zone_id=?)", new Object[] {LOGIN_SERVER, IdentityZoneHolder.get().getId()}, Integer.class), is(1)); IdentityProvider loginServer = new IdentityProvider() .setOriginKey(LOGIN_SERVER) .setIdentityZoneId(zone.getId()); EntityDeletedEvent<IdentityProvider> event = new EntityDeletedEvent<>(loginServer, null); gdao.onApplicationEvent(event); assertThat(jdbcTemplate.queryForObject("select count(*) from group_membership where group_id in (select id from groups where identity_zone_id=?)", new Object[] {IdentityZoneHolder.get().getId()}, Integer.class), is(0)); assertThat(jdbcTemplate.queryForObject("select count(*) from groups where identity_zone_id=?", new Object[] {IdentityZoneHolder.get().getId()}, Integer.class), is(3)); assertThat(jdbcTemplate.queryForObject("select count(*) from external_group_mapping where origin = ? and group_id in (select id from groups where identity_zone_id=?)", new Object[] {LOGIN_SERVER, IdentityZoneHolder.get().getId()}, Integer.class), is(0)); } @Test public void test_cannot_delete_uaa_zone() { addMembers(); assertThat(jdbcTemplate.queryForObject("select count(*) from group_membership where group_id in (select id from groups where identity_zone_id=?)", new Object[] {IdentityZoneHolder.get().getId()}, Integer.class), is(4)); assertThat(jdbcTemplate.queryForObject("select count(*) from groups where identity_zone_id=?", new Object[] {IdentityZoneHolder.get().getId()}, Integer.class), is(4)); gdao.onApplicationEvent(new EntityDeletedEvent<>(IdentityZone.getUaa(), null)); assertThat(jdbcTemplate.queryForObject("select count(*) from group_membership where group_id in (select id from groups where identity_zone_id=?)", new Object[] {IdentityZoneHolder.get().getId()}, Integer.class), is(4)); assertThat(jdbcTemplate.queryForObject("select count(*) from groups where identity_zone_id=?", new Object[] {IdentityZoneHolder.get().getId()}, Integer.class), is(4)); } @Test public void test_cannot_delete_uaa_provider() { IdentityZoneHolder.set(zone); addMembers(LOGIN_SERVER); assertThat(jdbcTemplate.queryForObject("select count(*) from group_membership where group_id in (select id from groups where identity_zone_id=?)", new Object[] {IdentityZoneHolder.get().getId()}, Integer.class), is(4)); assertThat(jdbcTemplate.queryForObject("select count(*) from groups where identity_zone_id=?", new Object[] {IdentityZoneHolder.get().getId()}, Integer.class), is(3)); IdentityProvider loginServer = new IdentityProvider() .setOriginKey(UAA) .setIdentityZoneId(zone.getId()); gdao.onApplicationEvent(new EntityDeletedEvent<>(loginServer, null)); assertThat(jdbcTemplate.queryForObject("select count(*) from group_membership where group_id in (select id from groups where identity_zone_id=?)", new Object[] {IdentityZoneHolder.get().getId()}, Integer.class), is(4)); assertThat(jdbcTemplate.queryForObject("select count(*) from groups where identity_zone_id=?", new Object[] {IdentityZoneHolder.get().getId()}, Integer.class), is(3)); } @Test public void canGetGroupsForMemberEvenWhenCycleExistsInGroupHierarchy() { addMember("g1", "m3", "USER", "READER"); addMember("g1", "g2", "GROUP", "READER"); addMember("g2", "g3", "GROUP", "READER"); addMember("g3", "g1", "GROUP", "READER"); Set<ScimGroup> groups = dao.getGroupsWithMember("m3", true); assertNotNull(groups); assertEquals(4, groups.size()); } @Test public void canAddMember() throws Exception { validateCount(0); ScimGroupMember m1 = new ScimGroupMember("m1", ScimGroupMember.Type.USER, null); ScimGroupMember m2 = dao.addMember("g2", m1); validateCount(1); assertEquals(ScimGroupMember.Type.USER, m2.getType()); assertEquals(ScimGroupMember.GROUP_MEMBER, m2.getRoles()); assertEquals("m1", m2.getMemberId()); validateUserGroups("m1", "test2"); } @Test(expected = ScimResourceNotFoundException.class) public void addMember_In_Different_Zone_Causes_Issues() throws Exception { String subdomain = generator.generate(); IdentityZone otherZone = MultitenancyFixture.identityZone(subdomain, subdomain); IdentityZoneHolder.set(otherZone); ScimGroupMember m1 = new ScimGroupMember("m1", ScimGroupMember.Type.USER, null); m1.setOrigin(OriginKeys.UAA); dao.addMember("g2", m1); } @Test(expected = ScimResourceNotFoundException.class) public void canAddMember_Validate_Origin_and_ZoneId() throws Exception { String subdomain = generator.generate(); IdentityZone otherZone = MultitenancyFixture.identityZone(subdomain, subdomain); IdentityZoneHolder.set(otherZone); validateCount(0); ScimGroupMember m1 = new ScimGroupMember("m1", ScimGroupMember.Type.USER, null); m1.setOrigin(OriginKeys.UAA); dao.addMember("g2", m1); } @Test public void canAddNestedGroupMember() { addMember("g2", "m1", "USER", "READER"); ScimGroupMember g2 = new ScimGroupMember("g2", ScimGroupMember.Type.GROUP, ScimGroupMember.GROUP_ADMIN); g2 = dao.addMember("g1", g2); assertEquals(ScimGroupMember.Type.GROUP, g2.getType()); assertEquals(ScimGroupMember.GROUP_ADMIN, g2.getRoles()); assertEquals("g2", g2.getMemberId()); validateUserGroups("m1", "test1.i", "test2"); } @Test(expected = InvalidScimResourceException.class) public void cannotNestGroupWithinItself() { ScimGroupMember g2 = new ScimGroupMember("g2", ScimGroupMember.Type.GROUP, ScimGroupMember.GROUP_ADMIN); dao.addMember("g2", g2); } @Test public void canGetMembers() throws Exception { addMember("g1", "m1", "USER", "READER"); addMember("g1", "g2", "GROUP", "READER"); addMember("g3", "m2", "USER", "READER,WRITER"); List<ScimGroupMember> members = dao.getMembers("g1", null, false); assertNotNull(members); assertEquals(2, members.size()); members = dao.getMembers("g2", null, false); assertNotNull(members); assertEquals(0, members.size()); } @Test public void canGetMembers_Fails_In_Other_Zone() throws Exception { addMember("g1", "m1", "USER", "READER"); addMember("g1", "g2", "GROUP", "READER"); addMember("g3", "m2", "USER", "READER,WRITER"); IdentityZoneHolder.set(MultitenancyFixture.identityZone(generator.generate(), generator.generate())); assertEquals(0, dao.getMembers("g1", null, false).size()); } @Test public void testBackwardsCompatibilityToMemberAuthorities() { addMember("g1", "m1", "USER", "READ"); addMember("g1", "g2", "GROUP", "member"); addMember("g1", "m2", "USER", "READER,write"); List<ScimGroupMember> members = dao.getMembers("g1", null, false); assertNotNull(members); assertEquals(3, members.size()); List<ScimGroupMember> readers = new ArrayList<ScimGroupMember>(), writers = new ArrayList<ScimGroupMember>(); for (ScimGroupMember member : members) { if (member.getRoles().contains(ScimGroupMember.Role.READER)) { readers.add(member); } if (member.getRoles().contains(ScimGroupMember.Role.WRITER)) { writers.add(member); } } assertEquals(2, readers.size()); assertEquals(1, writers.size()); } @Test public void canGetDefaultGroupsUsingGetGroupsForMember() { Set<ScimGroup> groups = dao.getGroupsWithMember("m1", false); assertNotNull(groups); assertEquals(1, groups.size()); } @Test public void canGetAdminMembers() { addMember("g1", "m3", "USER", "READER,WRITER"); addMember("g1", "g2", "GROUP", "READER"); assertEquals(1, dao.getMembers("g1", ScimGroupMember.Role.WRITER).size()); assertTrue(dao.getMembers("g1", ScimGroupMember.Role.WRITER).contains(new ScimGroupMember("m3"))); assertEquals(0, dao.getMembers("g2", ScimGroupMember.Role.WRITER).size()); } @Test public void canGetMembersByAuthority() { addMember("g1", "m3", "USER", "READER,WRITER"); addMember("g1", "g2", "GROUP", "READER,MEMBER"); addMember("g2", "g3", "GROUP", "MEMBER"); assertEquals(1, dao.getMembers("g1", ScimGroupMember.Role.MEMBER).size()); assertEquals(2, dao.getMembers("g1", ScimGroupMember.Role.READER).size()); assertEquals(1, dao.getMembers("g1", ScimGroupMember.Role.WRITER).size()); assertEquals(1, dao.getMembers("g2", ScimGroupMember.Role.MEMBER).size()); assertEquals(0, dao.getMembers("g2", ScimGroupMember.Role.WRITER).size()); } @Test public void canGetMemberById() throws Exception { addMember("g3", "m2", "USER", "READER,WRITER"); ScimGroupMember m = dao.getMemberById("g3", "m2"); assertEquals(ScimGroupMember.Type.USER, m.getType()); assertEquals(ScimGroupMember.GROUP_ADMIN, m.getRoles()); } @Test public void canUpdateMember() throws Exception { addMember("g1", "m1", "USER", "READER"); validateCount(1); ScimGroupMember m1 = new ScimGroupMember("m1", ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN); ScimGroupMember m2 = dao.updateMember("g1", m1); assertEquals(ScimGroupMember.GROUP_ADMIN, m2.getRoles()); assertNotSame(m1, m2); validateCount(1); validateUserGroups("m1", "test1"); } @Test public void canUpdateOrAddMembers() { dao.addMember("g1", new ScimGroupMember("m1", ScimGroupMember.Type.USER, ScimGroupMember.GROUP_MEMBER)); dao.addMember("g1", new ScimGroupMember("g2", ScimGroupMember.Type.GROUP, ScimGroupMember.GROUP_MEMBER)); dao.addMember("g2", new ScimGroupMember("m2", ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN)); validateCount(3); validateUserGroups("m1", "test1"); validateUserGroups("m2", "test2", "test1.i"); ScimGroupMember g2 = new ScimGroupMember("g2", ScimGroupMember.Type.GROUP, ScimGroupMember.GROUP_ADMIN); ScimGroupMember m3 = new ScimGroupMember("m3", ScimGroupMember.Type.USER, ScimGroupMember.GROUP_MEMBER); List<ScimGroupMember> members = dao.updateOrAddMembers("g1", Arrays.asList(g2, m3)); validateCount(3); assertEquals(2, members.size()); assertTrue(members.contains(new ScimGroupMember("g2", ScimGroupMember.Type.GROUP, null))); assertTrue(members.contains(new ScimGroupMember("m3", ScimGroupMember.Type.USER, null))); assertFalse(members.contains(new ScimGroupMember("m1", ScimGroupMember.Type.USER, null))); validateUserGroups("m3", "test1"); validateUserGroups("m2", "test2", "test1.i"); validateUserGroups("m1"); } @Test public void canRemoveMemberById() throws Exception { addMember("g1", "m1", "USER", "READER"); validateCount(1); dao.removeMemberById("g1", "m1"); validateCount(0); try { dao.getMemberById("g1", "m1"); fail("member should not exist"); } catch (MemberNotFoundException ex) { } } @Test public void canRemoveNestedGroupMember() { dao.addMember("g1", new ScimGroupMember("m1", ScimGroupMember.Type.USER, ScimGroupMember.GROUP_MEMBER)); dao.addMember("g1", new ScimGroupMember("g2", ScimGroupMember.Type.GROUP, ScimGroupMember.GROUP_MEMBER)); dao.addMember("g2", new ScimGroupMember("m2", ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN)); validateCount(3); validateUserGroups("m1", "test1"); validateUserGroups("m2", "test2", "test1.i"); dao.removeMemberById("g1", "g2"); try { dao.getMemberById("g1", "g2"); fail("member should not exist"); } catch (MemberNotFoundException ex) { } validateCount(2); validateUserGroups("m1", "test1"); validateUserGroups("m2", "test2"); } @Test public void canRemoveAllMembers() { dao.addMember("g1", new ScimGroupMember("m1", ScimGroupMember.Type.USER, ScimGroupMember.GROUP_MEMBER)); dao.addMember("g1", new ScimGroupMember("g2", ScimGroupMember.Type.GROUP, ScimGroupMember.GROUP_MEMBER)); dao.addMember("g2", new ScimGroupMember("m2", ScimGroupMember.Type.USER, ScimGroupMember.GROUP_ADMIN)); validateCount(3); validateUserGroups("m1", "test1"); validateUserGroups("m2", "test2", "test1.i"); dao.removeMembersByGroupId("g1"); validateCount(1); try { dao.getMemberById("g1", "m1"); fail("member should not exist"); } catch (MemberNotFoundException ex) { } validateUserGroups("m1"); validateUserGroups("m2", "test2"); } }