From 32bf50e0376eb388a9d4b838fba66360d62f0465 Mon Sep 17 00:00:00 2001 From: rmartinc Date: Mon, 1 Jun 2020 13:01:39 +0200 Subject: [PATCH] KEYCLOAK-14336: LDAP group membership is not visible under "Users in Role" tab for users imported from LDAP --- .../storage/ldap/LDAPStorageProvider.java | 21 +++ .../mappers/AbstractLDAPStorageMapper.java | 15 +- .../ldap/mappers/LDAPStorageMapper.java | 19 +++ .../membership/CommonLDAPGroupMapper.java | 3 +- .../mappers/membership/MembershipType.java | 13 +- .../role/RoleLDAPStorageMapper.java | 24 ++- .../rest/resource/TestLDAPResource.java | 46 ++++++ .../testsuite/util/LDAPTestUtils.java | 37 ++++- .../client/resources/TestingLDAPResource.java | 8 + .../federation/ldap/LDAPRoleMapperTest.java | 143 ++++++++++++++++++ 10 files changed, 312 insertions(+), 17 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPRoleMapperTest.java diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java index 59b995c6cd..568871ec0f 100755 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java @@ -408,6 +408,27 @@ public class LDAPStorageProvider implements UserStorageProvider, return Collections.emptyList(); } + @Override + public List getRoleMembers(RealmModel realm, RoleModel role) { + return getRoleMembers(realm, role, 0, Integer.MAX_VALUE - 1); + } + + @Override + public List getRoleMembers(RealmModel realm, RoleModel role, int firstResult, int maxResults) { + List mappers = realm.getComponents(model.getId(), LDAPStorageMapper.class.getName()); + List sortedMappers = mapperManager.sortMappersAsc(mappers); + for (ComponentModel mapperModel : sortedMappers) { + LDAPStorageMapper ldapMapper = mapperManager.getMapper(mapperModel); + List users = ldapMapper.getRoleMembers(realm, role, firstResult, maxResults); + + // Sufficient for now + if (users.size() > 0) { + return users; + } + } + return Collections.emptyList(); + } + public List loadUsersByUsernames(List usernames, RealmModel realm) { List result = new ArrayList<>(); for (String username : usernames) { diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/AbstractLDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/AbstractLDAPStorageMapper.java index dde5371481..511a8aff60 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/AbstractLDAPStorageMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/AbstractLDAPStorageMapper.java @@ -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 getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { return Collections.emptyList(); } + @Override + public List 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() { diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapper.java index 6749633ba0..0f2e1919be 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/LDAPStorageMapper.java @@ -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 Marek Posolda @@ -53,6 +55,16 @@ public interface LDAPStorageMapper extends Provider { */ List 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 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(); } diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/CommonLDAPGroupMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/CommonLDAPGroupMapper.java index 018b0e34d4..8e47fbe05d 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/CommonLDAPGroupMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/CommonLDAPGroupMapper.java @@ -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 Marek Posolda */ -public interface CommonLDAPGroupMapper { +public interface CommonLDAPGroupMapper extends LDAPStorageMapper { LDAPQuery createLDAPGroupQuery(); diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/MembershipType.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/MembershipType.java index 5497f83b6e..1ad802dd97 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/MembershipType.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/MembershipType.java @@ -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 getLDAPSubgroups(GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup) { + public Set 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 getGroupMembers(RealmModel realm, GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults) { + public List 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 getLDAPSubgroups(GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup) { + public Set getLDAPSubgroups(CommonLDAPGroupMapper groupMapper, LDAPObject ldapGroup) { return Collections.emptySet(); } @Override - public List getGroupMembers(RealmModel realm, GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults) { + public List 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 getLDAPSubgroups(GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup); + public abstract Set getLDAPSubgroups(CommonLDAPGroupMapper groupMapper, LDAPObject ldapGroup); - public abstract List getGroupMembers(RealmModel realm, GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults); + public abstract List getGroupMembers(RealmModel realm, CommonLDAPGroupMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults); } diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java index e735f07dd7..a3c23d733d 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/mappers/membership/role/RoleLDAPStorageMapper.java @@ -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 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); + } } diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestLDAPResource.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestLDAPResource.java index 8807b0bf19..d6692f7368 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestLDAPResource.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestLDAPResource.java @@ -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 diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/LDAPTestUtils.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/LDAPTestUtils.java index 6647efe72d..4a6d0ac27f 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/LDAPTestUtils.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/LDAPTestUtils.java @@ -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 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> additAttrs = new HashMap<>(); @@ -311,7 +338,11 @@ public class LDAPTestUtils { additAttrs.put(attrName, Collections.singleton(attrValue)); } - return getGroupMapper(mapperModel, ldapProvider, appRealm).createLDAPGroup(groupName, additAttrs); + 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) { diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingLDAPResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingLDAPResource.java index 99c2a06200..a0003bfcab 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingLDAPResource.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingLDAPResource.java @@ -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 diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPRoleMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPRoleMapperTest.java new file mode 100644 index 0000000000..586595c95d --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/ldap/LDAPRoleMapperTest.java @@ -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()); + } + }); + } +}