mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-27 14:22:47 +00:00
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.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
import org.springframework.core.GenericTypeResolver;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.security.access.ConfigAttribute;
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
import org.springframework.security.access.SecurityConfig;
|
import org.springframework.security.access.SecurityConfig;
|
||||||
import org.springframework.security.access.method.AbstractFallbackMethodSecurityMetadataSource;
|
import org.springframework.security.access.method.AbstractFallbackMethodSecurityMetadataSource;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sources method security metadata from Spring Security's {@link Secured} annotation.
|
* 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 Ben Alex
|
||||||
|
* @author Luke Taylor
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({"unchecked"})
|
||||||
public class SecuredAnnotationSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {
|
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) {
|
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) {
|
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() {
|
public Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ConfigAttribute> processAnnotation(Annotation a) {
|
private Collection<ConfigAttribute> processAnnotation(Annotation a) {
|
||||||
if (a == null || !(a instanceof Secured)) {
|
if (a == null) {
|
||||||
return 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);
|
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>(attributeTokens.length);
|
||||||
|
|
||||||
for(String token : attributeTokens) {
|
for(String token : attributeTokens) {
|
||||||
|
@ -3,8 +3,6 @@ package org.springframework.security.access.annotation;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.security.access.annotation.Secured;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Joe Scalise
|
* @author Joe Scalise
|
||||||
|
@ -16,12 +16,18 @@ package org.springframework.security.access.annotation;
|
|||||||
|
|
||||||
import static org.junit.Assert.*;
|
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.lang.reflect.Method;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
import org.springframework.security.access.ConfigAttribute;
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
import org.springframework.security.access.SecurityConfig;
|
import org.springframework.security.access.SecurityConfig;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,6 +88,7 @@ public class SecuredAnnotationSecurityMetadataDefinitionSourceTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void classLevelAttributesAreFound() {
|
public void classLevelAttributesAreFound() {
|
||||||
Collection<ConfigAttribute> attrs = this.mds.findAttributes(BusinessService.class);
|
Collection<ConfigAttribute> attrs = this.mds.findAttributes(BusinessService.class);
|
||||||
|
|
||||||
@ -96,6 +103,7 @@ public class SecuredAnnotationSecurityMetadataDefinitionSourceTests {
|
|||||||
assertEquals("ROLE_USER", sc.getAttribute());
|
assertEquals("ROLE_USER", sc.getAttribute());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void methodLevelAttributesAreFound() {
|
public void methodLevelAttributesAreFound() {
|
||||||
Method method = null;
|
Method method = null;
|
||||||
|
|
||||||
@ -130,26 +138,20 @@ public class SecuredAnnotationSecurityMetadataDefinitionSourceTests {
|
|||||||
assertTrue(user && admin);
|
assertTrue(user && admin);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
@Test
|
||||||
|
public void customAnnotationAttributesAreFound() throws Exception {
|
||||||
class Entity {
|
SecuredAnnotationSecurityMetadataSource mds =
|
||||||
public Entity(String someParameter) {}
|
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 {
|
class Department extends Entity {
|
||||||
private boolean active = true;
|
|
||||||
|
|
||||||
public Department(String name) {
|
public Department(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isActive() {
|
|
||||||
return this.active;
|
|
||||||
}
|
|
||||||
|
|
||||||
void deactive() {
|
|
||||||
this.active = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DepartmentService extends BusinessService {
|
interface DepartmentService extends BusinessService {
|
||||||
@ -158,10 +160,47 @@ interface DepartmentService extends BusinessService {
|
|||||||
Department someUserMethod3(Department dept);
|
Department someUserMethod3(Department dept);
|
||||||
}
|
}
|
||||||
|
|
||||||
class DepartmentServiceImpl extends BusinessServiceImpl <Department> implements DepartmentService {
|
class DepartmentServiceImpl extends BusinessServiceImpl<Department> implements DepartmentService {
|
||||||
|
|
||||||
@Secured({"ROLE_ADMIN"})
|
@Secured({"ROLE_ADMIN"})
|
||||||
public Department someUserMethod3(final Department dept) {
|
public Department someUserMethod3(final Department dept) {
|
||||||
return super.someUserMethod3(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…
x
Reference in New Issue
Block a user