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:
+101
@@ -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();
|
||||
}
|
||||
}
|
||||
+8
-5
@@ -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);
|
||||
|
||||
+31
@@ -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;
|
||||
}
|
||||
}
|
||||
+85
@@ -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() {}
|
||||
}
|
||||
|
||||
}
|
||||
+4
-14
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+60
@@ -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();
|
||||
}
|
||||
}
|
||||
+20
@@ -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>
|
||||
Reference in New Issue
Block a user