From 8a35f7da75c4c41d714fdc8aa8e7d8384b607cd0 Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Sun, 16 Sep 2007 18:56:00 +0000 Subject: [PATCH] SEC-558: Combine user mapping implementations into a single interface and make more use of DirContextOperations in SS LDAP APIs. --- .../acegisecurity/ldap/LdapUserSearch.java | 6 +- .../org/acegisecurity/ldap/LdapUtils.java | 4 +- .../ldap/SpringSecurityLdapTemplate.java | 22 +++--- .../search/FilterBasedLdapUserSearch.java | 19 +---- .../ldap/LdapAuthenticationProvider.java | 57 ++++++--------- .../providers/ldap/LdapAuthenticator.java | 3 +- .../ldap/LdapAuthoritiesPopulator.java | 6 +- .../AbstractLdapAuthenticator.java | 13 ---- .../ldap/authenticator/BindAuthenticator.java | 21 +++--- .../authenticator/LdapShaPasswordEncoder.java | 7 ++ .../PasswordComparisonAuthenticator.java | 30 ++++---- .../DefaultLdapAuthoritiesPopulator.java | 48 +++---------- .../ldap/LdapUserDetailsMapper.java | 71 +++++++------------ .../FilterBasedLdapUserSearchTests.java | 14 ++-- .../ldap/LdapAuthenticationProviderTests.java | 52 ++++++++------ .../authenticator/BindAuthenticatorTests.java | 31 ++------ .../ldap/authenticator/MockUserSearch.java | 10 +-- .../PasswordComparisonAuthenticatorTests.java | 35 ++++----- .../DefaultLdapAuthoritiesPopulatorTests.java | 60 ++++------------ .../ldap/LdapUserDetailsMapperTests.java | 7 +- 20 files changed, 189 insertions(+), 327 deletions(-) diff --git a/core/src/main/java/org/acegisecurity/ldap/LdapUserSearch.java b/core/src/main/java/org/acegisecurity/ldap/LdapUserSearch.java index c6fae2c27a..dcc9ab1358 100644 --- a/core/src/main/java/org/acegisecurity/ldap/LdapUserSearch.java +++ b/core/src/main/java/org/acegisecurity/ldap/LdapUserSearch.java @@ -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); } diff --git a/core/src/main/java/org/acegisecurity/ldap/LdapUtils.java b/core/src/main/java/org/acegisecurity/ldap/LdapUtils.java index cb2d8c51e4..bcd9e77c4f 100644 --- a/core/src/main/java/org/acegisecurity/ldap/LdapUtils.java +++ b/core/src/main/java/org/acegisecurity/ldap/LdapUtils.java @@ -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(); diff --git a/core/src/main/java/org/acegisecurity/ldap/SpringSecurityLdapTemplate.java b/core/src/main/java/org/acegisecurity/ldap/SpringSecurityLdapTemplate.java index 4274dfbd46..9592bb78fe 100644 --- a/core/src/main/java/org/acegisecurity/ldap/SpringSecurityLdapTemplate.java +++ b/core/src/main/java/org/acegisecurity/ldap/SpringSecurityLdapTemplate.java @@ -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())); } }); } diff --git a/core/src/main/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearch.java b/core/src/main/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearch.java index 0caf20312d..fcdb454f37 100644 --- a/core/src/main/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearch.java +++ b/core/src/main/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearch.java @@ -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(); diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticationProvider.java b/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticationProvider.java index c1069ea20d..4e1d7b61be 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticationProvider.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticationProvider.java @@ -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 UserDetails object that will be returned by the provider once the user has - * been authenticated.

The LdapAuthoritiesPopulator will be used to create the granted - * authorites for the user.

- *

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 ldapUser - * object and set the values of the username and password.

