|  | @@ -15,58 +15,57 @@
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  package org.springframework.security.providers.ldap;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +import org.springframework.security.Authentication;
 | 
	
		
			
				|  |  |  import org.springframework.security.AuthenticationException;
 | 
	
		
			
				|  |  | +import org.springframework.security.AuthenticationServiceException;
 | 
	
		
			
				|  |  |  import org.springframework.security.BadCredentialsException;
 | 
	
		
			
				|  |  |  import org.springframework.security.GrantedAuthority;
 | 
	
		
			
				|  |  | -import org.springframework.security.AuthenticationServiceException;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +import org.springframework.security.SpringSecurityMessageSource;
 | 
	
		
			
				|  |  | +import org.springframework.security.providers.AuthenticationProvider;
 | 
	
		
			
				|  |  |  import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 | 
	
		
			
				|  |  | -import org.springframework.security.providers.ldap.authenticator.LdapShaPasswordEncoder;
 | 
	
		
			
				|  |  | -import org.springframework.security.providers.encoding.PasswordEncoder;
 | 
	
		
			
				|  |  | -import org.springframework.security.providers.dao.AbstractUserDetailsAuthenticationProvider;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  import org.springframework.security.userdetails.UserDetails;
 | 
	
		
			
				|  |  | -import org.springframework.security.userdetails.ldap.UserDetailsContextMapper;
 | 
	
		
			
				|  |  |  import org.springframework.security.userdetails.ldap.LdapUserDetailsMapper;
 | 
	
		
			
				|  |  | +import org.springframework.security.userdetails.ldap.UserDetailsContextMapper;
 | 
	
		
			
				|  |  | +import org.springframework.context.support.MessageSourceAccessor;
 | 
	
		
			
				|  |  | +import org.springframework.ldap.NamingException;
 | 
	
		
			
				|  |  | +import org.springframework.ldap.core.DirContextOperations;
 | 
	
		
			
				|  |  | +import org.springframework.util.Assert;
 | 
	
		
			
				|  |  | +import org.springframework.util.StringUtils;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import org.apache.commons.logging.Log;
 | 
	
		
			
				|  |  |  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;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  | - * An {@link org.springframework.security.providers.AuthenticationProvider} implementation that provides integration with an
 | 
	
		
			
				|  |  | - * LDAP server.
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | - * <p>There are many ways in which an LDAP directory can be configured so this class delegates most of
 | 
	
		
			
				|  |  | + * An {@link org.springframework.security.providers.AuthenticationProvider} implementation that provides integration
 | 
	
		
			
				|  |  | + * with an LDAP server.
 | 
	
		
			
				|  |  | + * <p>
 | 
	
		
			
				|  |  | + * There are many ways in which an LDAP directory can be configured so this class delegates most of
 | 
	
		
			
				|  |  |   * its responsibilites to two separate strategy interfaces, {@link LdapAuthenticator}
 | 
	
		
			
				|  |  | - * and {@link LdapAuthoritiesPopulator}.</p>
 | 
	
		
			
				|  |  | + * and {@link LdapAuthoritiesPopulator}.
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  |   * <h3>LdapAuthenticator</h3>
 | 
	
		
			
				|  |  |   * This interface is responsible for performing the user authentication and retrieving
 | 
	
		
			
				|  |  |   * the user's information from the directory. Example implementations are {@link
 | 
	
		
			
				|  |  | - * org.springframework.security.providers.ldap.authenticator.BindAuthenticator BindAuthenticator} which authenticates the user by
 | 
	
		
			
				|  |  | - * "binding" as that user, and {@link org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator
 | 
	
		
			
				|  |  | - * PasswordComparisonAuthenticator} which performs a comparison of the supplied password with the value stored in the
 | 
	
		
			
				|  |  | - * directory, either by retrieving the password or performing an LDAP "compare" operation.
 | 
	
		
			
				|  |  | - * <p>The task of retrieving the user attributes is delegated to the authenticator because the permissions on the
 | 
	
		
			
				|  |  | + * org.springframework.security.providers.ldap.authenticator.BindAuthenticator BindAuthenticator} which authenticates
 | 
	
		
			
				|  |  | + * the user by "binding" as that user, and
 | 
	
		
			
				|  |  | + * {@link org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator PasswordComparisonAuthenticator}
 | 
	
		
			
				|  |  | + * which compares the supplied password with the value stored in the directory, using an LDAP "compare"
 | 
	
		
			
				|  |  | + * operation.
 | 
	
		
			
				|  |  | + * <p>
 | 
	
		
			
				|  |  | + * The task of retrieving the user attributes is delegated to the authenticator because the permissions on the
 | 
	
		
			
				|  |  |   * attributes may depend on the type of authentication being used; for example, if binding as the user, it may be
 | 
	
		
			
				|  |  | - * necessary to read them with the user's own permissions (using the same context used for the bind operation).</p>
 | 
	
		
			
				|  |  | + * necessary to read them with the user's own permissions (using the same context used for the bind operation).
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  |   * <h3>LdapAuthoritiesPopulator</h3>
 | 
	
		
			
				|  |  |   * Once the user has been authenticated, this interface is called to obtain the set of granted authorities for the
 | 
	
		
			
				|  |  |   * user.
 | 
	
		
			
				|  |  | - * The
 | 
	
		
			
				|  |  | - * {@link org.springframework.security.providers.ldap.populator.DefaultLdapAuthoritiesPopulator DefaultLdapAuthoritiesPopulator}
 | 
	
		
			
				|  |  | + * The {@link org.springframework.security.providers.ldap.populator.DefaultLdapAuthoritiesPopulator DefaultLdapAuthoritiesPopulator}
 | 
	
		
			
				|  |  |   * can be configured to obtain user role information from the user's attributes and/or to perform a search for
 | 
	
		
			
				|  |  |   * "groups" that the user is a member of and map these to roles.
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  | - * <p>A custom implementation could obtain the roles from a completely different source, for example from a database.
 | 
	
		
			
				|  |  | - * </p>
 | 
	
		
			
				|  |  | + * <p>
 | 
	
		
			
				|  |  | + * A custom implementation could obtain the roles from a completely different source, for example from a database.
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  |   * <h3>Configuration</h3>
 | 
	
		
			
				|  |  |   *
 | 
	
	
		
			
				|  | @@ -94,21 +93,22 @@ import org.springframework.ldap.core.DirContextOperations;
 | 
	
		
			
				|  |  |   *      </constructor-arg>
 | 
	
		
			
				|  |  |   *    </bean></pre>
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  | - * <p>This would set up the provider to access an LDAP server with URL
 | 
	
		
			
				|  |  | + * <p>
 | 
	
		
			
				|  |  | + * This would set up the provider to access an LDAP server with URL
 | 
	
		
			
				|  |  |   * <tt>ldap://monkeymachine:389/dc=springframework,dc=org</tt>. Authentication will be performed by attempting to bind
 | 
	
		
			
				|  |  |   * with the DN <tt>uid=<user-login-name>,ou=people,dc=springframework,dc=org</tt>. After successful
 | 
	
		
			
				|  |  |   * authentication, roles will be assigned to the user by searching under the DN
 | 
	
		
			
				|  |  |   * <tt>ou=groups,dc=springframework,dc=org</tt> with the default filter <tt>(member=<user's-DN>)</tt>. The role
 | 
	
		
			
				|  |  | - * name will be taken from the "ou" attribute of each match.</p>
 | 
	
		
			
				|  |  | + * name will be taken from the "ou" attribute of each match.
 | 
	
		
			
				|  |  |   * <p>
 | 
	
		
			
				|  |  |   * The authenticate method will reject empty passwords outright. LDAP servers may allow an anonymous
 | 
	
		
			
				|  |  |   * bind operation with an empty password, even if a DN is supplied. In practice this means that if
 | 
	
		
			
				|  |  | - * the LDAP directory is configured to allow unauthenitcated access, it might be possible to
 | 
	
		
			
				|  |  | + * the LDAP directory is configured to allow unauthenticated access, it might be possible to
 | 
	
		
			
				|  |  |   * authenticate as <i>any</i> user just by supplying an empty password.
 | 
	
		
			
				|  |  |   * More information on the misuse of unauthenticated access can be found in
 | 
	
		
			
				|  |  |   * <a href="http://www.ietf.org/internet-drafts/draft-ietf-ldapbis-authmeth-19.txt">
 | 
	
		
			
				|  |  |   * draft-ietf-ldapbis-authmeth-19.txt</a>.
 | 
	
		
			
				|  |  | - * </p>
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  |   * @author Luke Taylor
 | 
	
		
			
				|  |  |   * @version $Id$
 | 
	
	
		
			
				|  | @@ -116,18 +116,18 @@ import org.springframework.ldap.core.DirContextOperations;
 | 
	
		
			
				|  |  |   * @see org.springframework.security.providers.ldap.authenticator.BindAuthenticator
 | 
	
		
			
				|  |  |   * @see org.springframework.security.providers.ldap.populator.DefaultLdapAuthoritiesPopulator
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  | -public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
 | 
	
		
			
				|  |  | +public class LdapAuthenticationProvider implements AuthenticationProvider {
 | 
	
		
			
				|  |  |      //~ Static fields/initializers =====================================================================================
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private static final Log logger = LogFactory.getLog(LdapAuthenticationProvider.class);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      //~ Instance fields ================================================================================================
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      private LdapAuthenticator authenticator;
 | 
	
		
			
				|  |  |      private LdapAuthoritiesPopulator authoritiesPopulator;
 | 
	
		
			
				|  |  |      private UserDetailsContextMapper userDetailsContextMapper = new LdapUserDetailsMapper();
 | 
	
		
			
				|  |  | -    private PasswordEncoder passwordEncoder = new LdapShaPasswordEncoder();
 | 
	
		
			
				|  |  | -    private boolean includeDetailsObject = true;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      //~ Constructors ===================================================================================================
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -184,29 +184,20 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
 | 
	
		
			
				|  |  |          return userDetailsContextMapper;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    protected void additionalAuthenticationChecks(UserDetails userDetails,
 | 
	
		
			
				|  |  | -                                                  UsernamePasswordAuthenticationToken authentication)
 | 
	
		
			
				|  |  | -        throws AuthenticationException {
 | 
	
		
			
				|  |  | -		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);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
 | 
	
		
			
				|  |  | +        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
 | 
	
		
			
				|  |  | +            messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
 | 
	
		
			
				|  |  | +                "Only UsernamePasswordAuthenticationToken is supported"));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        UsernamePasswordAuthenticationToken userToken = (UsernamePasswordAuthenticationToken)authentication;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        String username = userToken.getName();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
 | 
	
		
			
				|  |  | -            throws AuthenticationException {
 | 
	
		
			
				|  |  |          if (!StringUtils.hasLength(username)) {
 | 
	
		
			
				|  |  |              throw new BadCredentialsException(messages.getMessage("LdapAuthenticationProvider.emptyUsername",
 | 
	
		
			
				|  |  |                      "Empty Username"));
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if (logger.isDebugEnabled()) {
 | 
	
		
			
				|  |  | -            logger.debug("Retrieving user " + username);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          String password = (String) authentication.getCredentials();
 | 
	
		
			
				|  |  |          Assert.notNull(password, "Null password was supplied in authentication token");
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -217,23 +208,27 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          try {
 | 
	
		
			
				|  |  | -            DirContextOperations user = getAuthenticator().authenticate(authentication);
 | 
	
		
			
				|  |  | +            DirContextOperations userData = getAuthenticator().authenticate(authentication);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            GrantedAuthority[] extraAuthorities = getAuthoritiesPopulator().getGrantedAuthorities(userData, username);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            GrantedAuthority[] extraAuthorities = getAuthoritiesPopulator().getGrantedAuthorities(user, username);
 | 
	
		
			
				|  |  | +            UserDetails user = userDetailsContextMapper.mapUserFromContext(userData, username, extraAuthorities);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            return userDetailsContextMapper.mapUserFromContext(user, username, extraAuthorities);
 | 
	
		
			
				|  |  | +            return createSuccessfulAuthentication(userToken, user);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        } catch (DataAccessException ldapAccessFailure) {
 | 
	
		
			
				|  |  | +        } catch (NamingException ldapAccessFailure) {
 | 
	
		
			
				|  |  |              throw new AuthenticationServiceException(ldapAccessFailure.getMessage(), ldapAccessFailure);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    public boolean isIncludeDetailsObject() {
 | 
	
		
			
				|  |  | -        return includeDetailsObject;
 | 
	
		
			
				|  |  | +    protected Authentication createSuccessfulAuthentication(UsernamePasswordAuthenticationToken authentication,
 | 
	
		
			
				|  |  | +            UserDetails user) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    public void setIncludeDetailsObject(boolean includeDetailsObject) {
 | 
	
		
			
				|  |  | -        this.includeDetailsObject = includeDetailsObject;
 | 
	
		
			
				|  |  | +    public boolean supports(Class authentication) {
 | 
	
		
			
				|  |  | +        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      //~ Inner Classes ==================================================================================================
 |