Coverage Report - org.acegisecurity.providers.ldap.LdapAuthenticationProvider
 
Classes in this File Line Coverage Branch Coverage Complexity
LdapAuthenticationProvider
87% 
100% 
1.917
 
 1  
 /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
 2  
  *
 3  
  * Licensed under the Apache License, Version 2.0 (the "License");
 4  
  * you may not use this file except in compliance with the License.
 5  
  * You may obtain a copy of the License at
 6  
  *
 7  
  *     http://www.apache.org/licenses/LICENSE-2.0
 8  
  *
 9  
  * Unless required by applicable law or agreed to in writing, software
 10  
  * distributed under the License is distributed on an "AS IS" BASIS,
 11  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  
  * See the License for the specific language governing permissions and
 13  
  * limitations under the License.
 14  
  */
 15  
 
 16  
 package org.acegisecurity.providers.ldap;
 17  
 
 18  
 import org.acegisecurity.AuthenticationException;
 19  
 import org.acegisecurity.BadCredentialsException;
 20  
 import org.acegisecurity.GrantedAuthority;
 21  
 import org.acegisecurity.AuthenticationServiceException;
 22  
 import org.acegisecurity.ldap.LdapDataAccessException;
 23  
 
 24  
 import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
 25  
 import org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider;
 26  
 
 27  
 import org.acegisecurity.userdetails.UserDetails;
 28  
 import org.acegisecurity.userdetails.ldap.LdapUserDetails;
 29  
 import org.acegisecurity.userdetails.ldap.LdapUserDetailsImpl;
 30  
 
 31  
 import org.apache.commons.logging.Log;
 32  
 import org.apache.commons.logging.LogFactory;
 33  
 
 34  
 import org.springframework.util.Assert;
 35  
 import org.springframework.util.StringUtils;
 36  
 import org.springframework.dao.DataAccessException;
 37  
 
 38  
 
 39  
 /**
 40  
  * An {@link org.acegisecurity.providers.AuthenticationProvider} implementation that provides integration with an
 41  
  * LDAP server.
 42  
  *
 43  
  * <p>There are many ways in which an LDAP directory can be configured so this class delegates most of
 44  
  * its responsibilites to two separate strategy interfaces, {@link LdapAuthenticator}
 45  
  * and {@link LdapAuthoritiesPopulator}.</p>
 46  
  *
 47  
  * <h3>LdapAuthenticator</h3>
 48  
  * This interface is responsible for performing the user authentication and retrieving
 49  
  * the user's information from the directory. Example implementations are {@link
 50  
  * org.acegisecurity.providers.ldap.authenticator.BindAuthenticator BindAuthenticator} which authenticates the user by
 51  
  * "binding" as that user, and {@link org.acegisecurity.providers.ldap.authenticator.PasswordComparisonAuthenticator
 52  
  * PasswordComparisonAuthenticator} which performs a comparison of the supplied password with the value stored in the
 53  
  * directory, either by retrieving the password or performing an LDAP "compare" operation.
 54  
  * <p>The task of retrieving the user attributes is delegated to the authenticator because the permissions on the
 55  
  * attributes may depend on the type of authentication being used; for example, if binding as the user, it may be
 56  
  * necessary to read them with the user's own permissions (using the same context used for the bind operation).</p>
 57  
  *
 58  
  * <h3>LdapAuthoritiesPopulator</h3>
 59  
  * Once the user has been authenticated, this interface is called to obtain the set of granted authorities for the
 60  
  * user.
 61  
  * The
 62  
  * {@link org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator DefaultLdapAuthoritiesPopulator}
 63  
  * can be configured to obtain user role information from the user's attributes and/or to perform a search for
 64  
  * "groups" that the user is a member of and map these to roles.
 65  
  *
 66  
  * <p>A custom implementation could obtain the roles from a completely different source, for example from a database.
 67  
  * </p>
 68  
  *
 69  
  * <h3>Configuration</h3>
 70  
  *
 71  
  * A simple configuration might be as follows:
 72  
  * <pre>
 73  
  *    &lt;bean id="initialDirContextFactory" class="org.acegisecurity.providers.ldap.DefaultInitialDirContextFactory">
 74  
  *      &lt;constructor-arg value="ldap://monkeymachine:389/dc=acegisecurity,dc=org"/>
 75  
  *      &lt;property name="managerDn">&lt;value>cn=manager,dc=acegisecurity,dc=org&lt;/value>&lt;/property>
 76  
  *      &lt;property name="managerPassword">&lt;value>password&lt;/value>&lt;/property>
 77  
  *    &lt;/bean>
 78  
  *
 79  
  *    &lt;bean id="ldapAuthProvider" class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
 80  
  *      &lt;constructor-arg>
 81  
  *        &lt;bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
 82  
  *          &lt;constructor-arg>&lt;ref local="initialDirContextFactory"/>&lt;/constructor-arg>
 83  
  *          &lt;property name="userDnPatterns">&lt;list>&lt;value>uid={0},ou=people&lt;/value>&lt;/list>&lt;/property>
 84  
  *        &lt;/bean>
 85  
  *      &lt;/constructor-arg>
 86  
  *      &lt;constructor-arg>
 87  
  *        &lt;bean class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
 88  
  *          &lt;constructor-arg>&lt;ref local="initialDirContextFactory"/>&lt;/constructor-arg>
 89  
  *          &lt;constructor-arg>&lt;value>ou=groups&lt;/value>&lt;/constructor-arg>
 90  
  *          &lt;property name="groupRoleAttribute">&lt;value>ou&lt;/value>&lt;/property>
 91  
  *        &lt;/bean>
 92  
  *      &lt;/constructor-arg>
 93  
  *    &lt;/bean></pre>
 94  
  *
 95  
  * <p>This would set up the provider to access an LDAP server with URL
 96  
  * <tt>ldap://monkeymachine:389/dc=acegisecurity,dc=org</tt>. Authentication will be performed by attempting to bind
 97  
  * with the DN <tt>uid=&lt;user-login-name&gt;,ou=people,dc=acegisecurity,dc=org</tt>. After successful
 98  
  * authentication, roles will be assigned to the user by searching under the DN
 99  
  * <tt>ou=groups,dc=acegisecurity,dc=org</tt> with the default filter <tt>(member=&lt;user's-DN&gt;)</tt>. The role
 100  
  * name will be taken from the "ou" attribute of each match.</p>
 101  
  * <p>
 102  
  * The authenticate method will reject empty passwords outright. LDAP servers may allow an anonymous
 103  
  * bind operation with an empty password, even if a DN is supplied. In practice this means that if
 104  
  * the LDAP directory is configured to allow unauthenitcated access, it might be possible to
 105  
  * authenticate as <i>any</i> user just by supplying an empty password.
 106  
  * More information on the misuse of unauthenticated access can be found in
 107  
  * <a href="http://www.ietf.org/internet-drafts/draft-ietf-ldapbis-authmeth-19.txt">
 108  
  * draft-ietf-ldapbis-authmeth-19.txt</a>.
 109  
  * </p>
 110  
  *
 111  
  * @author Luke Taylor
 112  
  * @version $Id: LdapAuthenticationProvider.java 1995 2007-08-30 21:12:16Z luke_t $
 113  
  *
 114  
  * @see org.acegisecurity.providers.ldap.authenticator.BindAuthenticator
 115  
  * @see org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator
 116  
  */
 117  
 public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
 118  
     //~ Static fields/initializers =====================================================================================
 119  
 
 120  2
     private static final Log logger = LogFactory.getLog(LdapAuthenticationProvider.class);
 121  
 
 122  
     //~ Instance fields ================================================================================================
 123  
 
 124  
     private LdapAuthenticator authenticator;
 125  
     private LdapAuthoritiesPopulator authoritiesPopulator;
 126  5
     private boolean includeDetailsObject = true;
 127  
 
 128  
     //~ Constructors ===================================================================================================
 129  
 
 130  
     /**
 131  
      * Create an instance with the supplied authenticator and authorities populator implementations.
 132  
      *
 133  
      * @param authenticator the authentication strategy (bind, password comparison, etc)
 134  
      *          to be used by this provider for authenticating users.
 135  
      * @param authoritiesPopulator the strategy for obtaining the authorities for a given user after they've been
 136  
      *          authenticated.
 137  
      */
 138  3
     public LdapAuthenticationProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) {
 139  3
         this.setAuthenticator(authenticator);
 140  3
         this.setAuthoritiesPopulator(authoritiesPopulator);
 141  3
     }
 142  
 
 143  
     /**
 144  
      * Creates an instance with the supplied authenticator and a null authorities populator.
 145  
      * In this case, the authorities must be mapped from the user context.
 146  
      *
 147  
      * @param authenticator the authenticator strategy.
 148  
      */
 149  2
     public LdapAuthenticationProvider(LdapAuthenticator authenticator) {
 150  2
         this.setAuthenticator(authenticator);
 151  2
         this.setAuthoritiesPopulator(new NullAuthoritiesPopulator());
 152  2
     }    
 153  
 
 154  
     //~ Methods ========================================================================================================
 155  
 
 156  
     private void setAuthenticator(LdapAuthenticator authenticator) {
 157  5
         Assert.notNull(authenticator, "An LdapAuthenticator must be supplied");
 158  5
         this.authenticator = authenticator;
 159  5
     }
 160  
 
 161  
     private LdapAuthenticator getAuthenticator() {
 162  3
         return authenticator;
 163  
     }
 164  
 
 165  
     private void setAuthoritiesPopulator(LdapAuthoritiesPopulator authoritiesPopulator) {
 166  5
         Assert.notNull(authoritiesPopulator, "An LdapAuthoritiesPopulator must be supplied");
 167  5
         this.authoritiesPopulator = authoritiesPopulator;
 168  5
     }
 169  
 
 170  
     protected LdapAuthoritiesPopulator getAuthoritiesPopulator() {
 171  4
         return authoritiesPopulator;
 172  
     }
 173  
 
 174  
     protected void additionalAuthenticationChecks(UserDetails userDetails,
 175  
                                                   UsernamePasswordAuthenticationToken authentication)
 176  
         throws AuthenticationException {
 177  2
         if (!userDetails.getPassword().equals(authentication.getCredentials().toString())) {
 178  1
             throw new BadCredentialsException(messages.getMessage(
 179  
                     "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"),
 180  
                     includeDetailsObject ? userDetails : null);
 181  
         }
 182  1
     }
 183  
 
 184  
     /**
 185  
      * Creates the final <tt>UserDetails</tt> object that will be returned by the provider once the user has
 186  
      * been authenticated.<p>The <tt>LdapAuthoritiesPopulator</tt> will be used to create the granted
 187  
      * authorites for the user.</p>
 188  
      *  <p>Can be overridden to customize the creation of the final UserDetails instance. The default will
 189  
      * merge any additional authorities retrieved from the populator with the propertis of original <tt>ldapUser</tt>
 190  
      * object and set the values of the username and password.</p>
 191  
      *
 192  
      * @param ldapUser The intermediate LdapUserDetails instance returned by the authenticator.
 193  
      * @param username the username submitted to the provider
 194  
      * @param password the password submitted to the provider
 195  
      *
 196  
      * @return The UserDetails for the successfully authenticated user.
 197  
      */
 198  
     protected UserDetails createUserDetails(LdapUserDetails ldapUser, String username, String password) {
 199  3
         LdapUserDetailsImpl.Essence user = new LdapUserDetailsImpl.Essence(ldapUser);
 200  3
         user.setUsername(username);
 201  3
         user.setPassword(password);
 202  
 
 203  3
         GrantedAuthority[] extraAuthorities = getAuthoritiesPopulator().getGrantedAuthorities(ldapUser);
 204  
 
 205  5
         for (int i = 0; i < extraAuthorities.length; i++) {
 206  2
             user.addAuthority(extraAuthorities[i]);
 207  
         }
 208  
 
 209  3
         return user.createUserDetails();
 210  
     }
 211  
 
 212  
     protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
 213  
         throws AuthenticationException {
 214  6
         if (!StringUtils.hasLength(username)) {
 215  2
             throw new BadCredentialsException(messages.getMessage("LdapAuthenticationProvider.emptyUsername",
 216  
                     "Empty Username"));
 217  
         }
 218  
 
 219  4
         if (logger.isDebugEnabled()) {
 220  0
             logger.debug("Retrieving user " + username);
 221  
         }
 222  
 
 223  4
         String password = (String) authentication.getCredentials();
 224  4
         Assert.notNull(password, "Null password was supplied in authentication token");
 225  
 
 226  4
         if (password.length() == 0) {
 227  1
             logger.debug("Rejecting empty password for user " + username);
 228  1
             throw new BadCredentialsException(messages.getMessage("LdapAuthenticationProvider.emptyPassword",
 229  
                     "Empty Password"));
 230  
         }
 231  
 
 232  
         try {
 233  3
             LdapUserDetails ldapUser = getAuthenticator().authenticate(username, password);
 234  
 
 235  3
             return createUserDetails(ldapUser, username, password);
 236  
 
 237  0
         } catch (DataAccessException ldapAccessFailure) {
 238  0
             throw new AuthenticationServiceException(ldapAccessFailure.getMessage(), ldapAccessFailure);
 239  
         }
 240  
     }
 241  
 
 242  
     public boolean isIncludeDetailsObject() {
 243  0
         return includeDetailsObject;
 244  
     }
 245  
 
 246  
     public void setIncludeDetailsObject(boolean includeDetailsObject) {
 247  0
         this.includeDetailsObject = includeDetailsObject;
 248  0
     }
 249  
 
 250  
     //~ Inner Classes ==================================================================================================
 251  
 
 252  4
     private static class NullAuthoritiesPopulator implements LdapAuthoritiesPopulator {
 253  
         public GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails) throws LdapDataAccessException {
 254  1
             return new GrantedAuthority[0];
 255  
         }
 256  
     }
 257  
 }
 258