- * - * @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); + + GrantedAuthority[] extraAuthorities = getAuthoritiesPopulator().getGrantedAuthorities(user, username); - return createUserDetails(ldapUser, username, password); + 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]; } } diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticator.java b/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticator.java index 626aa5c217..fa7cbc428a 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticator.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticator.java @@ -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); } diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthoritiesPopulator.java b/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthoritiesPopulator.java index f42382cbc8..3b4b00c55e 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthoritiesPopulator.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthoritiesPopulator.java @@ -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; } diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/AbstractLdapAuthenticator.java b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/AbstractLdapAuthenticator.java index 8e1cf4e164..d3ba536703 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/AbstractLdapAuthenticator.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/AbstractLdapAuthenticator.java @@ -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 userDnPatterns 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". diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticator.java b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticator.java index e661e4b075..58bbcca1fe 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticator.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticator.java @@ -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; diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/LdapShaPasswordEncoder.java b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/LdapShaPasswordEncoder.java index 1c650b5f1e..d91dc81e73 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/LdapShaPasswordEncoder.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/LdapShaPasswordEncoder.java @@ -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 forceLowerCasePrefix 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); diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticator.java b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticator.java index f751647cfc..54dc982ee9 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticator.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticator.java @@ -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; } diff --git a/core/src/main/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulator.java b/core/src/main/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulator.java index 5cc97d2ed7..a898001948 100644 --- a/core/src/main/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulator.java +++ b/core/src/main/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulator.java @@ -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 groupSearchBase has been - * set. - * @deprecated Subclasses should implement getAdditionalRoles instead. - */ - protected Set getGroupMembershipRoles(String userDn, Attributes userAttributes) { - return new HashSet(); - } - protected InitialDirContextFactory getInitialDirContextFactory() { return initialDirContextFactory; } diff --git a/core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapper.java b/core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapper.java index e8fafdf837..31fd128928 100644 --- a/core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapper.java +++ b/core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapper.java @@ -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; - - try { - retrievedPassword = passwordAttribute.get(); - } catch (NamingException e) { - throw new UncategorizedLdapException("Failed to get password attribute", e); - } + protected String mapPassword(Object passwordValue) { - 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 GrantedAuthoritys and added to the diff --git a/core/src/test/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearchTests.java b/core/src/test/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearchTests.java index 36c07f2d5f..24f253259d 100644 --- a/core/src/test/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearchTests.java +++ b/core/src/test/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearchTests.java @@ -22,6 +22,7 @@ import org.acegisecurity.userdetails.UsernameNotFoundException; import org.acegisecurity.userdetails.ldap.LdapUserDetails; import org.springframework.dao.IncorrectResultSizeDataAccessException; +import org.springframework.ldap.core.DirContextOperations; /** @@ -48,8 +49,8 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapIntegrationTests locator.setSearchTimeLimit(0); locator.setDerefLinkFlag(false); - LdapUserDetails bob = locator.searchForUser("bob"); - assertEquals("bob", bob.getUsername()); + DirContextOperations bob = locator.searchForUser("bob"); + assertEquals("bob", bob.getStringAttribute("uid")); // name is wrong with embedded apacheDS // assertEquals("uid=bob,ou=people,dc=acegisecurity,dc=org", bob.getDn()); @@ -61,9 +62,8 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapIntegrationTests "(&(cn=*)(!(|(uid={0})(uid=marissa))))", dirCtxFactory); // Search for bob, get back ben... - LdapUserDetails ben = locator.searchForUser("bob"); - String cn = (String) ben.getAttributes().get("cn").get(); - assertEquals("Ben Alex", cn); + DirContextOperations ben = locator.searchForUser("bob"); + assertEquals("Ben Alex", ben.getStringAttribute("cn")); // assertEquals("uid=ben,ou=people,"+ROOT_DN, ben.getDn()); } @@ -91,8 +91,8 @@ public class FilterBasedLdapUserSearchTests extends AbstractLdapIntegrationTests FilterBasedLdapUserSearch locator = new FilterBasedLdapUserSearch("", "(cn={0})", dirCtxFactory); locator.setSearchSubtree(true); - LdapUserDetails ben = locator.searchForUser("Ben Alex"); - assertEquals("ben", ben.getUsername()); + DirContextOperations ben = locator.searchForUser("Ben Alex"); + assertEquals("ben", ben.getStringAttribute("uid")); // assertEquals("uid=ben,ou=people,dc=acegisecurity,dc=org", ben.getDn()); } diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/LdapAuthenticationProviderTests.java b/core/src/test/java/org/acegisecurity/providers/ldap/LdapAuthenticationProviderTests.java index 7a68686b3f..4825e406ea 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/LdapAuthenticationProviderTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/LdapAuthenticationProviderTests.java @@ -24,8 +24,11 @@ import org.acegisecurity.GrantedAuthorityImpl; import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; import org.acegisecurity.userdetails.UserDetails; -import org.acegisecurity.userdetails.ldap.LdapUserDetails; import org.acegisecurity.userdetails.ldap.LdapUserDetailsImpl; +import org.acegisecurity.userdetails.ldap.LdapUserDetailsMapper; +import org.springframework.ldap.core.DirContextOperations; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; import java.util.ArrayList; @@ -54,14 +57,14 @@ public class LdapAuthenticationProviderTests extends TestCase { public void testDifferentCacheValueCausesException() { LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(), new MockAuthoritiesPopulator()); - UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("bob", "bobspassword"); + UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("ben", "benspassword"); // User is authenticated here - UserDetails user = ldapProvider.retrieveUser("bob", authRequest); + UserDetails user = ldapProvider.retrieveUser("ben", authRequest); // Assume the user details object is cached... // And a subsequent authentication request comes in on the cached data - authRequest = new UsernamePasswordAuthenticationToken("bob", "wrongpassword"); + authRequest = new UsernamePasswordAuthenticationToken("ben", "wrongpassword"); try { ldapProvider.additionalAuthenticationChecks(user, authRequest); @@ -95,14 +98,17 @@ public class LdapAuthenticationProviderTests extends TestCase { public void testNormalUsage() { LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator(), new MockAuthoritiesPopulator()); + LdapUserDetailsMapper userMapper = new LdapUserDetailsMapper(); + userMapper.setRoleAttributes(new String[] {"ou"}); + ldapProvider.setUserDetailsContextMapper(userMapper); assertNotNull(ldapProvider.getAuthoritiesPopulator()); - UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("bob", "bobspassword"); - UserDetails user = ldapProvider.retrieveUser("bob", authRequest); + UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("ben", "benspassword"); + UserDetails user = ldapProvider.retrieveUser("ben", authRequest); assertEquals(2, user.getAuthorities().length); - assertEquals("bobspassword", user.getPassword()); - assertEquals("bob", user.getUsername()); + assertEquals("{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=", user.getPassword()); + assertEquals("ben", user.getUsername()); ArrayList authorities = new ArrayList(); authorities.add(user.getAuthorities()[0].getAuthority()); @@ -116,8 +122,11 @@ public class LdapAuthenticationProviderTests extends TestCase { public void testUseWithNullAuthoritiesPopulatorReturnsCorrectRole() { LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(new MockAuthenticator()); - UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("bob", "bobspassword"); - UserDetails user = ldapProvider.retrieveUser("bob", authRequest); + LdapUserDetailsMapper userMapper = new LdapUserDetailsMapper(); + userMapper.setRoleAttributes(new String[] {"ou"}); + ldapProvider.setUserDetailsContextMapper(userMapper); + UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken("ben", "benspassword"); + UserDetails user = ldapProvider.retrieveUser("ben", authRequest); assertEquals(1, user.getAuthorities().length); assertEquals("ROLE_FROM_ENTRY", user.getAuthorities()[0].getAuthority()); } @@ -125,23 +134,20 @@ public class LdapAuthenticationProviderTests extends TestCase { //~ Inner Classes ================================================================================================== class MockAuthenticator implements LdapAuthenticator { - Attributes userAttributes = new BasicAttributes("cn", "bob"); - public LdapUserDetails authenticate(String username, String password) { - LdapUserDetailsImpl.Essence userEssence = new LdapUserDetailsImpl.Essence(); - userEssence.setPassword("{SHA}anencodedpassword"); - userEssence.setAttributes(userAttributes); + public DirContextOperations authenticate(String username, String password) { + DirContextAdapter ctx = new DirContextAdapter(); + ctx.setAttributeValue("ou", "FROM_ENTRY"); - if (username.equals("bob") && password.equals("bobspassword")) { - userEssence.setDn("cn=bob,ou=people,dc=acegisecurity,dc=org"); - userEssence.addAuthority(new GrantedAuthorityImpl("ROLE_FROM_ENTRY")); + if (username.equals("ben") && password.equals("benspassword")) { + ctx.setDn(new DistinguishedName("cn=ben,ou=people,dc=acegisecurity,dc=org")); + ctx.setAttributeValue("userPassword","{SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ="); - return userEssence.createUserDetails(); + return ctx; } else if (username.equals("jen") && password.equals("")) { - userEssence.setDn("cn=jen,ou=people,dc=acegisecurity,dc=org"); - userEssence.addAuthority(new GrantedAuthorityImpl("ROLE_FROM_ENTRY")); + ctx.setDn(new DistinguishedName("cn=jen,ou=people,dc=acegisecurity,dc=org")); - return userEssence.createUserDetails(); + return ctx; } throw new BadCredentialsException("Authentication failed."); @@ -169,7 +175,7 @@ public class LdapAuthenticationProviderTests extends TestCase { // assertEquals(2, auth.getAuthorities().length); // } class MockAuthoritiesPopulator implements LdapAuthoritiesPopulator { - public GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetailsll) { + public GrantedAuthority[] getGrantedAuthorities(DirContextOperations userCtx, String username) { return new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_FROM_POPULATOR")}; } } diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticatorTests.java b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticatorTests.java index 20f53c41f2..a600456ada 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticatorTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticatorTests.java @@ -17,14 +17,13 @@ package org.acegisecurity.providers.ldap.authenticator; import org.acegisecurity.AcegiMessageSource; import org.acegisecurity.BadCredentialsException; -import org.acegisecurity.GrantedAuthorityImpl; import org.acegisecurity.ldap.AbstractLdapIntegrationTests; import org.acegisecurity.ldap.InitialDirContextFactory; -import org.acegisecurity.userdetails.ldap.LdapUserDetails; -import org.acegisecurity.userdetails.ldap.LdapUserDetailsImpl; -import org.acegisecurity.userdetails.ldap.LdapUserDetailsMapper; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.DirContextOperations; /** @@ -48,8 +47,8 @@ public class BindAuthenticatorTests extends AbstractLdapIntegrationTests { public void testAuthenticationWithCorrectPasswordSucceeds() { authenticator.setUserDnPatterns(new String[] {"uid={0},ou=people"}); - LdapUserDetails user = authenticator.authenticate("bob", "bobspassword"); - assertEquals("bob", user.getUsername()); + DirContextOperations user = authenticator.authenticate("bob", "bobspassword"); + assertEquals("bob", user.getStringAttribute("uid")); } public void testAuthenticationWithInvalidUserNameFails() { @@ -62,10 +61,9 @@ public class BindAuthenticatorTests extends AbstractLdapIntegrationTests { } public void testAuthenticationWithUserSearch() throws Exception { - LdapUserDetailsImpl.Essence userEssence = new LdapUserDetailsImpl.Essence(); - userEssence.setDn("uid=bob,ou=people,dc=acegisecurity,dc=org"); + DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=bob,ou=people,dc=acegisecurity,dc=org")); - authenticator.setUserSearch(new MockUserSearch(userEssence.createUserDetails())); + authenticator.setUserSearch(new MockUserSearch(ctx)); authenticator.afterPropertiesSet(); authenticator.authenticate("bob", "bobspassword"); } @@ -79,21 +77,6 @@ public class BindAuthenticatorTests extends AbstractLdapIntegrationTests { } catch (BadCredentialsException expected) {} } - // TODO: Create separate tests for base class - public void testRoleRetrieval() { - authenticator.setUserDnPatterns(new String[] {"uid={0},ou=people"}); - - LdapUserDetailsMapper userMapper = new LdapUserDetailsMapper(); - userMapper.setRoleAttributes(new String[] {"uid"}); - - authenticator.setUserDetailsMapper(userMapper); - - LdapUserDetails user = authenticator.authenticate("bob", "bobspassword"); - - assertEquals(1, user.getAuthorities().length); - assertEquals(new GrantedAuthorityImpl("ROLE_BOB"), user.getAuthorities()[0]); - } - public void testUserDnPatternReturnsCorrectDn() { authenticator.setUserDnPatterns(new String[] {"cn={0},ou=people"}); assertEquals("cn=Joe,ou=people," + ((InitialDirContextFactory)getContextSource()).getRootDn(), authenticator.getUserDns("Joe").get(0)); diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/MockUserSearch.java b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/MockUserSearch.java index 0aadada115..f24223ac75 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/MockUserSearch.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/MockUserSearch.java @@ -18,11 +18,11 @@ package org.acegisecurity.providers.ldap.authenticator; import org.acegisecurity.ldap.LdapUserSearch; import org.acegisecurity.userdetails.ldap.LdapUserDetails; +import org.springframework.ldap.core.DirContextOperations; /** - * -DOCUMENT ME! + * * * @author Luke Taylor * @version $Id$ @@ -30,17 +30,17 @@ DOCUMENT ME! public class MockUserSearch implements LdapUserSearch { //~ Instance fields ================================================================================================ - LdapUserDetails user; + DirContextOperations user; //~ Constructors =================================================================================================== - public MockUserSearch(LdapUserDetails user) { + public MockUserSearch(DirContextOperations user) { this.user = user; } //~ Methods ======================================================================================================== - public LdapUserDetails searchForUser(String username) { + public DirContextOperations searchForUser(String username) { return user; } } diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticatorTests.java b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticatorTests.java index c5bdddb3ca..a7d44da48c 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticatorTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticatorTests.java @@ -23,9 +23,9 @@ import org.acegisecurity.ldap.InitialDirContextFactory; import org.acegisecurity.providers.encoding.PlaintextPasswordEncoder; 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.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; +import org.springframework.ldap.core.DirContextOperations; /** @@ -52,14 +52,13 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapIntegratio // com.sun.jndi.ldap.LdapPoolManager.showStats(System.out); } - public void testAllAttributesAreRetrivedByDefault() { - LdapUserDetails user = authenticator.authenticate("bob", "bobspassword"); + public void testAllAttributesAreRetrievedByDefault() { + DirContextAdapter user = (DirContextAdapter) authenticator.authenticate("bob", "bobspassword"); //System.out.println(user.getAttributes().toString()); assertEquals("User should have 5 attributes", 5, user.getAttributes().size()); } - public void testFailedSearchGivesUserNotFoundException() - throws Exception { + public void testFailedSearchGivesUserNotFoundException() throws Exception { authenticator = new PasswordComparisonAuthenticator((InitialDirContextFactory) getContextSource()); assertTrue("User DN matches shouldn't be available", authenticator.getUserDns("Bob").isEmpty()); authenticator.setUserSearch(new MockUserSearch(null)); @@ -95,10 +94,11 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapIntegratio } public void testLocalPasswordComparisonSucceedsWithCorrectPassword() { - LdapUserDetails user = authenticator.authenticate("bob", "bobspassword"); + DirContextOperations user = authenticator.authenticate("bob", "bobspassword"); // check username is retrieved. - assertEquals("bob", user.getUsername()); - assertEquals("bobspassword", user.getPassword()); + assertEquals("bob", user.getStringAttribute("uid")); + String password = new String((byte[])user.getObjectAttribute("userPassword")); + assertEquals("bobspassword", password); } public void testMultipleDnPatternsWorkOk() { @@ -110,7 +110,7 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapIntegratio authenticator.setUserAttributes(new String[] {"uid", "userPassword"}); authenticator.setPasswordEncoder(new PlaintextPasswordEncoder()); - LdapUserDetails user = authenticator.authenticate("Bob", "bobspassword"); + DirContextAdapter user = (DirContextAdapter) authenticator.authenticate("Bob", "bobspassword"); assertEquals("Should have retrieved 2 attribute (uid, userPassword)", 2, user.getAttributes().size()); } @@ -136,12 +136,8 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapIntegratio } public void testUseOfDifferentPasswordAttribute() { - LdapUserDetailsMapper mapper = new LdapUserDetailsMapper(); - mapper.setPasswordAttributeName("uid"); authenticator.setPasswordAttributeName("uid"); - authenticator.setUserDetailsMapper(mapper); - - LdapUserDetails bob = authenticator.authenticate("bob", "bob"); + authenticator.authenticate("bob", "bob"); } public void testLdapCompareWithDifferentPasswordAttributeSucceeds() { @@ -155,11 +151,10 @@ public class PasswordComparisonAuthenticatorTests extends AbstractLdapIntegratio authenticator = new PasswordComparisonAuthenticator((InitialDirContextFactory) getContextSource()); assertTrue("User DN matches shouldn't be available", authenticator.getUserDns("Bob").isEmpty()); - LdapUserDetailsImpl.Essence userEssence = new LdapUserDetailsImpl.Essence(); - userEssence.setDn("uid=Bob,ou=people,dc=acegisecurity,dc=org"); - userEssence.setPassword("bobspassword"); + DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=Bob,ou=people,dc=acegisecurity,dc=org")); + ctx.setAttributeValue("userPassword", "bobspassword"); - authenticator.setUserSearch(new MockUserSearch(userEssence.createUserDetails())); + authenticator.setUserSearch(new MockUserSearch(ctx)); authenticator.authenticate("ShouldntBeUsed", "bobspassword"); } } diff --git a/core/src/test/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java b/core/src/test/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java index 5158333530..356e4381b0 100644 --- a/core/src/test/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java +++ b/core/src/test/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java @@ -21,6 +21,8 @@ import org.acegisecurity.ldap.AbstractLdapIntegrationTests; import org.acegisecurity.ldap.InitialDirContextFactory; import org.acegisecurity.userdetails.ldap.LdapUserDetailsImpl; +import org.springframework.ldap.core.DirContextAdapter; +import org.springframework.ldap.core.DistinguishedName; import java.util.HashSet; import java.util.Set; @@ -45,39 +47,13 @@ public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapIntegratio } -// public void testUserAttributeMappingToRoles() { -// DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(); -// populator.setUserRoleAttributes(new String[] {"userRole", "otherUserRole"}); -// populator.getUserRoleAttributes(); -// -// Attributes userAttrs = new BasicAttributes(); -// BasicAttribute attr = new BasicAttribute("userRole", "role1"); -// attr.add("role2"); -// userAttrs.put(attr); -// attr = new BasicAttribute("otherUserRole", "role3"); -// attr.add("role2"); // duplicate -// userAttrs.put(attr); -// -// LdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence(); -// user.setDn("Ignored"); -// user.setUsername("Ignored"); -// user.setAttributes(userAttrs); -// -// GrantedAuthority[] authorities = -// populator.getGrantedAuthorities(user.createUserDetails()); -// assertEquals("User should have three roles", 3, authorities.length); - - // } public void testDefaultRoleIsAssignedWhenSet() { populator.setDefaultRole("ROLE_USER"); - LdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence(); - user.setDn("cn=notfound"); - user.setUsername("notfound"); - user.setAttributes(new BasicAttributes()); + DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("cn=notfound")); - GrantedAuthority[] authorities = populator.getGrantedAuthorities(user.createUserDetails()); + GrantedAuthority[] authorities = populator.getGrantedAuthorities(ctx, "notfound"); assertEquals(1, authorities.length); assertEquals("ROLE_USER", authorities[0].getAuthority()); } @@ -90,12 +66,9 @@ public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapIntegratio populator.setConvertToUpperCase(true); populator.setGroupSearchFilter("(member={0})"); - LdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence(); - user.setUsername("ben"); - user.setDn("uid=ben,ou=people,dc=acegisecurity,dc=org"); - user.setAttributes(new BasicAttributes()); + DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=ben,ou=people,dc=acegisecurity,dc=org")); - GrantedAuthority[] authorities = populator.getGrantedAuthorities(user.createUserDetails()); + GrantedAuthority[] authorities = populator.getGrantedAuthorities(ctx, "ben"); assertEquals("Should have 2 roles", 2, authorities.length); @@ -111,11 +84,10 @@ public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapIntegratio populator.setConvertToUpperCase(true); populator.setGroupSearchFilter("(ou={1})"); - LdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence(); - user.setUsername("manager"); - user.setDn("uid=ben,ou=people,dc=acegisecurity,dc=org"); + DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=ben,ou=people,dc=acegisecurity,dc=org")); + + GrantedAuthority[] authorities = populator.getGrantedAuthorities(ctx, "manager"); - GrantedAuthority[] authorities = populator.getGrantedAuthorities(user.createUserDetails()); assertEquals("Should have 1 role", 1, authorities.length); assertEquals("ROLE_MANAGER", authorities[0].getAuthority()); } @@ -124,11 +96,10 @@ public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapIntegratio populator.setGroupRoleAttribute("ou"); populator.setConvertToUpperCase(true); - LdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence(); - user.setUsername("manager"); - user.setDn("uid=ben,ou=people,dc=acegisecurity,dc=org"); + DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=ben,ou=people,dc=acegisecurity,dc=org")); + + GrantedAuthority[] authorities = populator.getGrantedAuthorities(ctx, "manager"); - GrantedAuthority[] authorities = populator.getGrantedAuthorities(user.createUserDetails()); assertEquals("Should have 2 roles", 2, authorities.length); Set roles = new HashSet(2); roles.add(authorities[0].getAuthority()); @@ -142,11 +113,10 @@ public class DefaultLdapAuthoritiesPopulatorTests extends AbstractLdapIntegratio populator.setConvertToUpperCase(true); populator.setSearchSubtree(true); - LdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence(); - user.setUsername("manager"); - user.setDn("uid=ben,ou=people,dc=acegisecurity,dc=org"); + DirContextAdapter ctx = new DirContextAdapter(new DistinguishedName("uid=ben,ou=people,dc=acegisecurity,dc=org")); + + GrantedAuthority[] authorities = populator.getGrantedAuthorities(ctx, "manager"); - GrantedAuthority[] authorities = populator.getGrantedAuthorities(user.createUserDetails()); assertEquals("Should have 3 roles", 3, authorities.length); Set roles = new HashSet(3); roles.add(authorities[0].getAuthority()); diff --git a/core/src/test/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapperTests.java b/core/src/test/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapperTests.java index b0a2c5385f..9290962413 100644 --- a/core/src/test/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapperTests.java +++ b/core/src/test/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapperTests.java @@ -22,6 +22,7 @@ import javax.naming.directory.BasicAttribute; import org.springframework.ldap.core.DirContextAdapter; import org.springframework.ldap.core.DistinguishedName; +import org.acegisecurity.GrantedAuthority; /** * Tests {@link LdapUserDetailsMapper}. @@ -44,7 +45,7 @@ public class LdapUserDetailsMapperTests extends TestCase { ctx.setAttributeValues("userRole", new String[] {"X", "Y", "Z"}); ctx.setAttributeValue("uid", "ani"); - LdapUserDetailsImpl user = (LdapUserDetailsImpl) mapper.mapFromContext(ctx); + LdapUserDetailsImpl user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx, "ani", new GrantedAuthority[0]); assertEquals(3, user.getAuthorities().length); } @@ -63,7 +64,7 @@ public class LdapUserDetailsMapperTests extends TestCase { DirContextAdapter ctx = new DirContextAdapter(attrs, new DistinguishedName("cn=someName")); ctx.setAttributeValue("uid", "ani"); - LdapUserDetailsImpl user = (LdapUserDetailsImpl) mapper.mapFromContext(ctx); + LdapUserDetailsImpl user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx, "ani", new GrantedAuthority[0]); assertEquals(1, user.getAuthorities().length); assertEquals("ROLE_X", user.getAuthorities()[0].getAuthority()); @@ -94,7 +95,7 @@ public class LdapUserDetailsMapperTests extends TestCase { DirContextAdapter ctx = new DirContextAdapter(attrs, new DistinguishedName("cn=someName")); ctx.setAttributeValue("uid", "ani"); - LdapUserDetails user = (LdapUserDetailsImpl) mapper.mapFromContext(ctx); + LdapUserDetails user = (LdapUserDetailsImpl) mapper.mapUserFromContext(ctx, "ani", new GrantedAuthority[0]); assertEquals("mypassword", user.getPassword()); }