KEYCLOAK-14336: LDAP group membership is not visible under "Users in Role" tab for users imported from LDAP

This commit is contained in:
rmartinc 2020-06-01 13:01:39 +02:00 committed by Marek Posolda
parent 25bb2e3ba2
commit 32bf50e037
10 changed files with 312 additions and 17 deletions

View file

@ -408,6 +408,27 @@ public class LDAPStorageProvider implements UserStorageProvider,
return Collections.emptyList();
}
@Override
public List<UserModel> getRoleMembers(RealmModel realm, RoleModel role) {
return getRoleMembers(realm, role, 0, Integer.MAX_VALUE - 1);
}
@Override
public List<UserModel> getRoleMembers(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
List<ComponentModel> mappers = realm.getComponents(model.getId(), LDAPStorageMapper.class.getName());
List<ComponentModel> sortedMappers = mapperManager.sortMappersAsc(mappers);
for (ComponentModel mapperModel : sortedMappers) {
LDAPStorageMapper ldapMapper = mapperManager.getMapper(mapperModel);
List<UserModel> users = ldapMapper.getRoleMembers(realm, role, firstResult, maxResults);
// Sufficient for now
if (users.size() > 0) {
return users;
}
}
return Collections.emptyList();
}
public List<UserModel> loadUsersByUsernames(List<String> usernames, RealmModel realm) {
List<UserModel> result = new ArrayList<>();
for (String username : usernames) {

View file

@ -29,6 +29,7 @@ import org.keycloak.storage.user.SynchronizationResult;
import javax.naming.AuthenticationException;
import java.util.Collections;
import java.util.List;
import org.keycloak.models.RoleModel;
/**
* Stateful per-request object
@ -47,22 +48,27 @@ public abstract class AbstractLDAPStorageMapper implements LDAPStorageMapper {
this.session = ldapProvider.getSession();
}
@Override
public SynchronizationResult syncDataFromFederationProviderToKeycloak(RealmModel realm) {
return new SynchronizationResult();
}
@Override
public SynchronizationResult syncDataFromKeycloakToFederationProvider(RealmModel realm) {
return new SynchronizationResult();
}
@Override
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
return Collections.emptyList();
}
@Override
public List<UserModel> getRoleMembers(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
return Collections.emptyList();
}
@Override
public boolean onAuthenticationFailure(LDAPObject ldapUser, UserModel user, AuthenticationException ldapException, RealmModel realm) {
return false;
}
@ -73,12 +79,11 @@ public abstract class AbstractLDAPStorageMapper implements LDAPStorageMapper {
return Boolean.parseBoolean(paramm);
}
@Override
public LDAPStorageProvider getLdapProvider() {
return ldapProvider;
}
@Override
public void close() {

View file

@ -27,6 +27,8 @@ import org.keycloak.storage.user.SynchronizationResult;
import javax.naming.AuthenticationException;
import java.util.List;
import org.keycloak.models.RoleModel;
import org.keycloak.storage.ldap.LDAPStorageProvider;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -53,6 +55,16 @@ public interface LDAPStorageMapper extends Provider {
*/
List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults);
/**
* Return empty list if doesn't support storing of roles
* @param realm
* @param role
* @param firstResult
* @param maxResults
* @return
*/
List<UserModel> getRoleMembers(RealmModel realm, RoleModel role, int firstResult, int maxResults);
/**
* Called when importing user from LDAP to local keycloak DB.
*
@ -101,4 +113,11 @@ public interface LDAPStorageMapper extends Provider {
* @return true if mapper processed the AuthenticationException and did some actions based on that. In that case, AuthenticationException won't be rethrown!
*/
boolean onAuthenticationFailure(LDAPObject ldapUser, UserModel user, AuthenticationException ldapException, RealmModel realm);
/**
* Gets the ldap provider associated to the mapper.
*
* @return
*/
public LDAPStorageProvider getLdapProvider();
}

View file

