keycloak-aplcache
Changes
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapper.java 8(+8 -0)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java 40(+4 -36)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java 8(+8 -0)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/MembershipType.java 127(+125 -2)
Details
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
index 714a97a..698a392 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
@@ -206,25 +206,9 @@ public class LDAPFederationProvider implements UserFederationProvider {
return Collections.emptyList();
}
- public List<UserModel> loadUsersByLDAPDns(Collection<LDAPDn> userDns, RealmModel realm) {
- // We have dns of users, who are members of our group. Load them now
- LDAPQuery query = LDAPUtils.createQueryForUserSearch(this, realm);
- LDAPQueryConditionsBuilder conditionsBuilder = new LDAPQueryConditionsBuilder();
- Condition[] orSubconditions = new Condition[userDns.size()];
- int index = 0;
- for (LDAPDn userDn : userDns) {
- Condition condition = conditionsBuilder.equal(userDn.getFirstRdnAttrName(), userDn.getFirstRdnAttrValue());
- orSubconditions[index] = condition;
- index++;
- }
- Condition orCondition = conditionsBuilder.orCondition(orSubconditions);
- query.addWhereCondition(orCondition);
- List<LDAPObject> ldapUsers = query.getResultList();
-
- // We have ldapUsers, Need to load users from KC DB or import them here
- List<UserModel> result = new LinkedList<>();
- for (LDAPObject ldapUser : ldapUsers) {
- String username = LDAPUtils.getUsername(ldapUser, getLdapIdentityStore().getConfig());
+ public List<UserModel> loadUsersByUsernames(List<String> usernames, RealmModel realm) {
+ List<UserModel> result = new ArrayList<>();
+ for (String username : usernames) {
UserModel kcUser = session.users().getUserByUsername(username, realm);
if (!model.getId().equals(kcUser.getFederationLink())) {
logger.warnf("Incorrect federation provider of user %s" + kcUser.getUsername());
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapper.java
index 9b08f5f..2a79a48 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapper.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapper.java
@@ -75,4 +75,12 @@ public abstract class AbstractLDAPFederationMapper {
String paramm = mapperModel.getConfig().get(paramName);
return Boolean.parseBoolean(paramm);
}
+
+ public LDAPFederationProvider getLdapProvider() {
+ return ldapProvider;
+ }
+
+ public RealmModel getRealm() {
+ return realm;
+ }
}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java
index 1d2a407..911e292 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java
@@ -1,6 +1,5 @@
package org.keycloak.federation.ldap.mappers.membership.group;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -27,8 +26,6 @@ import org.keycloak.federation.ldap.mappers.membership.UserRolesRetrieveStrategy
import org.keycloak.models.GroupModel;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.RoleContainerModel;
-import org.keycloak.models.RoleModel;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationSyncResult;
import org.keycloak.models.UserModel;
@@ -115,22 +112,8 @@ public class GroupLDAPFederationMapper extends AbstractLDAPFederationMapper impl
}
protected Set<LDAPDn> getLDAPSubgroups(LDAPObject ldapGroup) {
- return getLDAPMembersWithParent(ldapGroup, LDAPDn.fromString(config.getGroupsDn()));
- }
-
- // Get just those members of specified group, which are descendants of "requiredParentDn"
- protected Set<LDAPDn> getLDAPMembersWithParent(LDAPObject ldapGroup, LDAPDn requiredParentDn) {
- Set<String> allMemberships = LDAPUtils.getExistingMemberships(config.getMembershipLdapAttribute(), ldapGroup);
-
- // Filter and keep just groups
- Set<LDAPDn> result = new HashSet<>();
- for (String membership : allMemberships) {
- LDAPDn childDn = LDAPDn.fromString(membership);
- if (childDn.isDescendantOf(requiredParentDn)) {
- result.add(childDn);
- }
- }
- return result;
+ MembershipType membershipType = config.getMembershipTypeLdapAttribute();
+ return membershipType.getLDAPSubgroups(this, ldapGroup);
}
@@ -461,23 +444,8 @@ public class GroupLDAPFederationMapper extends AbstractLDAPFederationMapper impl
return Collections.emptyList();
}
- LDAPDn usersDn = LDAPDn.fromString(ldapProvider.getLdapIdentityStore().getConfig().getUsersDn());
- Set<LDAPDn> userDns = getLDAPMembersWithParent(ldapGroup, usersDn);
-
- if (userDns == null) {
- return Collections.emptyList();
- }
-
- if (userDns.size() <= firstResult) {
- return Collections.emptyList();
- }
-
- List<LDAPDn> dns = new ArrayList<>(userDns);
- int max = Math.min(dns.size(), firstResult + maxResults);
- dns = dns.subList(firstResult, max);
-
- // We have dns of users, who are members of our group. Load them now
- return ldapProvider.loadUsersByLDAPDns(dns, realm);
+ MembershipType membershipType = config.getMembershipTypeLdapAttribute();
+ return membershipType.getGroupMembers(this, ldapGroup, firstResult, maxResults);
}
public void addGroupMappingInLDAP(String groupName, LDAPObject ldapUser) {
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java
index 1e06fa9..464cc0d 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapperFactory.java
@@ -11,6 +11,7 @@ import org.keycloak.federation.ldap.LDAPConfig;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapper;
import org.keycloak.federation.ldap.mappers.AbstractLDAPFederationMapperFactory;
+import org.keycloak.federation.ldap.mappers.membership.CommonLDAPGroupMapperConfig;
import org.keycloak.federation.ldap.mappers.membership.LDAPGroupMapperMode;
import org.keycloak.federation.ldap.mappers.membership.MembershipType;
import org.keycloak.federation.ldap.mappers.membership.UserRolesRetrieveStrategy;
@@ -170,6 +171,13 @@ public class GroupLDAPFederationMapperFactory extends AbstractLDAPFederationMapp
public void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
checkMandatoryConfigAttribute(GroupMapperConfig.GROUPS_DN, "LDAP Groups DN", mapperModel);
checkMandatoryConfigAttribute(GroupMapperConfig.MODE, "Mode", mapperModel);
+
+ String mt = mapperModel.getConfig().get(CommonLDAPGroupMapperConfig.MEMBERSHIP_ATTRIBUTE_TYPE);
+ MembershipType membershipType = mt==null ? MembershipType.DN : Enum.valueOf(MembershipType.class, mt);
+ boolean preserveGroupInheritance = Boolean.parseBoolean(mapperModel.getConfig().get(GroupMapperConfig.PRESERVE_GROUP_INHERITANCE));
+ if (preserveGroupInheritance && membershipType != MembershipType.DN) {
+ throw new MapperConfigValidationException("Not possible to preserve group inheritance and use UID membership type together");
+ }
}
@Override
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/MembershipType.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/MembershipType.java
index 624ed3b..cee0c73 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/MembershipType.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/MembershipType.java
@@ -1,5 +1,24 @@
package org.keycloak.federation.ldap.mappers.membership;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.keycloak.federation.ldap.LDAPConfig;
+import org.keycloak.federation.ldap.LDAPFederationProvider;
+import org.keycloak.federation.ldap.LDAPUtils;
+import org.keycloak.federation.ldap.idm.model.LDAPDn;
+import org.keycloak.federation.ldap.idm.model.LDAPObject;
+import org.keycloak.federation.ldap.idm.query.Condition;
+import org.keycloak.federation.ldap.idm.query.internal.LDAPQuery;
+import org.keycloak.federation.ldap.idm.query.internal.LDAPQueryConditionsBuilder;
+import org.keycloak.federation.ldap.mappers.membership.group.GroupLDAPFederationMapper;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@@ -8,10 +27,114 @@ public enum MembershipType {
/**
* Used if LDAP role has it's members declared in form of their full DN. For example ( "member: uid=john,ou=users,dc=example,dc=com" )
*/
- DN,
+ DN {
+
+ @Override
+ public Set<LDAPDn> getLDAPSubgroups(GroupLDAPFederationMapper groupMapper, LDAPObject ldapGroup) {
+ CommonLDAPGroupMapperConfig config = groupMapper.getConfig();
+ return getLDAPMembersWithParent(ldapGroup, config.getMembershipLdapAttribute(), LDAPDn.fromString(config.getLDAPGroupsDn()));
+ }
+
+ // Get just those members of specified group, which are descendants of "requiredParentDn"
+ protected Set<LDAPDn> getLDAPMembersWithParent(LDAPObject ldapGroup, String membershipLdapAttribute, LDAPDn requiredParentDn) {
+ Set<String> allMemberships = LDAPUtils.getExistingMemberships(membershipLdapAttribute, ldapGroup);
+
+ // Filter and keep just groups
+ Set<LDAPDn> result = new HashSet<>();
+ for (String membership : allMemberships) {
+ LDAPDn childDn = LDAPDn.fromString(membership);
+ if (childDn.isDescendantOf(requiredParentDn)) {
+ result.add(childDn);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public List<UserModel> getGroupMembers(GroupLDAPFederationMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults) {
+ RealmModel realm = groupMapper.getRealm();
+ LDAPFederationProvider ldapProvider = groupMapper.getLdapProvider();
+ CommonLDAPGroupMapperConfig config = groupMapper.getConfig();
+
+ LDAPDn usersDn = LDAPDn.fromString(ldapProvider.getLdapIdentityStore().getConfig().getUsersDn());
+ Set<LDAPDn> userDns = getLDAPMembersWithParent(ldapGroup, config.getMembershipLdapAttribute(), usersDn);
+
+ if (userDns == null) {
+ return Collections.emptyList();
+ }
+
+ if (userDns.size() <= firstResult) {
+ return Collections.emptyList();
+ }
+
+ List<LDAPDn> dns = new ArrayList<>(userDns);
+ int max = Math.min(dns.size(), firstResult + maxResults);
+ dns = dns.subList(firstResult, max);
+
+ // If usernameAttrName is same like DN, we can just retrieve usernames from DNs
+ List<String> usernames = new LinkedList<>();
+ LDAPConfig ldapConfig = ldapProvider.getLdapIdentityStore().getConfig();
+ if (ldapConfig.getUsernameLdapAttribute().equals(ldapConfig.getRdnLdapAttribute())) {
+ for (LDAPDn userDn : dns) {
+ String username = userDn.getFirstRdnAttrValue();
+ usernames.add(username);
+ }
+ } else {
+ LDAPQuery query = LDAPUtils.createQueryForUserSearch(ldapProvider, realm);
+ LDAPQueryConditionsBuilder conditionsBuilder = new LDAPQueryConditionsBuilder();
+ Condition[] orSubconditions = new Condition[dns.size()];
+ int index = 0;
+ for (LDAPDn userDn : dns) {
+ Condition condition = conditionsBuilder.equal(userDn.getFirstRdnAttrName(), userDn.getFirstRdnAttrValue());
+ orSubconditions[index] = condition;
+ index++;
+ }
+ Condition orCondition = conditionsBuilder.orCondition(orSubconditions);
+ query.addWhereCondition(orCondition);
+ List<LDAPObject> ldapUsers = query.getResultList();
+ for (LDAPObject ldapUser : ldapUsers) {
+ String username = LDAPUtils.getUsername(ldapUser, ldapConfig);
+ usernames.add(username);
+ }
+ }
+
+ // We have dns of users, who are members of our group. Load them now
+ return ldapProvider.loadUsersByUsernames(usernames, realm);
+ }
+
+ },
+
/**
* Used if LDAP role has it's members declared in form of pure user uids. For example ( "memberUid: john" )
*/
- UID
+ UID {
+
+ // Group inheritance not supported for this config
+ @Override
+ public Set<LDAPDn> getLDAPSubgroups(GroupLDAPFederationMapper groupMapper, LDAPObject ldapGroup) {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public List<UserModel> getGroupMembers(GroupLDAPFederationMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults) {
+ String memberAttrName = groupMapper.getConfig().getMembershipLdapAttribute();
+ Set<String> memberUids = LDAPUtils.getExistingMemberships(memberAttrName, ldapGroup);
+
+ if (memberUids == null || memberUids.size() <= firstResult) {
+ return Collections.emptyList();
+ }
+
+ List<String> uids = new ArrayList<>(memberUids);
+ int max = Math.min(memberUids.size(), firstResult + maxResults);
+ uids = uids.subList(firstResult, max);
+
+ return groupMapper.getLdapProvider().loadUsersByUsernames(uids, groupMapper.getRealm());
+ }
+
+ };
+
+ public abstract Set<LDAPDn> getLDAPSubgroups(GroupLDAPFederationMapper groupMapper, LDAPObject ldapGroup);
+
+ public abstract List<UserModel> getGroupMembers(GroupLDAPFederationMapper groupMapper, LDAPObject ldapGroup, int firstResult, int maxResults);
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPGroupMapperSyncTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPGroupMapperSyncTest.java
index 33cb284..f7e17f6 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPGroupMapperSyncTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPGroupMapperSyncTest.java
@@ -59,6 +59,9 @@ public class LDAPGroupMapperSyncTest {
// Add group mapper
FederationTestUtils.addOrUpdateGroupMapper(appRealm, ldapModel, LDAPGroupMapperMode.LDAP_ONLY, descriptionAttrName);
+ // Remove all LDAP groups
+ FederationTestUtils.removeAllLDAPGroups(session, appRealm, ldapModel, "groupsMapper");
+
// Add some groups for testing
LDAPObject group1 = FederationTestUtils.createLDAPGroup(manager.getSession(), appRealm, ldapModel, "group1", descriptionAttrName, "group1 - description");
LDAPObject group11 = FederationTestUtils.createLDAPGroup(manager.getSession(), appRealm, ldapModel, "group11");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPGroupMapperTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPGroupMapperTest.java
index 4aaac41..8778735 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPGroupMapperTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/ldap/base/LDAPGroupMapperTest.java
@@ -64,6 +64,9 @@ public class LDAPGroupMapperTest {
// Add group mapper
FederationTestUtils.addOrUpdateGroupMapper(appRealm, ldapModel, LDAPGroupMapperMode.LDAP_ONLY, descriptionAttrName);
+ // Remove all LDAP groups
+ FederationTestUtils.removeAllLDAPGroups(session, appRealm, ldapModel, "groupsMapper");
+
// Add some groups for testing
LDAPObject group1 = FederationTestUtils.createLDAPGroup(manager.getSession(), appRealm, ldapModel, "group1", descriptionAttrName, "group1 - description");
LDAPObject group11 = FederationTestUtils.createLDAPGroup(manager.getSession(), appRealm, ldapModel, "group11");