diff --git a/aspects/pom.xml b/aspects/pom.xml new file mode 100644 index 0000000000..ec739b485d --- /dev/null +++ b/aspects/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + org.springframework.security + spring-security-parent + 3.0.0.CI-SNAPSHOT + + jar + spring-security-aspects + Spring Security - Aspects + + + commons-logging + commons-logging + + + org.aspectj + aspectjweaver + + + + org.springframework.security + spring-security-core + 3.0.0.CI-SNAPSHOT + + + + + + org.codehaus.mojo + aspectj-maven-plugin + 1.0 + + + + org.aspectj + com.springsource.org.aspectj.runtime + 1.6.3.RELEASE + + + org.aspectj + com.springsource.org.aspectj.tools + 1.6.3.RELEASE + + + + + + compile + test-compile + + + + + 1.6 + 1.6 + + + + + diff --git a/aspects/src/main/java/org/springframework/security/access/intercept/aspectj/aspect/AnnotationSecurityAspect.aj b/aspects/src/main/java/org/springframework/security/access/intercept/aspectj/aspect/AnnotationSecurityAspect.aj new file mode 100644 index 0000000000..b2d94bbcba --- /dev/null +++ b/aspects/src/main/java/org/springframework/security/access/intercept/aspectj/aspect/AnnotationSecurityAspect.aj @@ -0,0 +1,66 @@ +package org.springframework.security.access.intercept.aspectj.aspect; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.intercept.aspectj.AspectJCallback; +import org.springframework.security.access.intercept.aspectj.AspectJSecurityInterceptor; + +/** + * Concrete AspectJ transaction aspect using Spring Security @Secured annotation + * for JDK 1.5+. + * + *

+ * When using this aspect, you must annotate the implementation class + * (and/or methods within that class), not the interface (if any) that + * the class implements. AspectJ follows Java's rule that annotations on + * interfaces are not inherited. This will vary from Spring AOP. + * + * @author Mike Wiesner + * @since 1.0 + * @version $Id$ + */ +public aspect AnnotationSecurityAspect implements InitializingBean { + + /** + * Matches the execution of any public method in a type with the Secured + * annotation, or any subtype of a type with the Secured annotation. + */ + private pointcut executionOfAnyPublicMethodInAtSecuredType() : + execution(public * ((@Secured *)+).*(..)) && @this(Secured); + + /** + * Matches the execution of any method with the Secured annotation. + */ + private pointcut executionOfSecuredMethod() : + execution(* *(..)) && @annotation(Secured); + + private pointcut securedMethodExecution() : + executionOfAnyPublicMethodInAtSecuredType() || + executionOfSecuredMethod(); + + private AspectJSecurityInterceptor securityInterceptor; + + Object around(): securedMethodExecution() { + if (this.securityInterceptor == null) { + return proceed(); + } + + AspectJCallback callback = new AspectJCallback() { + public Object proceedWithObject() { + return proceed(); + } + }; + + return this.securityInterceptor.invoke(thisJoinPoint, callback); + } + + public void setSecurityInterceptor(AspectJSecurityInterceptor securityInterceptor) { + this.securityInterceptor = securityInterceptor; + } + + public void afterPropertiesSet() throws Exception { + if (this.securityInterceptor == null) + throw new IllegalArgumentException("securityInterceptor required"); + } + +} diff --git a/aspects/src/main/resources/META-INF/aop.xml b/aspects/src/main/resources/META-INF/aop.xml new file mode 100644 index 0000000000..a9d76e8e42 --- /dev/null +++ b/aspects/src/main/resources/META-INF/aop.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index b58ebf5764..9d738b9137 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ ntlm taglibs portlet + aspects samples diff --git a/samples/aspectj/pom.xml b/samples/aspectj/pom.xml new file mode 100644 index 0000000000..5226197829 --- /dev/null +++ b/samples/aspectj/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + org.springframework.security + spring-security-parent + 3.0.0.CI-SNAPSHOT + + spring-security-samples-aspectj + jar + Spring Security Sample AspectJ + + + + junit + junit + 4.6 + test + + + org.springframework.security + spring-security-core + ${project.version} + + + org.springframework.security + spring-security-aspects + ${project.version} + + + org.springframework + spring-test + test + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.0.2 + + 1.6 + 1.6 + + + + org.codehaus.mojo + aspectj-maven-plugin + 1.0 + + + + org.aspectj + com.springsource.org.aspectj.runtime + 1.6.3.RELEASE + + + org.aspectj + com.springsource.org.aspectj.tools + 1.6.3.RELEASE + + + + + + compile + test-compile + + + + + + + org.springframework.security + spring-security-aspects + + + 1.6 + 1.6 + + + + + diff --git a/samples/aspectj/src/main/java/sample/aspectj/SecuredService.java b/samples/aspectj/src/main/java/sample/aspectj/SecuredService.java new file mode 100644 index 0000000000..fee03b106b --- /dev/null +++ b/samples/aspectj/src/main/java/sample/aspectj/SecuredService.java @@ -0,0 +1,19 @@ +package sample.aspectj; + +import org.springframework.security.access.annotation.Secured; + +/** + * Service which is secured on the class level + * + * @author Mike Wiesner + * @since 3.0 + * @version $Id$ + */ +@Secured("ROLE_USER") +public class SecuredService { + + public void secureMethod() { + // nothing + } + +} diff --git a/samples/aspectj/src/main/java/sample/aspectj/Service.java b/samples/aspectj/src/main/java/sample/aspectj/Service.java new file mode 100644 index 0000000000..6a65009f62 --- /dev/null +++ b/samples/aspectj/src/main/java/sample/aspectj/Service.java @@ -0,0 +1,23 @@ +package sample.aspectj; + +import org.springframework.security.access.annotation.Secured; + +/** + * Service which is secured on method level + * + * @author Mike Wiesner + * @since 1.0 + * @version $Id$ + */ +public class Service { + + @Secured("ROLE_USER") + public void secureMethod() { + // nothing + } + + public void publicMethod() { + // nothing + } + +} diff --git a/samples/aspectj/src/main/resources/aspectj-context.xml b/samples/aspectj/src/main/resources/aspectj-context.xml new file mode 100644 index 0000000000..c21d2c2891 --- /dev/null +++ b/samples/aspectj/src/main/resources/aspectj-context.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/aspectj/src/test/java/sample/aspectj/AspectJInterceptorTests.java b/samples/aspectj/src/test/java/sample/aspectj/AspectJInterceptorTests.java new file mode 100644 index 0000000000..40f618ac1b --- /dev/null +++ b/samples/aspectj/src/test/java/sample/aspectj/AspectJInterceptorTests.java @@ -0,0 +1,78 @@ +package sample.aspectj; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:aspectj-context.xml") +public class AspectJInterceptorTests { + + @Autowired + private Service service; + + @Autowired + private SecuredService securedService; + + @Test + public void testPublicMethod() throws Exception { + service.publicMethod(); + } + + @Test(expected = AuthenticationCredentialsNotFoundException.class) + public void testSecuredMethodNotAuthenticated() throws Exception { + service.secureMethod(); + } + + @Test(expected = AccessDeniedException.class) + public void testSecuredMethodWrongRole() throws Exception { + Authentication token = new UsernamePasswordAuthenticationToken("test", "xxx", AuthorityUtils + .createAuthorityList("ROLE_ADMIN")); + SecurityContextHolder.getContext().setAuthentication(token); + service.secureMethod(); + } + + @Test + public void testSecuredMethodEverythingOk() throws Exception { + Authentication token = new UsernamePasswordAuthenticationToken("test", "xxx", AuthorityUtils + .createAuthorityList("ROLE_USER")); + SecurityContextHolder.getContext().setAuthentication(token); + service.secureMethod(); + } + + @Test(expected = AuthenticationCredentialsNotFoundException.class) + public void testSecuredClassNotAuthenticated() throws Exception { + securedService.secureMethod(); + } + + @Test(expected = AccessDeniedException.class) + public void testSecuredClassWrongRole() throws Exception { + Authentication token = new UsernamePasswordAuthenticationToken("test", "xxx", AuthorityUtils + .createAuthorityList("ROLE_ADMIN")); + SecurityContextHolder.getContext().setAuthentication(token); + securedService.secureMethod(); + } + + @Test + public void testSecuredClassEverythingOk() throws Exception { + Authentication token = new UsernamePasswordAuthenticationToken("test", "xxx", AuthorityUtils + .createAuthorityList("ROLE_USER")); + SecurityContextHolder.getContext().setAuthentication(token); + securedService.secureMethod(); + } + + @After + public void tearDown() { + SecurityContextHolder.clearContext(); + } + +} diff --git a/samples/pom.xml b/samples/pom.xml index e16d5efcce..b7901f49b6 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -17,7 +17,8 @@ openid ldap portlet - cas + cas + aspectj