瀏覽代碼

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

Luke Taylor 14 年之前
父節點
當前提交
3084ad878f

+ 19 - 0
core/src/main/java/org/springframework/security/access/annotation/AnnotationMetadataExtractor.java

@@ -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);
+}

+ 35 - 8
core/src/main/java/org/springframework/security/access/annotation/SecuredAnnotationSecurityMetadataSource.java

@@ -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.Collection;
-import java.util.List;
+import java.util.*;
 
 
+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) {
-        if (a == null || !(a instanceof Secured)) {
+    private Collection<ConfigAttribute> processAnnotation(Annotation a) {
+        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) {

+ 0 - 2
core/src/test/java/org/springframework/security/access/annotation/BusinessServiceImpl.java

@@ -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

+ 55 - 16
core/src/test/java/org/springframework/security/access/annotation/SecuredAnnotationSecurityMetadataDefinitionSourceTests.java

@@ -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);
     }
     }
 
 
-}
-
-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 {
 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));
+    }
+}