From 0a8699003f7d20e6845ea04e5e985f37d41e41c3 Mon Sep 17 00:00:00 2001 From: "Mark St. Godard" Date: Fri, 12 Aug 2005 02:57:38 +0000 Subject: [PATCH] Initial checkin of Security Java 5 Annotation support (see http://opensource.atlassian.com/projects/spring/browse/SEC-4) Note: I have created a new source dir "core-tiger" for Java 5 related core security classes, as well as test dir. Note: project.properties should compile this project using 1.5. WAR test application using Spring 1.2 Transaction Annotations and Security to follow --- .classpath | 4 + core-tiger/maven.xml | 39 +++++ core-tiger/project.properties | 12 ++ core-tiger/project.xml | 43 ++++++ .../org/acegisecurity/annotation/Secured.java | 54 +++++++ .../SecurityAnnotationAttributes.java | 132 ++++++++++++++++ .../annotation/BusinessService.java | 18 +++ .../SecurityAnnotationAttributesTests.java | 142 ++++++++++++++++++ 8 files changed, 444 insertions(+) create mode 100644 core-tiger/maven.xml create mode 100644 core-tiger/project.properties create mode 100644 core-tiger/project.xml create mode 100644 core-tiger/src/main/java/org/acegisecurity/annotation/Secured.java create mode 100644 core-tiger/src/main/java/org/acegisecurity/annotation/SecurityAnnotationAttributes.java create mode 100644 core-tiger/src/test/java/org/acegisecurity/annotation/BusinessService.java create mode 100644 core-tiger/src/test/java/org/acegisecurity/annotation/SecurityAnnotationAttributesTests.java diff --git a/.classpath b/.classpath index 8e5e899196..0c750a7693 100644 --- a/.classpath +++ b/.classpath @@ -55,5 +55,9 @@ + + + + diff --git a/core-tiger/maven.xml b/core-tiger/maven.xml new file mode 100644 index 0000000000..89017035e2 --- /dev/null +++ b/core-tiger/maven.xml @@ -0,0 +1,39 @@ + + + + + + + signature.alias defined; signing JAR(s)... + + + + + + + + + diff --git a/core-tiger/project.properties b/core-tiger/project.properties new file mode 100644 index 0000000000..59d5d1056e --- /dev/null +++ b/core-tiger/project.properties @@ -0,0 +1,12 @@ +# $Id$ + +# Values in this file will be overriden by any values with the same name +# in the user-created build.properties file. + +# Compile settings +# +# Java 1.5 is required due to the use of annotations for metadata. +# (main Acegi Security project / parent) is Java 1.3 compatible +# +maven.compile.target=1.5 +maven.compile.source=1.5 diff --git a/core-tiger/project.xml b/core-tiger/project.xml new file mode 100644 index 0000000000..07e85a4677 --- /dev/null +++ b/core-tiger/project.xml @@ -0,0 +1,43 @@ + + + ${basedir}/../project.xml + 3 + acegi-security-core-tiger + Acegi Security System for Spring - Java 5 (Tiger) + acegisecurity + /home/groups/a/ac/acegisecurity/htdocs/multiproject/acegi-security-core-tiger + + scm:cvs:pserver:anonymous@cvs.sourceforge.net:/cvsroot/acegisecurity:acegisecurity + scm:cvs:ext:${maven.username}@cvs.sourceforge.net:/cvsroot/acegisecurity:acegisecurity + http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/acegisecurity/acegisecurity/domain/ + + + + acegisecurity + acegi-security + 0.9.0-SNAPSHOT + jar + + + + + + ${basedir}/src/main/resources/ + / + + *.xsl + + false + + + ${basedir}/../ + META-INF + + notice.txt + + false + + + + + diff --git a/core-tiger/src/main/java/org/acegisecurity/annotation/Secured.java b/core-tiger/src/main/java/org/acegisecurity/annotation/Secured.java new file mode 100644 index 0000000000..988668287a --- /dev/null +++ b/core-tiger/src/main/java/org/acegisecurity/annotation/Secured.java @@ -0,0 +1,54 @@ +/* Copyright 2004, 2005 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.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Java 5 annotation for describing service layer security attributes. + * + *

The Secured annotation is used to define a list of security + * configuration attributes for business methods. This annotation can be used + * as a Java 5 alternative to XML configuration. + *

For example: + *

+ *     @Secured ({"ROLE_USER"})
+ *     public void create(Contact contact);
+ *     
+ *     @Secured ({"ROLE_USER", "ROLE_ADMIN"})
+ *     public void update(Contact contact);
+ *     
+ *     @Secured ({"ROLE_ADMIN"})
+ *     public void delete(Contact contact);
+ * 
+ * @author Mark St.Godard + * @version $Id$ + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Secured { + //~ Methods ================================================================ + + /** + * Returns the list of security configuration attributes. + * (i.e. ROLE_USER, ROLE_ADMIN etc.) + * @return String[] The secure method attributes + */ + public String[] value(); +} diff --git a/core-tiger/src/main/java/org/acegisecurity/annotation/SecurityAnnotationAttributes.java b/core-tiger/src/main/java/org/acegisecurity/annotation/SecurityAnnotationAttributes.java new file mode 100644 index 0000000000..7122c6380f --- /dev/null +++ b/core-tiger/src/main/java/org/acegisecurity/annotation/SecurityAnnotationAttributes.java @@ -0,0 +1,132 @@ +/* Copyright 2004, 2005 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.annotation; + +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; + +import net.sf.acegisecurity.SecurityConfig; + +import org.springframework.metadata.Attributes; + +/** + * Java 5 Annotation Attributes metadata implementation used for + * secure method interception. + * + *

This Attributes implementation will return security + * configuration for classes described using the Secured Java 5 + * annotation. + * + *

The SecurityAnnotationAttributes implementation can be used + * to configure a MethodDefinitionAttributes and + * MethodSecurityInterceptor bean definition (see below). + * + *

For example: + *

+ * <bean id="attributes" 
+ *     class="net.sf.acegisecurity.annotation.SecurityAnnotationAttributes"/>
+ * 
+ * <bean id="objectDefinitionSource" 
+ *     class="net.sf.acegisecurity.intercept.method.MethodDefinitionAttributes">
+ *     <property name="attributes">
+ *         <ref local="attributes"/>
+ *     </property>
+ * </bean>
+ * 
+ * <bean id="securityInterceptor" 
+ *     class="net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
+ *      . . .
+ *      <property name="objectDefinitionSource">
+ *          <ref local="objectDefinitionSource"/>
+ *      </property>
+ * </bean>
+ * 
+ * + *

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 net.sf.acegisecurity.annotation.Secured + */ +public class SecurityAnnotationAttributes implements Attributes { + + /** + * Get the Secured attributes for a given target class. + * @param method The target method + * @return Collection of SecurityConfig + * @see Attributes#getAttributes + */ + public Collection getAttributes(Class target) { + + Set attributes = new HashSet(); + + 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; + } + + public Collection getAttributes(Class clazz, Class filter) { + throw new UnsupportedOperationException("Unsupported operation"); + } + + /** + * Get the Secured attributes for a given target method. + * @param method The target method + * @return Collection of SecurityConfig + * @see Attributes#getAttributes + */ + public Collection getAttributes(Method method) { + Set attributes = new HashSet(); + + for (Annotation annotation : method.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; + } + + public Collection getAttributes(Method method, Class clazz) { + throw new UnsupportedOperationException("Unsupported operation"); + } + + public Collection getAttributes(Field field) { + throw new UnsupportedOperationException("Unsupported operation"); + } + + public Collection getAttributes(Field field, Class clazz) { + throw new UnsupportedOperationException("Unsupported operation"); + } + +} diff --git a/core-tiger/src/test/java/org/acegisecurity/annotation/BusinessService.java b/core-tiger/src/test/java/org/acegisecurity/annotation/BusinessService.java new file mode 100644 index 0000000000..c01d4c7ee7 --- /dev/null +++ b/core-tiger/src/test/java/org/acegisecurity/annotation/BusinessService.java @@ -0,0 +1,18 @@ +package net.sf.acegisecurity.annotation; + +@Secured ({"ROLE_USER"}) +public interface BusinessService { + + @Secured ({"ROLE_USER"}) + public void someUserMethod1(); + + @Secured ({"ROLE_USER"}) + public void someUserMethod2(); + + @Secured ({"ROLE_USER","ROLE_ADMIN"}) + public void someUserAndAdminMethod(); + + @Secured ({"ROLE_ADMIN"}) + public void someAdminMethod(); + +} diff --git a/core-tiger/src/test/java/org/acegisecurity/annotation/SecurityAnnotationAttributesTests.java b/core-tiger/src/test/java/org/acegisecurity/annotation/SecurityAnnotationAttributesTests.java new file mode 100644 index 0000000000..a352b31dc3 --- /dev/null +++ b/core-tiger/src/test/java/org/acegisecurity/annotation/SecurityAnnotationAttributesTests.java @@ -0,0 +1,142 @@ +/* Copyright 2004, 2005 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.annotation; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; + +import junit.framework.TestCase; +import net.sf.acegisecurity.SecurityConfig; + +import org.springframework.metadata.Attributes; + + +/** + * Tests for {@link net.sf.acegisecurity.annotation.SecurityAnnotationAttributes} + * + * @author Mark St.Godard + * @version $Revision$ + */ +public class SecurityAnnotationAttributesTests extends TestCase { + //~ Instance fields ======================================================== + + private Attributes attributes; + + //~ Methods ================================================================ + + public void testGetAttributesClass() { + Collection attrs = this.attributes.getAttributes(BusinessService.class); + + assertNotNull(attrs); + + // expect 1 annotation + assertTrue(attrs.size() == 1); + + // should have 1 SecurityConfig + SecurityConfig sc = (SecurityConfig) attrs.iterator().next(); + + assertTrue(sc.getAttribute().equals("ROLE_USER")); + } + + public void testGetAttributesClassClass() { + try{ + this.attributes.getAttributes(BusinessService.class, null); + fail("Unsupported method should have thrown an exception!"); + + }catch(UnsupportedOperationException expected){ + } + } + + public void testGetAttributesField() { + try{ + Field field = null; + this.attributes.getAttributes(field); + fail("Unsupported method should have thrown an exception!"); + + }catch(UnsupportedOperationException expected){ + + } + + } + + public void testGetAttributesFieldClass() { + try{ + Field field = null; + this.attributes.getAttributes(field, null); + fail("Unsupported method should have thrown an exception!"); + + }catch(UnsupportedOperationException expected){ + + } + + } + + public void testGetAttributesMethod() { + + Method method = null; + try{ + method = BusinessService.class.getMethod("someUserAndAdminMethod",new Class[] {}); + }catch(NoSuchMethodException unexpected){ + fail("Should be a method called 'someUserAndAdminMethod' on class!"); + } + Collection attrs = this.attributes.getAttributes(method); + + assertNotNull(attrs); + + // expect 2 attributes + assertTrue(attrs.size() == 2); + + boolean user = false; + boolean admin = false; + // should have 2 SecurityConfigs + for(Object obj: attrs){ + assertTrue(obj instanceof SecurityConfig); + SecurityConfig sc = (SecurityConfig)obj; + if(sc.getAttribute().equals("ROLE_USER")){ + user = true; + }else if(sc.getAttribute().equals("ROLE_ADMIN")){ + admin = true; + } + } + // expect to have ROLE_USER and ROLE_ADMIN + assertTrue(user && admin); + } + + public void testGetAttributesMethodClass() { + + Method method = null; + try{ + method = BusinessService.class.getMethod("someUserAndAdminMethod",new Class[] {}); + }catch(NoSuchMethodException unexpected){ + fail("Should be a method called 'someUserAndAdminMethod' on class!"); + } + + try{ + this.attributes.getAttributes(method,null); + fail("Unsupported method should have thrown an exception!"); + + }catch(UnsupportedOperationException expected){ + + } + + } + + protected void setUp() throws Exception { + // create the Annotations impl + this.attributes = new SecurityAnnotationAttributes(); + } +}