Skip to content

Commit

Permalink
KEYCLOAK-1359 LDAP & Active directory fixes and improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
mposolda committed Jun 4, 2015
1 parent d875b98 commit c3eb6df
Show file tree
Hide file tree
Showing 18 changed files with 240 additions and 143 deletions.
Expand Up @@ -45,16 +45,6 @@ public static LDAPObject addUserToLDAP(LDAPFederationProvider ldapProvider, Real
return ldapUser;
}

public static void removeAllUsers(LDAPFederationProvider ldapProvider, RealmModel realm) {
LDAPIdentityStore ldapStore = ldapProvider.getLdapIdentityStore();
LDAPIdentityQuery ldapQuery = LDAPUtils.createQueryForUserSearch(ldapProvider, realm);
List<LDAPObject> allUsers = ldapQuery.getResultList();

for (LDAPObject ldapUser : allUsers) {
ldapStore.remove(ldapUser);
}
}

public static LDAPIdentityQuery createQueryForUserSearch(LDAPFederationProvider ldapProvider, RealmModel realm) {
LDAPIdentityQuery ldapQuery = new LDAPIdentityQuery(ldapProvider);
LDAPConfig config = ldapProvider.getLdapIdentityStore().getConfig();
Expand Down
Expand Up @@ -71,7 +71,7 @@ public LDAPConfig getConfig() {
public void add(LDAPObject ldapObject) {
// id will be assigned by the ldap server
if (ldapObject.getUuid() != null) {
throw new IllegalStateException("Can't add object with already assigned uuid");
throw new ModelException("Can't add object with already assigned uuid");
}

String entryDN = ldapObject.getDn().toString();
Expand Down Expand Up @@ -108,20 +108,20 @@ public void remove(LDAPObject ldapObject) {

@Override
public List<LDAPObject> fetchQueryResults(LDAPIdentityQuery identityQuery) {
if (identityQuery.getSorting() != null && !identityQuery.getSorting().isEmpty()) {
throw new ModelException("LDAP Identity Store does not yet support sorted queries.");
}

List<LDAPObject> results = new ArrayList<>();

try {
if (identityQuery.getSorting() != null && !identityQuery.getSorting().isEmpty()) {
throw new ModelException("LDAP Identity Store does not yet support sorted queries.");
}

String baseDN = identityQuery.getSearchDn();

for (Condition condition : identityQuery.getConditions()) {

// Check if we are searching by ID
String uuidAttrName = getConfig().getUuidLDAPAttributeName();
if (condition.getParameter() != null && condition.getParameter().getName().equals(uuidAttrName)) {
if (condition.getParameter() != null && condition.getParameter().getName().equalsIgnoreCase(uuidAttrName)) {
if (EqualCondition.class.isInstance(condition)) {
EqualCondition equalCondition = (EqualCondition) condition;
SearchResult search = this.operationManager
Expand All @@ -147,7 +147,7 @@ public List<LDAPObject> fetchQueryResults(LDAPIdentityQuery identityQuery) {
}

for (SearchResult result : search) {
if (!result.getNameInNamespace().equals(baseDN)) {
if (!result.getNameInNamespace().equalsIgnoreCase(baseDN)) {
results.add(populateAttributedType(result, identityQuery.getReturningReadOnlyLdapAttributes()));
}
}
Expand Down Expand Up @@ -278,7 +278,7 @@ protected void applyCondition(StringBuilder filter, Condition condition) {

QueryParameter queryParameter = condition.getParameter();

if (!getConfig().getUuidLDAPAttributeName().equals(queryParameter.getName())) {
if (!getConfig().getUuidLDAPAttributeName().equalsIgnoreCase(queryParameter.getName())) {
String attributeName = queryParameter.getName();

if (attributeName != null) {
Expand Down Expand Up @@ -400,7 +400,7 @@ private LDAPObject populateAttributedType(SearchResult searchResult, Collection<

String ldapAttributeName = ldapAttribute.getID();

if (ldapAttributeName.toLowerCase().equals(getConfig().getUuidLDAPAttributeName().toLowerCase())) {
if (ldapAttributeName.equalsIgnoreCase(getConfig().getUuidLDAPAttributeName())) {
Object uuidValue = ldapAttribute.get();
ldapObject.setUuid(this.operationManager.decodeEntryUUID(uuidValue));
} else {
Expand All @@ -411,7 +411,7 @@ private LDAPObject populateAttributedType(SearchResult searchResult, Collection<
attrValues.add(attrVal);
}

if (ldapAttributeName.toLowerCase().equals(LDAPConstants.OBJECT_CLASS)) {
if (ldapAttributeName.equalsIgnoreCase(LDAPConstants.OBJECT_CLASS)) {
ldapObject.setObjectClasses(attrValues);
} else {
if (logger.isTraceEnabled()) {
Expand Down Expand Up @@ -444,7 +444,7 @@ protected BasicAttributes extractAttributes(LDAPObject ldapObject, boolean isCre
for (Map.Entry<String, Object> attrEntry : ldapObject.getAttributes().entrySet()) {
String attrName = attrEntry.getKey();
Object attrValue = attrEntry.getValue();
if (!ldapObject.getReadOnlyAttributeNames().contains(attrName) && (isCreate || !ldapObject.getRdnAttributeName().equals(attrName))) {
if (!ldapObject.getReadOnlyAttributeNames().contains(attrName) && (isCreate || !ldapObject.getRdnAttributeName().equalsIgnoreCase(attrName))) {

if (String.class.isInstance(attrValue)) {
if (attrValue.toString().trim().length() == 0) {
Expand All @@ -461,7 +461,7 @@ protected BasicAttributes extractAttributes(LDAPObject ldapObject, boolean isCre
} else if (attrValue == null || attrValue.toString().trim().length() == 0) {
entryAttributes.put(attrName, LDAPConstants.EMPTY_ATTRIBUTE_VALUE);
} else {
throw new IllegalArgumentException("Unexpected type of value of argument " + attrName + ". Value is " + attrValue);
throw new ModelException("Unexpected type of value of argument " + attrName + ". Value is " + attrValue);
}
}
}
Expand All @@ -473,9 +473,9 @@ protected BasicAttributes extractAttributes(LDAPObject ldapObject, boolean isCre
for (String objectClassValue : ldapObject.getObjectClasses()) {
objectClassAttribute.add(objectClassValue);

if (objectClassValue.equals(LDAPConstants.GROUP_OF_NAMES)
|| objectClassValue.equals(LDAPConstants.GROUP_OF_ENTRIES)
|| objectClassValue.equals(LDAPConstants.GROUP_OF_UNIQUE_NAMES)) {
if (objectClassValue.equalsIgnoreCase(LDAPConstants.GROUP_OF_NAMES)
|| objectClassValue.equalsIgnoreCase(LDAPConstants.GROUP_OF_ENTRIES)
|| objectClassValue.equalsIgnoreCase(LDAPConstants.GROUP_OF_UNIQUE_NAMES)) {
entryAttributes.put(LDAPConstants.MEMBER, LDAPConstants.EMPTY_MEMBER_ATTRIBUTE_VALUE);
}
}
Expand Down
Expand Up @@ -513,7 +513,6 @@ private <R> R execute(LdapOperation<R> operation) throws NamingException {
context = createLdapContext();
return operation.execute(context);
} catch (NamingException ne) {
logger.error("Could not create Ldap context or operation execution error.", ne);
throw ne;
} finally {
if (context != null) {
Expand Down
@@ -1,24 +1,19 @@
package org.keycloak.federation.ldap.mappers;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.LDAPUtils;
import org.keycloak.federation.ldap.idm.model.LDAPObject;
import org.keycloak.federation.ldap.idm.query.Condition;
import org.keycloak.federation.ldap.idm.query.QueryParameter;
import org.keycloak.federation.ldap.idm.query.internal.EqualCondition;
import org.keycloak.federation.ldap.idm.query.internal.LDAPIdentityQuery;
import org.keycloak.mappers.UserFederationMapper;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderConfigProperty;

/**
* Mapper useful for the LDAP deployments when some attribute (usually CN) is mapped to full name of user
Expand Down Expand Up @@ -130,7 +125,7 @@ public void beforeLDAPQuery(UserFederationMapperModel mapperModel, LDAPIdentityQ
fullName = firstNameCondition.getValue() + " " + lastNameCondition.getValue();
} else if (firstNameCondition != null) {
fullName = (String) firstNameCondition.getValue();
} else if (firstNameCondition != null) {
} else if (lastNameCondition != null) {
fullName = (String) lastNameCondition.getValue();
} else {
return;
Expand Down
@@ -1,14 +1,11 @@
package org.keycloak.federation.ldap.mappers;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.naming.directory.SearchControls;

import org.jboss.logging.Logger;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.idm.model.LDAPDn;
Expand Down Expand Up @@ -126,7 +123,7 @@ public LDAPIdentityQuery createRoleQuery(UserFederationMapperModel mapperModel,
String rolesDn = getRolesDn(mapperModel);
ldapQuery.setSearchDn(rolesDn);

Collection<String> roleObjectClasses = getRoleObjectClasses(mapperModel);
Collection<String> roleObjectClasses = getRoleObjectClasses(mapperModel, ldapProvider);
ldapQuery.addObjectClasses(roleObjectClasses);

String rolesRdnAttr = getRoleNameLdapAttribute(mapperModel);
Expand All @@ -144,11 +141,11 @@ protected RoleContainerModel getTargetRoleContainer(UserFederationMapperModel ma
} else {
String clientId = mapperModel.getConfig().get(CLIENT_ID);
if (clientId == null) {
throw new IllegalStateException("Using client roles mapping is requested, but parameter client.id not found!");
throw new ModelException("Using client roles mapping is requested, but parameter client.id not found!");
}
ClientModel client = realm.getClientByClientId(clientId);
if (client == null) {
throw new IllegalStateException("Can't found requested client with clientId: " + clientId);
throw new ModelException("Can't found requested client with clientId: " + clientId);
}
return client;
}
Expand All @@ -157,7 +154,7 @@ protected RoleContainerModel getTargetRoleContainer(UserFederationMapperModel ma
protected String getRolesDn(UserFederationMapperModel mapperModel) {
String rolesDn = mapperModel.getConfig().get(ROLES_DN);
if (rolesDn == null) {
throw new IllegalStateException("Roles DN is null! Check your configuration");
throw new ModelException("Roles DN is null! Check your configuration");
}
return rolesDn;
}
Expand All @@ -172,10 +169,11 @@ protected String getMembershipLdapAttribute(UserFederationMapperModel mapperMode
return membershipAttrName!=null ? membershipAttrName : LDAPConstants.MEMBER;
}

protected Collection<String> getRoleObjectClasses(UserFederationMapperModel mapperModel) {
protected Collection<String> getRoleObjectClasses(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider) {
String objectClasses = mapperModel.getConfig().get(ROLE_OBJECT_CLASSES);
if (objectClasses == null) {
objectClasses = "groupOfNames";
// For Active directory, the default is 'group' . For other servers 'groupOfNames'
objectClasses = ldapProvider.getLdapIdentityStore().getConfig().isActiveDirectory() ? LDAPConstants.GROUP : LDAPConstants.GROUP_OF_NAMES;
}
String[] objClasses = objectClasses.split(",");

Expand All @@ -191,18 +189,18 @@ protected Collection<String> getRoleObjectClasses(UserFederationMapperModel mapp

private Mode getMode(UserFederationMapperModel mapperModel) {
String modeString = mapperModel.getConfig().get(MODE);
if (modeString == null || modeString.trim().length() == 0) {
return Mode.LDAP_ONLY;
if (modeString == null || modeString.isEmpty()) {
throw new ModelException("Mode is missing! Check your configuration");
}

return Enum.valueOf(Mode.class, modeString.toUpperCase());
}

protected LDAPObject createLDAPRole(UserFederationMapperModel mapperModel, String roleName, LDAPFederationProvider ldapProvider) {
public LDAPObject createLDAPRole(UserFederationMapperModel mapperModel, String roleName, LDAPFederationProvider ldapProvider) {
LDAPObject ldapObject = new LDAPObject();
String roleNameAttribute = getRoleNameLdapAttribute(mapperModel);
ldapObject.setRdnAttributeName(roleNameAttribute);
ldapObject.setObjectClasses(getRoleObjectClasses(mapperModel));
ldapObject.setObjectClasses(getRoleObjectClasses(mapperModel, ldapProvider));
ldapObject.setAttribute(roleNameAttribute, roleName);

LDAPDn roleDn = LDAPDn.fromString(getRolesDn(mapperModel));
Expand Down Expand Up @@ -231,8 +229,8 @@ public void deleteRoleMappingInLDAP(UserFederationMapperModel mapperModel, LDAPF
Set<String> memberships = getExistingMemberships(mapperModel, ldapRole);
memberships.remove(ldapUser.getDn().toString());

// Some membership placeholder needs to be always here as "member" is mandatory attribute on some LDAP servers
if (memberships.size() == 0) {
// Some membership placeholder needs to be always here as "member" is mandatory attribute on some LDAP servers. But on active directory! (Empty membership is not allowed here)
if (memberships.size() == 0 && !ldapProvider.getLdapIdentityStore().getConfig().isActiveDirectory()) {
memberships.add(LDAPConstants.EMPTY_MEMBER_ATTRIBUTE_VALUE);
}

Expand Down Expand Up @@ -278,23 +276,6 @@ protected List<LDAPObject> getLDAPRoleMappings(UserFederationMapperModel mapperM
return ldapQuery.getResultList();
}

protected Set<RoleModel> getLDAPRoleMappingsConverted(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, RoleContainerModel roleContainer) {
List<LDAPObject> ldapRoles = getLDAPRoleMappings(mapperModel, ldapProvider, ldapUser);

Set<RoleModel> roles = new HashSet<RoleModel>();
String roleNameLdapAttr = getRoleNameLdapAttribute(mapperModel);
for (LDAPObject role : ldapRoles) {
String roleName = role.getAttributeAsString(roleNameLdapAttr);
RoleModel modelRole = roleContainer.getRole(roleName);
if (modelRole == null) {
// Add role to local DB
modelRole = roleContainer.addRole(roleName);
}
roles.add(modelRole);
}
return roles;
}

@Override
public UserModel proxy(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, UserModel delegate, RealmModel realm) {
final Mode mode = getMode(mapperModel);
Expand All @@ -321,6 +302,9 @@ public class LDAPRoleMappingsUserDelegate extends UserModelDelegate {
private final RealmModel realm;
private final Mode mode;

// Avoid loading role mappings from LDAP more times per-request
private Set<RoleModel> cachedLDAPRoleMappings;

public LDAPRoleMappingsUserDelegate(UserModel user, UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser,
RealmModel realm, Mode mode) {
super(user);
Expand Down Expand Up @@ -385,6 +369,7 @@ public void grantRole(RoleModel role) {
if (role.getContainer().equals(roleContainer)) {

// We need to create new role mappings in LDAP
cachedLDAPRoleMappings = null;
addRoleMappingInLDAP(mapperModel, role.getName(), ldapProvider, ldapUser);
} else {
super.grantRole(role);
Expand Down Expand Up @@ -415,6 +400,30 @@ public Set<RoleModel> getRoleMappings() {
return modelRoleMappings;
}

protected Set<RoleModel> getLDAPRoleMappingsConverted(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapUser, RoleContainerModel roleContainer) {
if (cachedLDAPRoleMappings != null) {
return new HashSet<>(cachedLDAPRoleMappings);
}

List<LDAPObject> ldapRoles = getLDAPRoleMappings(mapperModel, ldapProvider, ldapUser);

Set<RoleModel> roles = new HashSet<RoleModel>();
String roleNameLdapAttr = getRoleNameLdapAttribute(mapperModel);
for (LDAPObject role : ldapRoles) {
String roleName = role.getAttributeAsString(roleNameLdapAttr);
RoleModel modelRole = roleContainer.getRole(roleName);
if (modelRole == null) {
// Add role to local DB
modelRole = roleContainer.addRole(roleName);
}
roles.add(modelRole);
}

cachedLDAPRoleMappings = new HashSet<>(roles);

return roles;
}

@Override
public void deleteRoleMapping(RoleModel role) {
RoleContainerModel roleContainer = getTargetRoleContainer(mapperModel, realm);
Expand All @@ -438,6 +447,7 @@ public void deleteRoleMapping(RoleModel role) {
throw new ModelException("Not possible to delete LDAP role mappings as mapper mode is READ_ONLY");
} else {
// Delete ldap role mappings
cachedLDAPRoleMappings = null;
deleteRoleMappingInLDAP(mapperModel, ldapProvider, ldapUser, ldapRole);
}
}
Expand Down

0 comments on commit c3eb6df

Please sign in to comment.