@ -18,13 +18,14 @@
package org.keycloak.storage.ldap.mappers.membership;
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
import org.keycloak.storage.ldap.mappers.LDAPStorageMapper;
/**
* Mapper related to mapping of LDAP groups to keycloak model objects (either keycloak roles or keycloak groups)
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface CommonLDAPGroupMapper {
public interface CommonLDAPGroupMapper extends LDAPStorageMapper {
LDAPQuery createLDAPGroupQuery();

View file

@ -28,7 +28,6 @@ import org.keycloak.storage.ldap.idm.query.Condition;
import org.keycloak.storage.ldap.idm.query.EscapeStrategy;
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
import org.keycloak.storage.ldap.idm.query.internal.LDAPQueryConditionsBuilder;
import org.keycloak.storage.ldap.mappers.membership.group.GroupLDAPStorageMapper;
import java.util.ArrayList;
import java.util.Collections;
@ -48,7 +47,7 @@ public enum MembershipType {
DN {
@Override
public Set<LDAPDn> getLDAPSubgroups(GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup) {
public Set<LDAPDn> getLDAPSubgroups(CommonLDAPGroupMapper groupMapper, LDAPObject ldapGroup) {
CommonLDAPGroupMapperConfig config = groupMapper.getConfig();
return getLDAPMembersWithParent(groupMapper.getLdapProvider(), ldapGroup, config.getMembershipLdapAttribute(), LDAPDn.fromString(config.getLDAPGroupsDn()));
}
@ -69,7 +68,7 @@ public enum MembershipType {
}
@Override
public List<UserModel> getGroupMembers(RealmModel realm, GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults) {
public List<UserModel> getGroupMembers(RealmModel realm, CommonLDAPGroupMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults) {
LDAPStorageProvider ldapProvider = groupMapper.getLdapProvider();
CommonLDAPGroupMapperConfig config = groupMapper.getConfig();
@ -130,12 +129,12 @@ public enum MembershipType {
// Group inheritance not supported for this config
@Override
public Set<LDAPDn> getLDAPSubgroups(GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup) {
public Set<LDAPDn> getLDAPSubgroups(CommonLDAPGroupMapper groupMapper, LDAPObject ldapGroup) {
return Collections.emptySet();
}
@Override
public List<UserModel> getGroupMembers(RealmModel realm, GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults) {
public List<UserModel> getGroupMembers(RealmModel realm, CommonLDAPGroupMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults) {
LDAPStorageProvider ldapProvider = groupMapper.getLdapProvider();
LDAPConfig ldapConfig = ldapProvider.getLdapIdentityStore().getConfig();
@ -182,7 +181,7 @@ public enum MembershipType {
};
public abstract Set<LDAPDn> getLDAPSubgroups(GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup);
public abstract Set<LDAPDn> getLDAPSubgroups(CommonLDAPGroupMapper groupMapper, LDAPObject ldapGroup);
public abstract List<UserModel> getGroupMembers(RealmModel realm, GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults);
public abstract List<UserModel> getGroupMembers(RealmModel realm, CommonLDAPGroupMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults);
}

View file

@ -46,6 +46,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.keycloak.storage.ldap.mappers.membership.MembershipType;
/**
* Map realm roles or roles of particular client to LDAP groups
@ -76,7 +77,6 @@ public class RoleLDAPStorageMapper extends AbstractLDAPStorageMapper implements
return config;
}
@Override
public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, RealmModel realm, boolean isCreate) {
LDAPGroupMapperMode mode = config.getMode();
@ -471,5 +471,27 @@ public class RoleLDAPStorageMapper extends AbstractLDAPStorageMapper implements
}
}
public LDAPObject loadRoleGroupByName(String roleName) {
try (LDAPQuery ldapQuery = createRoleQuery(true)) {
Condition roleNameCondition = new LDAPQueryConditionsBuilder().equal(config.getRoleNameLdapAttribute(), roleName);
ldapQuery.addWhereCondition(roleNameCondition);
return ldapQuery.getFirstResult();
}
}
@Override
public List<UserModel> getRoleMembers(RealmModel realm, RoleModel role, int firstResult, int maxResults) {
if (config.getMode() == LDAPGroupMapperMode.IMPORT) {
// only results from Keycloak should be returned, or imported LDAP and KC items will duplicate
return Collections.emptyList();
}
LDAPObject ldapGroup = loadRoleGroupByName(role.getName());
if (ldapGroup == null) {
return Collections.emptyList();
}
MembershipType membershipType = config.getMembershipTypeLdapAttribute();
return membershipType.getGroupMembers(realm, this, ldapGroup, firstResult, maxResults);
}
}

View file

@ -41,6 +41,7 @@ import org.keycloak.storage.ldap.idm.model.LDAPObject;
import org.keycloak.storage.ldap.mappers.membership.LDAPGroupMapperMode;
import org.keycloak.storage.ldap.mappers.membership.MembershipType;
import org.keycloak.storage.ldap.mappers.membership.group.GroupLDAPStorageMapperFactory;
import org.keycloak.storage.ldap.mappers.membership.role.RoleLDAPStorageMapperFactory;
import org.keycloak.testsuite.util.LDAPTestUtils;
import org.keycloak.utils.MediaType;
@ -178,6 +179,51 @@ public class TestLDAPResource {
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, james, "Password1");
}
/**
* Prepare groups LDAP tests. Creates some LDAP mappers as well as some built-in GRoups and users in LDAP
*/
@POST
@Path("/configure-roles")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public void prepareRolesLDAPTest() {
ComponentModel ldapModel = LDAPTestUtils.getLdapProviderModel(session, realm);
LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
// Add role mapper
LDAPTestUtils.addOrUpdateRoleMapper(realm, ldapModel, LDAPGroupMapperMode.LDAP_ONLY);
// Remove all LDAP groups and users
LDAPTestUtils.removeAllLDAPGroups(session, realm, ldapModel, "rolesMapper");
LDAPTestUtils.removeAllLDAPUsers(ldapFedProvider, realm);
// Add some LDAP users for testing
LDAPObject john = LDAPTestUtils.addLDAPUser(ldapFedProvider, realm, "johnkeycloak", "John", "Doe", "john@email.org", null, "1234");
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, john, "Password1");
LDAPObject mary = LDAPTestUtils.addLDAPUser(ldapFedProvider, realm, "marykeycloak", "Mary", "Kelly", "mary@email.org", null, "5678");
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, mary, "Password1");
LDAPObject rob = LDAPTestUtils.addLDAPUser(ldapFedProvider, realm, "robkeycloak", "Rob", "Brown", "rob@email.org", null, "8910");
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, rob, "Password1");
LDAPObject james = LDAPTestUtils.addLDAPUser(ldapFedProvider, realm, "jameskeycloak", "James", "Brown", "james@email.org", null, "8910");
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, james, "Password1");
// Add some groups for testing
LDAPObject group1 = LDAPTestUtils.createLDAPGroup("rolesMapper", session, realm, ldapModel, "group1");
LDAPObject group2 = LDAPTestUtils.createLDAPGroup("rolesMapper", session, realm, ldapModel, "group2");
LDAPObject group3 = LDAPTestUtils.createLDAPGroup("rolesMapper", session, realm, ldapModel, "group3");
// add the users to the groups
LDAPUtils.addMember(ldapFedProvider, MembershipType.DN, LDAPConstants.MEMBER, "not-used", group1, john);
LDAPUtils.addMember(ldapFedProvider, MembershipType.DN, LDAPConstants.MEMBER, "not-used", group1, mary);
LDAPUtils.addMember(ldapFedProvider, MembershipType.DN, LDAPConstants.MEMBER, "not-used", group1, rob);
LDAPUtils.addMember(ldapFedProvider, MembershipType.DN, LDAPConstants.MEMBER, "not-used", group2, john);
LDAPUtils.addMember(ldapFedProvider, MembershipType.DN, LDAPConstants.MEMBER, "not-used", group2, mary);
// Sync LDAP groups to Keycloak DB roles
ComponentModel mapperModel = LDAPTestUtils.getSubcomponentByName(realm, ldapModel, "rolesMapper");
new RoleLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(realm);
}
/**
* Remove specified user directly just from the LDAP server

View file

@ -226,6 +226,23 @@ public class LDAPTestUtils {
}
}
public static void addOrUpdateRoleMapper(RealmModel realm, ComponentModel providerModel, LDAPGroupMapperMode mode, String... otherConfigOptions) {
ComponentModel mapperModel = getSubcomponentByName(realm, providerModel, "rolesMapper");
if (mapperModel != null) {
mapperModel.getConfig().putSingle(GroupMapperConfig.MODE, mode.toString());
updateGroupMapperConfigOptions(mapperModel, otherConfigOptions);
realm.updateComponent(mapperModel);
} else {
String baseDn = providerModel.getConfig().getFirst(LDAPConstants.BASE_DN);
mapperModel = KeycloakModelUtils.createComponentModel("rolesMapper", providerModel.getId(), RoleLDAPStorageMapperFactory.PROVIDER_ID, LDAPStorageMapper.class.getName(),
RoleMapperConfig.ROLES_DN, "ou=Groups," + baseDn,
RoleMapperConfig.USE_REALM_ROLES_MAPPING, "true",
GroupMapperConfig.MODE, mode.toString());
updateGroupMapperConfigOptions(mapperModel, otherConfigOptions);
realm.addComponentModel(mapperModel);
}
}
public static void updateGroupMapperConfigOptions(ComponentModel mapperModel, String... configOptions) {
for (int i=0 ; i<configOptions.length ; i+=2) {
String cfgName = configOptions[i];
@ -286,7 +303,13 @@ public class LDAPTestUtils {
public static void removeAllLDAPGroups(KeycloakSession session, RealmModel appRealm, ComponentModel ldapModel, String mapperName) {
ComponentModel mapperModel = getSubcomponentByName(appRealm, ldapModel, mapperName);
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
try (LDAPQuery roleQuery = getGroupMapper(mapperModel, ldapProvider, appRealm).createGroupQuery(false)) {
LDAPQuery query = null;
if (GroupLDAPStorageMapperFactory.PROVIDER_ID.equals(mapperModel.getProviderId())) {
query = getGroupMapper(mapperModel, ldapProvider, appRealm).createGroupQuery(false);
} else {
query = getRoleMapper(mapperModel, ldapProvider, appRealm).createRoleQuery(false);
}
try (LDAPQuery roleQuery = query) {
List<LDAPObject> ldapRoles = roleQuery.getResultList();
for (LDAPObject ldapRole : ldapRoles) {
ldapProvider.getLdapIdentityStore().remove(ldapRole);
@ -301,7 +324,11 @@ public class LDAPTestUtils {
}
public static LDAPObject createLDAPGroup(KeycloakSession session, RealmModel appRealm, ComponentModel ldapModel, String groupName, String... additionalAttrs) {
ComponentModel mapperModel = getSubcomponentByName(appRealm, ldapModel, "groupsMapper");
return createLDAPGroup("groupsMapper", session, appRealm, ldapModel, groupName, additionalAttrs);
}
public static LDAPObject createLDAPGroup(String mapperName, KeycloakSession session, RealmModel appRealm, ComponentModel ldapModel, String groupName, String... additionalAttrs) {
ComponentModel mapperModel = getSubcomponentByName(appRealm, ldapModel, mapperName);
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
Map<String, Set<String>> additAttrs = new HashMap<>();
@ -311,7 +338,11 @@ public class LDAPTestUtils {
additAttrs.put(attrName, Collections.singleton(attrValue));
}
if (GroupLDAPStorageMapperFactory.PROVIDER_ID.equals(mapperModel.getProviderId())) {
return getGroupMapper(mapperModel, ldapProvider, appRealm).createLDAPGroup(groupName, additAttrs);
} else {
return getRoleMapper(mapperModel, ldapProvider, appRealm).createLDAPRole(groupName);
}
}
public static LDAPObject updateLDAPGroup(KeycloakSession session, RealmModel appRealm, ComponentModel ldapModel, LDAPObject ldapObject) {

View file

@ -55,6 +55,14 @@ public interface TestingLDAPResource {
@Consumes(MediaType.APPLICATION_JSON)
void prepareGroupsLDAPTest();
/**
* Prepare groups LDAP tests. Creates some LDAP mappers as well as some built-in GRoups and users in LDAP
*/
@POST
@Path("/configure-roles")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
void prepareRolesLDAPTest();
/**
* Remove specified user directly just from the LDAP server

View file

@ -0,0 +1,143 @@
/*
* Copyright 2020 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.federation.ldap;
import java.util.stream.Collectors;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.storage.ldap.mappers.membership.role.RoleLDAPStorageMapperFactory;
import org.keycloak.storage.ldap.mappers.membership.role.RoleMapperConfig;
import static org.keycloak.testsuite.federation.ldap.AbstractLDAPTest.TEST_REALM_NAME;
import org.keycloak.testsuite.util.LDAPRule;
import org.keycloak.testsuite.util.LDAPTestUtils;
/**
*
* @author rmartinc
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class LDAPRoleMapperTest extends AbstractLDAPTest {
@ClassRule
public static LDAPRule ldapRule = new LDAPRule();
@Override
protected LDAPRule getLDAPRule() {
return ldapRule;
}
@Override
protected void afterImportTestRealm() {
testingClient.testing().ldap(TEST_REALM_NAME).prepareRolesLDAPTest();
}
@Test
public void test01RoleMapperRealmRoles() {
testingClient.server().run(session -> {
LDAPTestContext ctx = LDAPTestContext.init(session);
RealmModel appRealm = ctx.getRealm();
// check users
UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
Assert.assertNotNull(john);
Assert.assertThat(john.getRealmRoleMappings().stream().map(RoleModel::getName).collect(Collectors.toSet()), Matchers.containsInAnyOrder("group1", "group2"));
UserModel mary = session.users().getUserByUsername("marykeycloak", appRealm);
Assert.assertNotNull(mary);
Assert.assertThat(mary.getRealmRoleMappings().stream().map(RoleModel::getName).collect(Collectors.toSet()), Matchers.containsInAnyOrder("group1", "group2"));
UserModel rob = session.users().getUserByUsername("robkeycloak", appRealm);
Assert.assertNotNull(rob);
Assert.assertThat(rob.getRealmRoleMappings().stream().map(RoleModel::getName).collect(Collectors.toSet()), Matchers.containsInAnyOrder("group1"));
UserModel james = session.users().getUserByUsername("jameskeycloak", appRealm);
Assert.assertNotNull(james);
Assert.assertThat(james.getRealmRoleMappings(), Matchers.empty());
// check groups
RoleModel group1 = appRealm.getRole("group1");
Assert.assertNotNull(group1);
Assert.assertThat(session.users().getRoleMembers(appRealm, group1).stream().map(UserModel::getUsername).collect(Collectors.toSet()),
Matchers.containsInAnyOrder("johnkeycloak", "marykeycloak", "robkeycloak"));
RoleModel group2 = appRealm.getRole("group2");
Assert.assertNotNull(group2);
Assert.assertThat(session.users().getRoleMembers(appRealm, group2).stream().map(UserModel::getUsername).collect(Collectors.toSet()),
Matchers.containsInAnyOrder("johnkeycloak", "marykeycloak"));
RoleModel group3 = appRealm.getRole("group3");
Assert.assertNotNull(group3);
Assert.assertThat(session.users().getRoleMembers(appRealm, group3), Matchers.empty());
});
}
@Test
public void test02RoleMapperClientRoles() {
testingClient.server().run(session -> {
LDAPTestContext ctx = LDAPTestContext.init(session);
RealmModel appRealm = ctx.getRealm();
// create a client to set the roles in it
ClientModel rolesClient = appRealm.addClient("role-mapper-client", "role-mapper-client");
try {
ComponentModel mapperModel = LDAPTestUtils.getSubcomponentByName(appRealm, ctx.getLdapModel(), "rolesMapper");
LDAPTestUtils.updateGroupMapperConfigOptions(mapperModel,
RoleMapperConfig.USE_REALM_ROLES_MAPPING, "false",
RoleMapperConfig.CLIENT_ID, rolesClient.getClientId());
appRealm.updateComponent(mapperModel);
// synch to the client to create the roles at the client
new RoleLDAPStorageMapperFactory().create(session, mapperModel).syncDataFromFederationProviderToKeycloak(appRealm);
// check users
UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
Assert.assertNotNull(john);
Assert.assertThat(john.getClientRoleMappings(rolesClient).stream().map(RoleModel::getName).collect(Collectors.toSet()), Matchers.containsInAnyOrder("group1", "group2"));
UserModel mary = session.users().getUserByUsername("marykeycloak", appRealm);
Assert.assertNotNull(mary);
Assert.assertThat(mary.getClientRoleMappings(rolesClient).stream().map(RoleModel::getName).collect(Collectors.toSet()), Matchers.containsInAnyOrder("group1", "group2"));
UserModel rob = session.users().getUserByUsername("robkeycloak", appRealm);
Assert.assertNotNull(rob);
Assert.assertThat(rob.getClientRoleMappings(rolesClient).stream().map(RoleModel::getName).collect(Collectors.toSet()), Matchers.containsInAnyOrder("group1"));
UserModel james = session.users().getUserByUsername("jameskeycloak", appRealm);
Assert.assertNotNull(james);
Assert.assertThat(james.getClientRoleMappings(rolesClient).stream().map(RoleModel::getName).collect(Collectors.toSet()), Matchers.empty());
// check groups
RoleModel group1 = rolesClient.getRole("group1");
Assert.assertNotNull(group1);
Assert.assertThat(session.users().getRoleMembers(appRealm, group1).stream().map(UserModel::getUsername).collect(Collectors.toSet()),
Matchers.containsInAnyOrder("johnkeycloak", "marykeycloak", "robkeycloak"));
RoleModel group2 = rolesClient.getRole("group2");
Assert.assertNotNull(group2);
Assert.assertThat(session.users().getRoleMembers(appRealm, group2).stream().map(UserModel::getUsername).collect(Collectors.toSet()),
Matchers.containsInAnyOrder("johnkeycloak", "marykeycloak"));
RoleModel group3 = rolesClient.getRole("group3");
Assert.assertNotNull(group3);
Assert.assertThat(session.users().getRoleMembers(appRealm, group3), Matchers.empty());
} finally {
appRealm.removeClient(rolesClient.getClientId());
}
});
}
}