SEC-1491: Added AnnotationMetadataExtractor to SecuredAnnotationSecurityMetadataSource to allow a custom security annotation to be used.
This commit is contained in:
parent
244047ffe9
commit
3084ad878f
|
@ -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);
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue