Skip to content

Commit

Permalink
KEYCLOAK-1300 Added FullNameLDAPFederationMapper
Browse files Browse the repository at this point in the history
  • Loading branch information
mposolda committed May 22, 2015
1 parent 1490f10 commit 975337f
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 73 deletions.
Expand Up @@ -9,7 +9,6 @@
import org.keycloak.federation.ldap.mappers.LDAPFederationMapper;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationMapper;
import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserModel;

Expand Down Expand Up @@ -179,4 +178,9 @@ 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);
}
}
Expand Up @@ -186,7 +186,7 @@ public LDAPIdentityQuery setPaginationContext(byte[] paginationContext) {
}

public Set<Condition> getConditions() {
return unmodifiableSet(this.conditions);
return this.conditions;
}

}
@@ -1,84 +1,66 @@
package org.keycloak.federation.ldap.mappers;

import java.util.HashMap;
import java.util.Map;

import org.jboss.logging.Logger;
import org.keycloak.federation.ldap.LDAPFederationProvider;
import org.keycloak.federation.ldap.idm.model.LDAPObject;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.UserModelDelegate;

/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class TxAwareLDAPUserModelDelegate extends UserModelDelegate {
public abstract class AbstractTxAwareLDAPUserModelDelegate extends UserModelDelegate {

private static final Logger logger = Logger.getLogger(TxAwareLDAPUserModelDelegate.class);
public static final Logger logger = Logger.getLogger(AbstractTxAwareLDAPUserModelDelegate.class);

protected LDAPFederationProvider provider;
protected LDAPObject ldapObject;
protected LDAPTransaction transaction;

// Map of allowed writable UserModel attributes to LDAP attributes. Includes UserModel properties (firstName, lastName, email, ...)
private final Map<String, String> mappedAttributes = new HashMap<String, String>();
private final LDAPTransaction transaction;

public TxAwareLDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider, LDAPObject ldapObject) {
public AbstractTxAwareLDAPUserModelDelegate(UserModel delegate, LDAPFederationProvider provider, LDAPObject ldapObject) {
super(delegate);
this.provider = provider;
this.ldapObject = ldapObject;
this.transaction = findOrCreateTransaction();
}

public void addMappedAttribute(String userModelAttrName, String ldapAttrName) {
mappedAttributes.put(userModelAttrName, ldapAttrName);
}

@Override
public void setAttribute(String name, String value) {
setLDAPAttribute(name, value);

super.setAttribute(name, value);
}

@Override
public void setEmail(String email) {
setLDAPAttribute(UserModel.EMAIL, email);

super.setEmail(email);
}

@Override
public void setLastName(String lastName) {
setLDAPAttribute(UserModel.LAST_NAME, lastName);

super.setLastName(lastName);
public LDAPTransaction getTransaction() {
return transaction;
}

@Override
public void setFirstName(String firstName) {
setLDAPAttribute(UserModel.FIRST_NAME, firstName);
// Try to find transaction in any delegate. We want to enlist just single transaction per all delegates
protected LDAPTransaction findOrCreateTransaction() {
UserModelDelegate delegate = this;
while (true) {
UserModel deleg = delegate.getDelegate();
if (!(deleg instanceof UserModelDelegate)) {
// Existing transaction not available. Need to create new
return new LDAPTransaction();
} else {
delegate = (UserModelDelegate) deleg;
}

super.setFirstName(firstName);
// Check if it's transaction aware delegate
if (delegate instanceof AbstractTxAwareLDAPUserModelDelegate) {
AbstractTxAwareLDAPUserModelDelegate txDelegate = (AbstractTxAwareLDAPUserModelDelegate) delegate;
return txDelegate.getTransaction();
}
}
}

protected void setLDAPAttribute(String modelAttrName, String value) {
String ldapAttrName = mappedAttributes.get(modelAttrName);
if (ldapAttrName != null) {
protected void ensureTransactionStarted() {
if (transaction.state == TransactionState.NOT_STARTED) {
if (logger.isTraceEnabled()) {
logger.tracef("Pushing user attribute to LDAP. Model attribute name: %s, LDAP attribute name: %s, Attribute value: %s", modelAttrName, ldapAttrName, value);
logger.trace("Starting and enlisting transaction for object " + ldapObject.getDn().toString());
}

if (transaction == null) {
transaction = new LDAPTransaction();
provider.getSession().getTransaction().enlistAfterCompletion(transaction);
}

ldapObject.setAttribute(ldapAttrName, value);
this.provider.getSession().getTransaction().enlistAfterCompletion(transaction);
}
}



protected class LDAPTransaction implements KeycloakTransaction {

protected TransactionState state = TransactionState.NOT_STARTED;
Expand Down
@@ -0,0 +1,174 @@
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.models.LDAPConstants;
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
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class FullNameLDAPFederationMapper extends AbstractLDAPFederationMapper {

public static final String LDAP_FULL_NAME_ATTRIBUTE = "ldap.full.name.attribute";
public static final String READ_ONLY = "read.only";

@Override
public String getHelpText() {
return "Some help text - full name mapper - TODO";
}

@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}

@Override
public String getId() {
return "full-name-ldap-mapper";
}

@Override
public void importUserFromLDAP(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapObject, UserModel user, boolean isCreate) {
String ldapFullNameAttrName = getLdapFullNameAttrName(mapperModel);
String fullName = (String) ldapObject.getAttribute(ldapFullNameAttrName);
fullName = fullName.trim();
if (fullName != null) {
int lastSpaceIndex = fullName.lastIndexOf(" ");
if (lastSpaceIndex == -1) {
user.setLastName(fullName);
} else {
user.setFirstName(fullName.substring(0, lastSpaceIndex));
user.setLastName(fullName.substring(lastSpaceIndex + 1));
}
}
}

@Override
public void registerUserToLDAP(UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapObject, UserModel localUser) {
String ldapFullNameAttrName = getLdapFullNameAttrName(mapperModel);
String fullName = getFullName(localUser.getFirstName(), localUser.getLastName());
ldapObject.setAttribute(ldapFullNameAttrName, fullName);

if (isReadOnly(mapperModel)) {
ldapObject.addReadOnlyAttributeName(ldapFullNameAttrName);
}
}

@Override
public UserModel proxy(final UserFederationMapperModel mapperModel, LDAPFederationProvider ldapProvider, LDAPObject ldapObject, UserModel delegate) {
if (ldapProvider.getEditMode() == UserFederationProvider.EditMode.WRITABLE && !isReadOnly(mapperModel)) {


AbstractTxAwareLDAPUserModelDelegate txDelegate = new AbstractTxAwareLDAPUserModelDelegate(delegate, ldapProvider, ldapObject) {

@Override
public void setFirstName(String firstName) {
super.setFirstName(firstName);
setFullNameToLDAPObject();
}

@Override
public void setLastName(String lastName) {
super.setLastName(lastName);
setFullNameToLDAPObject();
}

private void setFullNameToLDAPObject() {
String fullName = getFullName(getFirstName(), getLastName());
if (logger.isTraceEnabled()) {
logger.tracef("Pushing full name attribute to LDAP. Full name: %s", fullName);
}

ensureTransactionStarted();

String ldapFullNameAttrName = getLdapFullNameAttrName(mapperModel);
ldapObject.setAttribute(ldapFullNameAttrName, fullName);
}

};

return txDelegate;
} else {
return delegate;
}
}

@Override
public void beforeLDAPQuery(UserFederationMapperModel mapperModel, LDAPIdentityQuery query) {
String ldapFullNameAttrName = getLdapFullNameAttrName(mapperModel);
query.addReturningLdapAttribute(ldapFullNameAttrName);

// Change conditions and compute condition for fullName from the conditions for firstName and lastName. Right now just "equal" condition is supported
EqualCondition firstNameCondition = null;
EqualCondition lastNameCondition = null;
Set<Condition> conditionsCopy = new HashSet<Condition>(query.getConditions());
for (Condition condition : conditionsCopy) {
QueryParameter param = condition.getParameter();
if (param != null) {
if (param.getName().equals(UserModel.FIRST_NAME)) {
firstNameCondition = (EqualCondition) condition;
query.getConditions().remove(condition);
} else if (param.getName().equals(UserModel.LAST_NAME)) {
lastNameCondition = (EqualCondition) condition;
query.getConditions().remove(condition);
} else if (param.getName().equals(LDAPConstants.GIVENNAME)) {
// Some previous mapper already converted it
firstNameCondition = (EqualCondition) condition;
} else if (param.getName().equals(LDAPConstants.SN)) {
// Some previous mapper already converted it
lastNameCondition = (EqualCondition) condition;
}
}
}


String fullName = null;
if (firstNameCondition != null && lastNameCondition != null) {
fullName = firstNameCondition.getValue() + " " + lastNameCondition.getValue();
} else if (firstNameCondition != null) {
fullName = (String) firstNameCondition.getValue();
} else if (firstNameCondition != null) {
fullName = (String) lastNameCondition.getValue();
} else {
return;
}
EqualCondition fullNameCondition = new EqualCondition(new QueryParameter(ldapFullNameAttrName), fullName);
query.getConditions().add(fullNameCondition);
}

protected String getLdapFullNameAttrName(UserFederationMapperModel mapperModel) {
String ldapFullNameAttrName = mapperModel.getConfig().get(LDAP_FULL_NAME_ATTRIBUTE);
return ldapFullNameAttrName == null ? LDAPConstants.CN : ldapFullNameAttrName;
}

protected String getFullName(String firstName, String lastName) {
if (firstName != null && lastName != null) {
return firstName + " " + lastName;
} else if (firstName != null) {
return firstName;
} else if (lastName != null) {
return lastName;
} else {
return LDAPConstants.EMPTY_ATTRIBUTE_VALUE;
}
}

private boolean isReadOnly(UserFederationMapperModel mapperModel) {
return LDAPUtils.parseBooleanParameter(mapperModel, READ_ONLY);
}
}

0 comments on commit 975337f

Please sign in to comment.