ソースを参照

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

Luke Taylor 18 年 前
コミット
8a35f7da75
20 ファイル変更189 行追加327 行削除
  1. 3 3
      core/src/main/java/org/acegisecurity/ldap/LdapUserSearch.java
  2. 2 2
      core/src/main/java/org/acegisecurity/ldap/LdapUtils.java
  3. 8 14
      core/src/main/java/org/acegisecurity/ldap/SpringSecurityLdapTemplate.java
  4. 3 16
      core/src/main/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearch.java
  5. 21 36
      core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticationProvider.java
  6. 2 1
      core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthenticator.java
  7. 3 3
      core/src/main/java/org/acegisecurity/providers/ldap/LdapAuthoritiesPopulator.java
  8. 0 13
      core/src/main/java/org/acegisecurity/providers/ldap/authenticator/AbstractLdapAuthenticator.java
  9. 10 11
      core/src/main/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticator.java
  10. 7 0
      core/src/main/java/org/acegisecurity/providers/ldap/authenticator/LdapShaPasswordEncoder.java
  11. 16 14
      core/src/main/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticator.java
  12. 8 40
      core/src/main/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulator.java
  13. 24 47
      core/src/main/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapper.java
  14. 7 7
      core/src/test/java/org/acegisecurity/ldap/search/FilterBasedLdapUserSearchTests.java
  15. 29 23
      core/src/test/java/org/acegisecurity/providers/ldap/LdapAuthenticationProviderTests.java
  16. 7 24
      core/src/test/java/org/acegisecurity/providers/ldap/authenticator/BindAuthenticatorTests.java
  17. 5 5
      core/src/test/java/org/acegisecurity/providers/ldap/authenticator/MockUserSearch.java
  18. 15 20
      core/src/test/java/org/acegisecurity/providers/ldap/authenticator/PasswordComparisonAuthenticatorTests.java
  19. 15 45
      core/src/test/java/org/acegisecurity/providers/ldap/populator/DefaultLdapAuthoritiesPopulatorTests.java
  20. 4 3
      core/src/test/java/org/acegisecurity/userdetails/ldap/LdapUserDetailsMapperTests.java

+ 3 - 3
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);
 }

+ 2 - 2
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();
 

+ 8 - 14
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()));
                 }
             });
     }

+ 3 - 16
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();
 

+ 21 - 36
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 <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);
+
+            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];
         }
     }

+ 2 - 1
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);
 }

+ 3 - 3
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;
 }

+ 0 - 13
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 <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".

+ 10 - 11
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;

+ 7 - 0
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 <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);

+ 16 - 14
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;
         }
 

+ 8 - 40
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 <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;
     }

+ 24 - 47
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 <tt>GrantedAuthority</tt>s and added to the

+ 7 - 7
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());
     }

+ 29 - 23
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")};
         }
     }

+ 7 - 24
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));

+ 5 - 5
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;
     }
 }

+ 15 - 20
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");
     }
 }

+ 15 - 45
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());

+ 4 - 3
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());
     }