SEC-1796: Check for annotated annotations at class/interface level. Previously only the specific security annotation was checked for. By delegating to Spring's AnnotationUtils, custom annotations carrying the security annotation are also detected.
This commit is contained in:
@@ -19,9 +19,11 @@ import static org.junit.Assert.*;
|
||||
import org.junit.*;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.access.intercept.method.MockMethodInvocation;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
@@ -37,7 +39,7 @@ import java.util.*;
|
||||
* @author Ben Alex
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class SecuredAnnotationSecurityMetadataDefinitionSourceTests {
|
||||
public class SecuredAnnotationSecurityMetadataSourceTests {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private SecuredAnnotationSecurityMetadataSource mds = new SecuredAnnotationSecurityMetadataSource();
|
||||
@@ -137,6 +139,7 @@ public class SecuredAnnotationSecurityMetadataDefinitionSourceTests {
|
||||
assertTrue(user && admin);
|
||||
}
|
||||
|
||||
// SEC-1491
|
||||
@Test
|
||||
public void customAnnotationAttributesAreFound() throws Exception {
|
||||
SecuredAnnotationSecurityMetadataSource mds =
|
||||
@@ -145,61 +148,117 @@ public class SecuredAnnotationSecurityMetadataDefinitionSourceTests {
|
||||
assertEquals(1, attrs.size());
|
||||
assertEquals(SecurityEnum.ADMIN, attrs.toArray()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
class Department extends Entity {
|
||||
public Department(String name) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
@Test
|
||||
public void annotatedAnnotationAtClassLevelIsDetected() throws Exception {
|
||||
MockMethodInvocation annotatedAtClassLevel = new MockMethodInvocation(new AnnotatedAnnotationAtClassLevel(), ReturnVoid.class, "doSomething", List.class);
|
||||
|
||||
interface DepartmentService extends BusinessService {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(annotatedAtClassLevel).toArray(new ConfigAttribute[0]);
|
||||
|
||||
@Secured({"ROLE_USER"})
|
||||
Department someUserMethod3(Department dept);
|
||||
}
|
||||
|
||||
class DepartmentServiceImpl extends BusinessServiceImpl<Department> implements DepartmentService {
|
||||
|
||||
@Secured({"ROLE_ADMIN"})
|
||||
public Department someUserMethod3(final Department dept) {
|
||||
return super.someUserMethod3(dept);
|
||||
}
|
||||
}
|
||||
|
||||
// SEC-1491 Related classes. PoC for custom annotation with enum value.
|
||||
|
||||
@CustomSecurityAnnotation(SecurityEnum.ADMIN)
|
||||
interface CustomAnnotatedService {
|
||||
}
|
||||
|
||||
class CustomAnnotatedServiceImpl implements CustomAnnotatedService {
|
||||
}
|
||||
|
||||
enum SecurityEnum implements ConfigAttribute, GrantedAuthority {
|
||||
ADMIN,
|
||||
USER;
|
||||
|
||||
public String getAttribute() {
|
||||
return toString();
|
||||
assertEquals(1, attrs.length);
|
||||
assertEquals("CUSTOM", attrs[0].getAttribute());
|
||||
}
|
||||
|
||||
public String getAuthority() {
|
||||
return toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface CustomSecurityAnnotation {
|
||||
SecurityEnum[] value();
|
||||
}
|
||||
|
||||
class CustomSecurityAnnotationMetadataExtractor implements AnnotationMetadataExtractor<CustomSecurityAnnotation> {
|
||||
|
||||
public Collection<? extends ConfigAttribute> extractAttributes(CustomSecurityAnnotation securityAnnotation) {
|
||||
SecurityEnum[] values = securityAnnotation.value();
|
||||
|
||||
return EnumSet.copyOf(Arrays.asList(values));
|
||||
@Test
|
||||
public void annotatedAnnotationAtInterfaceLevelIsDetected() throws Exception {
|
||||
MockMethodInvocation annotatedAtInterfaceLevel = new MockMethodInvocation(new AnnotatedAnnotationAtInterfaceLevel(), ReturnVoid2.class, "doSomething", List.class);
|
||||
|
||||
ConfigAttribute[] attrs = mds.getAttributes(annotatedAtInterfaceLevel).toArray(new ConfigAttribute[0]);
|
||||
|
||||
assertEquals(1, attrs.length);
|
||||
assertEquals("CUSTOM", attrs[0].getAttribute());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotatedAnnotationAtMethodLevelIsDetected() throws Exception {
|
||||
MockMethodInvocation annotatedAtMethodLevel = new MockMethodInvocation(new AnnotatedAnnotationAtMethodLevel(), ReturnVoid.class, "doSomething", List.class);
|
||||
ConfigAttribute[] attrs = mds.getAttributes(annotatedAtMethodLevel).toArray(new ConfigAttribute[0]);
|
||||
|
||||
assertEquals(1, attrs.length);
|
||||
assertEquals("CUSTOM", attrs[0].getAttribute());
|
||||
}
|
||||
|
||||
// Inner classes
|
||||
class Department extends Entity {
|
||||
public Department(String name) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
|
||||
interface DepartmentService extends BusinessService {
|
||||
@Secured({"ROLE_USER"})
|
||||
Department someUserMethod3(Department dept);
|
||||
}
|
||||
|
||||
class DepartmentServiceImpl extends BusinessServiceImpl<Department> implements DepartmentService {
|
||||
@Secured({"ROLE_ADMIN"})
|
||||
public Department someUserMethod3(final Department dept) {
|
||||
return super.someUserMethod3(dept);
|
||||
}
|
||||
}
|
||||
|
||||
// SEC-1491 Related classes. PoC for custom annotation with enum value.
|
||||
|
||||
@CustomSecurityAnnotation(SecurityEnum.ADMIN)
|
||||
interface CustomAnnotatedService {
|
||||
}
|
||||
|
||||
class CustomAnnotatedServiceImpl implements CustomAnnotatedService {
|
||||
}
|
||||
|
||||
enum SecurityEnum implements ConfigAttribute, GrantedAuthority {
|
||||
ADMIN,
|
||||
USER;
|
||||
|
||||
public String getAttribute() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
public String getAuthority() {
|
||||
return toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface CustomSecurityAnnotation {
|
||||
SecurityEnum[] value();
|
||||
}
|
||||
|
||||
class CustomSecurityAnnotationMetadataExtractor implements AnnotationMetadataExtractor<CustomSecurityAnnotation> {
|
||||
public Collection<? extends ConfigAttribute> extractAttributes(CustomSecurityAnnotation securityAnnotation) {
|
||||
SecurityEnum[] values = securityAnnotation.value();
|
||||
|
||||
return EnumSet.copyOf(Arrays.asList(values));
|
||||
}
|
||||
}
|
||||
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Secured("CUSTOM")
|
||||
public @interface AnnotatedAnnotation {}
|
||||
|
||||
public static interface ReturnVoid {
|
||||
public void doSomething(List<?> param);
|
||||
}
|
||||
|
||||
@AnnotatedAnnotation
|
||||
public static interface ReturnVoid2 {
|
||||
public void doSomething(List<?> param);
|
||||
}
|
||||
|
||||
@AnnotatedAnnotation
|
||||
public static class AnnotatedAnnotationAtClassLevel implements ReturnVoid {
|
||||
public void doSomething(List<?> param) {}
|
||||
}
|
||||
|
||||
public static class AnnotatedAnnotationAtInterfaceLevel implements ReturnVoid2 {
|
||||
public void doSomething(List<?> param) {}
|
||||
}
|
||||
|
||||
public static class AnnotatedAnnotationAtMethodLevel implements ReturnVoid {
|
||||
@AnnotatedAnnotation
|
||||
public void doSomething(List<?> param) {}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,11 @@ package org.springframework.security.access.expression.method;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -29,6 +34,9 @@ public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
private MockMethodInvocation listImpl1;
|
||||
private MockMethodInvocation notherListImpl1;
|
||||
private MockMethodInvocation notherListImpl2;
|
||||
private MockMethodInvocation annotatedAtClassLevel;
|
||||
private MockMethodInvocation annotatedAtInterfaceLevel;
|
||||
private MockMethodInvocation annotatedAtMethodLevel;
|
||||
|
||||
@Before
|
||||
public void setUpData() throws Exception {
|
||||
@@ -38,6 +46,9 @@ public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
listImpl1 = new MockMethodInvocation(new ReturnAListImpl1(), ReturnAList.class, "doSomething", List.class);
|
||||
notherListImpl1 = new MockMethodInvocation(new ReturnAnotherListImpl1(), ReturnAnotherList.class, "doSomething", List.class);
|
||||
notherListImpl2 = new MockMethodInvocation(new ReturnAnotherListImpl2(), ReturnAnotherList.class, "doSomething", List.class);
|
||||
annotatedAtClassLevel = new MockMethodInvocation(new CustomAnnotationAtClassLevel(), ReturnVoid.class, "doSomething", List.class);
|
||||
annotatedAtInterfaceLevel = new MockMethodInvocation(new CustomAnnotationAtInterfaceLevel(), ReturnVoid2.class, "doSomething", List.class);
|
||||
annotatedAtMethodLevel = new MockMethodInvocation(new CustomAnnotationAtMethodLevel(), ReturnVoid.class, "doSomething", List.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,6 +127,27 @@ public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
assertEquals("classMethodPreFilterExpression", pre.getFilterExpression().getExpressionString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customAnnotationAtClassLevelIsDetected() throws Exception {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(annotatedAtClassLevel).toArray(new ConfigAttribute[0]);
|
||||
|
||||
assertEquals(1, attrs.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customAnnotationAtInterfaceLevelIsDetected() throws Exception {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(annotatedAtInterfaceLevel).toArray(new ConfigAttribute[0]);
|
||||
|
||||
assertEquals(1, attrs.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customAnnotationAtMethodLevelIsDetected() throws Exception {
|
||||
ConfigAttribute[] attrs = mds.getAttributes(annotatedAtMethodLevel).toArray(new ConfigAttribute[0]);
|
||||
|
||||
assertEquals(1, attrs.length);
|
||||
}
|
||||
|
||||
//~ Inner Classes ==================================================================================================
|
||||
|
||||
public static interface ReturnVoid {
|
||||
@@ -172,4 +204,28 @@ public class PrePostAnnotationSecurityMetadataSourceTests {
|
||||
public List<?> doSomething(List<?> param) {return param;}
|
||||
}
|
||||
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@PreAuthorize("customAnnotationExpression")
|
||||
public @interface CustomAnnotation {}
|
||||
|
||||
@CustomAnnotation
|
||||
public static interface ReturnVoid2 {
|
||||
public void doSomething(List<?> param);
|
||||
}
|
||||
|
||||
@CustomAnnotation
|
||||
public static class CustomAnnotationAtClassLevel implements ReturnVoid {
|
||||
public void doSomething(List<?> param) {}
|
||||
}
|
||||
|
||||
public static class CustomAnnotationAtInterfaceLevel implements ReturnVoid2 {
|
||||
public void doSomething(List<?> param) {}
|
||||
}
|
||||
|
||||
public static class CustomAnnotationAtMethodLevel implements ReturnVoid {
|
||||
@CustomAnnotation
|
||||
public void doSomething(List<?> param) {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user