Преглед на файлове

SEC-664: Removed validateUserDetails method from AbstractRememberMeServices, wrapped the UserDetailsService in a status-checking one and added a catch block for AccountStatusExceptions. Also some minor tidying up of other remember-me classes.

Luke Taylor преди 18 години
родител
ревизия
84c7ac5e57

+ 8 - 16
core/src/main/java/org/springframework/security/ui/rememberme/AbstractRememberMeServices.java

@@ -7,6 +7,7 @@ import org.springframework.beans.factory.InitializingBean;
 import org.springframework.context.support.MessageSourceAccessor;
 import org.springframework.security.Authentication;
 import org.springframework.security.SpringSecurityMessageSource;
+import org.springframework.security.AccountStatusException;
 import org.springframework.security.providers.rememberme.RememberMeAuthenticationToken;
 import org.springframework.security.ui.AuthenticationDetailsSource;
 import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
@@ -14,6 +15,7 @@ import org.springframework.security.ui.logout.LogoutHandler;
 import org.springframework.security.userdetails.UserDetails;
 import org.springframework.security.userdetails.UserDetailsService;
 import org.springframework.security.userdetails.UsernameNotFoundException;
+import org.springframework.security.userdetails.decorator.StatusCheckingUserDetailsService;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.ServletRequestUtils;
@@ -27,6 +29,7 @@ import javax.servlet.http.HttpServletResponse;
  *
  * @author Luke Taylor
  * @version $Id$
+ * @since 2.0
  */
 public abstract class AbstractRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler {
 	//~ Static fields/initializers =====================================================================================
@@ -91,6 +94,10 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
             cancelCookie(request, response);
             logger.debug("Invalid remember-me cookie: " + invalidCookie.getMessage());
             return null;
+        } catch (AccountStatusException statusInvalid) {
+            cancelCookie(request, response);
+            logger.debug("Invalid UserDetails: " + statusInvalid.getMessage());
+            return null;
         } catch (RememberMeAuthenticationException e) {
             cancelCookie(request, response);
             logger.debug(e.getMessage());
@@ -175,21 +182,6 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
         return sb.toString();
     }
 
