Bläddra i källkod

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

Luke Taylor 14 år sedan
förälder
incheckning
d64efe9747

+ 10 - 1
cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java

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

+ 8 - 1
core/src/main/java/org/springframework/security/authentication/dao/AbstractUserDetailsAuthenticationProvider.java

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

+ 15 - 0
core/src/main/java/org/springframework/security/core/authority/mapping/GrantedAuthoritiesMapper.java

@@ -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);
+}

+ 14 - 0
core/src/main/java/org/springframework/security/core/authority/mapping/NullAuthoritiesMapper.java

@@ -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;
+    }
+}

+ 14 - 7
ldap/src/main/java/org/springframework/security/ldap/authentication/LdapAuthenticationProvider.java

@@ -33,6 +33,8 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.SpringSecurityMessageSource;
 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.UserDetails;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.security.ldap.ppolicy.PasswordPolicyException;
 import org.springframework.security.ldap.ppolicy.PasswordPolicyException;
@@ -140,6 +142,7 @@ public class LdapAuthenticationProvider implements AuthenticationProvider, Messa
     private UserDetailsContextMapper userDetailsContextMapper = new LdapUserDetailsMapper();
     private UserDetailsContextMapper userDetailsContextMapper = new LdapUserDetailsMapper();
     private boolean useAuthenticationRequestCredentials = true;
     private boolean useAuthenticationRequestCredentials = true;
     private boolean hideUserNotFoundExceptions = true;
     private boolean hideUserNotFoundExceptions = true;
+    private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
 
 
     //~ Constructors ===================================================================================================
     //~ 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() {
     protected UserDetailsContextMapper getUserDetailsContextMapper() {
         return userDetailsContextMapper;
         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
      * 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
      * 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.
      * Often it will not be possible to read the password from the directory, so defaults to true.
      *
      *
      * @param useAuthenticationRequestCredentials
      * @param useAuthenticationRequestCredentials
@@ -227,6 +230,10 @@ public class LdapAuthenticationProvider implements AuthenticationProvider, Messa
         this.messages = new MessageSourceAccessor(messageSource);
         this.messages = new MessageSourceAccessor(messageSource);
     }
     }
 
 
+    public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
+        this.authoritiesMapper = authoritiesMapper;
+    }
+
     public Authentication authenticate(Authentication authentication) throws AuthenticationException {
     public Authentication authenticate(Authentication authentication) throws AuthenticationException {
         Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
         Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
             messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
             messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
@@ -251,9 +258,8 @@ public class LdapAuthenticationProvider implements AuthenticationProvider, Messa
         try {
         try {
             DirContextOperations userData = getAuthenticator().authenticate(authentication);
             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);
             return createSuccessfulAuthentication(userToken, user);
         } catch (PasswordPolicyException ppe) {
         } 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 authentication the original authentication request token
      * @param user the <tt>UserDetails</tt> instance returned by the configured <tt>UserDetailsContextMapper</tt>.
      * @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) {
             UserDetails user) {
         Object password = useAuthenticationRequestCredentials ? authentication.getCredentials() : user.getPassword();
         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());
         result.setDetails(authentication.getDetails());
 
 
         return result;
         return result;

+ 14 - 6
openid/src/main/java/org/springframework/security/openid/OpenIDAuthenticationProvider.java

@@ -20,6 +20,8 @@ import org.springframework.security.authentication.AuthenticationServiceExceptio
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 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.AuthenticationUserDetailsService;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
 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.
  * Finalises the OpenID authentication by obtaining local authorities for the authenticated user.
  * <p>
  * <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
  * 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>
  * <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.
  * so additional properties such as email addresses, telephone numbers etc can easily be stored.
  *
  *
  * @author Robin Bramley, Opsera Ltd.
  * @author Robin Bramley, Opsera Ltd.
+ * @author Luke Taylor
  */
  */
 public class OpenIDAuthenticationProvider implements AuthenticationProvider, InitializingBean {
 public class OpenIDAuthenticationProvider implements AuthenticationProvider, InitializingBean {
     //~ Instance fields ================================================================================================
     //~ Instance fields ================================================================================================
 
 
     private AuthenticationUserDetailsService<OpenIDAuthenticationToken> userDetailsService;
     private AuthenticationUserDetailsService<OpenIDAuthenticationToken> userDetailsService;
+    private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
 
 
     //~ Methods ========================================================================================================
     //~ Methods ========================================================================================================
 
 
@@ -100,7 +104,7 @@ public class OpenIDAuthenticationProvider implements AuthenticationProvider, Ini
      * @return the token which will represent the authenticated user.
      * @return the token which will represent the authenticated user.
      */
      */
     protected Authentication createSuccessfulAuthentication(UserDetails userDetails, OpenIDAuthenticationToken auth) {
     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());
                 auth.getIdentityUrl(), auth.getAttributes());
     }
     }
 
 
@@ -124,4 +128,8 @@ public class OpenIDAuthenticationProvider implements AuthenticationProvider, Ini
     public boolean supports(Class<?> authentication) {
     public boolean supports(Class<?> authentication) {
         return OpenIDAuthenticationToken.class.isAssignableFrom(authentication);
         return OpenIDAuthenticationToken.class.isAssignableFrom(authentication);
     }
     }
+
+    public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
+        this.authoritiesMapper = authoritiesMapper;
+    }
 }
 }

+ 9 - 1
web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java

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