Package org.springframework.security.ldap.authentication

Source Code of org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator

/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.security.ldap.authentication;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.ldap.NameNotFoundException;
import org.springframework.ldap.NamingException;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.encoding.LdapShaPasswordEncoder;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.util.Assert;


/**
* An {@link org.springframework.security.ldap.authentication.LdapAuthenticator LdapAuthenticator} which compares the login
* password with the value stored in the directory using a remote LDAP "compare" operation.
*
* <p>
* If passwords are stored in digest form in the repository, then a suitable {@link PasswordEncoder}
* implementation must be supplied. By default, passwords are encoded using the {@link LdapShaPasswordEncoder}.
* Note that compare operations will not work if salted-SHA (SSHA) passwords are used, as it is not possible to
* know the salt value which is a random byte sequence generated by the directory.
*
* @author Luke Taylor
*/
public final class PasswordComparisonAuthenticator extends AbstractLdapAuthenticator {
    //~ Static fields/initializers =====================================================================================

    private static final Log logger = LogFactory.getLog(PasswordComparisonAuthenticator.class);

    //~ Instance fields ================================================================================================

    private PasswordEncoder passwordEncoder = new LdapShaPasswordEncoder();
    private String passwordAttributeName = "userPassword";
    private boolean usePasswordAttrCompare = false;

    //~ Constructors ===================================================================================================

    public PasswordComparisonAuthenticator(BaseLdapPathContextSource contextSource) {
        super(contextSource);
    }

    //~ Methods ========================================================================================================

    public DirContextOperations authenticate(final Authentication authentication) {
        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
                "Can only process UsernamePasswordAuthenticationToken objects");
        // locate the user and check the password

        DirContextOperations user = null;
        String username = authentication.getName();
        String password = (String)authentication.getCredentials();

        SpringSecurityLdapTemplate ldapTemplate = new SpringSecurityLdapTemplate(getContextSource());

        for (String userDn : getUserDns(username)) {
            try {
                user = ldapTemplate.retrieveEntry(userDn, getUserAttributes());
            } catch (NameNotFoundException ignore) {
            }
            if (user != null) {
                break;
            }
        }

        if (user == null && getUserSearch() != null) {
            user = getUserSearch().searchForUser(username);
        }

        if (user == null) {
            throw new UsernameNotFoundException("User not found: " + username, username);
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Performing LDAP compare of password attribute '" + passwordAttributeName + "' for user '" +
                    user.getDn() +"'");
        }

        if (usePasswordAttrCompare && isPasswordAttrCompare(user, password)) {
            return user;
        } else if(isLdapPasswordCompare(user, ldapTemplate, password)) {
            return user;
        }
        throw new BadCredentialsException(messages.getMessage("PasswordComparisonAuthenticator.badCredentials",
                "Bad credentials"));
    }

    private boolean isPasswordAttrCompare(DirContextOperations user, String password) {
        Object passwordAttrValue = user.getObjectAttribute(passwordAttributeName);
        return passwordEncoder.isPasswordValid(new String((byte[])passwordAttrValue), password, null);
    }

    private boolean isLdapPasswordCompare(DirContextOperations user,
            SpringSecurityLdapTemplate ldapTemplate, String password) {
        String encodedPassword = passwordEncoder.encodePassword(password, null);
        byte[] passwordBytes = Utf8.encode(encodedPassword);
        return ldapTemplate.compare(user.getDn().toString(), passwordAttributeName, passwordBytes);
    }

    public void setPasswordAttributeName(String passwordAttribute) {
        Assert.hasLength(passwordAttribute, "passwordAttributeName must not be empty or null");
        this.passwordAttributeName = passwordAttribute;
    }

    private void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        Assert.notNull(passwordEncoder, "passwordEncoder must not be null.");
        this.passwordEncoder = passwordEncoder;
    }

    public void setPasswordEncoder(Object passwordEncoder) {
        if (passwordEncoder instanceof PasswordEncoder) {
            this.usePasswordAttrCompare = false;
            setPasswordEncoder((PasswordEncoder) passwordEncoder);
            return;
        }

        if (passwordEncoder instanceof org.springframework.security.crypto.password.PasswordEncoder) {
            final org.springframework.security.crypto.password.PasswordEncoder delegate =
                    (org.springframework.security.crypto.password.PasswordEncoder)passwordEncoder;
            setPasswordEncoder(new PasswordEncoder() {
                public String encodePassword(String rawPass, Object salt) {
                    checkSalt(salt);
                    return delegate.encode(rawPass);
                }

                public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
                    checkSalt(salt);
                    return delegate.matches(rawPass, encPass);
                }

                private void checkSalt(Object salt) {
                    Assert.isNull(salt, "Salt value must be null when used with crypto module PasswordEncoder");
                }
            });
            this.usePasswordAttrCompare = true;
            return;
        }

        throw new IllegalArgumentException("passwordEncoder must be a PasswordEncoder instance");
    }
}
TOP

Related Classes of org.springframework.security.ldap.authentication.PasswordComparisonAuthenticator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.