Skip to content

Commit

Permalink
KEYCLOAK-630 Added Role Federation mapper. Fixes and refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
mposolda committed May 22, 2015
1 parent 975337f commit a9f1fda
Show file tree
Hide file tree
Showing 34 changed files with 1,213 additions and 209 deletions.
Expand Up @@ -16,7 +16,7 @@
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*
* TODO: init properties at startup instead of always compute them
* TODO: init properties at constructor instead of always compute them
*/
public class LDAPConfig {

Expand Down
Expand Up @@ -19,7 +19,7 @@
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserFederationMapper;
import org.keycloak.mappers.UserFederationMapper;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderModel;
Expand Down Expand Up @@ -114,7 +114,7 @@ protected UserModel proxy(RealmModel realm, UserModel local, LDAPObject ldapObje
List<UserFederationMapperModel> federationMappers = realm.getUserFederationMappers();
for (UserFederationMapperModel mapperModel : federationMappers) {
LDAPFederationMapper ldapMapper = getMapper(mapperModel);
proxied = ldapMapper.proxy(mapperModel, this, ldapObject, proxied);
proxied = ldapMapper.proxy(mapperModel, this, ldapObject, proxied, realm);
}

return proxied;
Expand Down Expand Up @@ -227,7 +227,7 @@ protected List<LDAPObject> searchLDAP(RealmModel realm, Map<String, String> attr

/**
* @param local
* @return ldapObject corresponding to local user or null if user is no longer in LDAP
* @return ldapUser corresponding to local user or null if user is no longer in LDAP
*/
protected LDAPObject loadAndValidateUser(RealmModel realm, UserModel local) {
LDAPObject ldapUser = loadLDAPUserByUsername(realm, local.getUsername());
Expand Down Expand Up @@ -271,7 +271,7 @@ protected UserModel importUserFromLDAP(RealmModel realm, LDAPObject ldapUser) {
List<UserFederationMapperModel> federationMappers = realm.getUserFederationMappers();
for (UserFederationMapperModel mapperModel : federationMappers) {
LDAPFederationMapper ldapMapper = getMapper(mapperModel);
ldapMapper.importUserFromLDAP(mapperModel, this, ldapUser, imported, true);
ldapMapper.onImportUserFromLDAP(mapperModel, this, ldapUser, imported, realm, true);
}

String userDN = ldapUser.getDn().toString();
Expand Down Expand Up @@ -314,7 +314,7 @@ public void preRemove(RealmModel realm) {
@Override
public void preRemove(RealmModel realm, RoleModel role) {
// complete I don't think we have to do anything here
// TODO: requires implementation... Maybe mappers callback
// TODO: requires implementation... Maybe mappers callback to ensure role deletion propagated to LDAP by RoleLDAPFederationMapper
}

public boolean validPassword(RealmModel realm, UserModel user, String password) {
Expand Down Expand Up @@ -407,7 +407,7 @@ protected UserFederationSyncResult importLDAPUsers(RealmModel realm, List<LDAPOb
List<UserFederationMapperModel> federationMappers = realm.getUserFederationMappers();
for (UserFederationMapperModel mapperModel : federationMappers) {
LDAPFederationMapper ldapMapper = getMapper(mapperModel);
ldapMapper.importUserFromLDAP(mapperModel, this, ldapUser, currentUser, false);
ldapMapper.onImportUserFromLDAP(mapperModel, this, ldapUser, currentUser, realm, false);
}

logger.debugf("Updated user from LDAP: %s", currentUser.getUsername());
Expand Down Expand Up @@ -477,8 +477,7 @@ public LDAPObject loadLDAPUserByUsername(RealmModel realm, String username) {
}

public LDAPFederationMapper getMapper(UserFederationMapperModel mapperModel) {
LDAPFederationMapper ldapMapper = (LDAPFederationMapper) getSession().getKeycloakSessionFactory()
.getProviderFactory(UserFederationMapper.class, mapperModel.getFederationMapperId());
LDAPFederationMapper ldapMapper = (LDAPFederationMapper) getSession().getProvider(UserFederationMapper.class, mapperModel.getFederationMapperId());
if (ldapMapper == null) {
throw new ModelException("Can't find mapper type with ID: " + mapperModel.getFederationMapperId());
}
Expand Down
Expand Up @@ -37,7 +37,7 @@ public static LDAPObject addUserToLDAP(LDAPFederationProvider ldapProvider, Real
List<UserFederationMapperModel> federationMappers = realm.getUserFederationMappers();
for (UserFederationMapperModel mapperModel : federationMappers) {
LDAPFederationMapper ldapMapper = ldapProvider.getMapper(mapperModel);
ldapMapper.registerUserToLDAP(mapperModel, ldapProvider, ldapObject, user);
ldapMapper.onRegisterUserToLDAP(mapperModel, ldapProvider, ldapObject, user, realm);
}

LDAPUtils.computeAndSetDn(ldapConfig, ldapObject);
Expand Down Expand Up @@ -161,10 +161,10 @@ private static String getFullName(String username, String firstName, String last
return fullName;
} */

// ldapObject has filled attributes, but doesn't have filled
// ldapUser has filled attributes, but doesn't have filled dn
public static void computeAndSetDn(LDAPConfig config, LDAPObject ldapObject) {
String rdnLdapAttrName = config.getRdnLdapAttribute();
String rdnLdapAttrValue = (String) ldapObject.getAttribute(rdnLdapAttrName);
String rdnLdapAttrValue = ldapObject.getAttributeAsString(rdnLdapAttrName);
if (rdnLdapAttrValue == null) {
throw new ModelException("RDN Attribute [" + rdnLdapAttrName + "] is not filled. Filled attributes: " + ldapObject.getAttributes());
}
Expand All @@ -176,11 +176,6 @@ public static void computeAndSetDn(LDAPConfig config, LDAPObject ldapObject) {

public static String getUsername(LDAPObject ldapUser, LDAPConfig config) {
String usernameAttr = config.getUsernameLdapAttribute();
return (String) ldapUser.getAttribute(usernameAttr);
}

public static boolean parseBooleanParameter(UserFederationMapperModel mapperModel, String paramName) {
String readOnly = mapperModel.getConfig().get(paramName);
return Boolean.parseBoolean(readOnly);
return ldapUser.getAttributeAsString(usernameAttr);
}
}
@@ -1,14 +1,16 @@
package org.keycloak.federation.ldap.idm.model;

import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class LDAPDn {

private final List<Entry> entries = new LinkedList<Entry>();
private final Deque<Entry> entries = new LinkedList<Entry>();

public static LDAPDn fromString(String dnString) {
LDAPDn dn = new LDAPDn();
Expand Down Expand Up @@ -43,15 +45,15 @@ public String toString() {
* @return string like "uid=joe" from the DN like "uid=joe,dc=something,dc=org"
*/
public String getFirstRdn() {
Entry firstEntry = entries.get(0);
Entry firstEntry = entries.getFirst();
return firstEntry.attrName + "=" + firstEntry.attrValue;
}

/**
* @return string attribute name like "uid" from the DN like "uid=joe,dc=something,dc=org"
*/
public String getFirstRdnAttrName() {
Entry firstEntry = entries.get(0);
Entry firstEntry = entries.getFirst();
return firstEntry.attrName;
}

Expand All @@ -60,28 +62,15 @@ public String getFirstRdnAttrName() {
* @return string like "dc=something,dc=org" from the DN like "uid=joe,dc=something,dc=org"
*/
public String getParentDn() {
StringBuilder builder = new StringBuilder();

int n = 0;
for (Entry rdn : entries) {
n++;
if (n > 2) {
builder.append(",");
}
if (n >= 2) {
builder.append(rdn.attrName).append("=").append(rdn.attrValue);
}
}

return builder.toString();
return new LinkedList<Entry>(entries).remove().toString();
}

public void addToHead(String rdnName, String rdnValue) {
entries.add(0, new Entry(rdnName, rdnValue));
entries.addFirst(new Entry(rdnName, rdnValue));
}

public void addToBottom(String rdnName, String rdnValue) {
entries.add(new Entry(rdnName, rdnValue));
entries.addLast(new Entry(rdnName, rdnValue));
}


Expand Down
Expand Up @@ -18,7 +18,7 @@ public class LDAPObject {

private final List<String> objectClasses = new LinkedList<String>();
private final List<String> readOnlyAttributeNames = new LinkedList<String>();
private final Map<String, Serializable> attributes = new HashMap<String, Serializable>();
private final Map<String, Object> attributes = new HashMap<String, Object>();

public String getUuid() {
return uuid;
Expand Down Expand Up @@ -61,7 +61,7 @@ public void setRdnAttributeName(String rdnAttributeName) {
this.rdnAttributeName = rdnAttributeName;
}

public void setAttribute(String attributeName, Serializable attributeValue) {
public void setAttribute(String attributeName, Object attributeValue) {
attributes.put(attributeName, attributeValue);
}

Expand All @@ -70,12 +70,21 @@ public void removeAttribute(String name) {
}


public Serializable getAttribute(String name) {
public Object getAttribute(String name) {
return attributes.get(name);
}

public String getAttributeAsString(String name) {
Object attrValue = attributes.get(name);
if (attrValue != null && !(attrValue instanceof String)) {
throw new IllegalStateException("Expected String but attribute was " + attrValue + " of type " + attrValue.getClass().getName());
}

return (String) attrValue;
}


public Map<String, Serializable> getAttributes() {
public Map<String, Object> getAttributes() {
return attributes;
}

Expand Down
Expand Up @@ -9,6 +9,8 @@
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeSet;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
Expand Down Expand Up @@ -389,34 +391,41 @@ private LDAPObject populateAttributedType(SearchResult searchResult, Collection<

while (ldapAttributes.hasMore()) {
Attribute ldapAttribute = ldapAttributes.next();
Serializable ldapAttributeValue;

try {
ldapAttributeValue = (Serializable) ldapAttribute.get();
ldapAttribute.get();
} catch (NoSuchElementException nsee) {
continue;
}

String ldapAttributeName = ldapAttribute.getID();

if (ldapAttributeName.toLowerCase().equals(getConfig().getUuidAttributeName().toLowerCase())) {
ldapObject.setUuid(this.operationManager.decodeEntryUUID(ldapAttributeValue));
} else if (ldapAttributeName.toLowerCase().equals(LDAPConstants.OBJECT_CLASS)) {
List<String> objectClasses = new LinkedList<String>();
Object uuidValue = ldapAttribute.get();
ldapObject.setUuid(this.operationManager.decodeEntryUUID(uuidValue));
} else {
Set<String> attrValues = new TreeSet<String>();
NamingEnumeration<?> enumm = ldapAttribute.getAll();
while (enumm.hasMoreElements()) {
String objectClass = enumm.next().toString();
objectClasses.add(objectClass);
}
ldapObject.setObjectClasses(objectClasses);
} else {
if (logger.isTraceEnabled()) {
logger.tracef("Populating ldap attribute [%s] with value [%s] for DN [%s].", ldapAttributeName, ldapAttributeValue, entryDN);
attrValues.add(objectClass);
}

ldapObject.setAttribute(ldapAttributeName, ldapAttributeValue);
if (uppercasedReadOnlyAttrNames.contains(ldapAttributeName.toUpperCase())) {
ldapObject.addReadOnlyAttributeName(ldapAttributeName);
if (ldapAttributeName.toLowerCase().equals(LDAPConstants.OBJECT_CLASS)) {
ldapObject.setObjectClasses(attrValues);
} else {
if (logger.isTraceEnabled()) {
logger.tracef("Populating ldap attribute [%s] with value [%s] for DN [%s].", ldapAttributeName, attrValues.toString(), entryDN);
}
if (attrValues.size() == 1) {
ldapObject.setAttribute(ldapAttributeName, attrValues.iterator().next());
} else {
ldapObject.setAttribute(ldapAttributeName, attrValues);
}

if (uppercasedReadOnlyAttrNames.contains(ldapAttributeName.toUpperCase())) {
ldapObject.addReadOnlyAttributeName(ldapAttributeName);
}
}
}
}
Expand Down Expand Up @@ -487,16 +496,16 @@ private LDAPObject populateAttributedType(SearchResult searchResult, Collection<
protected BasicAttributes extractAttributes(LDAPObject ldapObject, boolean isCreate) {
BasicAttributes entryAttributes = new BasicAttributes();

for (Map.Entry<String, Serializable> attrEntry : ldapObject.getAttributes().entrySet()) {
for (Map.Entry<String, Object> attrEntry : ldapObject.getAttributes().entrySet()) {
String attrName = attrEntry.getKey();
Serializable attrValue = attrEntry.getValue();
Object attrValue = attrEntry.getValue();
if (!ldapObject.getReadOnlyAttributeNames().contains(attrName) && (isCreate || !ldapObject.getRdnAttributeName().equals(attrName))) {

if (String.class.isInstance(attrValue)) {
entryAttributes.put(attrName, attrValue);
} else if (Collection.class.isInstance(attrValue)) {
BasicAttribute attr = new BasicAttribute(attrName);
Collection<String> valueCollection = (Collection<String>) attr;
Collection<String> valueCollection = (Collection<String>) attrValue;
for (String val : valueCollection) {
attr.add(val);
}
Expand Down
@@ -1,9 +1,6 @@
package org.keycloak.federation.ldap.mappers;

import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.UserFederationMapper;
import org.keycloak.models.UserFederationMapperModel;

/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
Expand All @@ -15,18 +12,8 @@ public void close() {

}

@Override
public UserFederationMapper create(KeycloakSession session) {
throw new RuntimeException("UNSUPPORTED METHOD");
}

@Override
public void init(Config.Scope config) {

}

@Override
public void postInit(KeycloakSessionFactory factory) {

protected boolean parseBooleanParameter(UserFederationMapperModel mapperModel, String paramName) {
String paramm = mapperModel.getConfig().get(paramName);
return Boolean.parseBoolean(paramm);
}
}
@@ -0,0 +1,25 @@
package org.keycloak.federation.ldap.mappers;

import org.keycloak.Config;
import org.keycloak.mappers.UserFederationMapperFactory;
import org.keycloak.models.KeycloakSessionFactory;

/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public abstract class AbstractLDAPFederationMapperFactory implements UserFederationMapperFactory {

@Override
public void init(Config.Scope config) {
}

@Override
public void postInit(KeycloakSessionFactory factory) {
}

@Override
public void close() {
}


}

0 comments on commit a9f1fda

Please sign in to comment.