/** * */ package org.org.springframework.security.addons; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.springframework.security.SecurityConfig; import org.springframework.metadata.Attributes; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** * This class allows to create SecurityConfig Attributes from a mapping between custom annotations and the String representation of these attributes.<br> * <p> * For example: * * <pre> * <bean id="attributes" class="org.org.springframework.security.addons.CustomSecurityAnnotationAttributes"> * <constructor-arg> * <map> * <entry key="org.org.springframework.security.addons.AdministrationOperation" value="ROLE_SUPERVISOR,ROLE_USER"/> * </map> * </constructor-arg> * </bean> * </pre> * * </p> * <p> * Therefore applying the : * * <pre> * @AdministrationOperation * </pre> * * annotation to a Method/Class will be fully equivalent to applying the * * <pre> * @Secured ({"ROLE_SUPERVISOR","ROLE_USER"}) * </pre> * * annotation used by Acegi's SecurityAnnotationAttributes, with the added value of not polluting your domain model with a technical annotation, and allowing the domain model to self-document its Security constraints. * * Please note that in the current implementation of Acegi, annotations are only dealt with properly if you put them on the Interface. * This is due to be modified, for a greater consistency with the whole Spring framework approach (see JIRA) * </p><br> * @author Pierre-Antoine Grégoire * @see SecurityConfig */ @SuppressWarnings("unchecked") public class CustomSecurityAnnotationAttributes implements Attributes { /** The annotations and attributes mapping. */ private final Map<Class, String[]> annotationsAndAttributesMapping; /** * Instantiates a new custom security annotation attributes.<br> * This constructor needs a mandatory annotation to Attributes mapping. * * @param mapping * a Map allowing the association of an annotation with a series of Attribute definitions (such as ROLE_*, RUNAS_*...etc). */ public CustomSecurityAnnotationAttributes(Map<Class, String[]> mapping) { super(); for (Class clazz : mapping.keySet()) { Assert.isAssignable(Annotation.class, clazz, "Mapped types should all be sub types of the java.lang.Annotation type."); } this.annotationsAndAttributesMapping = Collections.unmodifiableMap(mapping); } public Collection getAttributes(Method method) { Set<SecurityConfig> attributes = new HashSet<SecurityConfig>(); Annotation[] annotations = null; // Use AnnotationUtils if in classpath (ie Spring 1.2.9+ deployment) try { Class clazz = ClassUtils.forName("org.springframework.core.annotation.AnnotationUtils"); Method m = clazz.getMethod("getAnnotations", new Class[] { Method.class }); annotations = (Annotation[]) m.invoke(null, new Object[] { method }); } catch (Exception ex) { // Fallback to manual retrieval if no AnnotationUtils available annotations = method.getAnnotations(); } for (Annotation annotation : annotations) { String[] roles = annotationsAndAttributesMapping.get(annotation.annotationType()); if (roles != null) { for (String role : roles) { attributes.add(new SecurityConfig(role)); } } } return attributes; } public Collection getAttributes(Class target) { Set<SecurityConfig> attributes = new HashSet<SecurityConfig>(); for (Annotation annotation : target.getAnnotations()) { String[] roles = annotationsAndAttributesMapping.get(annotation.annotationType()); if (roles != null) { for (String role : roles) { attributes.add(new SecurityConfig(role)); } } } return attributes; } public Collection getAttributes(Method targetMethod, Class filter) { throw new UnsupportedOperationException("Unsupported operation"); } public Collection getAttributes(Field targetField) { throw new UnsupportedOperationException("Unsupported operation"); } public Collection getAttributes(Class targetClass, Class filter) { throw new UnsupportedOperationException("Unsupported operation"); } public Collection getAttributes(Field targetField, Class filter) { throw new UnsupportedOperationException("Unsupported operation"); } }