33 changed files with 935 additions and 1015 deletions
@ -0,0 +1,75 @@
@@ -0,0 +1,75 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.annotation; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.Method; |
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.List; |
||||
|
||||
import javax.annotation.security.DenyAll; |
||||
import javax.annotation.security.PermitAll; |
||||
import javax.annotation.security.RolesAllowed; |
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
import org.springframework.security.ConfigAttributeDefinition; |
||||
import org.springframework.security.intercept.method.AbstractFallbackMethodDefinitionSource; |
||||
|
||||
|
||||
/** |
||||
* Sources method security metadata from major JSR 250 security annotations. |
||||
* |
||||
* @author Ben Alex |
||||
* @version $Id$ |
||||
*/ |
||||
public class Jsr250MethodDefinitionSource extends AbstractFallbackMethodDefinitionSource { |
||||
|
||||
protected ConfigAttributeDefinition findAttributes(Class clazz) { |
||||
return processAnnotations(clazz.getAnnotations()); |
||||
} |
||||
|
||||
protected ConfigAttributeDefinition findAttributes(Method method, Class targetClass) { |
||||
return processAnnotations(AnnotationUtils.getAnnotations(method)); |
||||
} |
||||
|
||||
public Collection getConfigAttributeDefinitions() { |
||||
return null; |
||||
} |
||||
|
||||
private ConfigAttributeDefinition processAnnotations(Annotation[] annotations) { |
||||
if (annotations == null || annotations.length == 0) { |
||||
return null; |
||||
} |
||||
for (Annotation a: annotations) { |
||||
if (a instanceof DenyAll) { |
||||
return new ConfigAttributeDefinition(Jsr250SecurityConfig.DENY_ALL_ATTRIBUTE); |
||||
} |
||||
if (a instanceof PermitAll) { |
||||
return new ConfigAttributeDefinition(Jsr250SecurityConfig.PERMIT_ALL_ATTRIBUTE); |
||||
} |
||||
if (a instanceof RolesAllowed) { |
||||
RolesAllowed ra = (RolesAllowed) a; |
||||
List attributes = new ArrayList(); |
||||
for (String allowed : ra.value()) { |
||||
attributes.add(new Jsr250SecurityConfig(allowed)); |
||||
} |
||||
return new ConfigAttributeDefinition(attributes); |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
} |
||||
@ -1,140 +0,0 @@
@@ -1,140 +0,0 @@
|
||||
package org.springframework.security.annotation; |
||||
|
||||
import org.springframework.security.SecurityConfig; |
||||
import org.springframework.metadata.Attributes; |
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
|
||||
import javax.annotation.security.PermitAll; |
||||
import javax.annotation.security.RolesAllowed; |
||||
import javax.annotation.security.DenyAll; |
||||
import java.util.Collection; |
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
import java.util.ArrayList; |
||||
import java.lang.reflect.Method; |
||||
import java.lang.reflect.Field; |
||||
import java.lang.annotation.Annotation; |
||||
|
||||
/** |
||||
* Java 5 Annotation {@link Attributes} metadata implementation used for secure method interception using |
||||
* the security anotations defined in JSR-250. |
||||
* <p> |
||||
* This <code>Attributes</code> implementation will return security configuration for classes described using the |
||||
* Java JEE 5 annotations (<em>DenyAll</em>, <em>PermitAll</em> and <em>RolesAllowed</em>). |
||||
* <p> |
||||
* |
||||
* @author Mark St.Godard |
||||
* @author Usama Rashwan |
||||
* @author Luke Taylor |
||||
* @since 2.0 |
||||
* |
||||
* @see javax.annotation.security.RolesAllowed |
||||
*/ |
||||
|
||||
public class Jsr250SecurityAnnotationAttributes implements Attributes { |
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/** |
||||
* Get the <code>RolesAllowed</code> attributes for a given target class. |
||||
* This method will return an empty Collection because the call to getAttributes(method) will override the class
|
||||
* annotation. |
||||
* |
||||
* @param target The target Object |
||||
* @return Empty Collection of <code>SecurityConfig</code> |
||||
* |
||||
* @see Attributes#getAttributes |
||||
*/ |
||||
public Collection<SecurityConfig> getAttributes(Class target) { |
||||
return new HashSet<SecurityConfig>(); |
||||
} |
||||
|
||||
/** |
||||
* Get the attributes for a given target method, acording to JSR-250 precedence rules. |
||||
* |
||||
* @param method The target method |
||||
* @return Collection of <code>SecurityConfig</code> |
||||
* @see Attributes#getAttributes |
||||
*/ |
||||
public Collection<SecurityConfig> getAttributes(Method method) { |
||||
ArrayList<SecurityConfig> attributes = new ArrayList<SecurityConfig>(); |
||||
|
||||
if (AnnotationUtils.getAnnotation(method, DenyAll.class) != null) { |
||||
attributes.add(Jsr250SecurityConfig.DENY_ALL_ATTRIBUTE); |
||||
|
||||
return attributes; |
||||
} |
||||
|
||||
if (AnnotationUtils.getAnnotation(method, PermitAll.class) != null) { |
||||
attributes.add(Jsr250SecurityConfig.PERMIT_ALL_ATTRIBUTE); |
||||
|
||||
return attributes; |
||||
} |
||||
|
||||
RolesAllowed rolesAllowed = AnnotationUtils.getAnnotation(method, RolesAllowed.class); |
||||
|
||||
if (rolesAllowed != null) { |
||||
for (String role : rolesAllowed.value()) { |
||||
attributes.add(new Jsr250SecurityConfig(role)); |
||||
} |
||||
|
||||
return attributes; |
||||
} |
||||
|
||||
// Now check the class-level attributes:
|
||||
if (method.getDeclaringClass().getAnnotation(DenyAll.class) != null) { |
||||
attributes.add(Jsr250SecurityConfig.DENY_ALL_ATTRIBUTE); |
||||
|
||||
return attributes; |
||||
} |
||||
|
||||
if (method.getDeclaringClass().getAnnotation(PermitAll.class) != null) { |
||||
attributes.add(Jsr250SecurityConfig.PERMIT_ALL_ATTRIBUTE); |
||||
|
||||
return attributes; |
||||
} |
||||
|
||||
rolesAllowed = method.getDeclaringClass().getAnnotation(RolesAllowed.class); |
||||
|
||||
if (rolesAllowed != null) { |
||||
for (String role : rolesAllowed.value()) { |
||||
attributes.add(new Jsr250SecurityConfig(role)); |
||||
} |
||||
} |
||||
|
||||
return attributes; |
||||
} |
||||
|
||||
protected Collection<SecurityConfig> populateSecurityConfigWithRolesAllowed (Annotation[] annotations) { |
||||
Set<SecurityConfig> attributes = new HashSet<SecurityConfig>(); |
||||
for (Annotation annotation : annotations) { |
||||
// check for RolesAllowed annotations
|
||||
if (annotation instanceof RolesAllowed) { |
||||
RolesAllowed attr = (RolesAllowed) annotation; |
||||
|
||||
for (String auth : attr.value()) { |
||||
attributes.add(new SecurityConfig(auth)); |
||||
} |
||||
|
||||
break; |
||||
} |
||||
} |
||||
return attributes; |
||||
} |
||||
|
||||
public Collection getAttributes(Class clazz, Class filter) { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
public Collection getAttributes(Method method, Class clazz) { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
public Collection getAttributes(Field field) { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
public Collection getAttributes(Field field, Class clazz) { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
} |
||||
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.annotation; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.Method; |
||||
import java.util.Collection; |
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
import org.springframework.security.ConfigAttributeDefinition; |
||||
import org.springframework.security.intercept.method.AbstractFallbackMethodDefinitionSource; |
||||
|
||||
|
||||
/** |
||||
* Sources method security metadata from Spring Security's {@link Secured} annotation. |
||||
* |
||||
* @author Ben Alex |
||||
* @version $Id$ |
||||
*/ |
||||
public class SecuredMethodDefinitionSource extends AbstractFallbackMethodDefinitionSource { |
||||
|
||||
protected ConfigAttributeDefinition findAttributes(Class clazz) { |
||||
return processAnnotation(clazz.getAnnotation(Secured.class)); |
||||
} |
||||
|
||||
protected ConfigAttributeDefinition findAttributes(Method method, Class targetClass) { |
||||
return processAnnotation(AnnotationUtils.findAnnotation(method, Secured.class)); |
||||
} |
||||
|
||||
public Collection getConfigAttributeDefinitions() { |
||||
return null; |
||||
} |
||||
|
||||
private ConfigAttributeDefinition processAnnotation(Annotation a) { |
||||
if (a == null || !(a instanceof Secured)) { |
||||
return null; |
||||
} |
||||
Secured secured = (Secured) a; |
||||
return new ConfigAttributeDefinition(secured.value()); |
||||
} |
||||
} |
||||
@ -1,137 +0,0 @@
@@ -1,137 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package org.springframework.security.annotation; |
||||
|
||||
import org.springframework.security.SecurityConfig; |
||||
|
||||
import org.springframework.metadata.Attributes; |
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.Field; |
||||
import java.lang.reflect.Method; |
||||
|
||||
import java.util.Collection; |
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
|
||||
|
||||
/** |
||||
* Java 5 Annotation <code>Attributes</code> metadata implementation used for secure method interception.<p>This |
||||
* <code>Attributes</code> implementation will return security configuration for classes described using the |
||||
* <code>Secured</code> Java 5 annotation. |
||||
* <p> |
||||
* The <code>SecurityAnnotationAttributes</code> implementation can be used to configure a |
||||
* <code>MethodDefinitionAttributes</code> and <code>MethodSecurityInterceptor</code> bean definition (see below). |
||||
* <p> |
||||
* For example: |
||||
* <pre> |
||||
* <bean id="attributes" class="org.springframework.security.annotation.SecurityAnnotationAttributes"/> |
||||
* <bean id="objectDefinitionSource" |
||||
* class="org.springframework.security.intercept.method.MethodDefinitionAttributes"> |
||||
* <property name="attributes"><ref local="attributes"/></property> |
||||
* </bean> |
||||
* <bean id="securityInterceptor" |
||||
* class="org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor"> |
||||
* . . . |
||||
* <property name="objectDefinitionSource"><ref local="objectDefinitionSource"/></property> |
||||
* </bean> |
||||
* </pre> |
||||
* <p> |
||||
* These security annotations are similiar to the Commons Attributes approach, however they are using Java 5 |
||||
* language-level metadata support. |
||||
* |
||||
* @author Mark St.Godard |
||||
* @version $Id$ |
||||
* |
||||
* @see org.springframework.security.annotation.Secured |
||||
*/ |
||||
public class SecurityAnnotationAttributes implements Attributes { |
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/** |
||||
* Get the <code>Secured</code> attributes for a given target class. |
||||
* |
||||
* @param target The target method |
||||
* |
||||
* @return Collection of <code>SecurityConfig</code> |
||||
* |
||||
* @see Attributes#getAttributes |
||||
*/ |
||||
public Collection getAttributes(Class target) { |
||||
Set<SecurityConfig> attributes = new HashSet<SecurityConfig>(); |
||||
|
||||
for (Annotation annotation : target.getAnnotations()) { |
||||
// check for Secured annotations
|
||||
if (annotation instanceof Secured) { |
||||
Secured attr = (Secured) annotation; |
||||
|
||||
for (String auth : attr.value()) { |
||||
attributes.add(new SecurityConfig(auth)); |
||||
} |
||||
|
||||
break; |
||||
} |
||||
} |
||||
|
||||
return attributes; |
||||
} |
||||
|
||||
/** |
||||
* Get the <code>Secured</code> attributes for a given target method. |
||||
* |
||||
* @param method The target method |
||||
* |
||||
* @return Collection of <code>SecurityConfig</code> |
||||
* |
||||
* @see Attributes#getAttributes |
||||
*/ |
||||
public Collection getAttributes(Method method) { |
||||
Set<SecurityConfig> attributes = new HashSet<SecurityConfig>(); |
||||
Annotation[] annotations = AnnotationUtils.getAnnotations(method); |
||||
|
||||
for (Annotation annotation : annotations) { |
||||
// check for Secured annotations
|
||||
if (annotation instanceof Secured) { |
||||
Secured attr = (Secured) annotation; |
||||
|
||||
for (String auth : attr.value()) { |
||||
attributes.add(new SecurityConfig(auth)); |
||||
} |
||||
|
||||
break; |
||||
} |
||||
} |
||||
|
||||
return attributes; |
||||
} |
||||
|
||||
public Collection getAttributes(Class clazz, Class filter) { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
public Collection getAttributes(Method method, Class clazz) { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
public Collection getAttributes(Field field) { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
public Collection getAttributes(Field field, Class clazz) { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
} |
||||
@ -0,0 +1,104 @@
@@ -0,0 +1,104 @@
|
||||
package org.springframework.security.annotation; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
|
||||
import javax.annotation.security.DenyAll; |
||||
import javax.annotation.security.PermitAll; |
||||
import javax.annotation.security.RolesAllowed; |
||||
|
||||
import junit.framework.Assert; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.security.ConfigAttributeDefinition; |
||||
|
||||
/** |
||||
* @author Luke Taylor |
||||
* @author Ben Alex |
||||
* @version $Id$ |
||||
*/ |
||||
public class Jsr250MethodDefinitionSourceTests { |
||||
Jsr250MethodDefinitionSource mds = new Jsr250MethodDefinitionSource(); |
||||
A a = new A(); |
||||
UserAllowedClass userAllowed = new UserAllowedClass(); |
||||
DenyAllClass denyAll = new DenyAllClass(); |
||||
|
||||
@Test |
||||
public void methodWithRolesAllowedHasCorrectAttribute() throws Exception { |
||||
ConfigAttributeDefinition accessAttributes = mds.findAttributes(a.getClass().getMethod("adminMethod"), null); |
||||
assertEquals(1, accessAttributes.getConfigAttributes().size()); |
||||
assertEquals("ADMIN", accessAttributes.getConfigAttributes().iterator().next().toString()); |
||||
} |
||||
|
||||
@Test |
||||
public void permitAllMethodHasPermitAllAttribute() throws Exception { |
||||
ConfigAttributeDefinition accessAttributes = mds.findAttributes(a.getClass().getMethod("permitAllMethod"), null); |
||||
assertEquals(1, accessAttributes.getConfigAttributes().size()); |
||||
assertEquals("javax.annotation.security.PermitAll", accessAttributes.getConfigAttributes().iterator().next().toString()); |
||||
} |
||||
|
||||
@Test |
||||
public void noRoleMethodHasDenyAllAttributeWithDenyAllClass() throws Exception { |
||||
ConfigAttributeDefinition accessAttributes = mds.findAttributes(denyAll.getClass()); |
||||
assertEquals(1, accessAttributes.getConfigAttributes().size()); |
||||
assertEquals("javax.annotation.security.DenyAll", accessAttributes.getConfigAttributes().iterator().next().toString()); |
||||
} |
||||
|
||||
@Test |
||||
public void adminMethodHasAdminAttributeWithDenyAllClass() throws Exception { |
||||
ConfigAttributeDefinition accessAttributes = mds.findAttributes(denyAll.getClass().getMethod("adminMethod"), null); |
||||
assertEquals(1, accessAttributes.getConfigAttributes().size()); |
||||
assertEquals("ADMIN", accessAttributes.getConfigAttributes().iterator().next().toString()); |
||||
} |
||||
|
||||
@Test |
||||
public void noRoleMethodHasNoAttributes() throws Exception { |
||||
ConfigAttributeDefinition accessAttributes = mds.findAttributes(a.getClass().getMethod("noRoleMethod"), null); |
||||
Assert.assertNull(accessAttributes); |
||||
} |
||||
|
||||
@Test |
||||
public void classRoleIsAppliedToNoRoleMethod() throws Exception { |
||||
ConfigAttributeDefinition accessAttributes = mds.findAttributes(userAllowed.getClass().getMethod("noRoleMethod"), null); |
||||
Assert.assertNull(accessAttributes); |
||||
} |
||||
|
||||
@Test |
||||
public void methodRoleOverridesClassRole() throws Exception { |
||||
ConfigAttributeDefinition accessAttributes = mds.findAttributes(userAllowed.getClass().getMethod("adminMethod"), null); |
||||
assertEquals(1, accessAttributes.getConfigAttributes().size()); |
||||
assertEquals("ADMIN", accessAttributes.getConfigAttributes().iterator().next().toString()); |
||||
} |
||||
|
||||
//~ Inner Classes ======================================================================================================
|
||||
|
||||
public static class A { |
||||
|
||||
public void noRoleMethod() {} |
||||
|
||||
@RolesAllowed("ADMIN") |
||||
public void adminMethod() {} |
||||
|
||||
@PermitAll |
||||
public void permitAllMethod() {} |
||||
} |
||||
|
||||
@RolesAllowed("USER") |
||||
public static class UserAllowedClass { |
||||
public void noRoleMethod() {} |
||||
|
||||
@RolesAllowed("ADMIN") |
||||
public void adminMethod() {} |
||||
} |
||||
|
||||
@DenyAll |
||||
public static class DenyAllClass { |
||||
|
||||
public void noRoleMethod() {} |
||||
|
||||
@RolesAllowed("ADMIN") |
||||
public void adminMethod() {} |
||||
} |
||||
|
||||
|
||||
|
||||
} |
||||
@ -1,114 +0,0 @@
@@ -1,114 +0,0 @@
|
||||
package org.springframework.security.annotation; |
||||
|
||||
import org.springframework.security.SecurityConfig; |
||||
|
||||
import org.junit.Test; |
||||
import static org.junit.Assert.*; |
||||
|
||||
import java.util.List; |
||||
import java.util.ArrayList; |
||||
|
||||
import javax.annotation.security.RolesAllowed; |
||||
import javax.annotation.security.PermitAll; |
||||
import javax.annotation.security.DenyAll; |
||||
|
||||
/** |
||||
* @author Luke Taylor |
||||
* @version $Id$ |
||||
*/ |
||||
public class Jsr250SecurityAnnotationAttributesTests { |
||||
Jsr250SecurityAnnotationAttributes attributes = new Jsr250SecurityAnnotationAttributes(); |
||||
A a = new A(); |
||||
UserAllowedClass userAllowed = new UserAllowedClass(); |
||||
DenyAllClass denyAll = new DenyAllClass(); |
||||
|
||||
@Test |
||||
public void methodWithRolesAllowedHasCorrectAttribute() throws Exception { |
||||
// Method[] methods = a.getClass().getMethods();
|
||||
|
||||
List<SecurityConfig> accessAttributes = |
||||
new ArrayList<SecurityConfig>(attributes.getAttributes(a.getClass().getMethod("adminMethod"))); |
||||
assertEquals(1, accessAttributes.size()); |
||||
assertEquals("ADMIN", accessAttributes.get(0).getAttribute()); |
||||
} |
||||
|
||||
@Test |
||||
public void permitAllMethodHasPermitAllAttribute() throws Exception { |
||||
List<SecurityConfig> accessAttributes = |
||||
new ArrayList<SecurityConfig>(attributes.getAttributes(a.getClass().getMethod("permitAllMethod"))); |
||||
assertEquals(1, accessAttributes.size()); |
||||
assertEquals("javax.annotation.security.PermitAll", accessAttributes.get(0).getAttribute()); |
||||
} |
||||
|
||||
@Test |
||||
public void noRoleMethodHasDenyAllAttributeWithDenyAllClass() throws Exception { |
||||
List<SecurityConfig> accessAttributes = |
||||
new ArrayList<SecurityConfig>(attributes.getAttributes(denyAll.getClass().getMethod("noRoleMethod"))); |
||||
assertEquals(1, accessAttributes.size()); |
||||
assertEquals("javax.annotation.security.DenyAll", accessAttributes.get(0).getAttribute()); |
||||
} |
||||
|
||||
@Test |
||||
public void adminMethodHasAdminAttributeWithDenyAllClass() throws Exception { |
||||
List<SecurityConfig> accessAttributes = |
||||
new ArrayList<SecurityConfig>(attributes.getAttributes(denyAll.getClass().getMethod("adminMethod"))); |
||||
assertEquals(1, accessAttributes.size()); |
||||
assertEquals("ADMIN", accessAttributes.get(0).getAttribute()); |
||||
} |
||||
|
||||
@Test |
||||
public void noRoleMethodHasNoAttributes() throws Exception { |
||||
List<SecurityConfig> accessAttributes = |
||||
new ArrayList<SecurityConfig>(attributes.getAttributes(a.getClass().getMethod("noRoleMethod"))); |
||||
assertEquals(0, accessAttributes.size()); |
||||
} |
||||
|
||||
@Test |
||||
public void classRoleIsAppliedToNoRoleMethod() throws Exception { |
||||
List<SecurityConfig> accessAttributes = |
||||
new ArrayList<SecurityConfig>(attributes.getAttributes(userAllowed.getClass().getMethod("noRoleMethod"))); |
||||
assertEquals(1, accessAttributes.size()); |
||||
assertEquals("USER", accessAttributes.get(0).getAttribute()); |
||||
} |
||||
|
||||
@Test |
||||
public void methodRoleOverridesClassRole() throws Exception { |
||||
List<SecurityConfig> accessAttributes = |
||||
new ArrayList<SecurityConfig>(attributes.getAttributes(userAllowed.getClass().getMethod("adminMethod"))); |
||||
assertEquals(1, accessAttributes.size()); |
||||
assertEquals("ADMIN", accessAttributes.get(0).getAttribute()); |
||||
} |
||||
|
||||
//~ Inner Classes ======================================================================================================
|
||||
|
||||
public static class A { |
||||
|
||||
public void noRoleMethod() {} |
||||
|
||||
@RolesAllowed("ADMIN") |
||||
public void adminMethod() {} |
||||
|
||||
@PermitAll |
||||
public void permitAllMethod() {} |
||||
} |
||||
|
||||
@RolesAllowed("USER") |
||||
public static class UserAllowedClass { |
||||
public void noRoleMethod() {} |
||||
|
||||
@RolesAllowed("ADMIN") |
||||
public void adminMethod() {} |
||||
} |
||||
|
||||
@DenyAll |
||||
public static class DenyAllClass { |
||||
|
||||
public void noRoleMethod() {} |
||||
|
||||
@RolesAllowed("ADMIN") |
||||
public void adminMethod() {} |
||||
} |
||||
|
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,200 @@
@@ -0,0 +1,200 @@
|
||||
package org.springframework.security.intercept.method; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.aspectj.lang.JoinPoint; |
||||
import org.aspectj.lang.reflect.CodeSignature; |
||||
import org.springframework.security.ConfigAttributeDefinition; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ClassUtils; |
||||
import org.springframework.util.ObjectUtils; |
||||
|
||||
/** |
||||
* Abstract implementation of {@link MethodDefinitionSource} that supports both Spring AOP and AspectJ and |
||||
* caches configuration attribute resolution from: 1. specific target method; 2. target class; 3. declaring method; |
||||
* 4. declaring class/interface. |
||||
* |
||||
* <p> |
||||
* This class mimics the behaviour of Spring's AbstractFallbackTransactionAttributeSource class. |
||||
* </p> |
||||
* |
||||
* <p> |
||||
* Note that this class cannot extract security metadata where that metadata is expressed by way of |
||||
* a target method/class (ie #1 and #2 above) AND the target method/class is encapsulated in another |
||||
* proxy object. Spring Security does not walk a proxy chain to locate the concrete/final target object. |
||||
* Consider making Spring Security your final advisor (so it advises the final target, as opposed to |
||||
* another proxy), move the metadata to declared methods or interfaces the proxy implements, or provide |
||||
* your own replacement <tt>MethodDefinitionSource</tt>. |
||||
* </p> |
||||
* |
||||
* @author Ben Alex |
||||
* @version $Id$ |
||||
*/ |
||||
public abstract class AbstractFallbackMethodDefinitionSource implements MethodDefinitionSource { |
||||
|
||||
private static final Log logger = LogFactory.getLog(AbstractFallbackMethodDefinitionSource.class); |
||||
private final static Object NULL_CONFIG_ATTRIBUTE = new Object(); |
||||
private final Map attributeCache = new HashMap(); |
||||
|
||||
public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException { |
||||
Assert.notNull(object, "Object cannot be null"); |
||||
|
||||
if (object instanceof MethodInvocation) { |
||||
MethodInvocation mi = (MethodInvocation) object; |
||||
return getAttributes(mi.getMethod(), mi.getThis().getClass()); |
||||
} |
||||
|
||||
if (object instanceof JoinPoint) { |
||||
JoinPoint jp = (JoinPoint) object; |
||||
Class targetClass = jp.getTarget().getClass(); |
||||
String targetMethodName = jp.getStaticPart().getSignature().getName(); |
||||
Class[] types = ((CodeSignature) jp.getStaticPart().getSignature()).getParameterTypes(); |
||||
Class declaringType = ((CodeSignature) jp.getStaticPart().getSignature()).getDeclaringType(); |
||||
|
||||
Method method = ClassUtils.getMethodIfAvailable(declaringType, targetMethodName, types); |
||||
Assert.notNull(method, "Could not obtain target method from JoinPoint: '"+ jp + "'"); |
||||
|
||||
return getAttributes(method, targetClass); |
||||
} |
||||
|
||||
throw new IllegalArgumentException("Object must be a MethodInvocation or JoinPoint"); |
||||
} |
||||
|
||||
public final boolean supports(Class clazz) { |
||||
return (MethodInvocation.class.isAssignableFrom(clazz) || JoinPoint.class.isAssignableFrom(clazz)); |
||||
} |
||||
|
||||
public ConfigAttributeDefinition getAttributes(Method method, Class targetClass) { |
||||
// First, see if we have a cached value.
|
||||
Object cacheKey = new DefaultCacheKey(method, targetClass); |
||||
synchronized (this.attributeCache) { |
||||
Object cached = this.attributeCache.get(cacheKey); |
||||
if (cached != null) { |
||||
// Value will either be canonical value indicating there is no config attribute,
|
||||
// or an actual config attribute.
|
||||
if (cached == NULL_CONFIG_ATTRIBUTE) { |
||||
return null; |
||||
} |
||||
else { |
||||
return (ConfigAttributeDefinition) cached; |
||||
} |
||||
} |
||||
else { |
||||
// We need to work it out.
|
||||
ConfigAttributeDefinition cfgAtt = computeAttributes(method, targetClass); |
||||
// Put it in the cache.
|
||||
if (cfgAtt == null) { |
||||
this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE); |
||||
} |
||||
else { |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Adding security method [" + cacheKey + "] with attribute [" + cfgAtt + "]"); |
||||
} |
||||
this.attributeCache.put(cacheKey, cfgAtt); |
||||
} |
||||
return cfgAtt; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @param method the method for the current invocation (never <code>null</code>) |
||||
* @param targetClass the target class for this invocation (may be <code>null</code>) |
||||
* @return |
||||
*/ |
||||
private ConfigAttributeDefinition computeAttributes(Method method, Class targetClass) { |
||||
// The method may be on an interface, but we need attributes from the target class.
|
||||
// If the target class is null, the method will be unchanged.
|
||||
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); |
||||
// First try is the method in the target class.
|
||||
ConfigAttributeDefinition attr = findAttributes(specificMethod, targetClass); |
||||
if (attr != null) { |
||||
return attr; |
||||
} |
||||
|
||||
// Second try is the config attribute on the target class.
|
||||
attr = findAttributes(specificMethod.getDeclaringClass()); |
||||
if (attr != null) { |
||||
return attr; |
||||
} |
||||
|
||||
if (specificMethod != method) { |
||||
// Fallback is to look at the original method.
|
||||
attr = findAttributes(method, method.getDeclaringClass()); |
||||
if (attr != null) { |
||||
return attr; |
||||
} |
||||
// Last fallback is the class of the original method.
|
||||
return findAttributes(method.getDeclaringClass()); |
||||
} |
||||
return null; |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Obtains the security metadata applicable to the specified method invocation. |
||||
* |
||||
* <p> |
||||
* Note that the {@link Method#getDeclaringClass()} may not equal the <code>targetClass</code>. |
||||
* Both parameters are provided to assist subclasses which may wish to provide advanced |
||||
* capabilities related to method metadata being "registered" against a method even if the |
||||
* target class does not declare the method (ie the subclass may only inherit the method). |
||||
* |
||||
* @param method the method for the current invocation (never <code>null</code>) |
||||
* @param targetClass the target class for the invocation (may be <code>null</code>) |
||||
* @return the security metadata (or null if no metadata applies) |
||||
*/ |
||||
protected abstract ConfigAttributeDefinition findAttributes(Method method, Class targetClass); |
||||
|
||||
/** |
||||
* Obtains the security metadata registered against the specified class. |
||||
* |
||||
* <p> |
||||
* Subclasses should only return metadata expressed at a class level. Subclasses should NOT |
||||
* aggregate metadata for each method registered against a class, as the abstract superclass |
||||
* will separate invoke {@link #findAttributes(Method, Class)} for individual methods as |
||||
* appropriate. |
||||
* |
||||
* @param clazz the target class for the invocation (never <code>null</code>) |
||||
* @return the security metadata (or null if no metadata applies) |
||||
*/ |
||||
protected abstract ConfigAttributeDefinition findAttributes(Class clazz); |
||||
|
||||
private static class DefaultCacheKey { |
||||
|
||||
private final Method method; |
||||
private final Class targetClass; |
||||
|
||||
public DefaultCacheKey(Method method, Class targetClass) { |
||||
this.method = method; |
||||
this.targetClass = targetClass; |
||||
} |
||||
|
||||
public boolean equals(Object other) { |
||||
if (this == other) { |
||||
return true; |
||||
} |
||||
if (!(other instanceof DefaultCacheKey)) { |
||||
return false; |
||||
} |
||||
DefaultCacheKey otherKey = (DefaultCacheKey) other; |
||||
return (this.method.equals(otherKey.method) && |
||||
ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass)); |
||||
} |
||||
|
||||
public int hashCode() { |
||||
return this.method.hashCode() * 21 + (this.targetClass != null ? this.targetClass.hashCode() : 0); |
||||
} |
||||
|
||||
public String toString() { |
||||
return "CacheKey[" + (targetClass == null ? "-" : targetClass.getName()) + "; " + method + "]"; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
package org.springframework.security; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
|
||||
import junit.framework.Assert; |
||||
|
||||
import org.aspectj.lang.annotation.Pointcut; |
||||
import org.aspectj.weaver.tools.PointcutExpression; |
||||
import org.aspectj.weaver.tools.PointcutParser; |
||||
import org.aspectj.weaver.tools.PointcutPrimitive; |
||||
import org.junit.Test; |
||||
|
||||
/** |
||||
* A quick play with AspectJ pointcut parsing. Was contemplating using this for MapBasedMethodDefinitionSource refactoring, |
||||
* but decided to revisit at a future point. Requires aspectjweaver-1.5.3.jar in classpath. |
||||
* |
||||
* @author Ben Alex |
||||
*/ |
||||
|
||||
public class AspectJParsingTests { |
||||
private static final Set DEFAULT_SUPPORTED_PRIMITIVES = new HashSet(); |
||||
|
||||
@Pointcut("execution(int TargetObject.countLength(String))") |
||||
public void goodPointcut() {} |
||||
|
||||
static { |
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.CALL); |
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION); |
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS); |
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE); |
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS); |
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET); |
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN); |
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION); |
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN); |
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS); |
||||
DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET); |
||||
} |
||||
|
||||
@Test |
||||
public void testMatches() throws Exception { |
||||
PointcutParser parser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(DEFAULT_SUPPORTED_PRIMITIVES); |
||||
PointcutExpression expression = parser.parsePointcutExpression("org.springframework.security.AspectJParsingTests.goodPointcut()"); |
||||
|
||||
Method exec = OtherTargetObject.class.getMethod("countLength", new Class[] {String.class}); |
||||
Assert.assertTrue(expression.matchesMethodExecution(exec).alwaysMatches()); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue