/* * Copyright 2002-2014 the original author or authors. * * 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 java.util.Collection; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.MessageSource; import org.springframework.context.MessageSourceAware; import org.springframework.context.support.MessageSourceAccessor; import org.springframework.ldap.core.DirContextOperations; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.SpringSecurityMessageSource; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper; import org.springframework.security.ldap.userdetails.UserDetailsContextMapper; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * Base class for the standard {@code LdapAuthenticationProvider} and the * {@code ActiveDirectoryLdapAuthenticationProvider}. * * @author Luke Taylor * @since 3.1 */ public abstract class AbstractLdapAuthenticationProvider implements AuthenticationProvider, MessageSourceAware { protected final Log logger = LogFactory.getLog(getClass()); protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); private boolean useAuthenticationRequestCredentials = true; private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper(); protected UserDetailsContextMapper userDetailsContextMapper = new LdapUserDetailsMapper(); public Authentication authenticate(Authentication authentication) throws AuthenticationException { Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, this.messages.getMessage("LdapAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported")); final UsernamePasswordAuthenticationToken userToken = (UsernamePasswordAuthenticationToken) authentication; String username = userToken.getName(); String password = (String) authentication.getCredentials(); if (this.logger.isDebugEnabled()) { this.logger.debug("Processing authentication request for user: " + username); } if (!StringUtils.hasLength(username)) { throw new BadCredentialsException(this.messages.getMessage( "LdapAuthenticationProvider.emptyUsername", "Empty Username")); } if (!StringUtils.hasLength(password)) { throw new BadCredentialsException(this.messages.getMessage( "AbstractLdapAuthenticationProvider.emptyPassword", "Empty Password")); } Assert.notNull(password, "Null password was supplied in authentication token"); DirContextOperations userData = doAuthentication(userToken); UserDetails user = this.userDetailsContextMapper.mapUserFromContext(userData, authentication.getName(), loadUserAuthorities(userData, authentication.getName(), (String) authentication.getCredentials())); return createSuccessfulAuthentication(userToken, user); } protected abstract DirContextOperations doAuthentication( UsernamePasswordAuthenticationToken auth); protected abstract Collection<? extends GrantedAuthority> loadUserAuthorities( DirContextOperations userData, String username, String password); /** * Creates the final {@code Authentication} object which will be returned from the * {@code authenticate} method. * * @param authentication the original authentication request token * @param user the <tt>UserDetails</tt> instance returned by the configured * <tt>UserDetailsContextMapper</tt>. * @return the Authentication object for the fully authenticated user. */ protected Authentication createSuccessfulAuthentication( UsernamePasswordAuthenticationToken authentication, UserDetails user) { Object password = this.useAuthenticationRequestCredentials ? authentication.getCredentials() : user.getPassword(); UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken( user, password, this.authoritiesMapper.mapAuthorities(user.getAuthorities())); result.setDetails(authentication.getDetails()); return result; } public boolean supports(Class<?> authentication) { return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); } /** * Determines whether the supplied password will be used as the credentials in the * successful authentication token. If set to false, then the password will be * obtained from the UserDetails object created by the configured * {@code UserDetailsContextMapper}. Often it will not be possible to read the * password from the directory, so defaults to true. * * @param useAuthenticationRequestCredentials */ public void setUseAuthenticationRequestCredentials( boolean useAuthenticationRequestCredentials) { this.useAuthenticationRequestCredentials = useAuthenticationRequestCredentials; } public void setMessageSource(MessageSource messageSource) { this.messages = new MessageSourceAccessor(messageSource); } public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { this.authoritiesMapper = authoritiesMapper; } /** * Allows a custom strategy to be used for creating the <tt>UserDetails</tt> which * will be stored as the principal in the <tt>Authentication</tt> returned by the * {@link #createSuccessfulAuthentication(org.springframework.security.authentication.UsernamePasswordAuthenticationToken, org.springframework.security.core.userdetails.UserDetails)} * method. * * @param userDetailsContextMapper the strategy instance. If not set, defaults to a * simple <tt>LdapUserDetailsMapper</tt>. */ public void setUserDetailsContextMapper( UserDetailsContextMapper userDetailsContextMapper) { Assert.notNull(userDetailsContextMapper, "UserDetailsContextMapper must not be null"); this.userDetailsContextMapper = userDetailsContextMapper; } /** * Provides access to the injected {@code UserDetailsContextMapper} strategy for use * by subclasses. */ protected UserDetailsContextMapper getUserDetailsContextMapper() { return this.userDetailsContextMapper; } }