Browse Source

SEC-883: RoleHierarchyVoter
http://jira.springframework.org/browse/SEC-883. Added RoleHierarchyVoter and deprecated existing approach. Also moved TestingAuthenticationToken to test package structure.

Luke Taylor 17 years ago
parent
commit
1af7eed433

+ 2 - 0
core/src/main/java/org/springframework/security/userdetails/hierarchicalroles/UserDetailsServiceWrapper.java

@@ -25,6 +25,8 @@ import org.springframework.dao.DataAccessException;
  * instead of only the directly assigned authorities.
  *
  * @author Michael Mayr
+ * @deprecated use a {@link RoleHierarchyVoter} instead of populating the user Authentication object
+ * with the additional authorities.  
  */
 public class UserDetailsServiceWrapper implements UserDetailsService {
 

+ 1 - 0
core/src/main/java/org/springframework/security/userdetails/hierarchicalroles/UserDetailsWrapper.java

@@ -23,6 +23,7 @@ import org.springframework.security.userdetails.UserDetails;
  * delegated to the <tt>UserDetails</tt> implementation.
  *
  * @author Michael Mayr
+ * @deprecated use a {@link RoleHierarchyVoter} instead.
  */
 public class UserDetailsWrapper implements UserDetails {
 

+ 29 - 0
core/src/main/java/org/springframework/security/vote/RoleHierarchyVoter.java

@@ -0,0 +1,29 @@
+package org.springframework.security.vote;
+
+import org.springframework.security.Authentication;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.userdetails.hierarchicalroles.RoleHierarchy;
+import org.springframework.util.Assert;
+
+/**
+ * Extended RoleVoter which uses a {@link RoleHierarchy} definition to determine the 
+ * roles allocated to the current user before voting.
+ * 
+ * @author Luke Taylor
+ * @since 2.0.4
+ */
+public class RoleHierarchyVoter extends RoleVoter {
+    private RoleHierarchy roleHierarchy = null;
+        
+    public RoleHierarchyVoter(RoleHierarchy roleHierarchy) {
+    	Assert.notNull(roleHierarchy, "RoleHierarchy must not be null");
+    	this.roleHierarchy = roleHierarchy;
+    }
+
+    /**
+     * Calls the <tt>RoleHierarchy</tt> to obtain the complete set of user authorities.
+     */
+	GrantedAuthority[] extractAuthorities(Authentication authentication) {
+		return roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities());
+	}
+}

+ 5 - 1
core/src/main/java/org/springframework/security/vote/RoleVoter.java

@@ -95,6 +95,7 @@ public class RoleVoter implements AccessDecisionVoter {
     public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
         int result = ACCESS_ABSTAIN;
         Iterator iter = config.getConfigAttributes().iterator();
+        GrantedAuthority[] authorities = extractAuthorities(authentication);        
 
         while (iter.hasNext()) {
             ConfigAttribute attribute = (ConfigAttribute) iter.next();
@@ -102,7 +103,6 @@ public class RoleVoter implements AccessDecisionVoter {
             if (this.supports(attribute)) {
                 result = ACCESS_DENIED;
 
-                GrantedAuthority[] authorities = authentication.getAuthorities();
                 // Attempt to find a matching granted authority
                 for (int i = 0; i < authorities.length; i++) {
                     if (attribute.getAttribute().equals(authorities[i].getAuthority())) {
@@ -114,4 +114,8 @@ public class RoleVoter implements AccessDecisionVoter {
 
         return result;
     }
+    
+    GrantedAuthority[] extractAuthorities(Authentication authentication) {
+    	return authentication.getAuthorities();
+    }
 }

+ 3 - 5
core/src/test/java/org/springframework/security/ldap/SpringSecurityAuthenticationSourceTests.java

@@ -44,8 +44,7 @@ public class SpringSecurityAuthenticationSourceTests {
     @Test(expected=IllegalArgumentException.class)
     public void getPrincipalRejectsNonLdapUserDetailsObject() {
         AuthenticationSource source = new SpringSecurityAuthenticationSource();
-        SecurityContextHolder.getContext().setAuthentication(
-                new TestingAuthenticationToken(new Object(), "password", null));
+        SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(new Object(), "password"));
 
         source.getPrincipal();
     }
@@ -53,8 +52,7 @@ public class SpringSecurityAuthenticationSourceTests {
     @Test
     public void expectedCredentialsAreReturned() {
         AuthenticationSource source = new SpringSecurityAuthenticationSource();
-        SecurityContextHolder.getContext().setAuthentication(
-                new TestingAuthenticationToken(new Object(), "password", null));
+        SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(new Object(), "password"));
 
         assertEquals("password", source.getCredentials());
     }
@@ -66,7 +64,7 @@ public class SpringSecurityAuthenticationSourceTests {
         user.setDn(new DistinguishedName("uid=joe,ou=users"));
         AuthenticationSource source = new SpringSecurityAuthenticationSource();
         SecurityContextHolder.getContext().setAuthentication(
-                new TestingAuthenticationToken(user.createUserDetails(), null, null));
+        		new TestingAuthenticationToken(user.createUserDetails(), null));
 
         assertEquals("uid=joe, ou=users", source.getPrincipal());
     }

+ 0 - 0
core/src/main/java/org/springframework/security/providers/TestingAuthenticationProvider.java → core/src/test/java/org/springframework/security/providers/TestingAuthenticationProvider.java


+ 12 - 0
core/src/main/java/org/springframework/security/providers/TestingAuthenticationToken.java → core/src/test/java/org/springframework/security/providers/TestingAuthenticationToken.java

@@ -16,6 +16,7 @@
 package org.springframework.security.providers;
 
 import org.springframework.security.GrantedAuthority;
+import org.springframework.security.util.AuthorityUtils;
 
 
 /**
@@ -34,6 +35,17 @@ public class TestingAuthenticationToken extends AbstractAuthenticationToken {
 
     //~ Constructors ===================================================================================================
 
+    public TestingAuthenticationToken(Object principal, Object credentials) {
+    	super(null);
+        this.principal = principal;
+        this.credentials = credentials;    	
+    }    
+
+    
+    public TestingAuthenticationToken(Object principal, Object credentials, String... authorities) {
+    	this(principal, credentials, AuthorityUtils.stringArrayToAuthorityArray(authorities));
+    }    
+
     public TestingAuthenticationToken(Object principal, Object credentials, GrantedAuthority[] authorities) {
         super(authorities);
         this.principal = principal;

+ 0 - 77
core/src/test/java/org/springframework/security/providers/TestingAuthenticationTokenTests.java

@@ -1,77 +0,0 @@
-/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
- *
- * 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.providers;
-
-import junit.framework.TestCase;
-
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.GrantedAuthorityImpl;
-
-
-/**
- * Tests {@link TestingAuthenticationToken}.
- *
- * @author Ben Alex
- * @version $Id$
- */
-public class TestingAuthenticationTokenTests extends TestCase {
-    //~ Constructors ===================================================================================================
-
-    public TestingAuthenticationTokenTests() {
-        super();
-    }
-
-    public TestingAuthenticationTokenTests(String arg0) {
-        super(arg0);
-    }
-
-    //~ Methods ========================================================================================================
-
-    public static void main(String[] args) {
-        junit.textui.TestRunner.run(TestingAuthenticationTokenTests.class);
-    }
-
-    public final void setUp() throws Exception {
-        super.setUp();
-    }
-
-    public void testAuthenticated() {
-        TestingAuthenticationToken token = new TestingAuthenticationToken("Test", "Password", null);
-        assertTrue(!token.isAuthenticated());
-        token.setAuthenticated(true);
-        assertTrue(token.isAuthenticated());
-    }
-
-    public void testGetters() {
-        TestingAuthenticationToken token = new TestingAuthenticationToken("Test", "Password",
-                new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO")});
-        assertEquals("Test", token.getPrincipal());
-        assertEquals("Password", token.getCredentials());
-        assertEquals("ROLE_ONE", token.getAuthorities()[0].getAuthority());
-        assertEquals("ROLE_TWO", token.getAuthorities()[1].getAuthority());
-    }
-
-    public void testNoArgConstructorDoesntExist() {
-        Class clazz = TestingAuthenticationToken.class;
-
-        try {
-            clazz.getDeclaredConstructor((Class[]) null);
-            fail("Should have thrown NoSuchMethodException");
-        } catch (NoSuchMethodException expected) {
-            assertTrue(true);
-        }
-    }
-}

+ 1 - 1
core/src/test/java/org/springframework/security/ui/SessionFixationProtectionFilterTests.java

@@ -74,6 +74,6 @@ public class SessionFixationProtectionFilterTests {
     }    
 
     private void authenticateUser() {
-        SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass", null));
+        SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass"));
     }
 }

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

@@ -160,7 +160,7 @@ public class RememberMeProcessingFilterTests extends TestCase {
     public void testOnunsuccessfulLoginIsCalledWhenProviderRejectsAuth() throws Exception {
         Authentication remembered = new TestingAuthenticationToken("remembered", "password",
                 new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_REMEMBERED")});
-        final Authentication failedAuth = new TestingAuthenticationToken("failed", "", null);
+        final Authentication failedAuth = new TestingAuthenticationToken("failed", "");
 
         RememberMeProcessingFilter filter = new RememberMeProcessingFilter() {
             protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {

+ 24 - 0
core/src/test/java/org/springframework/security/vote/RoleHierarchyVoterTests.java

@@ -0,0 +1,24 @@
+package org.springframework.security.vote;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.providers.TestingAuthenticationToken;
+import org.springframework.security.userdetails.hierarchicalroles.RoleHierarchyImpl;
+
+public class RoleHierarchyVoterTests {
+
+	@Test
+	public void hierarchicalRoleIsIncludedInDecision() {
+        RoleHierarchyImpl roleHierarchyImpl = new RoleHierarchyImpl();
+        roleHierarchyImpl.setHierarchy("ROLE_A > ROLE_B");
+
+        // User has role A, role B is required
+        TestingAuthenticationToken auth = new TestingAuthenticationToken("user", "password", "ROLE_A");
+        RoleHierarchyVoter voter = new RoleHierarchyVoter(roleHierarchyImpl);
+        ConfigAttributeDefinition config = new ConfigAttributeDefinition("ROLE_B");
+        
+        assertEquals(RoleHierarchyVoter.ACCESS_GRANTED, voter.vote(auth, new Object(), config)); 
+	}
+}