diff --git a/.classpath b/.classpath index 0194f48102..204d6b1551 100644 --- a/.classpath +++ b/.classpath @@ -28,5 +28,6 @@ + diff --git a/changelog.txt b/changelog.txt index b51e64f483..e589e82262 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,8 +1,12 @@ Changes in version 0.7 (2004-xx-xx) ----------------------------------- +* Added AspectJ support (especially useful for instance-level security) * Added MethodDefinitionSourceAdvisor for performance and autoproxying * Added MethodDefinitionMap querying of interfaces defined by secure objects +* Refactored MethodDefinitionSource to work with Method, not MethodInvocation +* Refactored AbstractSecurityInterceptor to better support other AOP libraries +* Moved MethodSecurityInterceptor to ...intercept.method.aopalliance package * Documentation improvements Changes in version 0.6.1 (2004-09-25) diff --git a/core/src/main/java/org/acegisecurity/intercept/method/aspectj/AspectJCallback.java b/core/src/main/java/org/acegisecurity/intercept/method/aspectj/AspectJCallback.java new file mode 100644 index 0000000000..cf82edcfb4 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/intercept/method/aspectj/AspectJCallback.java @@ -0,0 +1,31 @@ +/* Copyright 2004 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 net.sf.acegisecurity.intercept.method.aspectj; + +/** + * Called by the {@link AspectJSecurityInterceptor} when it wishes for the + * AspectJ processing to continue. Typically implemented in the + * around() advice as a simple return proceed(); + * statement. + * + * @author Ben Alex + * @version $Id$ + */ +public interface AspectJCallback { + //~ Methods ================================================================ + + public Object proceedWithObject(); +} diff --git a/core/src/main/java/org/acegisecurity/intercept/method/aspectj/AspectJSecurityInterceptor.java b/core/src/main/java/org/acegisecurity/intercept/method/aspectj/AspectJSecurityInterceptor.java new file mode 100644 index 0000000000..c325de320f --- /dev/null +++ b/core/src/main/java/org/acegisecurity/intercept/method/aspectj/AspectJSecurityInterceptor.java @@ -0,0 +1,109 @@ +/* Copyright 2004 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 net.sf.acegisecurity.intercept.method.aspectj; + +import net.sf.acegisecurity.intercept.AbstractSecurityInterceptor; +import net.sf.acegisecurity.intercept.InterceptorStatusToken; +import net.sf.acegisecurity.intercept.ObjectDefinitionSource; +import net.sf.acegisecurity.intercept.method.MethodDefinitionSource; + +import org.aspectj.lang.JoinPoint; + + +/** + * Provides security interception of AspectJ method invocations. + * + *

+ * The ObjectDefinitionSource required by this security + * interceptor is of type {@link MethodDefinitionSource}. This is shared with + * the AOP Alliance based security interceptor + * (MethodSecurityInterceptor), since both work with Java + * Methods. + *

+ * + *

+ * The secure object type is org.aspectj.lang.JointPoint, which is + * passed from the relevant around() advice. The + * around() advice also passes an anonymous implementation of + * {@link AspectJCallback} which contains the call for AspectJ to continue + * processing: return proceed();. + *

+ * + *

+ * Refer to {@link AbstractSecurityInterceptor} for details on the workflow. + *

+ * + * @author Ben Alex + * @version $Id$ + */ +public class AspectJSecurityInterceptor extends AbstractSecurityInterceptor { + //~ Instance fields ======================================================== + + private MethodDefinitionSource objectDefinitionSource; + + //~ Methods ================================================================ + + public void setObjectDefinitionSource(MethodDefinitionSource newSource) { + this.objectDefinitionSource = newSource; + } + + public MethodDefinitionSource getObjectDefinitionSource() { + return this.objectDefinitionSource; + } + + public void afterPropertiesSet() { + super.afterPropertiesSet(); + + if (!this.getAccessDecisionManager().supports(JoinPoint.class)) { + throw new IllegalArgumentException( + "AccessDecisionManager does not support JointPoint"); + } + + if (!this.getRunAsManager().supports(JoinPoint.class)) { + throw new IllegalArgumentException( + "RunAsManager does not support JointPoint"); + } + } + + /** + * This method should be used to enforce security on a + * JoinPoint. + * + * @param jp The AspectJ joint point being invoked which requires a + * security decision + * @param advisorProceed the advice-defined anonymous class that implements + * AspectJCallback containing a simple return + * proceed(); statement + * + * @return The returned value from the method invocation + */ + public Object invoke(JoinPoint jp, AspectJCallback advisorProceed) { + Object result; + InterceptorStatusToken token = super.beforeInvocation(jp); + + try { + result = advisorProceed.proceedWithObject(); + } finally { + super.afterInvocation(token); + } + + return result; + } + + public ObjectDefinitionSource obtainObjectDefinitionSource() { + return this.objectDefinitionSource; + } +} diff --git a/core/src/main/java/org/acegisecurity/intercept/method/aspectj/package.html b/core/src/main/java/org/acegisecurity/intercept/method/aspectj/package.html new file mode 100644 index 0000000000..ea0a671118 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/intercept/method/aspectj/package.html @@ -0,0 +1,8 @@ + + +Enforces security for AspectJ JointPoints, delegating secure +object callbacks to the calling aspect. + +

Refer to the reference guide for information on usage. + + diff --git a/docs/reference/src/index.xml b/docs/reference/src/index.xml index 4ee72eb0a9..e00c770019 100644 --- a/docs/reference/src/index.xml +++ b/docs/reference/src/index.xml @@ -7,7 +7,7 @@ Reference Documentation - 0.6.1 + 0.7 @@ -202,8 +202,8 @@ directly. For example, it would be possible to build a new secure object to secure calls to a messaging system that does not use MethodInvocations. Most Spring applications will - simply use the two currently supported secure object types - (MethodInvocation and + simply use the three currently supported secure object types + (MethodInvocation, JoinPoint and FilterInterceptor) with complete transparency. @@ -214,7 +214,7 @@ Supported Secure Objects - The Acegi Security System for Spring currently supports two + The Acegi Security System for Spring currently supports three secure objects. The first handles an AOP Alliance @@ -224,12 +224,24 @@ standard Spring-hosted bean available as a MethodInvocation, the bean is simply published through a ProxyFactoryBean or - BeanNameAutoProxyCreator. Most Spring developers - would already be familiar with these due to their use in transactions - and other areas of Spring. + BeanNameAutoProxyCreator or + DefaultAdvisorAutoProxyCreator. Most Spring + developers would already be familiar with these due to their use in + transactions and other areas of Spring. - The second type is a FilterInvocation. This - is an object included with the Acegi Security System for Spring. It is + The second type is an AspectJ JoinPoint. + AspectJ has a particular use in securing domain object instances, as + these are most often managed outside the Spring bean container. By + using AspectJ, standard constructs such as new + Person(); can be used and full security will be applied to + them by Acegi Security. The + AspectJSecurityInterceptor is still managed by + Spring, which creates the aspect singleton and wires it with the + appropriate authentication managers, access decision managers and so + on. + + The third type is a FilterInvocation. This is + an object included with the Acegi Security System for Spring. It is created by an included filter and simply wraps the HTTP ServletRequest, ServletResponse and FilterChain. The @@ -410,9 +422,8 @@ - Call a secure object-specific - SecurityInterceptorCallback so that the request - execution can proceed. + Proceed with the request execution of the secure + object. @@ -424,8 +435,8 @@ - Return any result received from the - SecurityInterceptorCallback. + Return any result received from the secure object + execution. @@ -441,8 +452,8 @@ object-specific security interceptors are discussed below. - - MethodInvocation Security Interceptor + + AOP Alliance (MethodInvocation) Security Interceptor To secure MethodInvocations, developers simply add a properly configured @@ -452,10 +463,15 @@ ProxyFactoryBean or BeanNameAutoProxyCreator, as commonly used by many other parts of Spring (refer to the sample application for examples). - The MethodSecurityInterceptor is configured as + Alternatively, Acegi Security provides a + MethodDefinitionSourceAdvisor which may be used + with Spring's DefaultAdvisorAutoProxyCreator to + automatically chain the security interceptor in front of any beans + defined against the MethodSecurityInterceptor. The + MethodSecurityInterceptor itself is configured as follows: - <bean id="bankManagerSecurity" class="net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor"> + <bean id="bankManagerSecurity" class="net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"> <property name="validateConfigAttributes"><value>true</value></property> <property name="authenticationManager"><ref bean="authenticationManager"/></property> <property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property> @@ -572,6 +588,124 @@ false. + + AspectJ (JoinPoint) Security Interceptor + + The AspectJ security interceptor is very similar to the AOP + Alliance security interceptor discussed in the previous section. + Indeed we will only discuss the differences in this section. + + The AspectJ interceptor is named + AspectJSecurityInterceptor. Unlike the AOP Alliance + security interceptor, which relies on the Spring application context + to weave in the security interceptor via proxying, the + AspectJSecurityInterceptor is weaved in via the + AspectJ compiler. It would not be uncommon to use both types of + security interceptors in the same application, with + AspectJSecurityInterceptor being used for domain + object instance security and the AOP Alliance + MethodSecurityInterceptor being used for services + layer security. + + Let's first consider how the + AspectJSecurityInterceptor is configured in the + Spring application context: + + <bean id="bankManagerSecurity" class="net.sf.acegisecurity.intercept.method.aspectj.AspectJSecurityInterceptor"> + <property name="validateConfigAttributes"><value>true</value></property> + <property name="authenticationManager"><ref bean="authenticationManager"/></property> + <property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property> + <property name="runAsManager"><ref bean="runAsManager"/></property> + <property name="objectDefinitionSource"> + <value> + net.sf.acegisecurity.context.BankManager.delete*=ROLE_SUPERVISOR,RUN_AS_SERVER + net.sf.acegisecurity.context.BankManager.getBalance=ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER,RUN_AS_SERVER + </value> + </property> +</bean> + + As you can see, aside from the class name, the + AspectJSecurityInterceptor is exactly the same as + the AOP Alliance security interceptor. Indeed the two interceptors can + share the same objectDefinitionSource, as the + ObjectDefinitionSource works with + java.lang.reflect.Methods rather than an AOP + library-specific class. Of course, your access decisions have access + to the relevant AOP library-specific invocation (ie + MethodInvocation or JoinPoint) + and as such can consider a range of addition criteria when making + access decisions (such as method arguments). + + Next you'll need to define an AspectJ aspect. + For example: + + package net.sf.acegisecurity.samples.aspectj; + +import net.sf.acegisecurity.intercept.method.aspectj.AspectJSecurityInterceptor; +import net.sf.acegisecurity.intercept.method.aspectj.AspectJCallback; +import org.springframework.beans.factory.InitializingBean; + +public aspect DomainObjectInstanceSecurityAspect implements InitializingBean { + + private AspectJSecurityInterceptor securityInterceptor; + + pointcut domainObjectInstanceExecution(): target(PersistableEntity) + && execution(public * *(..)) && !within(DomainObjectInstanceSecurityAspect); + + Object around(): domainObjectInstanceExecution() { + if (this.securityInterceptor != null) { + AspectJCallback callback = new AspectJCallback() { + public Object proceedWithObject() { + return proceed(); + } + }; + return this.securityInterceptor.invoke(thisJoinPoint, callback); + } else { + return proceed(); + } + } + + public AspectJSecurityInterceptor getSecurityInterceptor() { + return securityInterceptor; + } + + public void setSecurityInterceptor(AspectJSecurityInterceptor securityInterceptor) { + this.securityInterceptor = securityInterceptor; + } + + public void afterPropertiesSet() throws Exception { + if (this.securityInterceptor == null) + throw new IllegalArgumentException("securityInterceptor required"); + } +} + + In the above example, the security interceptor will be applied + to every instance of PersistableEntity, which is an + abstract class not shown (you can use any other class or + pointcut expression you like). For those curious, + AspectJCallback is needed because the + proceed(); statement has special meaning only + within an around() body. The + AspectJSecurityInterceptor calls this anonymous + AspectJCallback class when it wants the target + object to continue. + + You will need to configure Spring to load the aspect and wire it + with the AspectJSecurityInterceptor. A bean + declaration which achieves this is shown below: + + <bean id="domainObjectInstanceSecurityAspect" + class="net.sf.acegisecurity.samples.aspectj.DomainObjectInstanceSecurityAspect" + factory-method="aspectOf"> + <property name="securityInterceptor"><ref bean="aspectJSecurityInterceptor"/></property> +</bean> + + That's it! Now you can create your beans from anywhere within + your application, using whatever means you think fit (eg new + Person();) and they will have the security interceptor + applied. + + FilterInvocation Security Interceptor diff --git a/project.xml b/project.xml index 99938e8414..4cde0b656b 100644 --- a/project.xml +++ b/project.xml @@ -1,8 +1,8 @@ 3 + acegi-security Acegi Security System for Spring acegi - acegi-security 0.7-SNAPSHOT net.sf.acegisecurity Acegi Security System for Spring @@ -306,9 +306,18 @@ acegi resin-extracted unknown + ${basedir}/lib/extracted/resin/resin-extracted.jar jar + + aspectj + aspectjrt + 1.2 + jar + http://eclipse.org/aspectj/ + + ${basedir}/src @@ -359,4 +368,4 @@ maven-clover-plugin - + \ No newline at end of file diff --git a/upgrade-06-07.txt b/upgrade-06-07.txt index c9e9e9a2ba..5175f99501 100644 --- a/upgrade-06-07.txt +++ b/upgrade-06-07.txt @@ -26,4 +26,9 @@ applications: providing a performance benefit as MethodSecurityInterceptor is not called for public (non-secure) objects. It also simplifies configuration. +- MethodSecurityInterceptor has moved from + net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor to + net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor. + A simple find and replace will suffice to update your application contexts. + $Id$