SEC-1492: Added GrantedAuthoritiesMapper to provide mapping of loaded authorities to those which are eventually stored in the user Authentication object.

This commit is contained in:
Luke Taylor 2010-11-25 15:19:37 +00:00
parent 89f80659a1
commit d64efe9747
7 changed files with 84 additions and 16 deletions

View File

@ -31,6 +31,8 @@ import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
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.*;
import org.springframework.util.Assert;
@ -59,6 +61,8 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
private String key;
private TicketValidator ticketValidator;
private ServiceProperties serviceProperties;
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
//~ Methods ========================================================================================================
@ -131,7 +135,8 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
final Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(), serviceProperties.getService());
final UserDetails userDetails = loadUserByAssertion(assertion);
userDetailsChecker.check(userDetails);
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(), userDetails.getAuthorities(), userDetails, assertion);
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(),
authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion);
} catch (final TicketValidationException e) {
throw new BadCredentialsException(e.getMessage(), e);
}
@ -194,6 +199,10 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
this.ticketValidator = ticketValidator;
}
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
public boolean supports(final Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)) ||
(CasAuthenticationToken.class.isAssignableFrom(authentication)) ||

View File

@ -28,6 +28,8 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
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.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;
@ -84,6 +86,7 @@ public abstract class AbstractUserDetailsAuthenticationProvider implements Authe
protected boolean hideUserNotFoundExceptions = true;
private UserDetailsChecker preAuthenticationChecks = new DefaultPreAuthenticationChecks();
private UserDetailsChecker postAuthenticationChecks = new DefaultPostAuthenticationChecks();
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
//~ Methods ========================================================================================================
@ -191,7 +194,7 @@ public abstract class AbstractUserDetailsAuthenticationProvider implements Authe
// Also ensure we return the original getDetails(), so that future
// authentication events after cache expiry contain the details
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal,
authentication.getCredentials(), user.getAuthorities());
authentication.getCredentials(), authoritiesMapper.mapAuthorities(user.getAuthorities()));
result.setDetails(authentication.getDetails());
return result;
@ -295,6 +298,10 @@ public abstract class AbstractUserDetailsAuthenticationProvider implements Authe
this.postAuthenticationChecks = postAuthenticationChecks;
}
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
private class DefaultPreAuthenticationChecks implements UserDetailsChecker {
public void check(UserDetails user) {
if (!user.isAccountNonLocked()) {

View File

@ -0,0 +1,15 @@
package org.springframework.security.core.authority.mapping;
import org.springframework.security.core.GrantedAuthority;
import java.util.*;
/**
* Mapping interface which can be injected into the authentication layer to convert the
* authorities loaded from storage into those which will be used in the {@code Authentication} object.
*
* @author Luke Taylor
*/
public interface GrantedAuthoritiesMapper {
Collection<? extends GrantedAuthority> mapAuthorities(Collection<? extends GrantedAuthority> authorities);
}

View File

@ -0,0 +1,14 @@
package org.springframework.security.core.authority.mapping;
import org.springframework.security.core.GrantedAuthority;
import java.util.*;
/**
* @author Luke Taylor
*/
public class NullAuthoritiesMapper implements GrantedAuthoritiesMapper {
public Collection<? extends GrantedAuthority> mapAuthorities(Collection<? extends GrantedAuthority> authorities) {
return authorities;
}
}

View File

@ -33,6 +33,8 @@ 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.core.userdetails.UsernameNotFoundException;
import org.springframework.security.ldap.ppolicy.PasswordPolicyException;
@ -140,6 +142,7 @@ public class LdapAuthenticationProvider implements AuthenticationProvider, Messa
private UserDetailsContextMapper userDetailsContextMapper = new LdapUserDetailsMapper();
private boolean useAuthenticationRequestCredentials = true;
private boolean hideUserNotFoundExceptions = true;
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
//~ Constructors ===================================================================================================
@ -201,7 +204,7 @@ public class LdapAuthenticationProvider implements AuthenticationProvider, Messa
}
/**
* Provides access to the injected <tt>UserDetailsContextMapper</tt> strategy for use by subclasses.
* Provides access to the injected {@code UserDetailsContextMapper} strategy for use by subclasses.
*/
protected UserDetailsContextMapper getUserDetailsContextMapper() {
return userDetailsContextMapper;
@ -214,7 +217,7 @@ public class LdapAuthenticationProvider implements AuthenticationProvider, Messa
/**
* 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 <tt>UserDetailsContextMapper</tt>.
* 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
@ -227,6 +230,10 @@ public class LdapAuthenticationProvider implements AuthenticationProvider, Messa
this.messages = new MessageSourceAccessor(messageSource);
}
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
@ -251,9 +258,8 @@ public class LdapAuthenticationProvider implements AuthenticationProvider, Messa
try {
DirContextOperations userData = getAuthenticator().authenticate(authentication);
Collection<? extends GrantedAuthority> extraAuthorities = loadUserAuthorities(userData, username, password);
UserDetails user = userDetailsContextMapper.mapUserFromContext(userData, username, extraAuthorities);
UserDetails user = userDetailsContextMapper.mapUserFromContext(userData, username,
loadUserAuthorities(userData, username, password));
return createSuccessfulAuthentication(userToken, user);
} catch (PasswordPolicyException ppe) {
@ -277,7 +283,7 @@ public class LdapAuthenticationProvider implements AuthenticationProvider, Messa
}
/**
* Creates the final <tt>Authentication</tt> object which will be returned from the <tt>authenticate</tt> method.
* 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>.
@ -287,7 +293,8 @@ public class LdapAuthenticationProvider implements AuthenticationProvider, Messa
UserDetails user) {
Object password = useAuthenticationRequestCredentials ? authentication.getCredentials() : user.getPassword();
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(user, password,
authoritiesMapper.mapAuthorities(user.getAuthorities()));
result.setDetails(authentication.getDetails());
return result;

View File

@ -20,6 +20,8 @@ import org.springframework.security.authentication.AuthenticationServiceExceptio
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
@ -30,21 +32,23 @@ import org.springframework.util.Assert;
/**
* Finalises the OpenID authentication by obtaining local authorities for the authenticated user.
* <p>
* The authorities are obtained by calling the configured <tt>UserDetailsService</tt>.
* The <code>UserDetails</code> it returns must, at minimum, contain the username and <code>GrantedAuthority</code>
* The authorities are obtained by calling the configured {@code UserDetailsService}.
* The {@code UserDetails} it returns must, at minimum, contain the username and {@code GrantedAuthority}
* objects applicable to the authenticated user. Note that by default, Spring Security ignores the password and
* enabled/disabled status of the <code>UserDetails</code> because this is
* authentication-related and should have been enforced by another provider server.
* enabled/disabled status of the {@code UserDetails} because this is authentication-related and should have been
* enforced by another provider server.
* <p>
* The <code>UserDetails</code> returned by implementations is stored in the generated <code>AuthenticationToken</code>,
* The {@code UserDetails} returned by implementations is stored in the generated {@code Authentication} token,
* so additional properties such as email addresses, telephone numbers etc can easily be stored.
*
* @author Robin Bramley, Opsera Ltd.
* @author Luke Taylor
*/
public class OpenIDAuthenticationProvider implements AuthenticationProvider, InitializingBean {
//~ Instance fields ================================================================================================
private AuthenticationUserDetailsService<OpenIDAuthenticationToken> userDetailsService;
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
//~ Methods ========================================================================================================
@ -100,7 +104,7 @@ public class OpenIDAuthenticationProvider implements AuthenticationProvider, Ini
* @return the token which will represent the authenticated user.
*/
protected Authentication createSuccessfulAuthentication(UserDetails userDetails, OpenIDAuthenticationToken auth) {
return new OpenIDAuthenticationToken(userDetails, userDetails.getAuthorities(),
return new OpenIDAuthenticationToken(userDetails, authoritiesMapper.mapAuthorities(userDetails.getAuthorities()),
auth.getIdentityUrl(), auth.getAttributes());
}
@ -124,4 +128,8 @@ public class OpenIDAuthenticationProvider implements AuthenticationProvider, Ini
public boolean supports(Class<?> authentication) {
return OpenIDAuthenticationToken.class.isAssignableFrom(authentication);
}
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
}

View File

@ -14,6 +14,8 @@ import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.core.Authentication;
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.codec.Base64;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;
@ -55,6 +57,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
private String key;
private int tokenValiditySeconds = TWO_WEEKS_S;
private boolean useSecureCookie = false;
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
public void afterPropertiesSet() throws Exception {
Assert.hasLength(key);
@ -147,7 +150,8 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
* @return the <tt>Authentication</tt> for the remember-me authenticated user
*/
protected Authentication createSuccessfulAuthentication(HttpServletRequest request, UserDetails user) {
RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(key, user, user.getAuthorities());
RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(key, user,
authoritiesMapper.mapAuthorities(user.getAuthorities()));
auth.setDetails(authenticationDetailsSource.buildDetails(request));
return auth;
}
@ -417,4 +421,8 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
public void setUserDetailsChecker(UserDetailsChecker userDetailsChecker) {
this.userDetailsChecker = userDetailsChecker;
}
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
}