Browse Source

SEC-536: Added UserDetailsService decorator class which will throw an appropriate exception if the returned UserDetails object has a status of locked, disabled etc.

Luke Taylor 17 years ago
parent
commit
c9dee10704

+ 58 - 0
core/src/main/java/org/springframework/security/userdetails/decorator/StatusCheckingUserDetailsService.java

@@ -0,0 +1,58 @@
+package org.springframework.security.userdetails.decorator;
+
+import org.springframework.security.userdetails.UserDetailsService;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.UsernameNotFoundException;
+import org.springframework.security.LockedException;
+import org.springframework.security.DisabledException;
+import org.springframework.security.AccountExpiredException;
+import org.springframework.security.CredentialsExpiredException;
+import org.springframework.security.SpringSecurityMessageSource;
+import org.springframework.security.AuthenticationException;
+import org.springframework.dao.DataAccessException;
+import org.springframework.context.support.MessageSourceAccessor;
+import org.springframework.util.Assert;
+
+/**
+ * Decorates a {@link UserDetailsService}, making it throw an exception if the account is locked, disabled etc. This
+ * removes the need for separate account status checks in classes which make use of a <tt>UserDetailsService</tt>. 
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ */
+public class StatusCheckingUserDetailsService implements UserDetailsService {
+    private UserDetailsService delegate;
+
+    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
+
+    public StatusCheckingUserDetailsService(UserDetailsService userDetailsService) {
+        this.delegate = userDetailsService;
+    }
+
+    public UserDetails loadUserByUsername(String username) throws AuthenticationException, DataAccessException {
+
+        UserDetails user = delegate.loadUserByUsername(username);
+
+        Assert.notNull(user, "UserDetailsService returned null user, an interface violation.");
+
+        if (!user.isAccountNonLocked()) {
+            throw new LockedException(messages.getMessage("UserDetailsService.locked", "User account is locked"));
+        }
+
+        if (!user.isEnabled()) {
+            throw new DisabledException(messages.getMessage("UserDetailsService.disabled", "User is disabled"));
+        }
+
+        if (!user.isAccountNonExpired()) {
+            throw new AccountExpiredException(messages.getMessage("UserDetailsService.expired",
+                    "User account has expired"));
+        }
+
+        if (!user.isCredentialsNonExpired()) {
+            throw new CredentialsExpiredException(messages.getMessage("UserDetailsService.credentialsExpired",
+                    "User credentials have expired"));
+        }
+
+        return user;
+    }
+}

+ 69 - 0
core/src/main/java/org/springframework/security/userdetails/decorator/StatusCheckingUserDetailsServiceTests.java

@@ -0,0 +1,69 @@
+package org.springframework.security.userdetails.decorator;
+
+import org.springframework.security.userdetails.UserDetailsService;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.UsernameNotFoundException;
+import org.springframework.security.userdetails.User;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.LockedException;
+import org.springframework.security.DisabledException;
+import org.springframework.security.CredentialsExpiredException;
+import org.springframework.security.AccountExpiredException;
+
+import org.springframework.dao.DataAccessException;
+
+import org.junit.Test;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * @author Luke Taylor
+ * @version $Id$
+ */
+public class StatusCheckingUserDetailsServiceTests {
+    private StatusCheckingUserDetailsService us = new StatusCheckingUserDetailsService(new MockUserDetailsService());
+
+    @Test
+    public void validAccountIsSuccessfullyLoaded() throws Exception {
+        us.loadUserByUsername("valid");
+    }
+
+    @Test(expected = LockedException.class)
+    public void lockedAccountThrowsLockedException() throws Exception {
+        us.loadUserByUsername("locked");
+    }
+
+    @Test(expected = DisabledException.class)
+    public void disabledAccountThrowsDisabledException() throws Exception {
+        us.loadUserByUsername("disabled");
+    }
+
+    @Test(expected = CredentialsExpiredException.class)
+    public void credentialsExpiredAccountThrowsCredentialsExpiredException() throws Exception {
+        us.loadUserByUsername("credentialsExpired");
+    }
+
+    @Test(expected = AccountExpiredException.class)
+    public void expiredAccountThrowsAccountExpiredException() throws Exception {
+        us.loadUserByUsername("expired");
+    }
+
+    class MockUserDetailsService implements UserDetailsService {
+        private Map <String, UserDetails> users = new HashMap <String, UserDetails>();
+        private GrantedAuthority[] auths = new GrantedAuthority[] {new GrantedAuthorityImpl("A")};
+
+        MockUserDetailsService() {
+            users.put("valid", new User("valid", "",true,true,true,true,auths));
+            users.put("locked", new User("locked", "",true,true,true,false,auths));
+            users.put("disabled", new User("disabled", "",false,true,true,true,auths));
+            users.put("credentialsExpired", new User("credentialsExpired", "",true,true,false,true,auths));
+            users.put("expired", new User("expired", "",true,false,true,true,auths));
+        }
+
+        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
+            return users.get(username);
+        }
+    }
+}