Coverage Report - org.acegisecurity.providers.ldap.authenticator.PasswordComparisonAuthenticator
 
Classes in this File Line Coverage Branch Coverage Complexity
PasswordComparisonAuthenticator
95% 
100% 
4.2
 
 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.authenticator;
 17  
 
 18  
 import org.acegisecurity.BadCredentialsException;
 19  
 
 20  
 import org.acegisecurity.ldap.InitialDirContextFactory;
 21  
 import org.acegisecurity.ldap.LdapTemplate;
 22  
 import org.acegisecurity.ldap.LdapUtils;
 23  
 
 24  
 import org.acegisecurity.providers.encoding.PasswordEncoder;
 25  
 
 26  
 import org.acegisecurity.userdetails.UsernameNotFoundException;
 27  
 import org.acegisecurity.userdetails.ldap.LdapUserDetails;
 28  
 import org.acegisecurity.userdetails.ldap.LdapUserDetailsImpl;
 29  
 
 30  
 import org.apache.commons.logging.Log;
 31  
 import org.apache.commons.logging.LogFactory;
 32  
 
 33  
 import org.springframework.util.Assert;
 34  
 
 35  
 import java.util.Iterator;
 36  
 
 37  
 
 38  
 /**
 39  
  * An {@link org.acegisecurity.providers.ldap.LdapAuthenticator LdapAuthenticator} which compares the login
 40  
  * password with the value stored in the directory.
 41  
  *
 42  
  * <p>
 43  
  * This can be achieved either by retrieving the password attribute for the user and comparing it locally,
 44  
  * or by peforming an LDAP "compare" operation. If the password attribute (default "userPassword") is found in the
 45  
  * retrieved attributes it will be compared locally. If not, the remote comparison will be attempted.
 46  
  * </p>
 47  
  * <p>
 48  
  * If passwords are stored in digest form in the repository, then a suitable {@link PasswordEncoder}
 49  
  * implementation must be supplied. By default, passwords are encoded using the {@link LdapShaPasswordEncoder}.
 50  
  * </p>
 51  
  *
 52  
  * @author Luke Taylor
 53  
  * @version $Id: PasswordComparisonAuthenticator.java 1784 2007-02-24 21:00:24Z luke_t $
 54  
  */
 55  
 public final class PasswordComparisonAuthenticator extends AbstractLdapAuthenticator {
 56  
     //~ Static fields/initializers =====================================================================================
 57  
 
 58  2
     private static final Log logger = LogFactory.getLog(PasswordComparisonAuthenticator.class);
 59  
 
 60  
     //~ Instance fields ================================================================================================
 61  
 
 62  13
     private PasswordEncoder passwordEncoder = new LdapShaPasswordEncoder();
 63  13
     private String passwordAttributeName = "userPassword";
 64  
 
 65  
     //~ Constructors ===================================================================================================
 66  
 
 67  
     public PasswordComparisonAuthenticator(InitialDirContextFactory initialDirContextFactory) {
 68  13
         super(initialDirContextFactory);
 69  13
     }
 70  
 
 71  
     //~ Methods ========================================================================================================
 72  
 
 73  
     public LdapUserDetails authenticate(final String username, final String password) {
 74  
         // locate the user and check the password
 75  10
         LdapUserDetails user = null;
 76  
 
 77  10
         Iterator dns = getUserDns(username).iterator();
 78  
 
 79  10
         LdapTemplate ldapTemplate = new LdapTemplate(getInitialDirContextFactory());
 80  
 
 81  19
         while (dns.hasNext() && (user == null)) {
 82  9
             final String userDn = (String) dns.next();
 83  
 
 84  9
             if (ldapTemplate.nameExists(userDn)) {
 85  8
                 LdapUserDetailsImpl.Essence userEssence = (LdapUserDetailsImpl.Essence)
 86  
                         ldapTemplate.retrieveEntry(userDn, getUserDetailsMapper(), getUserAttributes());
 87  8
                 userEssence.setUsername(username);
 88  8
                 user = userEssence.createUserDetails();
 89  
             }
 90  9
         }
 91  
 
 92  10
         if ((user == null) && (getUserSearch() != null)) {
 93  2
             user = getUserSearch().searchForUser(username);
 94  
         }
 95  
 
 96  10
         if (user == null) {
 97  1
             throw new UsernameNotFoundException(username);
 98  
         }
 99  
 
 100  9
         String retrievedPassword = user.getPassword();
 101  
 
 102  9
         if (retrievedPassword != null) {
 103  8
             if (!verifyPassword(password, retrievedPassword)) {
 104  1
                 throw new BadCredentialsException(messages.getMessage(
 105  
                         "PasswordComparisonAuthenticator.badCredentials", "Bad credentials"));
 106  
             }
 107  
 
 108  7
             return user;
 109  
         }
 110  
 
 111  1
         if (logger.isDebugEnabled()) {
 112  0
             logger.debug("Password attribute wasn't retrieved for user '" + username + "' using mapper "
 113  
                 + getUserDetailsMapper() + ". Performing LDAP compare of password attribute '" + passwordAttributeName
 114  
                 + "'");
 115  
         }
 116  
 
 117  1
         String encodedPassword = passwordEncoder.encodePassword(password, null);
 118  1
         byte[] passwordBytes = LdapUtils.getUtf8Bytes(encodedPassword);
 119  
 
 120  1
         if (!ldapTemplate.compare(user.getDn(), passwordAttributeName, passwordBytes)) {
 121  0
             throw new BadCredentialsException(messages.getMessage("PasswordComparisonAuthenticator.badCredentials",
 122  
                     "Bad credentials"));
 123  
         }
 124  
 
 125  1
         return user;
 126  
     }
 127  
 
 128  
     public void setPasswordAttributeName(String passwordAttribute) {
 129  1
         Assert.hasLength(passwordAttribute, "passwordAttributeName must not be empty or null");
 130  1
         this.passwordAttributeName = passwordAttribute;
 131  1
     }
 132  
 
 133  
     public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
 134  2
         Assert.notNull(passwordEncoder, "passwordEncoder must not be null.");
 135  1
         this.passwordEncoder = passwordEncoder;
 136  1
     }
 137  
 
 138  
     /**
 139  
      * Allows the use of both simple and hashed passwords in the directory.
 140  
      *
 141  
      * @param password the password supplied by the user
 142  
      * @param ldapPassword the (possibly hashed) password (from the directory)
 143  
      *
 144  
      * @return true if they match
 145  
      */
 146  
     private boolean verifyPassword(String password, String ldapPassword) {
 147  8
         if (ldapPassword.equals(password)) {
 148  6
             return true;
 149  
         }
 150  
 
 151  2
         if (passwordEncoder.isPasswordValid(ldapPassword, password, null)) {
 152  1
             return true;
 153  
         }
 154  
 
 155  1
         return false;
 156  
     }
 157  
 }