KEYCLOAK-14336: LDAP group membership is not visible under "Users in Role" tab for users imported from LDAP
This commit is contained in:
parent
25bb2e3ba2
commit
32bf50e037
10 changed files with 312 additions and 17 deletions
|
@ -408,6 +408,27 @@ public class LDAPStorageProvider implements UserStorageProvider,
|
||||||
return Collections.emptyList();
|
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) {
|
public List<UserModel> loadUsersByUsernames(List<String> usernames, RealmModel realm) {
|
||||||
List<UserModel> result = new ArrayList<>();
|
List<UserModel> result = new ArrayList<>();
|
||||||
for (String username : usernames) {
|
for (String username : usernames) {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.keycloak.storage.user.SynchronizationResult;
|
||||||
import javax.naming.AuthenticationException;
|
import javax.naming.AuthenticationException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stateful per-request object
|
* Stateful per-request object
|
||||||
|
@ -47,22 +48,27 @@ public abstract class AbstractLDAPStorageMapper implements LDAPStorageMapper {
|
||||||
this.session = ldapProvider.getSession();
|
this.session = ldapProvider.getSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public SynchronizationResult syncDataFromFederationProviderToKeycloak(RealmModel realm) {
|
public SynchronizationResult syncDataFromFederationProviderToKeycloak(RealmModel realm) {
|
||||||
return new SynchronizationResult();
|
return new SynchronizationResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public SynchronizationResult syncDataFromKeycloakToFederationProvider(RealmModel realm) {
|
public SynchronizationResult syncDataFromKeycloakToFederationProvider(RealmModel realm) {
|
||||||
return new SynchronizationResult();
|
return new SynchronizationResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
|
public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) {
|
||||||
return Collections.emptyList();
|
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) {
|
public boolean onAuthenticationFailure(LDAPObject ldapUser, UserModel user, AuthenticationException ldapException, RealmModel realm) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -73,12 +79,11 @@ public abstract class AbstractLDAPStorageMapper implements LDAPStorageMapper {
|
||||||
return Boolean.parseBoolean(paramm);
|
return Boolean.parseBoolean(paramm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public LDAPStorageProvider getLdapProvider() {
|
public LDAPStorageProvider getLdapProvider() {
|
||||||
return ldapProvider;
|
return ldapProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,8 @@ import org.keycloak.storage.user.SynchronizationResult;
|
||||||
|
|
||||||
import javax.naming.AuthenticationException;
|
import javax.naming.AuthenticationException;
|
||||||
import java.util.List;
|
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>
|
* @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);
|
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.
|
* 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!
|
* @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);
|
boolean onAuthenticationFailure(LDAPObject ldapUser, UserModel user, AuthenticationException ldapException, RealmModel realm);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the ldap provider associated to the mapper.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public LDAPStorageProvider getLdapProvider();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,14 @@
|
||||||
package org.keycloak.storage.ldap.mappers.membership;
|
package org.keycloak.storage.ldap.mappers.membership;
|
||||||
|
|
||||||
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
|
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)
|
* 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>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public interface CommonLDAPGroupMapper {
|
public interface CommonLDAPGroupMapper extends LDAPStorageMapper {
|
||||||
|
|
||||||
LDAPQuery createLDAPGroupQuery();
|
LDAPQuery createLDAPGroupQuery();
|
||||||
|
|
||||||
|
|
|
@ -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.EscapeStrategy;
|
||||||
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
|
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
|
||||||
import org.keycloak.storage.ldap.idm.query.internal.LDAPQueryConditionsBuilder;
|
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.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -48,7 +47,7 @@ public enum MembershipType {
|
||||||
DN {
|
DN {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<LDAPDn> getLDAPSubgroups(GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup) {
|
public Set<LDAPDn> getLDAPSubgroups(CommonLDAPGroupMapper groupMapper, LDAPObject ldapGroup) {
|
||||||
CommonLDAPGroupMapperConfig config = groupMapper.getConfig();
|
CommonLDAPGroupMapperConfig config = groupMapper.getConfig();
|
||||||
return getLDAPMembersWithParent(groupMapper.getLdapProvider(), ldapGroup, config.getMembershipLdapAttribute(), LDAPDn.fromString(config.getLDAPGroupsDn()));
|
return getLDAPMembersWithParent(groupMapper.getLdapProvider(), ldapGroup, config.getMembershipLdapAttribute(), LDAPDn.fromString(config.getLDAPGroupsDn()));
|
||||||
}
|
}
|
||||||
|
@ -69,7 +68,7 @@ public enum MembershipType {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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();
|
LDAPStorageProvider ldapProvider = groupMapper.getLdapProvider();
|
||||||
CommonLDAPGroupMapperConfig config = groupMapper.getConfig();
|
CommonLDAPGroupMapperConfig config = groupMapper.getConfig();
|
||||||
|
|
||||||
|
@ -130,12 +129,12 @@ public enum MembershipType {
|
||||||
|
|
||||||
// Group inheritance not supported for this config
|
// Group inheritance not supported for this config
|
||||||
@Override
|
@Override
|
||||||
public Set<LDAPDn> getLDAPSubgroups(GroupLDAPStorageMapper groupMapper, LDAPObject ldapGroup) {
|
public Set<LDAPDn> getLDAPSubgroups(CommonLDAPGroupMapper groupMapper, LDAPObject ldapGroup) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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();
|
LDAPStorageProvider ldapProvider = groupMapper.getLdapProvider();
|
||||||
LDAPConfig ldapConfig = ldapProvider.getLdapIdentityStore().getConfig();
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.keycloak.storage.ldap.mappers.membership.MembershipType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map realm roles or roles of particular client to LDAP groups
|
* Map realm roles or roles of particular client to LDAP groups
|
||||||
|
@ -76,7 +77,6 @@ public class RoleLDAPStorageMapper extends AbstractLDAPStorageMapper implements
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, RealmModel realm, boolean isCreate) {
|
public void onImportUserFromLDAP(LDAPObject ldapUser, UserModel user, RealmModel realm, boolean isCreate) {
|
||||||
LDAPGroupMapperMode mode = config.getMode();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.LDAPGroupMapperMode;
|
||||||
import org.keycloak.storage.ldap.mappers.membership.MembershipType;
|
import org.keycloak.storage.ldap.mappers.membership.MembershipType;
|
||||||
import org.keycloak.storage.ldap.mappers.membership.group.GroupLDAPStorageMapperFactory;
|
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.testsuite.util.LDAPTestUtils;
|
||||||
import org.keycloak.utils.MediaType;
|
import org.keycloak.utils.MediaType;
|
||||||
|
|
||||||
|
@ -178,6 +179,51 @@ public class TestLDAPResource {
|
||||||
LDAPTestUtils.updateLDAPPassword(ldapFedProvider, james, "Password1");
|
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
|
* Remove specified user directly just from the LDAP server
|
||||||
|
|
|
@ -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) {
|
public static void updateGroupMapperConfigOptions(ComponentModel mapperModel, String... configOptions) {
|
||||||
for (int i=0 ; i<configOptions.length ; i+=2) {
|
for (int i=0 ; i<configOptions.length ; i+=2) {
|
||||||
String cfgName = configOptions[i];
|
String cfgName = configOptions[i];
|
||||||
|
@ -286,7 +303,13 @@ public class LDAPTestUtils {
|
||||||
public static void removeAllLDAPGroups(KeycloakSession session, RealmModel appRealm, ComponentModel ldapModel, String mapperName) {
|
public static void removeAllLDAPGroups(KeycloakSession session, RealmModel appRealm, ComponentModel ldapModel, String mapperName) {
|
||||||
ComponentModel mapperModel = getSubcomponentByName(appRealm, ldapModel, mapperName);
|
ComponentModel mapperModel = getSubcomponentByName(appRealm, ldapModel, mapperName);
|
||||||
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
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();
|
List<LDAPObject> ldapRoles = roleQuery.getResultList();
|
||||||
for (LDAPObject ldapRole : ldapRoles) {
|
for (LDAPObject ldapRole : ldapRoles) {
|
||||||
ldapProvider.getLdapIdentityStore().remove(ldapRole);
|
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) {
|
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);
|
LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel);
|
||||||
|
|
||||||
Map<String, Set<String>> additAttrs = new HashMap<>();
|
Map<String, Set<String>> additAttrs = new HashMap<>();
|
||||||
|
@ -311,7 +338,11 @@ public class LDAPTestUtils {
|
||||||
additAttrs.put(attrName, Collections.singleton(attrValue));
|
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) {
|
public static LDAPObject updateLDAPGroup(KeycloakSession session, RealmModel appRealm, ComponentModel ldapModel, LDAPObject ldapObject) {
|
||||||
|
|
|
@ -55,6 +55,14 @@ public interface TestingLDAPResource {
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
void prepareGroupsLDAPTest();
|
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
|
* Remove specified user directly just from the LDAP server
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue