1
0
mirror of synced 2026-08-04 09:17:02 +00:00

SEC-558: Combine user mapping implementations into a single interface and make more use of DirContextOperations in SS LDAP APIs.

This commit is contained in:
Luke Taylor
2007-09-16 18:56:00 +00:00
parent 56deb3dd83
commit 8a35f7da75
20 changed files with 189 additions and 327 deletions
@@ -15,7 +15,7 @@
package org.acegisecurity.ldap;
import org.acegisecurity.userdetails.ldap.LdapUserDetails;
import org.springframework.ldap.core.DirContextOperations;
/**
@@ -37,7 +37,7 @@ public interface LdapUserSearch {
*
* @param username the login name supplied to the authentication service.
*
* @return an LdapUserDetailsImpl object containing the user's full DN and requested attributes.
* @return a DirContextOperations object containing the user's full DN and requested attributes.
*/
LdapUserDetails searchForUser(String username);
DirContextOperations searchForUser(String username);
}
@@ -26,6 +26,7 @@ import java.io.UnsupportedEncodingException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.Name;
/**
@@ -73,8 +74,7 @@ public final class LdapUtils {
*
* @throws NamingException any exceptions thrown by the context are propagated.
*/
public static String getRelativeName(String fullDn, Context baseCtx)
throws NamingException {
public static String getRelativeName(String fullDn, Context baseCtx) throws NamingException {
String baseDn = baseCtx.getNameInNamespace();
@@ -27,6 +27,7 @@ import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.AttributesMapperCallbackHandler;
import org.springframework.ldap.core.DirContextOperations;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -41,6 +42,7 @@ import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.Context;
import javax.naming.NameClassPair;
import javax.naming.Name;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
@@ -136,22 +138,19 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
* Composes an object from the attributes of the given DN.
*
* @param dn the directory entry which will be read
* @param mapper maps the attributes to the required object
* @param attributesToRetrieve the named attributes which will be retrieved from the directory entry.
*
* @return the object created by the mapper
*/
public Object retrieveEntry(final String dn, final ContextMapper mapper, final String[] attributesToRetrieve) {
public DirContextOperations retrieveEntry(final String dn, final String[] attributesToRetrieve) {
return executeReadOnly(new ContextExecutor() {
return (DirContextOperations) executeReadOnly(new ContextExecutor() {
public Object executeWithContext(DirContext ctx) throws NamingException {
Attributes attrs = ctx.getAttributes(LdapUtils.getRelativeName(dn, ctx), attributesToRetrieve);
// Object object = ctx.lookup(LdapUtils.getRelativeName(dn, ctx));
DirContextAdapter ctxAdapter = new DirContextAdapter(attrs, new DistinguishedName(dn));
return mapper.mapFromContext(ctxAdapter);
return new DirContextAdapter(attrs, new DistinguishedName(dn));
}
});
}
@@ -227,17 +226,15 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
* @param base
* @param filter
* @param params
* @param mapper
*
* @return the object created by the mapper from the matching entry
*
* @throws IncorrectResultSizeDataAccessException if no results are found or the search returns more than one
* result.
*/
public Object searchForSingleEntry(final String base, final String filter, final Object[] params,
final ContextMapper mapper) {
public DirContextOperations searchForSingleEntry(final String base, final String filter, final Object[] params) {
return executeReadOnly(new ContextExecutor() {
return (DirContextOperations) executeReadOnly(new ContextExecutor() {
public Object executeWithContext(DirContext ctx)
throws NamingException {
@@ -269,10 +266,7 @@ public class SpringSecurityLdapTemplate extends org.springframework.ldap.core.Ld
dn.append(nameInNamespace);
}
DirContextAdapter ctxAdapter = new DirContextAdapter(
searchResult.getAttributes(), new DistinguishedName(dn.toString()));
return mapper.mapFromContext(ctxAdapter);
return new DirContextAdapter(searchResult.getAttributes(), new DistinguishedName(dn.toString()));
}
});
}
@@ -20,9 +20,6 @@ import org.acegisecurity.ldap.SpringSecurityLdapTemplate;
import org.acegisecurity.ldap.LdapUserSearch;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.acegisecurity.userdetails.ldap.LdapUserDetails;
import org.acegisecurity.userdetails.ldap.LdapUserDetailsImpl;
import org.acegisecurity.userdetails.ldap.LdapUserDetailsMapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -32,6 +29,7 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.util.Assert;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextOperations;
import javax.naming.directory.SearchControls;
@@ -53,7 +51,6 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
//~ Instance fields ================================================================================================
private ContextSource initialDirContextFactory;
private LdapUserDetailsMapper userDetailsMapper = new LdapUserDetailsMapper();
/**
* The LDAP SearchControls object used for the search. Shared between searches so shouldn't be modified
@@ -105,7 +102,7 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
*
* @throws UsernameNotFoundException if no matching entry is found.
*/
public LdapUserDetails searchForUser(String username) {
public DirContextOperations searchForUser(String username) {
if (logger.isDebugEnabled()) {
logger.debug("Searching for user '" + username + "', with user search "
+ this.toString());
@@ -117,14 +114,8 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
try {
LdapUserDetailsImpl user = (LdapUserDetailsImpl) template.searchForSingleEntry(
searchBase, searchFilter, new String[] {username}, userDetailsMapper);
return template.searchForSingleEntry(searchBase, searchFilter, new String[] {username});
if (!username.equals(user.getUsername())) {
logger.debug("Search returned user object with different username: " + user.getUsername());
}
return user;
} catch (IncorrectResultSizeDataAccessException notFound) {
if (notFound.getActualSize() == 0) {
throw new UsernameNotFoundException("User " + username + " not found in directory.");
@@ -163,10 +154,6 @@ public class FilterBasedLdapUserSearch implements LdapUserSearch {
searchControls.setTimeLimit(searchTimeLimit);
}
public void setUserDetailsMapper(LdapUserDetailsMapper userDetailsMapper) {
this.userDetailsMapper = userDetailsMapper;
}
public String toString() {
StringBuffer sb = new StringBuffer();
@@ -19,14 +19,15 @@ import org.acegisecurity.AuthenticationException;
import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.AuthenticationServiceException;
import org.acegisecurity.ldap.LdapDataAccessException;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.providers.ldap.authenticator.LdapShaPasswordEncoder;
import org.acegisecurity.providers.encoding.PasswordEncoder;
import org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.ldap.LdapUserDetails;
import org.acegisecurity.userdetails.ldap.LdapUserDetailsImpl;
import org.acegisecurity.userdetails.ldap.UserDetailsContextMapper;
import org.acegisecurity.userdetails.ldap.LdapUserDetailsMapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -34,6 +35,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.ldap.core.DirContextOperations;
/**
@@ -123,6 +125,8 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
private LdapAuthenticator authenticator;
private LdapAuthoritiesPopulator authoritiesPopulator;
private UserDetailsContextMapper userDetailsContextMapper = new LdapUserDetailsMapper();
private PasswordEncoder passwordEncoder = new LdapShaPasswordEncoder();
private boolean includeDetailsObject = true;
//~ Constructors ===================================================================================================
@@ -149,7 +153,7 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
public LdapAuthenticationProvider(LdapAuthenticator authenticator) {
this.setAuthenticator(authenticator);
this.setAuthoritiesPopulator(new NullAuthoritiesPopulator());
}
}
//~ Methods ========================================================================================================
@@ -171,44 +175,23 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
return authoritiesPopulator;
}
public void setUserDetailsContextMapper(UserDetailsContextMapper userDetailsContextMapper) {
Assert.notNull(userDetailsContextMapper, "UserDetailsContextMapper must not be null");
this.userDetailsContextMapper = userDetailsContextMapper;
}
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if (!userDetails.getPassword().equals(authentication.getCredentials().toString())) {
String presentedPassword = authentication.getCredentials() == null ? "" :
authentication.getCredentials().toString();
if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, null)) {
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
includeDetailsObject ? userDetails : null);
}
}
/**
* Creates the final <tt>UserDetails</tt> object that will be returned by the provider once the user has
* been authenticated.<p>The <tt>LdapAuthoritiesPopulator</tt> will be used to create the granted
* authorites for the user.</p>
* <p>Can be overridden to customize the creation of the final UserDetails instance. The default will
* merge any additional authorities retrieved from the populator with the propertis of original <tt>ldapUser</tt>
* object and set the values of the username and password.</p>
*
* @param ldapUser The intermediate LdapUserDetails instance returned by the authenticator.
* @param username the username submitted to the provider
* @param password the password submitted to the provider
*
* @return The UserDetails for the successfully authenticated user.
*/
protected UserDetails createUserDetails(LdapUserDetails ldapUser, String username, String password) {
LdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence(ldapUser);
user.setUsername(username);
user.setPassword(password);
GrantedAuthority[] extraAuthorities = getAuthoritiesPopulator().getGrantedAuthorities(ldapUser);
for (int i = 0; i < extraAuthorities.length; i++) {
user.addAuthority(extraAuthorities[i]);
}
return user.createUserDetails();
}
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if (!StringUtils.hasLength(username)) {
@@ -230,9 +213,11 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
}
try {
LdapUserDetails ldapUser = getAuthenticator().authenticate(username, password);
DirContextOperations user = getAuthenticator().authenticate(username, password);
return createUserDetails(ldapUser, username, password);
GrantedAuthority[] extraAuthorities = getAuthoritiesPopulator().getGrantedAuthorities(user, username);
return userDetailsContextMapper.mapUserFromContext(user, username, extraAuthorities);
} catch (DataAccessException ldapAccessFailure) {
throw new AuthenticationServiceException(ldapAccessFailure.getMessage(), ldapAccessFailure);
@@ -250,7 +235,7 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
//~ Inner Classes ==================================================================================================
private static class NullAuthoritiesPopulator implements LdapAuthoritiesPopulator {
public GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails) throws LdapDataAccessException {
public GrantedAuthority[] getGrantedAuthorities(DirContextOperations userDetails, String username) {
return new GrantedAuthority[0];
}
}
@@ -16,6 +16,7 @@
package org.acegisecurity.providers.ldap;
import org.acegisecurity.userdetails.ldap.LdapUserDetails;
import org.springframework.ldap.core.DirContextOperations;
/**
@@ -40,5 +41,5 @@ public interface LdapAuthenticator {
*
* @return the details of the successfully authenticated user.
*/
LdapUserDetails authenticate(String username, String password);
DirContextOperations authenticate(String username, String password);
}
@@ -20,6 +20,7 @@ import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.ldap.LdapDataAccessException;
import org.acegisecurity.userdetails.ldap.LdapUserDetails;
import org.springframework.ldap.core.DirContextOperations;
/**
@@ -38,12 +39,11 @@ public interface LdapAuthoritiesPopulator {
/**
* Get the list of authorities for the user.
*
* @param userDetails the user details object which was returned by the LDAP authenticator.
* @param user the context object which was returned by the LDAP authenticator.
*
* @return the granted authorities for the given user.
*
* @throws LdapDataAccessException if there is a problem accessing the directory.
*/
GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails)
throws LdapDataAccessException;
GrantedAuthority[] getGrantedAuthorities(DirContextOperations user, String username) throws LdapDataAccessException;
}
@@ -22,8 +22,6 @@ import org.acegisecurity.ldap.LdapUserSearch;
import org.acegisecurity.providers.ldap.LdapAuthenticator;
import org.acegisecurity.userdetails.ldap.LdapUserDetailsMapper;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
@@ -31,7 +29,6 @@ import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert;
import org.springframework.ldap.core.ContextMapper;
import java.text.MessageFormat;
@@ -49,7 +46,6 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
//~ Instance fields ================================================================================================
private InitialDirContextFactory initialDirContextFactory;
private LdapUserDetailsMapper userDetailsMapper = new LdapUserDetailsMapper();
/** Optional search object which can be used to locate a user when a simple DN match isn't sufficient */
private LdapUserSearch userSearch;
@@ -110,10 +106,6 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
return userAttributes;
}
protected ContextMapper getUserDetailsMapper() {
return userDetailsMapper;
}
/**
* Builds list of possible DNs for the user, worked out from the <tt>userDnPatterns</tt> property. The
* returned value includes the root DN of the provider URL used to configure the
@@ -159,11 +151,6 @@ public abstract class AbstractLdapAuthenticator implements LdapAuthenticator, In
this.userAttributes = userAttributes;
}
public void setUserDetailsMapper(LdapUserDetailsMapper userDetailsMapper) {
Assert.notNull("userDetailsMapper must not be null");
this.userDetailsMapper = userDetailsMapper;
}
/**
* Sets the pattern which will be used to supply a DN for the user. The pattern should be the name relative
* to the root DN. The pattern argument {0} will contain the username. An example would be "cn={0},ou=people".
@@ -27,8 +27,10 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextOperations;
import javax.naming.directory.DirContext;
import javax.naming.Name;
import java.util.Iterator;
@@ -58,21 +60,21 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
//~ Methods ========================================================================================================
public LdapUserDetails authenticate(String username, String password) {
LdapUserDetails user = null;
public DirContextOperations authenticate(String username, String password) {
DirContextOperations user = null;
// If DN patterns are configured, try authenticating with them directly
Iterator dns = getUserDns(username).iterator();
while (dns.hasNext() && (user == null)) {
while (dns.hasNext() && user == null) {
user = bindWithDn((String) dns.next(), username, password);
}
// Otherwise use the configured locator to find the user
// and authenticate with the returned DN.
if ((user == null) && (getUserSearch() != null)) {
LdapUserDetails userFromSearch = getUserSearch().searchForUser(username);
user = bindWithDn(userFromSearch.getDn(), username, password);
if (user == null && getUserSearch() != null) {
DirContextOperations userFromSearch = getUserSearch().searchForUser(username);
user = bindWithDn(userFromSearch.getDn().toString(), username, password);
}
if (user == null) {
@@ -83,15 +85,13 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
return user;
}
private LdapUserDetails bindWithDn(String userDn, String username, String password) {
private DirContextOperations bindWithDn(String userDn, String username, String password) {
SpringSecurityLdapTemplate template = new SpringSecurityLdapTemplate(
new BindWithSpecificDnContextSource(getInitialDirContextFactory(), userDn, password));
try {
LdapUserDetailsImpl user = (LdapUserDetailsImpl) template.retrieveEntry(userDn,
getUserDetailsMapper(), getUserAttributes());
return template.retrieveEntry(userDn, getUserAttributes());
return user;
} catch (BadCredentialsException e) {
// This will be thrown if an invalid user name is used and the method may
// be called multiple times to try different names, so we trap the exception
@@ -117,7 +117,6 @@ public class BindAuthenticator extends AbstractLdapAuthenticator {
private String userDn;
private String password;
public BindWithSpecificDnContextSource(InitialDirContextFactory ctxFactory, String userDn, String password) {
this.ctxFactory = ctxFactory;
this.userDn = userDn;
@@ -32,6 +32,9 @@ import java.security.MessageDigest;
* base-64 encoded and have the label "{SHA}" (or "{SSHA}") prepended to the encoded hash. These can be made lower-case
* in the encoded password, if required, by setting the <tt>forceLowerCasePrefix</tt> property to true.
*
* Also supports plain text passwords, so can safely be used in cases when both encoded and non-encoded passwords are in
* use or when a null implementation is required.
*
* @author Luke Taylor
* @version $Id$
*/
@@ -129,6 +132,10 @@ public class LdapShaPasswordEncoder implements PasswordEncoder {
public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
String encPassWithoutPrefix;
if (!encPass.startsWith("{")) {
return encPass.equals(rawPass);
}
if (encPass.startsWith(SSHA_PREFIX) || encPass.startsWith(SSHA_PREFIX_LC)) {
encPassWithoutPrefix = encPass.substring(6);
salt = extractSalt(encPass);
@@ -24,13 +24,12 @@ import org.acegisecurity.ldap.LdapUtils;
import org.acegisecurity.providers.encoding.PasswordEncoder;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.acegisecurity.userdetails.ldap.LdapUserDetails;
import org.acegisecurity.userdetails.ldap.LdapUserDetailsImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.ldap.core.DirContextOperations;
import java.util.Iterator;
@@ -70,20 +69,19 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
//~ Methods ========================================================================================================
public LdapUserDetails authenticate(final String username, final String password) {
public DirContextOperations authenticate(final String username, final String password) {
// locate the user and check the password
LdapUserDetails user = null;
DirContextOperations user = null;
Iterator dns = getUserDns(username).iterator();
SpringSecurityLdapTemplate ldapTemplate = new SpringSecurityLdapTemplate(getInitialDirContextFactory());
while (dns.hasNext() && (user == null)) {
while (dns.hasNext() && user == null) {
final String userDn = (String) dns.next();
if (ldapTemplate.nameExists(userDn)) {
user = (LdapUserDetailsImpl)
ldapTemplate.retrieveEntry(userDn, getUserDetailsMapper(), getUserAttributes());
user = ldapTemplate.retrieveEntry(userDn, getUserAttributes());
}
}
@@ -95,7 +93,7 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
throw new UsernameNotFoundException(username);
}
String retrievedPassword = user.getPassword();
Object retrievedPassword = user.getObjectAttribute(passwordAttributeName);
if (retrievedPassword != null) {
if (!verifyPassword(password, retrievedPassword)) {
@@ -107,15 +105,14 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
}
if (logger.isDebugEnabled()) {
logger.debug("Password attribute wasn't retrieved for user '" + username + "' using mapper "
+ getUserDetailsMapper() + ". Performing LDAP compare of password attribute '" + passwordAttributeName
+ "'");
logger.debug("Password attribute wasn't retrieved for user '" + username
+ "'. Performing LDAP compare of password attribute '" + passwordAttributeName + "'");
}
String encodedPassword = passwordEncoder.encodePassword(password, null);
byte[] passwordBytes = LdapUtils.getUtf8Bytes(encodedPassword);
if (!ldapTemplate.compare(user.getDn(), passwordAttributeName, passwordBytes)) {
if (!ldapTemplate.compare(user.getDn().toString(), passwordAttributeName, passwordBytes)) {
throw new BadCredentialsException(messages.getMessage("PasswordComparisonAuthenticator.badCredentials",
"Bad credentials"));
}
@@ -141,12 +138,17 @@ public final class PasswordComparisonAuthenticator extends AbstractLdapAuthentic
*
* @return true if they match
*/
private boolean verifyPassword(String password, String ldapPassword) {
protected boolean verifyPassword(String password, Object ldapPassword) {
if (!(ldapPassword instanceof String)) {
// Assume it's binary
ldapPassword = new String((byte[]) ldapPassword);
}
if (ldapPassword.equals(password)) {
return true;
}
if (passwordEncoder.isPasswordValid(ldapPassword, password, null)) {
if (passwordEncoder.isPasswordValid((String)ldapPassword, password, null)) {
return true;
}
@@ -29,6 +29,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.ldap.core.DirContextOperations;
import java.util.HashSet;
import java.util.Iterator;
@@ -156,11 +157,11 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
* roles for the given user (on top of those obtained from the standard
* search implemented by this class).
*
* @param ldapUser the user who's roles are required
* @param user the context representing the user who's roles are required
* @return the extra roles which will be merged with those returned by the group search
*/
protected Set getAdditionalRoles(LdapUserDetails ldapUser) {
protected Set getAdditionalRoles(DirContextOperations user, String username) {
return null;
}
@@ -168,26 +169,19 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
* Obtains the authorities for the user who's directory entry is represented by
* the supplied LdapUserDetails object.
*
* @param userDetails the user who's authorities are required
* @param user the user who's authorities are required
* @return the set of roles granted to the user.
*/
public final GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails) {
String userDn = userDetails.getDn();
public final GrantedAuthority[] getGrantedAuthorities(DirContextOperations user, String username) {
String userDn = user.getDn().toString();
if (logger.isDebugEnabled()) {
logger.debug("Getting authorities for user " + userDn);
}
Set roles = getGroupMembershipRoles(userDn, userDetails.getUsername());
Set roles = getGroupMembershipRoles(userDn, username);
// Temporary use of deprecated method
Set oldGroupRoles = getGroupMembershipRoles(userDn, userDetails.getAttributes());
if (oldGroupRoles != null) {
roles.addAll(oldGroupRoles);
}
Set extraRoles = getAdditionalRoles(userDetails);
Set extraRoles = getAdditionalRoles(user, username);
if (extraRoles != null) {
roles.addAll(extraRoles);
@@ -200,19 +194,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
return (GrantedAuthority[]) roles.toArray(new GrantedAuthority[roles.size()]);
}
// protected Set getRolesFromUserAttributes(String userDn, Attributes userAttributes) {
// Set userRoles = new HashSet();
//
// for(int i=0; userRoleAttributes != null && i < userRoleAttributes.length; i++) {
// Attribute roleAttribute = userAttributes.get(userRoleAttributes[i]);
//
// addAttributeValuesToRoleSet(roleAttribute, userRoles);
// }
//
// return userRoles;
// }
public Set getGroupMembershipRoles(String userDn, String username) {
Set authorities = new HashSet();
@@ -247,19 +228,6 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
return authorities;
}
/**
* Searches for groups the user is a member of.
*
* @param userDn the user's distinguished name.
* @param userAttributes the retrieved user's attributes (unused by default).
* @return the set of roles obtained from a group membership search, or null if <tt>groupSearchBase</tt> has been
* set.
* @deprecated Subclasses should implement <tt>getAdditionalRoles</tt> instead.
*/
protected Set getGroupMembershipRoles(String userDn, Attributes userAttributes) {
return new HashSet();
}
protected InitialDirContextFactory getInitialDirContextFactory() {
return initialDirContextFactory;
}
@@ -17,6 +17,7 @@ package org.acegisecurity.userdetails.ldap;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.userdetails.UserDetails;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -25,6 +26,7 @@ import org.springframework.util.Assert;
import org.springframework.ldap.UncategorizedLdapException;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
@@ -36,11 +38,10 @@ import javax.naming.directory.Attribute;
* @author Luke Taylor
* @version $Id$
*/
public class LdapUserDetailsMapper implements ContextMapper {
public class LdapUserDetailsMapper implements UserDetailsContextMapper {
//~ Instance fields ================================================================================================
private final Log logger = LogFactory.getLog(LdapUserDetailsMapper.class);
private String usernameAttributeName = "uid";
private String passwordAttributeName = "userPassword";
private String rolePrefix = "ROLE_";
private String[] roleAttributes = null;
@@ -48,25 +49,21 @@ public class LdapUserDetailsMapper implements ContextMapper {
//~ Methods ========================================================================================================
public Object mapFromContext(Object ctxObj) {
Assert.isInstanceOf(DirContextAdapter.class, ctxObj, "Can only map from DirContextAdapter instances");
DirContextAdapter ctx = (DirContextAdapter)ctxObj;
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, GrantedAuthority[] authorities) {
String dn = ctx.getNameInNamespace();
logger.debug("Mapping user details from context with DN: " + dn);
LdapUserDetailsImpl.Essence essence = new LdapUserDetailsImpl.Essence();
essence.setDn(dn);
essence.setAttributes(ctx.getAttributes());
Attribute passwordAttribute = ctx.getAttributes().get(passwordAttributeName);
Object passwordValue = ctx.getObjectAttribute(passwordAttributeName);
if (passwordAttribute != null) {
essence.setPassword(mapPassword(passwordAttribute));
if (passwordValue != null) {
essence.setPassword(mapPassword(passwordValue));
}
essence.setUsername(mapUsername(ctx));
essence.setUsername(username);
// Map the roles
for (int i = 0; (roleAttributes != null) && (i < roleAttributes.length); i++) {
@@ -86,53 +83,38 @@ public class LdapUserDetailsMapper implements ContextMapper {
}
}
// Add the supplied authorities
for (int i=0; i < authorities.length; i++) {
essence.addAuthority(authorities[i]);
}
return essence.createUserDetails();
//return essence;
}
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
}
/**
* Extension point to allow customized creation of the user's password from
* the attribute stored in the directory.
*
* @param passwordAttribute the attribute instance containing the password
* @param passwordValue the value of the password attribute
* @return a String representation of the password.
*/
protected String mapPassword(Attribute passwordAttribute) {
Object retrievedPassword = null;
protected String mapPassword(Object passwordValue) {
try {
retrievedPassword = passwordAttribute.get();
} catch (NamingException e) {
throw new UncategorizedLdapException("Failed to get password attribute", e);
}
if (!(retrievedPassword instanceof String)) {
if (!(passwordValue instanceof String)) {
// Assume it's binary
retrievedPassword = new String((byte[]) retrievedPassword);
passwordValue = new String((byte[]) passwordValue);
}
return (String) retrievedPassword;
return (String) passwordValue;
}
protected String mapUsername(DirContextAdapter ctx) {
Attribute usernameAttribute = ctx.getAttributes().get(usernameAttributeName);
String username;
if (usernameAttribute == null) {
throw new UncategorizedLdapException(
"Failed to get attribute " + usernameAttributeName + " from context");
}
try {
username = (String) usernameAttribute.get();
} catch (NamingException e) {
throw new UncategorizedLdapException("Failed to get username from attribute " + usernameAttributeName, e);
}
return username;
}
/**
* Creates a GrantedAuthority from a role attribute. Override to customize
* authority object creation.
@@ -175,11 +157,6 @@ public class LdapUserDetailsMapper implements ContextMapper {
this.passwordAttributeName = passwordAttributeName;
}
public void setUsernameAttributeName(String usernameAttributeName) {
this.usernameAttributeName = usernameAttributeName;
}
/**
* The names of any attributes in the user's entry which represent application
* roles. These will be converted to <tt>GrantedAuthority</tt>s and added to the