SEC-1491: Added AnnotationMetadataExtractor to SecuredAnnotationSecurityMetadataSource to allow a custom security annotation to be used.

This commit is contained in:
Luke Taylor 2011-04-04 19:48:27 +01:00
parent 244047ffe9
commit 3084ad878f
4 changed files with 109 additions and 26 deletions

View File

@ -0,0 +1,19 @@
package org.springframework.security.access.annotation;
import org.springframework.security.access.ConfigAttribute;
import java.lang.annotation.Annotation;
import java.util.*;
/**
* Strategy to process a custom security annotation to extract the relevant {@code ConfigAttribute}s for
* securing a method.
* <p>
* Used by {@code SecuredAnnotationSecurityMetadataSource}.
*
* @author Luke Taylor
*/
public interface AnnotationMetadataExtractor<A extends Annotation> {
Collection<? extends ConfigAttribute> extractAttributes(A securityAnnotation);
}

View File

@ -17,41 +17,68 @@ package org.springframework.security.access.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.*;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.method.AbstractFallbackMethodSecurityMetadataSource;
import org.springframework.util.Assert;
/**
* Sources method security metadata from Spring Security's {@link Secured} annotation.
* <p>
* Can also be used with custom security annotations by injecting an {@link AnnotationMetadataExtractor}.
* The annotation type will then be obtained from the generic parameter type supplied to this interface
*
* @author Ben Alex
* @author Luke Taylor
*/
@SuppressWarnings({"unchecked"})
public class SecuredAnnotationSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {
private AnnotationMetadataExtractor annotationExtractor;
private Class<? extends Annotation> annotationType;
public SecuredAnnotationSecurityMetadataSource() {
this(new SecuredAnnotationMetadataExtractor());
}
public SecuredAnnotationSecurityMetadataSource(AnnotationMetadataExtractor annotationMetadataExtractor) {
Assert.notNull(annotationMetadataExtractor);
annotationExtractor = annotationMetadataExtractor;
annotationType = (Class<? extends Annotation>) GenericTypeResolver.resolveTypeArgument(
annotationExtractor.getClass(), AnnotationMetadataExtractor.class);
Assert.notNull(annotationType, annotationExtractor.getClass().getName()
+ " must supply a generic parameter for AnnotationMetadataExtractor");
}
protected Collection<ConfigAttribute> findAttributes(Class<?> clazz) {
return processAnnotation(clazz.getAnnotation(Secured.class));
return processAnnotation(clazz.getAnnotation(annotationType));
}
protected Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) {
return processAnnotation(AnnotationUtils.findAnnotation(method, Secured.class));
return processAnnotation(AnnotationUtils.findAnnotation(method, annotationType));
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
private List<ConfigAttribute> processAnnotation(Annotation a) {
if (a == null || !(a instanceof Secured)) {
private Collection<ConfigAttribute> processAnnotation(Annotation a) {
if (a == null) {
return null;
}
String[] attributeTokens = ((Secured) a).value();
return annotationExtractor.extractAttributes(a);
}
}
class SecuredAnnotationMetadataExtractor implements AnnotationMetadataExtractor<Secured> {
public Collection<ConfigAttribute> extractAttributes(Secured secured) {
String[] attributeTokens = secured.value();
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>(attributeTokens.length);
for(String token : attributeTokens) {

View File

@ -3,8 +3,6 @@ package org.springframework.security.access.annotation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.access.annotation.Secured;
/**
*
* @author Joe Scalise

View File

@ -16,12 +16,18 @@ package org.springframework.security.access.annotation;
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.lang.reflect.Method;
import java.util.Collection;
import java.util.*;
import org.junit.*;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.core.GrantedAuthority;
/**
@ -82,6 +88,7 @@ public class SecuredAnnotationSecurityMetadataDefinitionSourceTests {
}
}
@Test
public void classLevelAttributesAreFound() {
Collection<ConfigAttribute> attrs = this.mds.findAttributes(BusinessService.class);
@ -96,6 +103,7 @@ public class SecuredAnnotationSecurityMetadataDefinitionSourceTests {
assertEquals("ROLE_USER", sc.getAttribute());
}
@Test
public void methodLevelAttributesAreFound() {
Method method = null;
@ -130,26 +138,20 @@ public class SecuredAnnotationSecurityMetadataDefinitionSourceTests {
assertTrue(user && admin);
}
}
class Entity {
public Entity(String someParameter) {}
@Test
public void customAnnotationAttributesAreFound() throws Exception {
SecuredAnnotationSecurityMetadataSource mds =
new SecuredAnnotationSecurityMetadataSource(new CustomSecurityAnnotationMetadataExtractor());
Collection<ConfigAttribute> attrs = mds.findAttributes(CustomAnnotatedService.class);
assertEquals(1, attrs.size());
assertEquals(SecurityEnum.ADMIN, attrs.toArray()[0]);
}
}
class Department extends Entity {
private boolean active = true;
public Department(String name) {
super(name);
}
public boolean isActive() {
return this.active;
}
void deactive() {
this.active = true;
}
}
interface DepartmentService extends BusinessService {
@ -158,10 +160,47 @@ interface DepartmentService extends BusinessService {
Department someUserMethod3(Department dept);
}
class DepartmentServiceImpl extends BusinessServiceImpl <Department> implements DepartmentService {
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));
}
}