001: /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
002: *
003: * Licensed under the Apache License, Version 2.0 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015:
016: package org.acegisecurity.providers.ldap.authenticator;
017:
018: import org.acegisecurity.BadCredentialsException;
019:
020: import org.acegisecurity.ldap.InitialDirContextFactory;
021: import org.acegisecurity.ldap.LdapTemplate;
022: import org.acegisecurity.ldap.LdapUtils;
023:
024: import org.acegisecurity.providers.encoding.PasswordEncoder;
025:
026: import org.acegisecurity.userdetails.UsernameNotFoundException;
027: import org.acegisecurity.userdetails.ldap.LdapUserDetails;
028: import org.acegisecurity.userdetails.ldap.LdapUserDetailsImpl;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032:
033: import org.springframework.util.Assert;
034:
035: import java.util.Iterator;
036:
037: /**
038: * An {@link org.acegisecurity.providers.ldap.LdapAuthenticator LdapAuthenticator} which compares the login
039: * password with the value stored in the directory.
040: *
041: * <p>
042: * This can be achieved either by retrieving the password attribute for the user and comparing it locally,
043: * or by peforming an LDAP "compare" operation. If the password attribute (default "userPassword") is found in the
044: * retrieved attributes it will be compared locally. If not, the remote comparison will be attempted.
045: * </p>
046: * <p>
047: * If passwords are stored in digest form in the repository, then a suitable {@link PasswordEncoder}
048: * implementation must be supplied. By default, passwords are encoded using the {@link LdapShaPasswordEncoder}.
049: * </p>
050: *
051: * @author Luke Taylor
052: * @version $Id: PasswordComparisonAuthenticator.java 1784 2007-02-24 21:00:24Z luke_t $
053: */
054: public final class PasswordComparisonAuthenticator extends
055: AbstractLdapAuthenticator {
056: //~ Static fields/initializers =====================================================================================
057:
058: private static final Log logger = LogFactory
059: .getLog(PasswordComparisonAuthenticator.class);
060:
061: //~ Instance fields ================================================================================================
062:
063: private PasswordEncoder passwordEncoder = new LdapShaPasswordEncoder();
064: private String passwordAttributeName = "userPassword";
065:
066: //~ Constructors ===================================================================================================
067:
068: public PasswordComparisonAuthenticator(
069: InitialDirContextFactory initialDirContextFactory) {
070: super (initialDirContextFactory);
071: }
072:
073: //~ Methods ========================================================================================================
074:
075: public LdapUserDetails authenticate(final String username,
076: final String password) {
077: // locate the user and check the password
078: LdapUserDetails user = null;
079:
080: Iterator dns = getUserDns(username).iterator();
081:
082: LdapTemplate ldapTemplate = new LdapTemplate(
083: getInitialDirContextFactory());
084:
085: while (dns.hasNext() && (user == null)) {
086: final String userDn = (String) dns.next();
087:
088: if (ldapTemplate.nameExists(userDn)) {
089: LdapUserDetailsImpl.Essence userEssence = (LdapUserDetailsImpl.Essence) ldapTemplate
090: .retrieveEntry(userDn, getUserDetailsMapper(),
091: getUserAttributes());
092: userEssence.setUsername(username);
093: user = userEssence.createUserDetails();
094: }
095: }
096:
097: if ((user == null) && (getUserSearch() != null)) {
098: user = getUserSearch().searchForUser(username);
099: }
100:
101: if (user == null) {
102: throw new UsernameNotFoundException(username);
103: }
104:
105: String retrievedPassword = user.getPassword();
106:
107: if (retrievedPassword != null) {
108: if (!verifyPassword(password, retrievedPassword)) {
109: throw new BadCredentialsException(
110: messages
111: .getMessage(
112: "PasswordComparisonAuthenticator.badCredentials",
113: "Bad credentials"));
114: }
115:
116: return user;
117: }
118:
119: if (logger.isDebugEnabled()) {
120: logger
121: .debug("Password attribute wasn't retrieved for user '"
122: + username
123: + "' using mapper "
124: + getUserDetailsMapper()
125: + ". Performing LDAP compare of password attribute '"
126: + passwordAttributeName + "'");
127: }
128:
129: String encodedPassword = passwordEncoder.encodePassword(
130: password, null);
131: byte[] passwordBytes = LdapUtils.getUtf8Bytes(encodedPassword);
132:
133: if (!ldapTemplate.compare(user.getDn(), passwordAttributeName,
134: passwordBytes)) {
135: throw new BadCredentialsException(messages.getMessage(
136: "PasswordComparisonAuthenticator.badCredentials",
137: "Bad credentials"));
138: }
139:
140: return user;
141: }
142:
143: public void setPasswordAttributeName(String passwordAttribute) {
144: Assert.hasLength(passwordAttribute,
145: "passwordAttributeName must not be empty or null");
146: this .passwordAttributeName = passwordAttribute;
147: }
148:
149: public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
150: Assert.notNull(passwordEncoder,
151: "passwordEncoder must not be null.");
152: this .passwordEncoder = passwordEncoder;
153: }
154:
155: /**
156: * Allows the use of both simple and hashed passwords in the directory.
157: *
158: * @param password the password supplied by the user
159: * @param ldapPassword the (possibly hashed) password (from the directory)
160: *
161: * @return true if they match
162: */
163: private boolean verifyPassword(String password, String ldapPassword) {
164: if (ldapPassword.equals(password)) {
165: return true;
166: }
167:
168: if (passwordEncoder.isPasswordValid(ldapPassword, password,
169: null)) {
170: return true;
171: }
172:
173: return false;
174: }
175: }
|