-    /**
-     * Provided for subclass convenience to check the account status of a loaded user.
-     *
-     * @throws UsernameNotFoundException if the username could not be located by the configured UserDetailsService.
-     * @throws RememberMeAuthenticationException if the account is locked or disabled.
-     */
-    protected void validateUserDetails(UserDetails user) throws UsernameNotFoundException,
-            RememberMeAuthenticationException {
-
-        if (!user.isAccountNonExpired() || !user.isCredentialsNonExpired() || !user.isEnabled()) {
-            throw new RememberMeAuthenticationException("Remember-me login was valid for user " +
-                    user.getUsername() + ", but account is expired, has expired credentials or is disabled");
-        }
-    }
-
     public final void loginFail(HttpServletRequest request, HttpServletResponse response) {
         logger.debug("Interactive login attempt was unsuccessful.");
         cancelCookie(request, response);
@@ -327,7 +319,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
     }
 
     public void setUserDetailsService(UserDetailsService userDetailsService) {
-        this.userDetailsService = userDetailsService;
+        this.userDetailsService = new StatusCheckingUserDetailsService(userDetailsService);
     }
 
     public void setKey(String key) {

+ 1 - 0
core/src/main/java/org/springframework/security/ui/rememberme/JdbcTokenRepositoryImpl.java

@@ -18,6 +18,7 @@ import java.util.Date;
  *
  * @author Luke Taylor
  * @version $Id$
+ * @since 2.0
  */
 public class JdbcTokenRepositoryImpl extends JdbcDaoSupport implements PersistentTokenRepository {
     //~ Static fields/initializers =====================================================================================

+ 1 - 2
core/src/main/java/org/springframework/security/ui/rememberme/PersistentTokenBasedRememberMeServices.java

@@ -34,6 +34,7 @@ import java.util.Date;
  *
  * @author Luke Taylor
  * @version $Id$
+ * @since 2.0
  */
 public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices {
 
@@ -112,8 +113,6 @@ public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeSe
 
         UserDetails user = getUserDetailsService().loadUserByUsername(token.getUsername());
 
-        validateUserDetails(user);
-
         return user;
     }
 

+ 2 - 1
core/src/main/java/org/springframework/security/ui/rememberme/PersistentTokenRepository.java

@@ -7,10 +7,11 @@ import java.util.Date;
  * login tokens for a user.
  *
  * @see JdbcTokenRepositoryImpl
- * @see InMemoryTokenRepositoryImpl 
+ * @see InMemoryTokenRepositoryImpl
  *
  * @author Luke Taylor
  * @version $Id$
+ * @since 2.0
  */
 public interface PersistentTokenRepository {
 

+ 5 - 12
core/src/main/java/org/springframework/security/ui/rememberme/TokenBasedRememberMeServices.java

@@ -39,16 +39,12 @@ import java.util.Date;
  * credentials - not the time period they last logged in via remember-me. The
  * implementation will only send a remember-me token if the parameter defined by
  * {@link #setParameter(String)} is present.
- * </p>
  *
  * <p>
  * An {@link org.springframework.security.userdetails.UserDetailsService} is required by
  * this implementation, so that it can construct a valid
- * <code>Authentication</code> from the returned {@link
- * org.springframework.security.userdetails.UserDetails}. This is also necessary so that
- * the user's password is available and can be checked as part of the encoded
- * cookie.
- * </p>
+ * <code>Authentication</code> from the returned {@link org.springframework.security.userdetails.UserDetails}.
+ * This is also necessary so that the user's password is available and can be checked as part of the encoded cookie.
  *
  * <p>
  * The cookie encoded by this implementation adopts the following form:
@@ -57,7 +53,6 @@ import java.util.Date;
  * username + &quot;:&quot; + expiryTime + &quot;:&quot; + Md5Hex(username + &quot;:&quot; + expiryTime + &quot;:&quot; + password + &quot;:&quot; + key)
  * </pre>
  *
- * </p>
  * <p>
  * As such, if the user changes their password, any remember-me token will be
  * invalidated. Equally, the system administrator may invalidate every
@@ -69,17 +64,17 @@ import java.util.Date;
  * implementation (as we do not want to rely on a database for remember-me
  * services) and as such high security applications should be aware of this
  * occasionally undesired disclosure of a valid username.
- * </p>
+ *
  * <p>
  * This is a basic remember-me implementation which is suitable for many
  * applications. However, we recommend a database-based implementation if you
  * require a more secure remember-me approach (see {@link PersistentTokenBasedRememberMeServices}).
- * </p>
+ *
  * <p>
  * By default the tokens will be valid for 14 days from the last successful
  * authentication attempt. This can be changed using
  * {@link #setTokenValiditySeconds(int)}.
- * </p>
+ * 
  *
  * @author Ben Alex
  * @version $Id$
@@ -116,8 +111,6 @@ public class TokenBasedRememberMeServices extends AbstractRememberMeServices {
 
         UserDetails userDetails = getUserDetailsService().loadUserByUsername(cookieTokens[0]);
 
-        validateUserDetails(userDetails);
-
         // Check signature of token matches remaining details.
         // Must do this after user lookup, as we need the DAO-derived password.
         // If efficiency was a major issue, just add in a UserCache implementation,

+ 0 - 1
core/src/test/java/org/springframework/security/ui/rememberme/AbstractRememberMeServicesTests.java

@@ -259,7 +259,6 @@ public class AbstractRememberMeServicesTests {
             }
 
             UserDetails user = getUserDetailsService().loadUserByUsername("joe");
-            validateUserDetails(user);
 
             return user;
         }

+ 5 - 10
core/src/test/java/org/springframework/security/ui/rememberme/TokenBasedRememberMeServicesTests.java

@@ -84,8 +84,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         return tokenValueBase64;
     }
 
-    public void testAutoLoginIfDoesNotPresentAnyCookies()
-        throws Exception {
+    public void testAutoLoginIfDoesNotPresentAnyCookies() throws Exception {
         TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
         services.setKey("key");
         services.setUserDetailsService(new MockAuthenticationDao(null, true));
@@ -104,8 +103,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         assertNull(returnedCookie); // shouldn't try to invalidate our cookie
     }
 
-    public void testAutoLoginIfDoesNotPresentRequiredCookie()
-        throws Exception {
+    public void testAutoLoginIfDoesNotPresentRequiredCookie() throws Exception {
         TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
         services.setKey("key");
         services.setUserDetailsService(new MockAuthenticationDao(null, true));
@@ -150,8 +148,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         assertEquals(0, returnedCookie.getMaxAge());
     }
 
-    public void testAutoLoginIfMissingThreeTokensInCookieValue()
-        throws Exception {
+    public void testAutoLoginIfMissingThreeTokensInCookieValue() throws Exception {
         UserDetails user = new User("someone", "password", true, true, true, true,
                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ABC")});
 
@@ -201,8 +198,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         assertEquals(0, returnedCookie.getMaxAge());
     }
 
-    public void testAutoLoginIfSignatureBlocksDoesNotMatchExpectedValue()
-        throws Exception {
+    public void testAutoLoginIfSignatureBlocksDoesNotMatchExpectedValue() throws Exception {
         UserDetails user = new User("someone", "password", true, true, true, true,
                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ABC")});
 
@@ -228,8 +224,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
         assertEquals(0, returnedCookie.getMaxAge());
     }
 
-    public void testAutoLoginIfTokenDoesNotContainANumberInCookieValue()
-        throws Exception {
+    public void testAutoLoginIfTokenDoesNotContainANumberInCookieValue() throws Exception {
         UserDetails user = new User("someone", "password", true, true, true, true,
                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ABC")});
 

+ 3 - 3
samples/tutorial/src/main/webapp/WEB-INF/applicationContext-security-ns.xml

@@ -23,9 +23,9 @@
         -->
         <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
 <!--
-    Uncomment to enable X509 client authentication support
+    Uncomment to enable X509 client authentication support -->
         <x509 />
--->
+
         <!-- All of this is unnecessary if auto-config="true" -->
         <form-login />
         <anonymous />
@@ -68,7 +68,7 @@ Uncomment to authenticate against an embedded LDAP server.
     <authentication-provider>
         <password-encoder hash="md5"/>
         <user-service>
-            <user name="rod" password="a564de63c2d0da68cf47586ee05984d7" authorities="ROLE_SUPERVISOR,ROLE_USER,ROLE_TELLER" />
+            <user name="rod" password="a564de63c2d0da68cf47586ee05984d7" locked="true" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
 	        <user name="dianne" password="65d15fe9156f9c4bbffd98085992a44e" authorities="ROLE_USER,ROLE_TELLER" />
             <user name="scott" password="2b58af6dddbd072ed27ffc86725d7d3a" authorities="ROLE_USER" />
 	    </user-service>