1
0
mirror of synced 2026-08-31 22:46:02 +00:00

SEC-507: Initial support for JSR-250 "RolesAllowed" attributes.

Added jsr250 boolean to annotation-driven element to determine whether JSR-250 annotations should be used in preference to the traditional Acegi "Secured" attribute.
This commit is contained in:
Luke Taylor
2008-01-10 20:19:15 +00:00
parent fad2b597af
commit d66b9693ba
11 changed files with 344 additions and 26 deletions
@@ -0,0 +1,101 @@
package org.springframework.security.annotation;
import org.springframework.security.SecurityConfig;
import org.springframework.metadata.Attributes;
import org.springframework.core.annotation.AnnotationUtils;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.annotation.Annotation;
/**
* Java 5 Annotation <code>Attributes</code> metadata implementation used for secure method interception.
* <p>
* This <code>Attributes</code> implementation will return security configuration for classes described using the
* <code>RolesAllowed</code> Java JEE 5 annotation.
* <p>
* The <code>SecurityAnnotationAttributes</code> implementation can be used to configure a
* <code>MethodDefinitionAttributes</code> and <code>MethodSecurityInterceptor</code> bean definition.
*
* @author Mark St.Godard
* @author Usama Rashwan
* @author Luke Taylor
* @since 2.0
*
* @see javax.annotation.security.RolesAllowed
*/
public class Jsr250SecurityAnnotationAttributes implements Attributes {
//~ Methods ========================================================================================================
/**
* Get the <code>RolesAllowed</code> attributes for a given target class.
* This method will return an empty Collection because the call to getAttributes(method) will override the class
* annotation.
*
* @param target The target Object
* @return Empty Collection of <code>SecurityConfig</code>
*
* @see Attributes#getAttributes
*/
public Collection<SecurityConfig> getAttributes(Class target) {
return new HashSet<SecurityConfig>();
}
/**
* Get the <code>RolesAllowed</code> attributes for a given target method.
*
* @param method The target method
* @return Collection of <code>SecurityConfig</code>
* @see Attributes#getAttributes
*/
public Collection<SecurityConfig> getAttributes(Method method) {
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
Collection<SecurityConfig> attributes = populateSecurityConfigWithRolesAllowed(annotations);
// if there is no RolesAllowed defined on the Method then we will use the one defined on the class
// level , according to JSR 250
if (attributes.size()==0 && !method.isAnnotationPresent(PermitAll.class)) {
attributes = populateSecurityConfigWithRolesAllowed(method.getDeclaringClass().getDeclaredAnnotations());
}
return attributes;
}
protected Collection<SecurityConfig> populateSecurityConfigWithRolesAllowed (Annotation[] annotations) {
Set<SecurityConfig> attributes = new HashSet<SecurityConfig>();
for (Annotation annotation : annotations) {
// check for RolesAllowed annotations
if (annotation instanceof RolesAllowed) {
RolesAllowed attr = (RolesAllowed) annotation;
for (String auth : attr.value()) {
attributes.add(new SecurityConfig(auth));
}
break;
}
}
return attributes;
}
public Collection getAttributes(Class clazz, Class filter) {
throw new UnsupportedOperationException();
}
public Collection getAttributes(Method method, Class clazz) {
throw new UnsupportedOperationException();
}
public Collection getAttributes(Field field) {
throw new UnsupportedOperationException();
}
public Collection getAttributes(Field field, Class clazz) {
throw new UnsupportedOperationException();
}
}
@@ -15,26 +15,29 @@
package org.springframework.security.annotation;
import javax.annotation.security.RolesAllowed;
/**
* DOCUMENT ME!
*
* @author $author$
* @version $Revision: 2145 $
*/
* @version $Id$
*/
@Secured({"ROLE_USER"})
public interface BusinessService {
//~ Methods ========================================================================================================
@Secured({"ROLE_ADMIN"})
@RolesAllowed({"ROLE_ADMIN"})
public void someAdminMethod();
@Secured({"ROLE_USER", "ROLE_ADMIN"})
@RolesAllowed({"ROLE_USER", "ROLE_ADMIN"})
public void someUserAndAdminMethod();
@Secured({"ROLE_USER"})
@RolesAllowed({"ROLE_USER"})
public void someUserMethod1();
@Secured({"ROLE_USER"})
@RolesAllowed({"ROLE_USER"})
public void someUserMethod2();
public int someOther(int input);
@@ -0,0 +1,31 @@
package org.springframework.security.annotation;
import javax.annotation.security.RolesAllowed;
/**
*
* @author Luke Taylor
* @version $Id$
*/
public class Jsr250BusinessServiceImpl implements BusinessService {
@RolesAllowed({"ROLE_USER"})
public void someUserMethod1() {
}
@RolesAllowed({"ROLE_USER"})
public void someUserMethod2() {
}
@RolesAllowed({"ROLE_USER", "ROLE_ADMIN"})
public void someUserAndAdminMethod() {
}
@RolesAllowed({"ROLE_ADMIN"})
public void someAdminMethod() {
}
public int someOther(int input) {
return input;
}
}
@@ -0,0 +1,85 @@
package org.springframework.security.annotation;
import org.springframework.security.SecurityConfig;
import org.junit.Test;
import static org.junit.Assert.*;
import java.util.List;
import java.util.ArrayList;
import javax.annotation.security.RolesAllowed;
import javax.annotation.security.PermitAll;
/**
* @author Luke Taylor
* @version $Id$
*/
public class Jsr250SecurityAnnotationAttributesTests {
Jsr250SecurityAnnotationAttributes attributes = new Jsr250SecurityAnnotationAttributes();
A a = new A();
B b = new B();
@Test
public void methodWithRolesAllowedHasCorrectAttribute() throws Exception {
// Method[] methods = a.getClass().getMethods();
List<SecurityConfig> accessAttributes =
new ArrayList<SecurityConfig>(attributes.getAttributes(a.getClass().getMethod("adminMethod")));
assertEquals(1, accessAttributes.size());
assertEquals("ADMIN", accessAttributes.get(0).getAttribute());
}
@Test
public void permitAllMethodHasNoAttributes() throws Exception {
List<SecurityConfig> accessAttributes =
new ArrayList<SecurityConfig>(attributes.getAttributes(a.getClass().getMethod("permitAllMethod")));
assertEquals(0, accessAttributes.size());
}
@Test
public void noRoleMethodHasNoAttributes() throws Exception {
List<SecurityConfig> accessAttributes =
new ArrayList<SecurityConfig>(attributes.getAttributes(a.getClass().getMethod("noRoleMethod")));
assertEquals(0, accessAttributes.size());
}
@Test
public void classRoleIsAppliedNoRoleMethod() throws Exception {
List<SecurityConfig> accessAttributes =
new ArrayList<SecurityConfig>(attributes.getAttributes(b.getClass().getMethod("noRoleMethod")));
assertEquals(1, accessAttributes.size());
assertEquals("USER", accessAttributes.get(0).getAttribute());
}
@Test
public void methodRoleOverridesClassRole() throws Exception {
List<SecurityConfig> accessAttributes =
new ArrayList<SecurityConfig>(attributes.getAttributes(b.getClass().getMethod("adminMethod")));
assertEquals(1, accessAttributes.size());
assertEquals("ADMIN", accessAttributes.get(0).getAttribute());
}
//~ Inner Classes ======================================================================================================
public static class A {
public void noRoleMethod() {}
@RolesAllowed("ADMIN")
public void adminMethod() {}
@PermitAll
public void permitAllMethod() {}
}
@RolesAllowed("USER")
public static class B {
public void noRoleMethod() {}
@RolesAllowed("ADMIN")
public void adminMethod() {}
}
}
@@ -1,7 +1,5 @@
package org.springframework.security.config;
import static org.junit.Assert.fail;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -37,13 +35,9 @@ public class AnnotationDrivenBeanDefinitionParserTests {
SecurityContextHolder.clearContext();
}
@Test
@Test(expected=AuthenticationCredentialsNotFoundException.class)
public void targetShouldPreventProtectedMethodInvocationWithNoContext() {
try {
target.someUserMethod1();
fail("Expected AuthenticationCredentialsNotFoundException");
} catch (AuthenticationCredentialsNotFoundException expected) {
}
target.someUserMethod1();
}
@Test
@@ -55,16 +49,12 @@ public class AnnotationDrivenBeanDefinitionParserTests {
target.someUserMethod1();
}
@Test
@Test(expected=AccessDeniedException.class)
public void targetShouldPreventProtectedMethodInvocationWithIncorrectRole() {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_SOMEOTHERROLE")});
SecurityContextHolder.getContext().setAuthentication(token);
try {
target.someAdminMethod();
fail("Expected AccessDeniedException");
} catch (AccessDeniedException expected) {
}
target.someAdminMethod();
}
}
@@ -0,0 +1,60 @@
package org.springframework.security.config;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.AccessDeniedException;
import org.springframework.security.AuthenticationCredentialsNotFoundException;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.annotation.BusinessService;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
/**
* @author Luke Taylor
* @version $Id$
*/
public class Jsr250AnnotationDrivenBeanDefinitionParserTests {
private ClassPathXmlApplicationContext appContext;
private BusinessService target;
@Before
public void loadContext() {
appContext = new ClassPathXmlApplicationContext("/org/springframework/security/config/jsr250-annotated-method-security.xml");
target = (BusinessService) appContext.getBean("target");
}
@After
public void closeAppContext() {
if (appContext != null) {
appContext.close();
}
SecurityContextHolder.clearContext();
}
@Test(expected=AuthenticationCredentialsNotFoundException.class)
public void targetShouldPreventProtectedMethodInvocationWithNoContext() {
target.someUserMethod1();
}
@Test
public void targetShouldAllowProtectedMethodInvocationWithCorrectRole() {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_USER")});
SecurityContextHolder.getContext().setAuthentication(token);
target.someUserMethod1();
}
@Test(expected=AccessDeniedException.class)
public void targetShouldPreventProtectedMethodInvocationWithIncorrectRole() {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_SOMEOTHERROLE")});
SecurityContextHolder.getContext().setAuthentication(token);
target.someAdminMethod();
}
}
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns="http://www.springframework.org/schema/security"
xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
<b:bean id="target" class="org.springframework.security.annotation.Jsr250BusinessServiceImpl"/>
<annotation-driven jsr250="true"/>
<authentication-provider>
<user-service>
<user name="bob" password="bobspassword" authorities="ROLE_A,ROLE_B" />
<user name="bill" password="billspassword" authorities="ROLE_A,ROLE_B,AUTH_OTHER" />
</user-service>
</authentication-provider>
</b:beans>