1
0
mirror of synced 2026-07-09 04:40:13 +00:00

Compare commits

..

57 Commits

Author SHA1 Message Date
Ben Alex 6fe569556c Use type in same module (Maven requirement). 2006-11-17 03:18:07 +00:00
Ben Alex 197a011ac5 Relocate resource files to comply with Maven directory conventions. 2006-11-17 03:06:30 +00:00
Ben Alex aff04805e7 Prepare for 1.0.3. 2006-11-17 02:58:17 +00:00
Ben Alex 3e3db43b37 SEC-239: New ACL module. 2006-11-17 02:29:28 +00:00
Ben Alex 44cae6b2ee SEC-274: Correctly handle bridge methods, using AnnotationUtils when on classpath. 2006-11-17 02:17:45 +00:00
Ben Alex 1081c267d9 SEC-239: New ACL module. 2006-11-17 02:03:23 +00:00
Ben Alex 9f512c384e SEC-239: New ACL module. 2006-11-17 02:01:21 +00:00
Ben Alex 2984913051 SEC-393: More elegantly deal with setProviders(List) type safety enforcement. 2006-11-16 02:15:43 +00:00
Carlos Sanchez 5e819af782 SEC-388: Upgrade other Spring dependencies to 1.2.8. 2006-11-15 22:54:54 +00:00
Ben Alex 0044400ca6 SEC-388: Ensure Acegi Security 1.0.3 operates against Spring 1.2.8. 2006-11-14 22:09:49 +00:00
Ben Alex 1b4a098760 SEC-354: Add label-based voter. 2006-11-14 22:07:36 +00:00
Ben Alex 4d166a6867 SEC-333: Error in last commit, should default to regexp processor, not Ant Path processor, in the case of empty or null strings. 2006-11-14 21:52:51 +00:00
Ben Alex 780130d0f3 SEC-333: Eliminate dependecy on ORO when Ant Paths being used instead of Regular Expressions. 2006-11-14 20:55:24 +00:00
Ben Alex 89e95310f3 Version number. 2006-11-14 06:03:01 +00:00
Ben Alex 775840a565 SEC-374: Allow GrantedAuthority[]s assigned to switched user identity to be filtered. 2006-11-14 05:49:56 +00:00
Ben Alex 13ef76801f Add Sanjiv Jivan's blod entry. 2006-11-14 04:58:37 +00:00
Ben Alex b941577198 SEC-274: Reproduce bug report. 2006-11-14 04:26:12 +00:00
Ben Alex f5ce0250b4 SEC-310: Add AbstractProcessingFilter.getAuthenticationDetailsSource(). 2006-11-14 03:30:04 +00:00
Ben Alex e179574077 Fix JavaDocs. 2006-11-14 03:19:16 +00:00
Ben Alex 508966f0c7 SEC-313: Add integration test for Contacts. 2006-11-14 03:18:42 +00:00
Ben Alex 84671f9d68 SEC-382: Clean up resource bundle errors. 2006-11-14 02:30:00 +00:00
Ben Alex 8dda52eeaa SEC-322: Workaround bug in WebSphere. 2006-11-14 02:21:27 +00:00
Ben Alex 5640eb0511 SEC-378: Use trim instead of replacement for space removal. 2006-11-14 01:55:44 +00:00
Ben Alex ad6c501379 SEC-360: Minor correction in patch applied yesterday. 2006-11-14 01:09:35 +00:00
Ben Alex 2a65d386d5 SEC-384: Remove Commons-Lang dependency. 2006-11-14 00:59:17 +00:00
Ben Alex 59bf8602d2 SEC-356: Add cloneFromHttpSession property. 2006-11-14 00:43:00 +00:00
Ben Alex 5911234f65 SEC-359: Logout even if not logged in. 2006-11-13 06:05:28 +00:00
Ben Alex fa6b4480b1 SEC-360: Provide server side forward option instead of redirection. 2006-11-13 00:17:07 +00:00
Ben Alex f0ae6f53a7 SEC-327: Add includeDetailsObject property. 2006-11-12 23:55:50 +00:00
Ben Alex f28ce39bde SEC-365: Provide an alwaysRemember property, together with an abstract method so subclasses can determine custom behaviour. 2006-11-12 23:28:57 +00:00
Ben Alex 71eba94cf2 SEC-371: Use AbstractTicketValidator for logger. 2006-11-12 23:10:09 +00:00
Ben Alex 380daacd15 SEC-383: Correct missing comma in acl_permission create statement. 2006-11-12 23:03:16 +00:00
Ben Alex 0f517cb8e2 SEC-375: Publish AuthorizationFailureEvent event when AccessDeniedException thrown by AfterInvocationProvider. 2006-11-12 22:06:37 +00:00
Ben Alex b8d0722251 SEC-367: Added clarification of immutability contract. 2006-11-12 21:36:52 +00:00
Ben Alex 43dbe6c991 SEC-364: Fix context path handling. 2006-11-12 21:31:31 +00:00
Ben Alex 5ef0a1a4e6 Remove Domain sub-project. 2006-11-12 21:15:22 +00:00
Luke Taylor 4c8029d234 Corrected coverage link 2006-11-02 22:13:30 +00:00
Luke Taylor 61c786013d Added apt version of standalone.html 2006-10-30 19:36:47 +00:00
Luke Taylor 06ef71f44e Remove section on checking out code using maven. Plain subverson is much easier. 2006-10-30 18:33:15 +00:00
Luke Taylor 7969510d95 removed html versions of files converted to apt 2006-10-29 20:11:09 +00:00
Luke Taylor 6445be9532 Converted petclinic tutorial file to apt. 2006-10-29 20:08:26 +00:00
Luke Taylor efc8a98a4c Converted "downloads.html" file to apt. 2006-10-29 18:50:57 +00:00
Luke Taylor 368fc4372a Converted "suggested steps" file to apt. Removed upgrade links prior to 0.7. 2006-10-29 18:36:35 +00:00
Luke Taylor 5005e9c973 Remove index.xml (again?) 2006-10-28 19:26:32 +00:00
Luke Taylor 4d46213a59 removed index.html in favour of generated one 2006-10-28 17:21:44 +00:00
Luke Taylor 7852a830a3 Initial attempt at converting site sources to be mvn 2 compatible. 2006-10-28 16:06:36 +00:00
Carlos Sanchez 10d6859dad Added ACL table SQL for some databases 2006-10-17 22:24:57 +00:00
Ben Alex 172026f875 SEC-377: Remove Commons Lang dependency. 2006-10-14 00:17:19 +00:00
Ben Alex c89582652a Add Alan Stewart. 2006-10-14 00:14:13 +00:00
Ben Alex b1ac45320f Version change. 2006-10-08 08:39:39 +00:00
Ben Alex c292826475 SEC-373: Add byte array encryption/decryption support. 2006-10-07 09:45:51 +00:00
Carlos Sanchez 59572e2168 [maven-release-plugin] prepare for next development iteration 2006-10-04 22:07:51 +00:00
Carlos Sanchez 36fdbe0c97 [maven-release-plugin] prepare release acegi-security-webwork-0.1 2006-10-04 22:07:18 +00:00
Carlos Sanchez 52b10d6d34 Use latest parent 2006-10-04 22:06:03 +00:00
Carlos Sanchez b7595a6165 Bump to next version 2006-10-04 22:05:33 +00:00
Carlos Sanchez 02526b1461 Use latest release 2006-10-04 22:03:16 +00:00
Ben Alex 335e2c9848 Make ready for 1.0.3-SNAPSHOT. 2006-10-04 10:55:31 +00:00
231 changed files with 10087 additions and 7456 deletions
+18 -20
View File
@@ -1,26 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="samples/attributes/src/main/java"/>
<classpathentry kind="src" path="core/src/main/java"/>
<classpathentry kind="src" path="core/src/main/resources"/>
<classpathentry kind="src" path="core/src/test/java"/>
<classpathentry kind="src" path="core/src/test/resources"/>
<classpathentry kind="src" path="sandbox/other/src/main/java"/>
<classpathentry kind="src" path="samples/acegifier/src/test/resources"/>
<classpathentry kind="src" path="samples/acegifier/src/main/resources"/>
<classpathentry kind="src" path="sandbox/other/src/test/java"/>
<classpathentry kind="src" path="samples/contacts/src/main/java"/>
<classpathentry kind="src" path="samples/contacts/src/main/resources"/>
<classpathentry kind="src" path="samples/contacts/src/test/java"/>
<classpathentry kind="src" path="samples/contacts/src/test/resources"/>
<classpathentry kind="src" path="samples/acegifier/src/main/java"/>
<classpathentry kind="src" path="samples/acegifier/src/main/resources"/>
<classpathentry kind="src" path="samples/acegifier/src/test/java"/>
<classpathentry kind="src" path="domain/src/main/java"/>
<classpathentry kind="src" path="domain/src/main/resource"/>
<classpathentry kind="src" path="domain/src/test/java"/>
<classpathentry kind="src" path="domain/src/test/resources"/>
<classpathentry kind="src" path="adapters/catalina/src/main/resources"/>
<classpathentry kind="src" path="samples/acegifier/src/test/resources"/>
<classpathentry kind="src" path="samples/attributes/src/main/java"/>
<classpathentry kind="src" path="samples/attributes/src/main/resources"/>
<classpathentry kind="src" path="samples/attributes/src/test/java"/>
<classpathentry kind="src" path="adapters/cas/src/test/resources"/>
<classpathentry kind="src" path="adapters/cas/src/main/resources"/>
<classpathentry kind="src" path="core/src/main/resources"/>
<classpathentry kind="src" path="core/src/test/resources"/>
<classpathentry kind="src" path="samples/contacts/src/main/java"/>
<classpathentry kind="src" path="adapters/cas/src/main/java"/>
<classpathentry kind="src" path="adapters/cas/src/test/java"/>
<classpathentry kind="src" path="adapters/catalina/src/main/java"/>
<classpathentry kind="src" path="adapters/catalina/src/main/resources"/>
<classpathentry kind="src" path="adapters/catalina/src/test/java"/>
<classpathentry kind="src" path="adapters/jboss/src/main/java"/>
<classpathentry kind="src" path="adapters/jboss/src/test/java"/>
@@ -28,12 +30,10 @@
<classpathentry kind="src" path="adapters/jetty/src/test/java"/>
<classpathentry kind="src" path="adapters/resin/src/main/java"/>
<classpathentry kind="src" path="adapters/resin/src/test/java"/>
<classpathentry kind="src" path="core/src/main/java"/>
<classpathentry kind="src" path="core/src/test/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="var" path="MAVEN_REPO/com.caucho/jars/resin-3.0.9.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/org.springframework/jars/spring-2.0-m2.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/org.springframework/jars/spring-mock-2.0-m2.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/org.springframework/jars/spring-1.2.8.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/org.springframework/jars/spring-mock-1.2.8.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/aopalliance/jars/aopalliance-1.0.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/aspectj/jars/aspectjrt-1.2.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/cas/jars/casclient-2.0.11.jar"/>
@@ -41,7 +41,7 @@
<classpathentry kind="var" path="MAVEN_REPO/commons-codec/jars/commons-codec-1.3.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/commons-collections/jars/commons-collections-3.1.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/commons-logging/jars/commons-logging-1.0.4.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/hsqldb/jars/hsqldb-1.7.3.0.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/hsqldb/jars/hsqldb-1.8.0.4.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/oro/jars/oro-2.0.8.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/javax.servlet/jars/servlet-api-2.4.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/tomcat/jars/catalina-4.1.9.jar"/>
@@ -52,16 +52,15 @@
<classpathentry kind="var" path="MAVEN_REPO/ehcache/jars/ehcache-1.1.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/javax.servlet/jars/jsp-api-2.0.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/hibernate/jars/hibernate-3.0.3.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/commons-lang/jars/commons-lang-2.0.jar"/>
<classpathentry sourcepath="DIST_BASE/commons-beanutils-1.6.1-src/src/java" kind="var" path="MAVEN_REPO/commons-beanutils/jars/commons-beanutils-1.6.1.jar"/>
<classpathentry kind="src" path="samples/contacts-tiger/src/main/java"/>
<classpathentry kind="src" path="core-tiger/src/main/java"/>
<classpathentry kind="src" path="core-tiger/src/test/java"/>
<classpathentry kind="src" path="core-tiger/src/main/resources"/>
<classpathentry kind="src" path="core-tiger/src/test/java"/>
<classpathentry kind="src" path="core-tiger/src/test/resources"/>
<classpathentry kind="src" path="samples/annotations/src/main/java"/>
<classpathentry kind="src" path="samples/annotations/src/main/resources"/>
<classpathentry kind="src" path="samples/annotations/src/test/java"/>
<classpathentry kind="src" path="samples/contacts-tiger/src/main/java"/>
<classpathentry kind="var" path="MAVEN_REPO/org.samba.jcifs/jars/jcifs-1.2.6.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/dom4j/jars/dom4j-1.6.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/xerces/jars/xercesImpl-2.6.2.jar"/>
@@ -75,7 +74,6 @@
<classpathentry kind="var" path="MAVEN_REPO/org.apache.directory.shared/jars/shared-ldap-0.9.5.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/antlr/jars/antlr-2.7.2.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/ldapsdk/jars/ldapsdk-4.1.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/org.springframework/jars/spring-hibernate3-2.0-m2.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/cas/jars/cas-server-3.0.4.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/net.sourceforge.retroweaver/jars/retroweaver-1.0fcs.jar"/>
<classpathentry kind="var" path="MAVEN_REPO/taglibs/jars/standard-1.0.6.jar"/>
+1 -1
View File
@@ -13,7 +13,7 @@
<dependency>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security</artifactId>
<version>1.0.2</version>
<version>1.0.3</version>
<type>jar</type>
<url>http://acegisecurity.org</url>
<properties>
+1 -1
View File
@@ -14,7 +14,7 @@
<dependency>
<groupId>org.acegisecurity</groupId>
<artifactId>acegi-security</artifactId>
<version>1.0.2</version>
<version>1.0.3</version>
<type>jar</type>
</dependency>
</dependencies>
@@ -12,13 +12,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.acegisecurity.annotation;
import org.acegisecurity.SecurityConfig;
import org.springframework.metadata.Attributes;
import org.springframework.util.ClassUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -43,6 +44,8 @@ import java.util.Set;
* &lt;/bean></pre></p>
* <p>These security annotations are similiar to the Commons Attributes approach, however they are using Java 5
* language-level metadata support.</p>
* <p>This class should be used with Spring 2.0 or above, as it relies upon utility classes in Spring 2.0 for
* correct introspection of annotations on bridge methods.</p>
*
* @author Mark St.Godard
* @version $Id$
@@ -96,7 +99,19 @@ public class SecurityAnnotationAttributes implements Attributes {
public Collection getAttributes(Method method) {
Set<SecurityConfig> attributes = new HashSet<SecurityConfig>();
for (Annotation annotation : method.getAnnotations()) {
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) {
// check for Secured annotations
if (annotation instanceof Secured) {
Secured attr = (Secured) annotation;
@@ -0,0 +1,28 @@
package org.acegisecurity.annotation;
/**
*
* @author Joe Scalise
*/
public class BusinessServiceImpl<E extends Entity> implements 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() {
}
public E someUserMethod3(final E entity) {
return entity;
}
}
@@ -0,0 +1,28 @@
package org.acegisecurity.annotation;
/**
*
* @author Joe Scalise
*/
public class Department extends Entity {
//~ Instance fields ========================================================
private boolean active = true;
//~ Constructors ===========================================================
public Department(String name) {
super(name);
}
//~ Methods ================================================================
public boolean isActive() {
return this.active;
}
void deactive() {
this.active = true;
}
}
@@ -0,0 +1,11 @@
package org.acegisecurity.annotation;
/**
*
* @author Joe Scalise
*/
public interface DepartmentService extends BusinessService {
@Secured({"ROLE_USER"})
Department someUserMethod3(Department dept);
}
@@ -0,0 +1,12 @@
package org.acegisecurity.annotation;
/**
* @author Joe Scalise
*/
public class DepartmentServiceImpl extends BusinessServiceImpl <Department> implements DepartmentService {
@Secured({"ROLE_ADMIN"})
public Department someUserMethod3(final Department dept) {
return super.someUserMethod3(dept);
}
}
@@ -0,0 +1,11 @@
package org.acegisecurity.annotation;
/**
* Class to act as a superclass for annotations testing.
*
* @author Ben Alex
*
*/
public class Entity {
public Entity(String someParameter) {}
}
@@ -12,13 +12,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.acegisecurity.annotation;
import junit.framework.TestCase;
import org.acegisecurity.SecurityConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.metadata.Attributes;
import java.lang.reflect.Field;
@@ -31,12 +33,14 @@ import java.util.Collection;
* Tests for {@link org.acegisecurity.annotation.SecurityAnnotationAttributes}
*
* @author Mark St.Godard
* @version $Revision$
* @author Joe Scalise
* @version $Id$
*/
public class SecurityAnnotationAttributesTests extends TestCase {
//~ Instance fields ================================================================================================
private Attributes attributes;
private Log logger = LogFactory.getLog(SecurityAnnotationAttributesTests.class);
//~ Methods ========================================================================================================
@@ -45,6 +49,67 @@ public class SecurityAnnotationAttributesTests extends TestCase {
this.attributes = new SecurityAnnotationAttributes();
}
public void testGenericsSuperclassDeclarationsAreIncludedWhenSubclassesOverride() {
Method method = null;
try {
method = DepartmentServiceImpl.class.getMethod("someUserMethod3", new Class[] {Department.class});
} catch (NoSuchMethodException unexpected) {
fail("Should be a superMethod called 'someUserMethod3' on class!");
}
Collection attrs = this.attributes.getAttributes(method);
if (logger.isDebugEnabled()) {
logger.debug("attrs: ");
logger.debug(attrs);
}
assertNotNull(attrs);
// expect 1 attribute
assertTrue("Did not find 1 attribute", attrs.size() == 1);
// should have 1 SecurityConfig
for (Object obj : attrs) {
assertTrue(obj instanceof SecurityConfig);
SecurityConfig sc = (SecurityConfig) obj;
assertEquals("Found an incorrect role", "ROLE_ADMIN", sc.getAttribute());
}
Method superMethod = null;
try {
superMethod = DepartmentServiceImpl.class.getMethod("someUserMethod3", new Class[] {Entity.class});
} catch (NoSuchMethodException unexpected) {
fail("Should be a superMethod called 'someUserMethod3' on class!");
}
System.out.println(superMethod);
Collection superAttrs = this.attributes.getAttributes(superMethod);
if (logger.isDebugEnabled()) {
logger.debug("superAttrs: ");
logger.debug(superAttrs);
}
assertNotNull(superAttrs);
// TODO: Enable this part of the test once we can build against Spring 2.0+ and above only (SEC-274)
/*
// expect 1 attribute
assertTrue("Did not find 1 attribute", superAttrs.size() == 1);
// should have 1 SecurityConfig
for (Object obj : superAttrs) {
assertTrue(obj instanceof SecurityConfig);
SecurityConfig sc = (SecurityConfig) obj;
assertEquals("Found an incorrect role", "ROLE_ADMIN", sc.getAttribute());
}
*/
}
public void testGetAttributesClass() {
Collection attrs = this.attributes.getAttributes(BusinessService.class);
+2 -5
View File
@@ -20,17 +20,14 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-remoting</artifactId>
<version>1.2.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>1.2.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-support</artifactId>
<version>1.2.7</version>
<scope>runtime</scope>
</dependency>
<dependency>
@@ -96,7 +93,7 @@
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.7.3.0</version>
<version>1.8.0.4</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -152,4 +149,4 @@
</resources>
</build>
</project>
</project>
@@ -1,25 +1,24 @@
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.sid.Sid;
import java.io.Serializable;
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.sid.Sid;
import java.io.Serializable;
/**
* Represents an individual permission assignment within an {@link Acl}.
*
@@ -30,28 +29,28 @@ import java.io.Serializable;
* @author Ben Alex
* @version $Id$
*
*/
public interface AccessControlEntry {
//~ Methods ========================================================================================================
public Acl getAcl();
/**
* Obtains an identifier that represents this ACE.
*
* @return the identifier, or <code>null</code> if unsaved
*/
public Serializable getId();
public Permission getPermission();
public Sid getSid();
/**
* Indicates the a Permission is being granted to the relevant Sid. If false,
* indicates the permission is being revoked/blocked.
*
* @return true if being granted, false otherwise
*/
public boolean isGranting();
}
*/
public interface AccessControlEntry {
//~ Methods ========================================================================================================
public Acl getAcl();
/**
* Obtains an identifier that represents this ACE.
*
* @return the identifier, or <code>null</code> if unsaved
*/
public Serializable getId();
public Permission getPermission();
public Sid getSid();
/**
* Indicates the a Permission is being granted to the relevant Sid. If false, indicates the permission is
* being revoked/blocked.
*
* @return true if being granted, false otherwise
*/
public boolean isGranting();
}
@@ -1,26 +1,25 @@
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.sid.Sid;
import java.io.Serializable;
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.sid.Sid;
import java.io.Serializable;
/**
* Represents an access control list (ACL) for a domain object.
*
@@ -40,105 +39,114 @@ import java.io.Serializable;
*
* @author Ben Alex
* @version $Id$
*/
public interface Acl extends Serializable {
//~ Methods ========================================================================================================
/**
* Returns all of the entries represented by the present <code>Acl</code> (not parents).<p>This method is
* typically used for administrative purposes.</p>
* <p>The order that entries appear in the array is unspecified. However, if implementations use
* particular ordering logic in authorization decisions, the entries returned by this method <em>MUST</em> be
* ordered in that manner.</p>
* <p>Do <em>NOT</em> use this method for making authorization decisions. Instead use {@link
* #isGranted(Permission[], Sid[])}.</p>
* <p>This method must operate correctly even if the <code>Acl</code> only represents a subset of
* <code>Sid</code>s. The caller is responsible for correctly handling the result if only a subset of
* <code>Sid</code>s is represented.</p>
*
* @return the list of entries represented by the <code>Acl</code>
*/
public AccessControlEntry[] getEntries();
/**
* Obtains the domain object this <code>Acl</code> provides entries for. This is immutable once an
* <code>Acl</code> is created.
*
* @return the object identity
*/
public ObjectIdentity getObjectIdentity();
/**
* A domain object may have a parent for the purpose of ACL inheritance. If there is a parent, its ACL can
* be accessed via this method. In turn, the parent's parent (grandparent) can be accessed and so on.<p>This
* method solely represents the presence of a navigation hierarchy between the parent <code>Acl</code> and this
* <code>Acl</code>. For actual inheritance to take place, the {@link #isEntriesInheriting()} must also be
* <code>true</code>.</p>
* <p>This method must operate correctly even if the <code>Acl</code> only represents a subset of
* <code>Sid</code>s. The caller is responsible for correctly handling the result if only a subset of
* <code>Sid</code>s is represented.</p>
*
* @return the parent <code>Acl</code>
*/
public Acl getParentAcl();
/**
* Indicates whether the ACL entries from the {@link #getParentAcl()} should flow down into the current
* <code>Acl</code>.<p>The mere link between an <code>Acl</code> and a parent <code>Acl</code> on its own
* is insufficient to cause ACL entries to inherit down. This is because a domain object may wish to have entirely
* independent entries, but maintain the link with the parent for navigation purposes. Thus, this method denotes
* whether or not the navigation relationship also extends to the actual inheritence of entries.</p>
*
* @return <code>true</code> if parent ACL entries inherit into the current <code>Acl</code>
*/
public boolean isEntriesInheriting();
/**
* This is the actual authorization logic method, and must be used whenever ACL authorization decisions are
* required.<p>An array of <code>Sid</code>s are presented, representing security identifies of the current
* principal. In addition, an array of <code>Permission</code>s is presented which will have one or more bits set
* in order to indicate the permissions needed for an affirmative authorization decision. An array is presented
* because holding <em>any</em> of the <code>Permission</code>s inside the array will be sufficient for an
* affirmative authorization.</p>
* <p>The actual approach used to make authorization decisions is left to the implementation and is not
* specified by this interface. For example, an implementation <em>MAY</em> search the current ACL in the order
* the ACL entries have been stored. If a single entry is found that has the same active bits as are shown in a
* passed <code>Permission</code>, that entry's grant or deny state may determine the authorization decision. If
* the case of a deny state, the deny decision will only be relevant if all other <code>Permission</code>s passed
* in the array have also been unsuccessfully searched. If no entry is found that match the bits in the current
* ACL, provided that {@link #isEntriesInheriting()} is <code>true</code>, the authorization decision may be
* passed to the parent ACL. If there is no matching entry, the implementation MAY throw an exception, or make a
* predefined authorization decision.</p>
* <p>This method must operate correctly even if the <code>Acl</code> only represents a subset of
* <code>Sid</code>s. The caller is responsible for correctly handling the result if only a subset of
* <code>Sid</code>s is represented.</p>
*
* @param permission the permission or permissions required
* @param sids the security identities held by the principal
* @param administrativeMode if <code>true</code> denotes the query is for administrative purposes and no logger or
* auditing (if supported by the implementation) should be undertaken
*
* @return <code>true</code> is authorization is granted
*
* @throws NotFoundException MAY be thrown if an implementation cannot make an authoritative authorization decision
* @throws UnloadedSidException thrown if the <code>Acl</code> does not have details for one or more of the
* <code>Sid</code>s passed as arguments
*/
public boolean isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode)
throws NotFoundException, UnloadedSidException;
/**
* For efficiency reasons an <code>Acl</code> may be loaded and <em>not</em> contain entries for every
* <code>Sid</code> in the system. If an <code>Acl</code> has been loaded and does not represent every
* <code>Sid</code>, all methods of the <code>Sid</code> can only be used within the limited scope of the
* <code>Sid</code> instances it actually represents.<p>It is normal to load an <code>Acl</code> for only
* particular <code>Sid</code>s if read-only authorization decisions are being made. However, if user interface
* reporting or modification of <code>Acl</code>s are desired, an <code>Acl</code> should be loaded with all
* <code>Sid</code>s. This method denotes whether or not the specified <code>Sid</code>s have been loaded or not.</p>
*
* @param sids DOCUMENT ME!
*
* @return <code>true</code> if every passed <code>Sid</code> is represented by this <code>Acl</code> instance
*/
public boolean isSidLoaded(Sid[] sids);
}
*/
public interface Acl extends Serializable {
//~ Methods ========================================================================================================
/**
* Returns all of the entries represented by the present <code>Acl</code> (not parents).<p>This method is
* typically used for administrative purposes.</p>
* <p>The order that entries appear in the array is unspecified. However, if implementations use
* particular ordering logic in authorization decisions, the entries returned by this method <em>MUST</em> be
* ordered in that manner.</p>
* <p>Do <em>NOT</em> use this method for making authorization decisions. Instead use {@link
* #isGranted(Permission[], Sid[])}.</p>
* <p>This method must operate correctly even if the <code>Acl</code> only represents a subset of
* <code>Sid</code>s. The caller is responsible for correctly handling the result if only a subset of
* <code>Sid</code>s is represented.</p>
*
* @return the list of entries represented by the <code>Acl</code>
*/
public AccessControlEntry[] getEntries();
/**
* Obtains the domain object this <code>Acl</code> provides entries for. This is immutable once an
* <code>Acl</code> is created.
*
* @return the object identity
*/
public ObjectIdentity getObjectIdentity();
/**
* Determines the owner of the <code>Acl</code>. The meaning of ownership varies by implementation and is
* unspecified.
*
* @return the owner (may be null if the implementation does not use ownership concepts)
*/
public Sid getOwner();
/**
* A domain object may have a parent for the purpose of ACL inheritance. If there is a parent, its ACL can
* be accessed via this method. In turn, the parent's parent (grandparent) can be accessed and so on.<p>This
* method solely represents the presence of a navigation hierarchy between the parent <code>Acl</code> and this
* <code>Acl</code>. For actual inheritance to take place, the {@link #isEntriesInheriting()} must also be
* <code>true</code>.</p>
* <p>This method must operate correctly even if the <code>Acl</code> only represents a subset of
* <code>Sid</code>s. The caller is responsible for correctly handling the result if only a subset of
* <code>Sid</code>s is represented.</p>
*
* @return the parent <code>Acl</code>
*/
public Acl getParentAcl();
/**
* Indicates whether the ACL entries from the {@link #getParentAcl()} should flow down into the current
* <code>Acl</code>.<p>The mere link between an <code>Acl</code> and a parent <code>Acl</code> on its own
* is insufficient to cause ACL entries to inherit down. This is because a domain object may wish to have entirely
* independent entries, but maintain the link with the parent for navigation purposes. Thus, this method denotes
* whether or not the navigation relationship also extends to the actual inheritence of entries.</p>
*
* @return <code>true</code> if parent ACL entries inherit into the current <code>Acl</code>
*/
public boolean isEntriesInheriting();
/**
* This is the actual authorization logic method, and must be used whenever ACL authorization decisions are
* required.<p>An array of <code>Sid</code>s are presented, representing security identifies of the current
* principal. In addition, an array of <code>Permission</code>s is presented which will have one or more bits set
* in order to indicate the permissions needed for an affirmative authorization decision. An array is presented
* because holding <em>any</em> of the <code>Permission</code>s inside the array will be sufficient for an
* affirmative authorization.</p>
* <p>The actual approach used to make authorization decisions is left to the implementation and is not
* specified by this interface. For example, an implementation <em>MAY</em> search the current ACL in the order
* the ACL entries have been stored. If a single entry is found that has the same active bits as are shown in a
* passed <code>Permission</code>, that entry's grant or deny state may determine the authorization decision. If
* the case of a deny state, the deny decision will only be relevant if all other <code>Permission</code>s passed
* in the array have also been unsuccessfully searched. If no entry is found that match the bits in the current
* ACL, provided that {@link #isEntriesInheriting()} is <code>true</code>, the authorization decision may be
* passed to the parent ACL. If there is no matching entry, the implementation MAY throw an exception, or make a
* predefined authorization decision.</p>
* <p>This method must operate correctly even if the <code>Acl</code> only represents a subset of
* <code>Sid</code>s.</p>
*
* @param permission the permission or permissions required
* @param sids the security identities held by the principal
* @param administrativeMode if <code>true</code> denotes the query is for administrative purposes and no logging
* or auditing (if supported by the implementation) should be undertaken
*
* @return <code>true</code> is authorization is granted
*
* @throws NotFoundException MUST be thrown if an implementation cannot make an authoritative authorization
* decision, usually because there is no ACL information for this particular permission and/or SID
* @throws UnloadedSidException thrown if the <code>Acl</code> does not have details for one or more of the
* <code>Sid</code>s passed as arguments
*/
public boolean isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode)
throws NotFoundException, UnloadedSidException;
/**
* For efficiency reasons an <code>Acl</code> may be loaded and <em>not</em> contain entries for every
* <code>Sid</code> in the system. If an <code>Acl</code> has been loaded and does not represent every
* <code>Sid</code>, all methods of the <code>Sid</code> can only be used within the limited scope of the
* <code>Sid</code> instances it actually represents.<p>It is normal to load an <code>Acl</code> for only
* particular <code>Sid</code>s if read-only authorization decisions are being made. However, if user interface
* reporting or modification of <code>Acl</code>s are desired, an <code>Acl</code> should be loaded with all
* <code>Sid</code>s. This method denotes whether or not the specified <code>Sid</code>s have been loaded or not.</p>
*
* @param sids one or more security identities the caller is interest in knowing whether this <code>Sid</code>
* supports
*
* @return <code>true</code> if every passed <code>Sid</code> is represented by this <code>Acl</code> instance
*/
public boolean isSidLoaded(Sid[] sids);
}
@@ -1,106 +1,105 @@
/* 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.acegisecurity.acls;
import org.springframework.util.Assert;
/**
* Utility methods for displaying ACL information.
*
* @author Ben Alex
* @version $Id$
*/
public class AclFormattingUtils {
//~ Methods ========================================================================================================
public static String demergePatterns(String original, String removeBits) {
Assert.notNull(original, "Original string required");
Assert.notNull(removeBits, "Bits To Remove string required");
Assert.isTrue(original.length() == removeBits.length(),
"Original and Bits To Remove strings must be identical length");
char[] replacement = new char[original.length()];
for (int i = 0; i < original.length(); i++) {
if (removeBits.charAt(i) == Permission.RESERVED_OFF) {
replacement[i] = original.charAt(i);
} else {
replacement[i] = Permission.RESERVED_OFF;
}
}
return new String(replacement);
}
public static String mergePatterns(String original, String extraBits) {
Assert.notNull(original, "Original string required");
Assert.notNull(extraBits, "Extra Bits string required");
Assert.isTrue(original.length() == extraBits.length(),
"Original and Extra Bits strings must be identical length");
char[] replacement = new char[extraBits.length()];
for (int i = 0; i < extraBits.length(); i++) {
if (extraBits.charAt(i) == Permission.RESERVED_OFF) {
replacement[i] = original.charAt(i);
} else {
replacement[i] = extraBits.charAt(i);
}
}
return new String(replacement);
}
private static String printBinary(int i, char on, char off) {
String s = Integer.toString(i, 2);
String pattern = Permission.THIRTY_TWO_RESERVED_OFF;
String temp2 = pattern.substring(0, pattern.length() - s.length()) + s;
return temp2.replace('0', off).replace('1', on);
}
/**
* Returns a representation of the active bits in the presented mask, with each active bit being denoted by
* character "".<p>Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.</p>
*
* @param i the integer bit mask to print the active bits for
*
* @return a 32-character representation of the bit mask
*/
public static String printBinary(int i) {
return AclFormattingUtils.printBinary(i, '*', Permission.RESERVED_OFF);
}
/**
* Returns a representation of the active bits in the presented mask, with each active bit being denoted by
* the passed character.<p>Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.</p>
*
* @param mask the integer bit mask to print the active bits for
* @param code the character to print when an active bit is detected
*
* @return a 32-character representation of the bit mask
*/
public static String printBinary(int mask, char code) {
Assert.doesNotContain(new Character(code).toString(), new Character(Permission.RESERVED_ON).toString(),
Permission.RESERVED_ON + " is a reserved character code");
Assert.doesNotContain(new Character(code).toString(), new Character(Permission.RESERVED_OFF).toString(),
Permission.RESERVED_OFF + " is a reserved character code");
return AclFormattingUtils.printBinary(mask, Permission.RESERVED_ON, Permission.RESERVED_OFF)
.replace(Permission.RESERVED_ON, code);
}
}
/* 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.acegisecurity.acls;
import org.springframework.util.Assert;
/**
* Utility methods for displaying ACL information.
*
* @author Ben Alex
* @version $Id$
*/
public class AclFormattingUtils {
//~ Methods ========================================================================================================
public static String demergePatterns(String original, String removeBits) {
Assert.notNull(original, "Original string required");
Assert.notNull(removeBits, "Bits To Remove string required");
Assert.isTrue(original.length() == removeBits.length(),
"Original and Bits To Remove strings must be identical length");
char[] replacement = new char[original.length()];
for (int i = 0; i < original.length(); i++) {
if (removeBits.charAt(i) == Permission.RESERVED_OFF) {
replacement[i] = original.charAt(i);
} else {
replacement[i] = Permission.RESERVED_OFF;
}
}
return new String(replacement);
}
public static String mergePatterns(String original, String extraBits) {
Assert.notNull(original, "Original string required");
Assert.notNull(extraBits, "Extra Bits string required");
Assert.isTrue(original.length() == extraBits.length(),
"Original and Extra Bits strings must be identical length");
char[] replacement = new char[extraBits.length()];
for (int i = 0; i < extraBits.length(); i++) {
if (extraBits.charAt(i) == Permission.RESERVED_OFF) {
replacement[i] = original.charAt(i);
} else {
replacement[i] = extraBits.charAt(i);
}
}
return new String(replacement);
}
private static String printBinary(int i, char on, char off) {
String s = Integer.toString(i, 2);
String pattern = Permission.THIRTY_TWO_RESERVED_OFF;
String temp2 = pattern.substring(0, pattern.length() - s.length()) + s;
return temp2.replace('0', off).replace('1', on);
}
/**
* Returns a representation of the active bits in the presented mask, with each active bit being denoted by
* character "".<p>Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.</p>
*
* @param i the integer bit mask to print the active bits for
*
* @return a 32-character representation of the bit mask
*/
public static String printBinary(int i) {
return AclFormattingUtils.printBinary(i, '*', Permission.RESERVED_OFF);
}
/**
* Returns a representation of the active bits in the presented mask, with each active bit being denoted by
* the passed character.<p>Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.</p>
*
* @param mask the integer bit mask to print the active bits for
* @param code the character to print when an active bit is detected
*
* @return a 32-character representation of the bit mask
*/
public static String printBinary(int mask, char code) {
Assert.doesNotContain(new Character(code).toString(), new Character(Permission.RESERVED_ON).toString(),
Permission.RESERVED_ON + " is a reserved character code");
Assert.doesNotContain(new Character(code).toString(), new Character(Permission.RESERVED_OFF).toString(),
Permission.RESERVED_OFF + " is a reserved character code");
return AclFormattingUtils.printBinary(mask, Permission.RESERVED_ON, Permission.RESERVED_OFF)
.replace(Permission.RESERVED_ON, code);
}
}
@@ -1,66 +1,100 @@
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.sid.Sid;
import java.util.Map;
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.sid.Sid;
import java.util.Map;
/**
* Provides retrieval of {@link Acl} instances.
*
* @author Ben Alex
* @version $Id$
*/
public interface AclService {
//~ Methods ========================================================================================================
/**
* Obtains all the <code>Acl</code>s that apply for the passed <code>Object</code>s.<p>The returned map is
* keyed on the passed objects, with the values being the <code>Acl</code> instances. Any unknown objects will not
* have a map key.</p>
*
* @param objects the objects to find ACL information for
*
* @return a map with zero or more elements (never <code>null</code>)
*
* @throws NotFoundException DOCUMENT ME!
*/
public Map readAclsById(ObjectIdentity[] objects) throws NotFoundException;
/**
* Obtains all the <code>Acl</code>s that apply for the passed <code>Object</code>s, but only for the
* security identifies passed.<p>Implementations <em>MAY</em> provide a subset of the ACLs via this method
* although this is NOT a requirement. This is intended to allow performance optimisations within implementations.
* Callers should therefore use this method in preference to the alternative overloaded version which does not
* have performance optimisation opportunities.</p>
* <p>The returned map is keyed on the passed objects, with the values being the <code>Acl</code>
* instances. Any unknown objects (or objects for which the interested <code>Sid</code>s do not have entries) will
* not have a map key.</p>
*
* @param objects the objects to find ACL information for
* @param sids the security identities for which ACL information is required (may be <code>null</code> to denote
* all entries)
*
* @return a map with zero or more elements (never <code>null</code>)
*
* @throws NotFoundException DOCUMENT ME!
*/
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids)
throws NotFoundException;
}
*/
public interface AclService {
//~ Methods ========================================================================================================
/**
* Locates all object identities that use the specified parent. This is useful for administration tools.
*
* @param parentIdentity to locate children of
*
* @return the children (or <code>null</code> if none were found)
*/
public ObjectIdentity[] findChildren(ObjectIdentity parentIdentity);
/**
* Same as {@link #readAclsById(ObjectIdentity[])} except it returns only a single Acl.<p>This method
* should not be called as it does not leverage the underlaying implementation's potential ability to filter
* <code>Acl</code> entries based on a {@link Sid} parameter.</p>
*
* @param object DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws NotFoundException DOCUMENT ME!
*/
public Acl readAclById(ObjectIdentity object) throws NotFoundException;
/**
* Same as {@link #readAclsById(ObjectIdentity[], Sid[])} except it returns only a single Acl.
*
* @param object DOCUMENT ME!
* @param sids DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws NotFoundException DOCUMENT ME!
*/
public Acl readAclById(ObjectIdentity object, Sid[] sids)
throws NotFoundException;
/**
* Obtains all the <code>Acl</code>s that apply for the passed <code>Object</code>s.<p>The returned map is
* keyed on the passed objects, with the values being the <code>Acl</code> instances. Any unknown objects will not
* have a map key.</p>
*
* @param objects the objects to find ACL information for
*
* @return a map with zero or more elements (never <code>null</code>)
*
* @throws NotFoundException DOCUMENT ME!
*/
public Map readAclsById(ObjectIdentity[] objects) throws NotFoundException;
/**
* Obtains all the <code>Acl</code>s that apply for the passed <code>Object</code>s, but only for the
* security identifies passed.<p>Implementations <em>MAY</em> provide a subset of the ACLs via this method
* although this is NOT a requirement. This is intended to allow performance optimisations within implementations.
* Callers should therefore use this method in preference to the alternative overloaded version which does not
* have performance optimisation opportunities.</p>
* <p>The returned map is keyed on the passed objects, with the values being the <code>Acl</code>
* instances. Any unknown objects (or objects for which the interested <code>Sid</code>s do not have entries) will
* not have a map key.</p>
*
* @param objects the objects to find ACL information for
* @param sids the security identities for which ACL information is required (may be <code>null</code> to denote
* all entries)
*
* @return a map with zero or more elements (never <code>null</code>)
*
* @throws NotFoundException DOCUMENT ME!
*/
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids)
throws NotFoundException;
}
@@ -1,49 +1,48 @@
/* 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.acegisecurity.acls;
import org.acegisecurity.AcegiSecurityException;
/**
* Thrown if an <code>Acl</code> entry already exists for the object.
*
* @author Ben Alex
* @version $Id$
*/
public class AlreadyExistsException extends AcegiSecurityException {
//~ Constructors ===================================================================================================
/* 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.acegisecurity.acls;
import org.acegisecurity.AcegiSecurityException;
/**
* Thrown if an <code>Acl</code> entry already exists for the object.
*
* @author Ben Alex
* @version $Id$
*/
public class AlreadyExistsException extends AcegiSecurityException {
//~ Constructors ===================================================================================================
/**
* Constructs an <code>AlreadyExistsException</code> with the specified message.
*
* @param msg the detail message
*/
public AlreadyExistsException(String msg) {
super(msg);
}
*/
public AlreadyExistsException(String msg) {
super(msg);
}
/**
* Constructs an <code>AlreadyExistsException</code> with the specified message
* and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public AlreadyExistsException(String msg, Throwable t) {
super(msg, t);
}
}
*/
public AlreadyExistsException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -1,31 +1,30 @@
/* 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.acegisecurity.acls;
/* 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.acegisecurity.acls;
/**
* Represents an ACE that provides auditing information.
*
* @author Ben Alex
* @version $Id$
*
*/
public interface AuditableAccessControlEntry extends AccessControlEntry {
//~ Methods ========================================================================================================
public boolean isAuditFailure();
public boolean isAuditSuccess();
}
*/
public interface AuditableAccessControlEntry extends AccessControlEntry {
//~ Methods ========================================================================================================
public boolean isAuditFailure();
public boolean isAuditSuccess();
}
@@ -1,29 +1,31 @@
/* 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.acegisecurity.acls;
/* 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.acegisecurity.acls;
import java.io.Serializable;
/**
* A mutable ACL that provides audit capabilities.
*
* @author Ben Alex
* @version $Id$
*
*/
public interface AuditableAcl extends MutableAcl {
//~ Methods ========================================================================================================
public void updateAuditing(Long aceId, boolean auditSuccess, boolean auditFailure);
}
*/
public interface AuditableAcl extends MutableAcl {
//~ Methods ========================================================================================================
public void updateAuditing(Serializable aceId, boolean auditSuccess, boolean auditFailure);
}
@@ -1,50 +1,49 @@
/* 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.acegisecurity.acls;
import org.acegisecurity.AcegiSecurityException;
/**
* Thrown if an {@link Acl} cannot be deleted because children <code>Acl</code>s exist.
*
* @author Ben Alex
* @version $Id$
*/
public class ChildrenExistException extends AcegiSecurityException {
//~ Constructors ===================================================================================================
/* 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.acegisecurity.acls;
import org.acegisecurity.AcegiSecurityException;
/**
* Thrown if an {@link Acl} cannot be deleted because children <code>Acl</code>s exist.
*
* @author Ben Alex
* @version $Id$
*/
public class ChildrenExistException extends AcegiSecurityException {
//~ Constructors ===================================================================================================
/**
* Constructs an <code>ChildrenExistException</code> with the specified
* message.
*
* @param msg the detail message
*/
public ChildrenExistException(String msg) {
super(msg);
}
*/
public ChildrenExistException(String msg) {
super(msg);
}
/**
* Constructs an <code>ChildrenExistException</code> with the specified
* message and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public ChildrenExistException(String msg, Throwable t) {
super(msg, t);
}
}
*/
public ChildrenExistException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -1,49 +1,48 @@
/* 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.acegisecurity.acls;
import org.acegisecurity.AcegiSecurityException;
/**
* Thrown if an ACL identity could not be extracted from an object.
*
* @author Ben Alex
* @version $Id$
*/
public class IdentityUnavailableException extends AcegiSecurityException {
//~ Constructors ===================================================================================================
/* 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.acegisecurity.acls;
import org.acegisecurity.AcegiSecurityException;
/**
* Thrown if an ACL identity could not be extracted from an object.
*
* @author Ben Alex
* @version $Id$
*/
public class IdentityUnavailableException extends AcegiSecurityException {
//~ Constructors ===================================================================================================
/**
* Constructs an <code>IdentityUnavailableException</code> with the specified message.
*
* @param msg the detail message
*/
public IdentityUnavailableException(String msg) {
super(msg);
}
*/
public IdentityUnavailableException(String msg) {
super(msg);
}
/**
* Constructs an <code>IdentityUnavailableException</code> with the specified message
* and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public IdentityUnavailableException(String msg, Throwable t) {
super(msg, t);
}
}
*/
public IdentityUnavailableException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -1,25 +1,24 @@
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.sid.Sid;
import java.io.Serializable;
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.sid.Sid;
import java.io.Serializable;
/**
* A mutable <code>Acl</code>.
*
@@ -30,31 +29,47 @@ import java.io.Serializable;
*
* @author Ben Alex
* @version $Id$
*/
public interface MutableAcl extends Acl {
//~ Methods ========================================================================================================
public void deleteAce(Long aceId) throws NotFoundException;
/**
* Obtains an identifier that represents this <code>MutableAcl</code>.
*
* @return the identifier, or <code>null</code> if unsaved
*/
public Serializable getId();
public void insertAce(Long afterAceId, Permission permission, Sid sid, boolean granting)
throws NotFoundException;
public void setEntriesInheriting(boolean entriesInheriting);
/**
* Changes the parent of this ACL.
*
* @param newParent the new parent
*/
public void setParent(MutableAcl newParent);
public void updateAce(Long aceId, Permission permission)
throws NotFoundException;
}
*/
public interface MutableAcl extends Acl {
//~ Methods ========================================================================================================
public void deleteAce(Serializable aceId) throws NotFoundException;
/**
* Retrieves all of the non-deleted {@link AccessControlEntry} instances currently stored by the
* <code>MutableAcl</code>. The returned objects should be immutable outside the package, and therefore it is safe
* to return them to the caller for informational purposes. The <code>AccessControlEntry</code> information is
* needed so that invocations of update and delete methods on the <code>MutableAcl</code> can refer to a valid
* {@link AccessControlEntry#getId()}.
*
* @return DOCUMENT ME!
*/
public AccessControlEntry[] getEntries();
/**
* Obtains an identifier that represents this <code>MutableAcl</code>.
*
* @return the identifier, or <code>null</code> if unsaved
*/
public Serializable getId();
public void insertAce(Serializable afterAceId, Permission permission, Sid sid, boolean granting)
throws NotFoundException;
/**
* Change the value returned by {@link Acl#isEntriesInheriting()}.
*
* @param entriesInheriting the new value
*/
public void setEntriesInheriting(boolean entriesInheriting);
/**
* Changes the parent of this ACL.
*
* @param newParent the new parent
*/
public void setParent(MutableAcl newParent);
public void updateAce(Serializable aceId, Permission permission)
throws NotFoundException;
}
@@ -1,74 +1,65 @@
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
/**
* Provides support for creating and storing <code>Acl</code> instances.
*
* @author Ben Alex
* @version $Id$
*/
public interface MutableAclService extends AclService {
//~ Methods ========================================================================================================
/**
* Creates an empty <code>Acl</code> object in the database. It will have no entries. The returned object
* will then be used to add entries.
*
* @param object the object identity to create
*
* @return an ACL object with its ID set
*
* @throws AlreadyExistsException if the passed object identity already has a record
*/
public MutableAcl createAcl(ObjectIdentity object)
throws AlreadyExistsException;
/**
* Removes the specified entry from the database.
*
* @param object the object identity to remove
* @param deleteChildren whether to cascade the delete to children
*
* @throws ChildrenExistException if the deleteChildren argument was <code>false</code> but children exist
*/
public void deleteAcl(ObjectIdentity object, boolean deleteChildren)
throws ChildrenExistException;
/**
* Locates all object identities that use the specified parent. This is useful for administration tools,
* and before issuing a {@link #deleteAcl(ObjectIdentity, boolean)}.
*
* @param parentIdentity to locate children of
*
* @return the children (or <code>null</code> if none were found)
*/
public ObjectIdentity[] findChildren(ObjectIdentity parentIdentity);
/**
* Changes an existing <code>Acl</code> in the database.
*
* @param acl to modify
*
* @throws NotFoundException if the relevant record could not be found (did you remember to use {@link
* #createAcl(ObjectIdentity)} to create the object, rather than creating it with the <code>new</code>
* keyword?)
*/
public void updateAcl(MutableAcl acl) throws NotFoundException;
}
*/
public interface MutableAclService extends AclService {
//~ Methods ========================================================================================================
/**
* Creates an empty <code>Acl</code> object in the database. It will have no entries. The returned object
* will then be used to add entries.
*
* @param objectIdentity the object identity to create
*
* @return an ACL object with its ID set
*
* @throws AlreadyExistsException if the passed object identity already has a record
*/
public MutableAcl createAcl(ObjectIdentity objectIdentity)
throws AlreadyExistsException;
/**
* Removes the specified entry from the database.
*
* @param objectIdentity the object identity to remove
* @param deleteChildren whether to cascade the delete to children
*
* @throws ChildrenExistException if the deleteChildren argument was <code>false</code> but children exist
*/
public void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren)
throws ChildrenExistException;
/**
* Changes an existing <code>Acl</code> in the database.
*
* @param acl to modify
*
* @return DOCUMENT ME!
*
* @throws NotFoundException if the relevant record could not be found (did you remember to use {@link
* #createAcl(ObjectIdentity)} to create the object, rather than creating it with the <code>new</code>
* keyword?)
*/
public MutableAcl updateAcl(MutableAcl acl) throws NotFoundException;
}
@@ -1,49 +1,48 @@
/* 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.acegisecurity.acls;
import org.acegisecurity.AcegiSecurityException;
/**
* Thrown if an ACL-related object cannot be found.
*
* @author Ben Alex
* @version $Id$
*/
public class NotFoundException extends AcegiSecurityException {
//~ Constructors ===================================================================================================
/* 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.acegisecurity.acls;
import org.acegisecurity.AcegiSecurityException;
/**
* Thrown if an ACL-related object cannot be found.
*
* @author Ben Alex
* @version $Id$
*/
public class NotFoundException extends AcegiSecurityException {
//~ Constructors ===================================================================================================
/**
* Constructs an <code>NotFoundException</code> with the specified message.
*
* @param msg the detail message
*/
public NotFoundException(String msg) {
super(msg);
}
*/
public NotFoundException(String msg) {
super(msg);
}
/**
* Constructs an <code>NotFoundException</code> with the specified message
* and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public NotFoundException(String msg, Throwable t) {
super(msg, t);
}
}
*/
public NotFoundException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -1,23 +1,22 @@
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.sid.Sid;
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.sid.Sid;
/**
* A mutable ACL that provides ownership capabilities.
*
@@ -28,11 +27,9 @@ import org.acegisecurity.acls.sid.Sid;
*
* @author Ben Alex
* @version $Id$
*/
public interface OwnershipAcl extends MutableAcl {
//~ Methods ========================================================================================================
public Sid getOwner();
public void setOwner(Sid newOwner);
}
*/
public interface OwnershipAcl extends MutableAcl {
//~ Methods ========================================================================================================
public void setOwner(Sid newOwner);
}
@@ -1,57 +1,56 @@
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.sid.Sid;
/* 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.acegisecurity.acls;
import org.acegisecurity.acls.sid.Sid;
/**
* Represents a permission granted to a {@link Sid} for a given domain object.
*
* @author Ben Alex
* @version $Id$
*/
public interface Permission {
//~ Static fields/initializers =====================================================================================
public static final char RESERVED_ON = '~';
public static final char RESERVED_OFF = '.';
public static final String THIRTY_TWO_RESERVED_OFF = "................................";
//~ Methods ========================================================================================================
/**
* Returns the bits that represents the permission.
*
* @return the bits that represent the permission
*/
public int getMask();
/**
* Returns a 32-character long bit pattern <code>String</code> representing this permission.<p>Implementations
* are free to format the pattern as they see fit, although under no circumstances may {@link #RESERVED_OFF} or
* {@link #RESERVED_ON} be used within the pattern. An exemption is in the case of {@link #RESERVED_OFF} which is
* used to denote a bit that is off (clear). Implementations may also elect to use {@link #RESERVED_ON} internally
* for computation purposes, although this method may not return any <code>String</code> containing {@link
* #RESERVED_ON}.</p>
* <p>The returned String must be 32 characters in length.</p>
* <p>This method is only used for user interface and logging purposes. It is not used in any permission
* calculations. Therefore, duplication of characters within the output is permitted.</p>
*
* @return a 32-character bit pattern
*/
public String getPattern();
}
*/
public interface Permission {
//~ Static fields/initializers =====================================================================================
public static final char RESERVED_ON = '~';
public static final char RESERVED_OFF = '.';
public static final String THIRTY_TWO_RESERVED_OFF = "................................";
//~ Methods ========================================================================================================
/**
* Returns the bits that represents the permission.
*
* @return the bits that represent the permission
*/
public int getMask();
/**
* Returns a 32-character long bit pattern <code>String</code> representing this permission.<p>Implementations
* are free to format the pattern as they see fit, although under no circumstances may {@link #RESERVED_OFF} or
* {@link #RESERVED_ON} be used within the pattern. An exemption is in the case of {@link #RESERVED_OFF} which is
* used to denote a bit that is off (clear). Implementations may also elect to use {@link #RESERVED_ON} internally
* for computation purposes, although this method may not return any <code>String</code> containing {@link
* #RESERVED_ON}.</p>
* <p>The returned String must be 32 characters in length.</p>
* <p>This method is only used for user interface and logging purposes. It is not used in any permission
* calculations. Therefore, duplication of characters within the output is permitted.</p>
*
* @return a 32-character bit pattern
*/
public String getPattern();
}
@@ -1,50 +1,49 @@
/* 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.acegisecurity.acls;
import org.acegisecurity.AcegiSecurityException;
/**
* Thrown if an {@link Acl} cannot perform an operation because it only loaded a subset of <code>Sid</code>s and
* the caller has requested details for an unloaded <code>Sid</code>.
*
* @author Ben Alex
* @version $Id$
*/
public class UnloadedSidException extends AcegiSecurityException {
//~ Constructors ===================================================================================================
/* 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.acegisecurity.acls;
import org.acegisecurity.AcegiSecurityException;
/**
* Thrown if an {@link Acl} cannot perform an operation because it only loaded a subset of <code>Sid</code>s and
* the caller has requested details for an unloaded <code>Sid</code>.
*
* @author Ben Alex
* @version $Id$
*/
public class UnloadedSidException extends AcegiSecurityException {
//~ Constructors ===================================================================================================
/**
* Constructs an <code>NotFoundException</code> with the specified message.
*
* @param msg the detail message
*/
public UnloadedSidException(String msg) {
super(msg);
}
*/
public UnloadedSidException(String msg) {
super(msg);
}
/**
* Constructs an <code>NotFoundException</code> with the specified message
* and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public UnloadedSidException(String msg, Throwable t) {
super(msg, t);
}
}
*/
public UnloadedSidException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -1,149 +1,133 @@
/* 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.acegisecurity.acls.domain;
import org.acegisecurity.acls.AccessControlEntry;
import org.acegisecurity.acls.Acl;
import org.acegisecurity.acls.AuditableAccessControlEntry;
import org.acegisecurity.acls.Permission;
import org.acegisecurity.acls.sid.Sid;
import org.springframework.util.Assert;
import java.io.Serializable;
/**
* An immutable default implementation of <code>AccessControlEntry</code>.
*
* @author Ben Alex
* @version $Id$
*/
public class AccessControlEntryImpl implements AccessControlEntry, AuditableAccessControlEntry {
//~ Instance fields ================================================================================================
private Acl acl;
private Permission permission;
private Serializable id;
private Sid sid;
private boolean aceDirty = false;
private boolean auditFailure = false;
private boolean auditSuccess = false;
private boolean granting;
//~ Constructors ===================================================================================================
public AccessControlEntryImpl(Serializable id, Acl acl, Sid sid, Permission permission, boolean granting,
boolean auditSuccess, boolean auditFailure) {
Assert.notNull(acl, "Acl required");
Assert.notNull(sid, "Sid required");
Assert.notNull(permission, "Permission required");
this.id = id;
this.acl = acl; // can be null
this.sid = sid;
this.permission = permission;
this.granting = granting;
this.auditSuccess = auditSuccess;
this.auditFailure = auditFailure;
}
//~ Methods ========================================================================================================
public void clearDirtyFlags() {
this.aceDirty = false;
}
public boolean equals(Object arg0) {
if (!(arg0 instanceof AccessControlEntryImpl)) {
return false;
}
AccessControlEntryImpl rhs = (AccessControlEntryImpl) arg0;
if ((this.aceDirty != rhs.isAceDirty()) || (this.auditFailure != rhs.isAuditFailure())
|| (this.auditSuccess != rhs.isAuditSuccess()) || (this.granting != rhs.isGranting())
|| !this.acl.equals(rhs.getAcl()) || !this.id.equals(rhs.getId())
|| !this.permission.equals(rhs.getPermission()) || !this.sid.equals(rhs.getSid())) {
return false;
}
return true;
}
public Acl getAcl() {
return acl;
}
public Serializable getId() {
return id;
}
public Permission getPermission() {
return permission;
}
public Sid getSid() {
return sid;
}
public boolean isAceDirty() {
return aceDirty;
}
public boolean isAuditFailure() {
return auditFailure;
}
public boolean isAuditSuccess() {
return auditSuccess;
}
public boolean isGranting() {
return granting;
}
void setAuditFailure(boolean auditFailure) {
this.auditFailure = auditFailure;
this.aceDirty = true;
}
void setAuditSuccess(boolean auditSuccess) {
this.auditSuccess = auditSuccess;
this.aceDirty = true;
}
void setId(Serializable id) {
this.id = id;
}
void setPermission(Permission permission) {
Assert.notNull(permission, "Permission required");
this.permission = permission;
this.aceDirty = true;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("AccessControlEntryImpl[");
sb.append("id: ").append(this.id).append("; ");
sb.append("granting: ").append(this.granting).append("; ");
sb.append("sid: ").append(this.sid).append("; ");
sb.append("permission: ").append(this.permission);
sb.append("]");
return sb.toString();
}
}
/* 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.acegisecurity.acls.domain;
import org.acegisecurity.acls.AccessControlEntry;
import org.acegisecurity.acls.Acl;
import org.acegisecurity.acls.AuditableAccessControlEntry;
import org.acegisecurity.acls.Permission;
import org.acegisecurity.acls.sid.Sid;
import org.springframework.util.Assert;
import java.io.Serializable;
/**
* An immutable default implementation of <code>AccessControlEntry</code>.
*
* @author Ben Alex
* @version $Id$
*/
public class AccessControlEntryImpl implements AccessControlEntry, AuditableAccessControlEntry {
//~ Instance fields ================================================================================================
private Acl acl;
private Permission permission;
private Serializable id;
private Sid sid;
private boolean auditFailure = false;
private boolean auditSuccess = false;
private boolean granting;
//~ Constructors ===================================================================================================
public AccessControlEntryImpl(Serializable id, Acl acl, Sid sid, Permission permission, boolean granting,
boolean auditSuccess, boolean auditFailure) {
Assert.notNull(acl, "Acl required");
Assert.notNull(sid, "Sid required");
Assert.notNull(permission, "Permission required");
this.id = id;
this.acl = acl; // can be null
this.sid = sid;
this.permission = permission;
this.granting = granting;
this.auditSuccess = auditSuccess;
this.auditFailure = auditFailure;
}
//~ Methods ========================================================================================================
public boolean equals(Object arg0) {
if (!(arg0 instanceof AccessControlEntryImpl)) {
return false;
}
AccessControlEntryImpl rhs = (AccessControlEntryImpl) arg0;
if ((this.auditFailure != rhs.isAuditFailure()) || (this.auditSuccess != rhs.isAuditSuccess())
|| (this.granting != rhs.isGranting()) || !this.acl.equals(rhs.getAcl()) || !this.id.equals(rhs.getId())
|| !this.permission.equals(rhs.getPermission()) || !this.sid.equals(rhs.getSid())) {
return false;
}
return true;
}
public Acl getAcl() {
return acl;
}
public Serializable getId() {
return id;
}
public Permission getPermission() {
return permission;
}
public Sid getSid() {
return sid;
}
public boolean isAuditFailure() {
return auditFailure;
}
public boolean isAuditSuccess() {
return auditSuccess;
}
public boolean isGranting() {
return granting;
}
void setAuditFailure(boolean auditFailure) {
this.auditFailure = auditFailure;
}
void setAuditSuccess(boolean auditSuccess) {
this.auditSuccess = auditSuccess;
}
void setPermission(Permission permission) {
Assert.notNull(permission, "Permission required");
this.permission = permission;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("AccessControlEntryImpl[");
sb.append("id: ").append(this.id).append("; ");
sb.append("granting: ").append(this.granting).append("; ");
sb.append("sid: ").append(this.sid).append("; ");
sb.append("permission: ").append(this.permission).append("; ");
sb.append("auditSuccess: ").append(this.auditSuccess).append("; ");
sb.append("auditFailure: ").append(this.auditFailure);
sb.append("]");
return sb.toString();
}
}
@@ -13,39 +13,26 @@
* limitations under the License.
*/
package org.acegisecurity.domain.impl;
package org.acegisecurity.acls.domain;
import java.io.Serializable;
import org.acegisecurity.acls.Acl;
/**
* A persistable entity that uses a <code>Long</code> based identity.
*
* Strategy used by {@link AclImpl} to determine whether a principal is permitted to call
* adminstrative methods on the <code>AclImpl</code>.
*
* @author Ben Alex
* @version $Id$
*/
public abstract class PersistableEntityLong extends AbstractPersistableEntity {
//~ Instance fields ================================================================================================
public interface AclAuthorizationStrategy {
//~ Static fields/initializers =====================================================================================
private Long id;
public static final int CHANGE_OWNERSHIP = 0;
public static final int CHANGE_AUDITING = 1;
public static final int CHANGE_GENERAL = 2;
//~ Methods ========================================================================================================
/**
* Obtains the persistence identity of this instance.
*
* @return DOCUMENT ME!
*/
public Long getId() {
return this.id;
}
/**
* Required solely because Hibernate
*
* @return DOCUMENT ME!
*/
public Serializable getInternalId() {
return this.id;
}
public void securityCheck(Acl acl, int changeType);
}
@@ -0,0 +1,125 @@
/* 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.acegisecurity.acls.domain;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.acls.Acl;
import org.acegisecurity.acls.Permission;
import org.acegisecurity.acls.sid.PrincipalSid;
import org.acegisecurity.acls.sid.Sid;
import org.acegisecurity.acls.sid.SidRetrievalStrategy;
import org.acegisecurity.acls.sid.SidRetrievalStrategyImpl;
import org.acegisecurity.context.SecurityContextHolder;
import org.springframework.util.Assert;
/**
* Default implementation of {@link AclAuthorizationStrategy}.<p>Permission will be granted provided the current
* principal is either the owner (as defined by the ACL), has {@link BasePermission#ADMINISTRATION} (as defined by the
* ACL and via a {@link Sid} retrieved for the current principal via {@link #sidRetrievalStrategy}), or if the current
* principal holds the relevant system-wide {@link GrantedAuthority} and injected into the constructor.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
//~ Instance fields ================================================================================================
private GrantedAuthority gaGeneralChanges;
private GrantedAuthority gaModifyAuditing;
private GrantedAuthority gaTakeOwnership;
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
//~ Constructors ===================================================================================================
/**
* Constructor. The only mandatory parameter relates to the system-wide {@link GrantedAuthority} instances that
* can be held to always permit ACL changes.
*
* @param auths an array of <code>GrantedAuthority</code>s that have
* special permissions (index 0 is the authority needed to change
* ownership, index 1 is the authority needed to modify auditing details,
* index 2 is the authority needed to change other ACL and ACE details) (required)
*/
public AclAuthorizationStrategyImpl(GrantedAuthority[] auths) {
Assert.notEmpty(auths, "GrantedAuthority[] with three elements required");
Assert.isTrue(auths.length == 3, "GrantedAuthority[] with three elements required");
this.gaTakeOwnership = auths[0];
this.gaModifyAuditing = auths[1];
this.gaGeneralChanges = auths[2];
}
//~ Methods ========================================================================================================
public void securityCheck(Acl acl, int changeType) {
if ((SecurityContextHolder.getContext() == null)
|| (SecurityContextHolder.getContext().getAuthentication() == null)
|| !SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) {
throw new AccessDeniedException("Authenticated principal required to operate with ACLs");
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// Check if authorized by virtue of ACL ownership
Sid currentUser = new PrincipalSid(authentication);
if (currentUser.equals(acl.getOwner()) && ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) {
return;
}
// Not authorized by ACL ownership; try via adminstrative permissions
GrantedAuthority requiredAuthority = null;
if (changeType == CHANGE_AUDITING) {
requiredAuthority = this.gaModifyAuditing;
} else if (changeType == CHANGE_GENERAL) {
requiredAuthority = this.gaGeneralChanges;
} else if (changeType == CHANGE_OWNERSHIP) {
requiredAuthority = this.gaTakeOwnership;
} else {
throw new IllegalArgumentException("Unknown change type");
}
// Iterate this principal's authorities to determine right
GrantedAuthority[] auths = authentication.getAuthorities();
for (int i = 0; i < auths.length; i++) {
if (requiredAuthority.equals(auths[i])) {
return;
}
}
// Try to get permission via ACEs within the ACL
Sid[] sids = sidRetrievalStrategy.getSids(authentication);
if (acl.isGranted(new Permission[] {BasePermission.ADMINISTRATION}, sids, false)) {
return;
}
throw new AccessDeniedException(
"Principal does not have required ACL permissions to perform requested operation");
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
}
@@ -12,13 +12,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.acegisecurity.acls.domain;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.acls.AccessControlEntry;
import org.acegisecurity.acls.Acl;
import org.acegisecurity.acls.AuditableAcl;
@@ -28,11 +23,8 @@ import org.acegisecurity.acls.OwnershipAcl;
import org.acegisecurity.acls.Permission;
import org.acegisecurity.acls.UnloadedSidException;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.sid.PrincipalSid;
import org.acegisecurity.acls.sid.Sid;
import org.acegisecurity.context.SecurityContextHolder;
import org.springframework.util.Assert;
import java.io.Serializable;
@@ -49,29 +41,17 @@ import java.util.Vector;
* @version $Id
*/
public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
//~ Static fields/initializers =====================================================================================
private static final int CHANGE_OWNERSHIP = 0;
private static final int CHANGE_AUDITING = 1;
private static final int CHANGE_GENERAL = 2;
//~ Instance fields ================================================================================================
private Acl parentAcl;
private AuditLogger auditLogger = new ConsoleAuditLogger(); // AuditableAcl
private GrantedAuthority gaGeneralChanges;
private GrantedAuthority gaModifyAuditing;
private GrantedAuthority gaTakeOwnership;
private AclAuthorizationStrategy aclAuthorizationStrategy;
private AuditLogger auditLogger;
private List aces = new Vector();
private List deletedAces = new Vector();
private Long id;
private ObjectIdentity objectIdentity;
private Serializable id;
private Sid owner; // OwnershipAcl
private Sid[] loadedSids = null; // includes all SIDs the WHERE clause covered, even if there was no ACE for a SID
private boolean aclDirty = false; // for snapshot detection
private boolean addedAces = false; // for snapshot detection
private boolean entriesInheriting = false;
private boolean updatedAces = false; // for snapshot detection
private boolean entriesInheriting = true;
//~ Constructors ===================================================================================================
@@ -81,17 +61,19 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
*
* @param objectIdentity the object identity this ACL relates to (required)
* @param id the primary key assigned to this ACL (required)
* @param auths an array of <code>GrantedAuthority</code>s that have
* special permissions (index 0 is the authority needed to change
* ownership, index 1 is the authority needed to modify auditing details,
* index 2 is the authority needed to change other ACL and ACE details) (required)
* @param aclAuthorizationStrategy authorization strategy (required)
* @param auditLogger audit logger (required)
*/
public AclImpl(ObjectIdentity objectIdentity, Long id, GrantedAuthority[] auths) {
public AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy,
AuditLogger auditLogger) {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(id, "Id required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
Assert.notNull(auditLogger, "AuditLogger required");
this.objectIdentity = objectIdentity;
this.id = id;
this.setAuthorities(auths);
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.auditLogger = auditLogger;
}
/**
@@ -100,10 +82,8 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
*
* @param objectIdentity the object identity this ACL relates to (required)
* @param id the primary key assigned to this ACL (required)
* @param auths an array of <code>GrantedAuthority</code>s that have
* special permissions (index 0 is the authority needed to change
* ownership, index 1 is the authority needed to modify auditing details,
* index 2 is the authority needed to change other ACL and ACE details) (required)
* @param aclAuthorizationStrategy authorization strategy (required)
* @param auditLogger audit logger (required)
* @param parentAcl the parent (may be <code>null</code>)
* @param loadedSids the loaded SIDs if only a subset were loaded (may be
* <code>null</code>)
@@ -111,14 +91,17 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
* this ACL
* @param owner the owner (required)
*/
public AclImpl(ObjectIdentity objectIdentity, Long id, Acl parentAcl, GrantedAuthority[] auths, Sid[] loadedSids,
boolean entriesInheriting, Sid owner) {
public AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy,
AuditLogger auditLogger, Acl parentAcl, Sid[] loadedSids, boolean entriesInheriting, Sid owner) {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(id, "Id required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
Assert.notNull(owner, "Owner required");
Assert.notNull(auditLogger, "AuditLogger required");
this.objectIdentity = objectIdentity;
this.id = id;
setAuthorities(auths);
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.auditLogger = auditLogger;
this.parentAcl = parentAcl; // may be null
this.loadedSids = loadedSids; // may be null
this.entriesInheriting = entriesInheriting;
@@ -133,17 +116,8 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
//~ Methods ========================================================================================================
/**
* Clears the dirty flags on the <code>Acl</code>, but not any associated ACEs.
*/
public void clearDirtyFlags() {
this.aclDirty = false;
this.addedAces = false;
this.updatedAces = false;
}
public void deleteAce(Long aceId) throws NotFoundException {
securityCheck(CHANGE_GENERAL);
public void deleteAce(Serializable aceId) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
synchronized (aces) {
int offset = findAceOffset(aceId);
@@ -152,12 +126,11 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
throw new NotFoundException("Requested ACE ID not found");
}
aces.remove(offset);
deletedAces.add(aceId);
this.aces.remove(offset);
}
}
private int findAceOffset(Long aceId) {
private int findAceOffset(Serializable aceId) {
Assert.notNull(aceId, "ACE ID is required");
synchronized (aces) {
@@ -174,7 +147,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
}
public AccessControlEntry[] getEntries() {
// Can safely return AccessControlEntry directly, as they're immutable
// Can safely return AccessControlEntry directly, as they're immutable outside the ACL package
return (AccessControlEntry[]) aces.toArray(new AccessControlEntry[] {});
}
@@ -194,9 +167,9 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
return parentAcl;
}
public void insertAce(Long afterAceId, Permission permission, Sid sid, boolean granting)
public void insertAce(Serializable afterAceId, Permission permission, Sid sid, boolean granting)
throws NotFoundException {
securityCheck(CHANGE_GENERAL);
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.notNull(permission, "Permission required");
Assert.notNull(sid, "Sid required");
@@ -210,17 +183,11 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
throw new NotFoundException("Requested ACE ID not found");
}
aces.add(offset + 1, ace);
this.aces.add(offset + 1, ace);
} else {
aces.add(ace);
this.aces.add(ace);
}
}
this.addedAces = true;
}
public boolean isAclDirty() {
return aclDirty;
}
public boolean isEntriesInheriting() {
@@ -231,18 +198,19 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
* Determines authorization. The order of the <code>permission</code> and <code>sid</code> arguments is
* <em>extremely important</em>! The method will iterate through each of the <code>permission</code>s in the order
* specified. For each iteration, all of the <code>sid</code>s will be considered, again in the order they are
* presented. The iteration of each <code>permission:sid</code> combination will then inspect the ACEs in the
* order they appear in the ACL. When the <em>first full match</em> is found (ie an ACE that has the SID currently
* being searched for and the exact permission bit mask being search for), the grant or deny flag for that ACE
* will prevail. If the ACE specifies to grant access, the method will return <code>true</code>. If the ACE
* specifies to deny access, the loop will stop and the next <code>permission</code> iteration will be performed.
* If each permission indicates to deny access, the first deny ACE found will be considered the reason for the
* failure (as it was the first match found, and is therefore the one most logically requiring changes - although
* not always). If absolutely no matching ACE was found at all for any permission, the parent ACL will be tried
* (provided that there is a parent and {@link #isEntriesInheriting()} is <code>true</code>. The parent ACL will
* also scan its parent and so on. If ultimately no matching ACE is found, a <code>NotFoundException</code> will
* be thrown and the caller will need to decide how to handle the permission check. Similarly, if any of the
* passed SIDs were not loaded by the ACL, the <code>UnloadedSidException</code> will be thrown.
* presented. A search will then be performed for the first {@link AccessControlEntry} object that directly
* matches that <code>permission:sid</code> combination. When the <em>first full match</em> is found (ie an ACE
* that has the SID currently being searched for and the exact permission bit mask being search for), the grant or
* deny flag for that ACE will prevail. If the ACE specifies to grant access, the method will return
* <code>true</code>. If the ACE specifies to deny access, the loop will stop and the next <code>permission</code>
* iteration will be performed. If each permission indicates to deny access, the first deny ACE found will be
* considered the reason for the failure (as it was the first match found, and is therefore the one most logically
* requiring changes - although not always). If absolutely no matching ACE was found at all for any permission,
* the parent ACL will be tried (provided that there is a parent and {@link #isEntriesInheriting()} is
* <code>true</code>. The parent ACL will also scan its parent and so on. If ultimately no matching ACE is found,
* a <code>NotFoundException</code> will be thrown and the caller will need to decide how to handle the permission
* check. Similarly, if any of the SID arguments presented to the method were not loaded by the ACL,
* <code>UnloadedSidException</code> will be thrown.
*
* @param permission the exact permissions to scan for (order is important)
* @param sids the exact SIDs to scan for (order is important)
@@ -335,7 +303,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
return true;
}
// This ACL applies to a SID subset. Iterate to check it applies
// This ACL applies to a SID subset only. Iterate to check it applies.
for (int i = 0; i < sids.length; i++) {
boolean found = false;
@@ -356,83 +324,22 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
return true;
}
protected void securityCheck(int changeType) {
if ((SecurityContextHolder.getContext() == null)
|| (SecurityContextHolder.getContext().getAuthentication() == null)
|| !SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) {
throw new AccessDeniedException("Authenticated principal required to operate with ACLs");
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// Check if authorized by virtue of ACL ownership
Sid currentUser = new PrincipalSid(authentication);
if (currentUser.equals(this.owner) && ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) {
return;
}
// Not authorized by ACL ownership; try via adminstrative permissions
GrantedAuthority requiredAuthority = null;
if (changeType == CHANGE_AUDITING) {
requiredAuthority = this.gaModifyAuditing;
} else if (changeType == CHANGE_GENERAL) {
requiredAuthority = this.gaGeneralChanges;
} else if (changeType == CHANGE_OWNERSHIP) {
requiredAuthority = this.gaTakeOwnership;
} else {
throw new IllegalArgumentException("Unknown change type");
}
// Iterate this principal's authorities to determine right
GrantedAuthority[] auths = authentication.getAuthorities();
for (int i = 0; i < auths.length; i++) {
if (requiredAuthority.equals(auths[i])) {
return;
}
}
throw new AccessDeniedException(
"Principal does not have required ACL permissions to perform requested operation");
}
/**
* Change the special adminstrative permissions honoured by this object.<p>Normally a principal must be the
* owner of the ACL in order to make most changes. The authorities passed to this method provide a way for
* non-owners to modify the ACL (and indeed modify audit parameters, which are not available to ACL owners).</p>
*
* @param auths an array of <code>GrantedAuthority</code>s that have administrative permissions (index 0 is the
* authority needed to change ownership, index 1 is the authority needed to modify auditing details, index
* 2 is the authority needed to change other ACL and ACE details)
*/
private void setAuthorities(GrantedAuthority[] auths) {
Assert.notEmpty(auths, "GrantedAuthority[] with three elements required");
Assert.isTrue(auths.length == 3, "GrantedAuthority[] with three elements required");
this.gaTakeOwnership = auths[0];
this.gaModifyAuditing = auths[1];
this.gaGeneralChanges = auths[2];
}
public void setEntriesInheriting(boolean entriesInheriting) {
securityCheck(CHANGE_GENERAL);
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
this.entriesInheriting = entriesInheriting;
this.aclDirty = true;
}
public void setOwner(Sid newOwner) {
securityCheck(CHANGE_OWNERSHIP);
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.notNull(newOwner, "Owner required");
this.owner = newOwner;
this.aclDirty = true;
}
public void setParent(MutableAcl newParent) {
securityCheck(CHANGE_GENERAL);
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.notNull(newParent, "New Parent required");
Assert.isTrue(!newParent.equals(this), "Cannot be the parent of yourself");
this.parentAcl = newParent;
this.aclDirty = true;
}
public String toString() {
@@ -455,6 +362,10 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
sb.append(iterator.next().toString()).append("\r\n");
}
if (count == 0) {
sb.append("no ACEs; ");
}
sb.append("inheriting: ").append(this.entriesInheriting).append("; ");
sb.append("parent: ").append((this.parentAcl == null) ? "Null" : this.parentAcl.getObjectIdentity().toString());
sb.append("]");
@@ -462,9 +373,9 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
return sb.toString();
}
public void updateAce(Long aceId, Permission permission)
public void updateAce(Serializable aceId, Permission permission)
throws NotFoundException {
securityCheck(CHANGE_GENERAL);
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
synchronized (aces) {
int offset = findAceOffset(aceId);
@@ -476,12 +387,10 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(offset);
ace.setPermission(permission);
}
this.updatedAces = true;
}
public void updateAuditing(Long aceId, boolean auditSuccess, boolean auditFailure) {
securityCheck(CHANGE_AUDITING);
public void updateAuditing(Serializable aceId, boolean auditSuccess, boolean auditFailure) {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_AUDITING);
synchronized (aces) {
int offset = findAceOffset(aceId);
@@ -494,7 +403,5 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
ace.setAuditSuccess(auditSuccess);
ace.setAuditFailure(auditFailure);
}
this.updatedAces = true;
}
}
@@ -1,32 +1,31 @@
/* 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.acegisecurity.acls.domain;
import org.acegisecurity.acls.AccessControlEntry;
/* 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.acegisecurity.acls.domain;
import org.acegisecurity.acls.AccessControlEntry;
/**
* Used by <code>AclImpl</code> to log audit events.
*
* @author Ben Alex
* @version $Id$
*
*/
public interface AuditLogger {
//~ Methods ========================================================================================================
public void logIfNeeded(boolean granted, AccessControlEntry ace);
}
*/
public interface AuditLogger {
//~ Methods ========================================================================================================
public void logIfNeeded(boolean granted, AccessControlEntry ace);
}
@@ -0,0 +1,164 @@
/* 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.acegisecurity.acls.domain;
import org.acegisecurity.acls.AclFormattingUtils;
import org.acegisecurity.acls.Permission;
import org.springframework.util.Assert;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
/**
* A set of standard permissions.
*
* @author Ben Alex
* @version $Id$
*/
public class BasePermission implements Permission {
//~ Static fields/initializers =====================================================================================
public static final Permission READ = new BasePermission(1 << 0, 'R'); // 1
public static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2
public static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4
public static final Permission DELETE = new BasePermission(1 << 3, 'D'); // 8
public static final Permission ADMINISTRATION = new BasePermission(1 << 4, 'A'); // 16
private static Map locallyDeclaredPermissionsByInteger = new HashMap();
private static Map locallyDeclaredPermissionsByName = new HashMap();
static {
Field[] fields = BasePermission.class.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
try {
Object fieldValue = fields[i].get(null);
if (BasePermission.class.isAssignableFrom(fieldValue.getClass())) {
// Found a BasePermission static field
BasePermission perm = (BasePermission) fieldValue;
locallyDeclaredPermissionsByInteger.put(new Integer(perm.getMask()), perm);
locallyDeclaredPermissionsByName.put(fields[i].getName(), perm);
}
} catch (Exception ignore) {}
}
}
//~ Instance fields ================================================================================================
private char code;
private int mask;
//~ Constructors ===================================================================================================
private BasePermission(int mask, char code) {
this.mask = mask;
this.code = code;
}
//~ Methods ========================================================================================================
/**
* Dynamically creates a <code>CumulativePermission</code> or <code>BasePermission</code> representing the
* active bits in the passed mask.
*
* @param mask to build
*
* @return a Permission representing the requested object
*/
public static Permission buildFromMask(int mask) {
if (locallyDeclaredPermissionsByInteger.containsKey(new Integer(mask))) {
// The requested mask has an exactly match against a statically-defined BasePermission, so return it
return (Permission) locallyDeclaredPermissionsByInteger.get(new Integer(mask));
}
// To get this far, we have to use a CumulativePermission
CumulativePermission permission = new CumulativePermission();
for (int i = 0; i < 32; i++) {
int permissionToCheck = 1 << i;
if ((mask & permissionToCheck) == permissionToCheck) {
Permission p = (Permission) locallyDeclaredPermissionsByInteger.get(new Integer(permissionToCheck));
Assert.state(p != null,
"Mask " + permissionToCheck + " does not have a corresponding static BasePermission");
permission.set(p);
}
}
return permission;
}
public static Permission[] buildFromMask(int[] masks) {
if ((masks == null) || (masks.length == 0)) {
return new Permission[] {};
}
List list = new Vector();
for (int i = 0; i < masks.length; i++) {
list.add(BasePermission.buildFromMask(masks[i]));
}
return (Permission[]) list.toArray(new Permission[] {});
}
public static Permission buildFromName(String name) {
Assert.isTrue(locallyDeclaredPermissionsByName.containsKey(name), "Unknown permission '" + name + "'");
return (Permission) locallyDeclaredPermissionsByName.get(name);
}
public static Permission[] buildFromName(String[] names) {
if ((names == null) || (names.length == 0)) {
return new Permission[] {};
}
List list = new Vector();
for (int i = 0; i < names.length; i++) {
list.add(BasePermission.buildFromName(names[i]));
}
return (Permission[]) list.toArray(new Permission[] {});
}
public boolean equals(Object arg0) {
if (!(arg0 instanceof BasePermission)) {
return false;
}
BasePermission rhs = (BasePermission) arg0;
return (this.mask == rhs.getMask());
}
public int getMask() {
return mask;
}
public String getPattern() {
return AclFormattingUtils.printBinary(mask, code);
}
public String toString() {
return "BasePermission[" + getPattern() + "=" + mask + "]";
}
}
@@ -1,46 +1,45 @@
/* 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.acegisecurity.acls.domain;
import org.acegisecurity.acls.AccessControlEntry;
import org.acegisecurity.acls.AuditableAccessControlEntry;
import org.springframework.util.Assert;
/**
* DOCUMENT ME!
*
* @author $author$
* @version $Revision$
*/
public class ConsoleAuditLogger implements AuditLogger {
//~ Methods ========================================================================================================
public void logIfNeeded(boolean granted, AccessControlEntry ace) {
Assert.notNull(ace, "AccessControlEntry required");
if (ace instanceof AuditableAccessControlEntry) {
AuditableAccessControlEntry auditableAce = (AuditableAccessControlEntry) ace;
if (granted && auditableAce.isAuditSuccess()) {
System.out.println("GRANTED due to ACE: " + ace);
} else if (!granted && auditableAce.isAuditFailure()) {
System.out.println("DENIED due to ACE: " + ace);
}
}
}
}
/* 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.acegisecurity.acls.domain;
import org.acegisecurity.acls.AccessControlEntry;
import org.acegisecurity.acls.AuditableAccessControlEntry;
import org.springframework.util.Assert;
/**
* A bsaic implementation of {@link AuditLogger}.
*
* @author Ben Alex
* @version $Id$
*/
public class ConsoleAuditLogger implements AuditLogger {
//~ Methods ========================================================================================================
public void logIfNeeded(boolean granted, AccessControlEntry ace) {
Assert.notNull(ace, "AccessControlEntry required");
if (ace instanceof AuditableAccessControlEntry) {
AuditableAccessControlEntry auditableAce = (AuditableAccessControlEntry) ace;
if (granted && auditableAce.isAuditSuccess()) {
System.out.println("GRANTED due to ACE: " + ace);
} else if (!granted && auditableAce.isAuditFailure()) {
System.out.println("DENIED due to ACE: " + ace);
}
}
}
}
@@ -1,79 +1,78 @@
/* 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.acegisecurity.acls.domain;
import org.acegisecurity.acls.AclFormattingUtils;
import org.acegisecurity.acls.Permission;
/**
* Represents a <code>Permission</code> that is constructed at runtime from other permissions.<p>Methods return
* <code>this</code>, in order to facilitate method chaining.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class CumulativePermission implements Permission {
//~ Instance fields ================================================================================================
private String pattern = THIRTY_TWO_RESERVED_OFF;
private int mask = 0;
//~ Methods ========================================================================================================
public CumulativePermission clear(Permission permission) {
this.mask &= ~permission.getMask();
this.pattern = AclFormattingUtils.demergePatterns(this.pattern, permission.getPattern());
return this;
}
public CumulativePermission clear() {
this.mask = 0;
this.pattern = THIRTY_TWO_RESERVED_OFF;
return this;
}
public boolean equals(Object arg0) {
if (!(arg0 instanceof CumulativePermission)) {
return false;
}
CumulativePermission rhs = (CumulativePermission) arg0;
return (this.mask == rhs.getMask());
}
public int getMask() {
return this.mask;
}
public String getPattern() {
return this.pattern;
}
public CumulativePermission set(Permission permission) {
this.mask |= permission.getMask();
this.pattern = AclFormattingUtils.mergePatterns(this.pattern, permission.getPattern());
return this;
}
public String toString() {
return "CumulativePermission[" + pattern + "=" + this.mask + "]";
}
}
/* 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.acegisecurity.acls.domain;
import org.acegisecurity.acls.AclFormattingUtils;
import org.acegisecurity.acls.Permission;
/**
* Represents a <code>Permission</code> that is constructed at runtime from other permissions.<p>Methods return
* <code>this</code>, in order to facilitate method chaining.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class CumulativePermission implements Permission {
//~ Instance fields ================================================================================================
private String pattern = THIRTY_TWO_RESERVED_OFF;
private int mask = 0;
//~ Methods ========================================================================================================
public CumulativePermission clear(Permission permission) {
this.mask &= ~permission.getMask();
this.pattern = AclFormattingUtils.demergePatterns(this.pattern, permission.getPattern());
return this;
}
public CumulativePermission clear() {
this.mask = 0;
this.pattern = THIRTY_TWO_RESERVED_OFF;
return this;
}
public boolean equals(Object arg0) {
if (!(arg0 instanceof CumulativePermission)) {
return false;
}
CumulativePermission rhs = (CumulativePermission) arg0;
return (this.mask == rhs.getMask());
}
public int getMask() {
return this.mask;
}
public String getPattern() {
return this.pattern;
}
public CumulativePermission set(Permission permission) {
this.mask |= permission.getMask();
this.pattern = AclFormattingUtils.mergePatterns(this.pattern, permission.getPattern());
return this;
}
public String toString() {
return "CumulativePermission[" + pattern + "=" + this.mask + "]";
}
}
@@ -0,0 +1,5 @@
<html>
<body>
Basic implementation of access control lists (ACLs) interfaces.
</body>
</html>
@@ -1,39 +1,42 @@
/* 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.acegisecurity.acls.jdbc;
import org.acegisecurity.acls.domain.AclImpl;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
/* 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.acegisecurity.acls.jdbc;
import org.acegisecurity.acls.MutableAcl;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import java.io.Serializable;
/**
* A caching layer for {@link JdbcAclService}.
*
* @author Ben Alex
* @version $Id$
*
*/
public interface AclCache {
//~ Methods ========================================================================================================
public void evictFromCache(Long pk);
public AclImpl getFromCache(ObjectIdentity objectIdentity);
public AclImpl getFromCache(Long pk);
public void putInCache(AclImpl acl); // should walk tree as well!
}
*/
public interface AclCache {
//~ Methods ========================================================================================================
public void evictFromCache(Serializable pk);
public void evictFromCache(ObjectIdentity objectIdentity);
public MutableAcl getFromCache(ObjectIdentity objectIdentity);
public MutableAcl getFromCache(Serializable pk);
public void putInCache(MutableAcl acl);
}
@@ -1,93 +1,115 @@
/* 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.acegisecurity.acls.jdbc;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Element;
import org.acegisecurity.acls.domain.AclImpl;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.springframework.util.Assert;
/**
* DOCUMENT ME!
*
* @author $author$
* @version $Revision$
*/
public class EhCacheBasedAclCache implements AclCache {
//~ Instance fields ================================================================================================
private Cache cache;
//~ Constructors ===================================================================================================
public EhCacheBasedAclCache(Cache cache) {
Assert.notNull(cache, "Cache required");
this.cache = cache;
}
//~ Methods ========================================================================================================
public void evictFromCache(Long pk) {
AclImpl acl = getFromCache(pk);
if (acl != null) {
cache.remove(pk);
cache.remove(acl.getObjectIdentity());
}
}
public AclImpl getFromCache(ObjectIdentity objectIdentity) {
Element element = null;
try {
element = cache.get(objectIdentity);
} catch (CacheException ignored) {}
if (element == null) {
return null;
}
return (AclImpl) element.getValue();
}
public AclImpl getFromCache(Long pk) {
Element element = null;
try {
element = cache.get(pk);
} catch (CacheException ignored) {}
if (element == null) {
return null;
}
return (AclImpl) element.getValue();
}
public void putInCache(AclImpl acl) {
if ((acl.getParentAcl() != null) && acl.getParentAcl() instanceof AclImpl) {
putInCache((AclImpl) acl.getParentAcl());
}
cache.put(new Element(acl.getObjectIdentity(), acl));
cache.put(new Element(acl.getId(), acl));
}
}
/* 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.acegisecurity.acls.jdbc;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Element;
import org.acegisecurity.acls.MutableAcl;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.springframework.util.Assert;
import java.io.Serializable;
/**
* Simple implementation of {@link AclCache} that delegates to EH-CACHE.
*
* @author Ben Alex
* @version $Id$
*/
public class EhCacheBasedAclCache implements AclCache {
//~ Instance fields ================================================================================================
private Cache cache;
//~ Constructors ===================================================================================================
public EhCacheBasedAclCache(Cache cache) {
Assert.notNull(cache, "Cache required");
this.cache = cache;
}
//~ Methods ========================================================================================================
public void evictFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
MutableAcl acl = getFromCache(pk);
if (acl != null) {
cache.remove(acl.getId());
cache.remove(acl.getObjectIdentity());
}
}
public void evictFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
MutableAcl acl = getFromCache(objectIdentity);
if (acl != null) {
cache.remove(acl.getId());
cache.remove(acl.getObjectIdentity());
}
}
public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
Element element = null;
try {
element = cache.get(objectIdentity);
} catch (CacheException ignored) {}
if (element == null) {
return null;
}
return (MutableAcl) element.getValue();
}
public MutableAcl getFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
Element element = null;
try {
element = cache.get(pk);
} catch (CacheException ignored) {}
if (element == null) {
return null;
}
return (MutableAcl) element.getValue();
}
public void putInCache(MutableAcl acl) {
Assert.notNull(acl, "Acl required");
Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required");
Assert.notNull(acl.getId(), "ID required");
if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
putInCache((MutableAcl) acl.getParentAcl());
}
cache.put(new Element(acl.getObjectIdentity(), acl));
cache.put(new Element(acl.getId(), acl));
}
}
@@ -0,0 +1,114 @@
/* 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.acegisecurity.acls.jdbc;
import org.acegisecurity.acls.Acl;
import org.acegisecurity.acls.AclService;
import org.acegisecurity.acls.NotFoundException;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.objectidentity.ObjectIdentityImpl;
import org.acegisecurity.acls.sid.Sid;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.Assert;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
/**
* Simple JDBC-based implementation of <code>AclService</code>.<p>Requires the "dirty" flags in {@link
* org.acegisecurity.acls.domain.AclImpl} and {@link org.acegisecurity.acls.domain.AccessControlEntryImpl} to be set,
* so that the implementation can detect changed parameters easily.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class JdbcAclService implements AclService {
//~ Static fields/initializers =====================================================================================
protected static final Log log = LogFactory.getLog(JdbcAclService.class);
private static final String selectAclObjectWithParent = "SELECT obj.object_id_identity obj_id, class.class class "
+ "FROM acl_object_identity obj, acl_object_identity parent, acl_class class "
+ "WHERE obj.parent_object = parent.id AND obj.object_id_class = class.id "
+ "AND parent.object_id_identity = ? AND parent.object_id_class = ("
+ "SELECT id FROM acl_class WHERE acl_class.class = ?)";
//~ Instance fields ================================================================================================
protected JdbcTemplate jdbcTemplate;
private LookupStrategy lookupStrategy;
//~ Constructors ===================================================================================================
public JdbcAclService(DataSource dataSource, LookupStrategy lookupStrategy) {
Assert.notNull(dataSource, "DataSource required");
Assert.notNull(lookupStrategy, "LookupStrategy required");
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.lookupStrategy = lookupStrategy;
}
//~ Methods ========================================================================================================
public ObjectIdentity[] findChildren(ObjectIdentity parentIdentity) {
Object[] args = {parentIdentity.getIdentifier(), parentIdentity.getJavaType().getName()};
List objects = jdbcTemplate.query(selectAclObjectWithParent, args,
new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum)
throws SQLException {
String javaType = rs.getString("class");
String identifier = rs.getString("obj_id");
return new ObjectIdentityImpl(javaType, identifier);
}
});
return (ObjectIdentityImpl[]) objects.toArray(new ObjectIdentityImpl[] {});
}
public Acl readAclById(ObjectIdentity object, Sid[] sids)
throws NotFoundException {
Map map = readAclsById(new ObjectIdentity[] {object}, sids);
if (map.size() == 0) {
throw new NotFoundException("Could not find ACL");
} else {
return (Acl) map.get(object);
}
}
public Acl readAclById(ObjectIdentity object) throws NotFoundException {
return readAclById(object, null);
}
public Map readAclsById(ObjectIdentity[] objects) {
return readAclsById(objects, null);
}
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids)
throws NotFoundException {
return lookupStrategy.readAclsById(objects, sids);
}
}
@@ -0,0 +1,368 @@
/* 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.acegisecurity.acls.jdbc;
import org.acegisecurity.Authentication;
import org.acegisecurity.acls.AccessControlEntry;
import org.acegisecurity.acls.Acl;
import org.acegisecurity.acls.AlreadyExistsException;
import org.acegisecurity.acls.ChildrenExistException;
import org.acegisecurity.acls.MutableAcl;
import org.acegisecurity.acls.MutableAclService;
import org.acegisecurity.acls.NotFoundException;
import org.acegisecurity.acls.domain.AccessControlEntryImpl;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.objectidentity.ObjectIdentityImpl;
import org.acegisecurity.acls.sid.GrantedAuthoritySid;
import org.acegisecurity.acls.sid.PrincipalSid;
import org.acegisecurity.acls.sid.Sid;
import org.acegisecurity.context.SecurityContextHolder;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import java.lang.reflect.Array;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
/**
* Provides a base implementation of {@link MutableAclService}.
*
* @author Ben Alex
* @author Johannes Zlattinger
* @version $Id$
*/
public class JdbcMutableAclService extends JdbcAclService implements MutableAclService {
//~ Instance fields ================================================================================================
private AclCache aclCache;
private String deleteClassByClassNameString = "DELETE FROM acl_class WHERE class=?";
private String deleteEntryByObjectIdentityForeignKey = "DELETE FROM acl_entry WHERE acl_object_identity=?";
private String deleteObjectIdentityByPrimaryKey = "DELETE FROM acl_object_identity WHERE id=?";
private String identityQuery = "call identity()";
private String insertClass = "INSERT INTO acl_class (id, class) VALUES (null, ?)";
private String insertEntry = "INSERT INTO acl_entry "
+ "(id, acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)"
+ "VALUES (null, ?, ?, ?, ?, ?, ?, ?)";
private String insertObjectIdentity = "INSERT INTO acl_object_identity "
+ "(id, object_id_class, object_id_identity, owner_sid, entries_inheriting) " + "VALUES (null, ?, ?, ?, ?)";
private String insertSid = "INSERT INTO acl_sid (id, principal, sid) VALUES (null, ?, ?)";
private String selectClassPrimaryKey = "SELECT id FROM acl_class WHERE class=?";
private String selectCountObjectIdentityRowsForParticularClassNameString = "SELECT COUNT(acl_object_identity.id) "
+ "FROM acl_object_identity, acl_class WHERE acl_class.id = acl_object_identity.object_id_class and class=?";
private String selectObjectIdentityPrimaryKey = "SELECT acl_object_identity.id FROM acl_object_identity, acl_class "
+ "WHERE acl_object_identity.object_id_class = acl_class.id and acl_class.class=? "
+ "and acl_object_identity.object_id_identity = ?";
private String selectSidPrimaryKey = "SELECT id FROM acl_sid WHERE principal=? AND sid=?";
private String updateObjectIdentity = "UPDATE acl_object_identity SET "
+ "parent_object = ?, owner_sid = ?, entries_inheriting = ?" + "where id = ?";
//~ Constructors ===================================================================================================
public JdbcMutableAclService(DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) {
super(dataSource, lookupStrategy);
Assert.notNull(aclCache, "AclCache required");
this.aclCache = aclCache;
}
//~ Methods ========================================================================================================
public MutableAcl createAcl(ObjectIdentity objectIdentity)
throws AlreadyExistsException {
Assert.notNull(objectIdentity, "Object Identity required");
// Check this object identity hasn't already been persisted
if (retrieveObjectIdentityPrimaryKey(objectIdentity) != null) {
throw new AlreadyExistsException("Object identity '" + objectIdentity + "' already exists");
}
// Need to retrieve the current principal, in order to know who "owns" this ACL (can be changed later on)
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
PrincipalSid sid = new PrincipalSid(auth);
// Create the acl_object_identity row
createObjectIdentity(objectIdentity, sid);
// Retrieve the ACL via superclass (ensures cache registration, proper retrieval etc)
Acl acl = readAclById(objectIdentity);
Assert.isInstanceOf(MutableAcl.class, acl, "MutableAcl should be been returned");
return (MutableAcl) acl;
}
/**
* Creates a new row in acl_entry for every ACE defined in the passed MutableAcl object.
*
* @param acl containing the ACEs to insert
*/
protected void createEntries(final MutableAcl acl) {
jdbcTemplate.batchUpdate(insertEntry,
new BatchPreparedStatementSetter() {
public int getBatchSize() {
return acl.getEntries().length;
}
public void setValues(PreparedStatement stmt, int i)
throws SQLException {
AccessControlEntry entry_ = (AccessControlEntry) Array.get(acl.getEntries(), i);
Assert.isTrue(entry_ instanceof AccessControlEntryImpl, "Unknown ACE class");
AccessControlEntryImpl entry = (AccessControlEntryImpl) entry_;
stmt.setLong(1, ((Long) acl.getId()).longValue());
stmt.setInt(2, i);
stmt.setLong(3, createOrRetrieveSidPrimaryKey(entry.getSid(), true).longValue());
stmt.setInt(4, entry.getPermission().getMask());
stmt.setBoolean(5, entry.isGranting());
stmt.setBoolean(6, entry.isAuditSuccess());
stmt.setBoolean(7, entry.isAuditFailure());
}
});
}
/**
* Creates an entry in the acl_object_identity table for the passed ObjectIdentity. The Sid is also
* necessary, as acl_object_identity has defined the sid column as non-null.
*
* @param object to represent an acl_object_identity for
* @param owner for the SID column (will be created if there is no acl_sid entry for this particular Sid already)
*/
protected void createObjectIdentity(ObjectIdentity object, Sid owner) {
Long sidId = createOrRetrieveSidPrimaryKey(owner, true);
Long classId = createOrRetrieveClassPrimaryKey(object.getJavaType(), true);
jdbcTemplate.update(insertObjectIdentity,
new Object[] {classId, object.getIdentifier().toString(), sidId, new Boolean(true)});
}
/**
* Retrieves the primary key from acl_class, creating a new row if needed and the allowCreate property is
* true.
*
* @param clazz to find or create an entry for (this implementation uses the fully-qualified class name String)
* @param allowCreate true if creation is permitted if not found
*
* @return the primary key or null if not found
*/
protected Long createOrRetrieveClassPrimaryKey(Class clazz, boolean allowCreate) {
List classIds = jdbcTemplate.queryForList(selectClassPrimaryKey, new Object[] {clazz.getName()}, Long.class);
Long classId = null;
if (classIds.isEmpty()) {
if (allowCreate) {
classId = null;
jdbcTemplate.update(insertClass, new Object[] {clazz.getName()});
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), "Transaction must be running");
classId = new Long(jdbcTemplate.queryForLong(identityQuery));
}
} else {
classId = (Long) classIds.iterator().next();
}
return classId;
}
/**
* Retrieves the primary key from acl_sid, creating a new row if needed and the allowCreate property is
* true.
*
* @param sid to find or create
* @param allowCreate true if creation is permitted if not found
*
* @return the primary key or null if not found
*
* @throws IllegalArgumentException DOCUMENT ME!
*/
protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
Assert.notNull(sid, "Sid required");
String sidName = null;
boolean principal = true;
if (sid instanceof PrincipalSid) {
sidName = ((PrincipalSid) sid).getPrincipal();
} else if (sid instanceof GrantedAuthoritySid) {
sidName = ((GrantedAuthoritySid) sid).getGrantedAuthority();
principal = false;
} else {
throw new IllegalArgumentException("Unsupported implementation of Sid");
}
List sidIds = jdbcTemplate.queryForList(selectSidPrimaryKey, new Object[] {new Boolean(principal), sidName},
Long.class);
Long sidId = null;
if (sidIds.isEmpty()) {
if (allowCreate) {
sidId = null;
jdbcTemplate.update(insertSid, new Object[] {new Boolean(principal), sidName});
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), "Transaction must be running");
sidId = new Long(jdbcTemplate.queryForLong(identityQuery));
}
} else {
sidId = (Long) sidIds.iterator().next();
}
return sidId;
}
public void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren)
throws ChildrenExistException {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(objectIdentity.getIdentifier(), "Object Identity doesn't provide an identifier");
// Recursively call this method for children, or handle children if they don't want automatic recursion
ObjectIdentity[] children = findChildren(objectIdentity);
if (deleteChildren) {
for (int i = 0; i < children.length; i++) {
deleteAcl(children[i], true);
}
} else if (children.length > 0) {
throw new ChildrenExistException("Cannot delete '" + objectIdentity + "' (has " + children.length
+ " children)");
}
// Delete this ACL's ACEs in the acl_entry table
deleteEntries(objectIdentity);
// Delete this ACL's acl_object_identity row
deleteObjectIdentityAndOptionallyClass(objectIdentity);
// Clear the cache
aclCache.evictFromCache(objectIdentity);
}
/**
* Deletes all ACEs defined in the acl_entry table belonging to the presented ObjectIdentity
*
* @param oid the rows in acl_entry to delete
*/
protected void deleteEntries(ObjectIdentity oid) {
jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey, new Object[] {retrieveObjectIdentityPrimaryKey(oid)});
}
/**
* Deletes a single row from acl_object_identity that is associated with the presented ObjectIdentity. In
* addition, deletes the corresponding row from acl_class if there are no more entries in acl_object_identity that
* use that particular acl_class. This keeps the acl_class table reasonably small.
*
* @param oid to delete the acl_object_identity (and clean up acl_class for that class name if appropriate)
*/
protected void deleteObjectIdentityAndOptionallyClass(ObjectIdentity oid) {
// Delete the acl_object_identity row
jdbcTemplate.update(deleteObjectIdentityByPrimaryKey, new Object[] {retrieveObjectIdentityPrimaryKey(oid)});
// Delete the acl_class row, assuming there are no other references to it in acl_object_identity
Object[] className = {oid.getJavaType().getName()};
long numObjectIdentities = jdbcTemplate.queryForLong(selectCountObjectIdentityRowsForParticularClassNameString,
className);
if (numObjectIdentities == 0) {
// No more rows
jdbcTemplate.update(deleteClassByClassNameString, className);
}
}
/**
* Retrieves the primary key from the acl_object_identity table for the passed ObjectIdentity. Unlike some
* other methods in this implementation, this method will NOT create a row (use {@link
* #createObjectIdentity(ObjectIdentity, Sid)} instead).
*
* @param oid to find
*
* @return the object identity or null if not found
*/
protected Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
try {
return new Long(jdbcTemplate.queryForLong(selectObjectIdentityPrimaryKey,
new Object[] {oid.getJavaType().getName(), oid.getIdentifier()}));
} catch (DataAccessException notFound) {
return null;
}
}
/**
* This implementation will simply delete all ACEs in the database and recreate them on each invocation of
* this method. A more comprehensive implementation might use dirty state checking, or more likely use ORM
* capabilities for create, update and delete operations of {@link MutableAcl}.
*
* @param acl DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws NotFoundException DOCUMENT ME!
*/
public MutableAcl updateAcl(MutableAcl acl) throws NotFoundException {
Assert.notNull(acl.getId(), "Object Identity doesn't provide an identifier");
// Delete this ACL's ACEs in the acl_entry table
deleteEntries(acl.getObjectIdentity());
// Create this ACL's ACEs in the acl_entry table
createEntries(acl);
// Change the mutable columns in acl_object_identity
updateObjectIdentity(acl);
// Clear the cache
aclCache.evictFromCache(acl.getObjectIdentity());
// Retrieve the ACL via superclass (ensures cache registration, proper retrieval etc)
return (MutableAcl) super.readAclById(acl.getObjectIdentity());
}
/**
* Updates an existing acl_object_identity row, with new information presented in the passed MutableAcl
* object. Also will create an acl_sid entry if needed for the Sid that owns the MutableAcl.
*
* @param acl to modify (a row must already exist in acl_object_identity)
*
* @throws NotFoundException DOCUMENT ME!
*/
protected void updateObjectIdentity(MutableAcl acl) {
Long parentId = null;
if (acl.getParentAcl() != null) {
Assert.isInstanceOf(ObjectIdentityImpl.class, acl.getParentAcl().getObjectIdentity(),
"Implementation only supports ObjectIdentityImpl");
ObjectIdentityImpl oii = (ObjectIdentityImpl) acl.getParentAcl().getObjectIdentity();
parentId = retrieveObjectIdentityPrimaryKey(oii);
}
Assert.notNull(acl.getOwner(), "Owner is required in this implementation");
Long ownerSid = createOrRetrieveSidPrimaryKey(acl.getOwner(), true);
int count = jdbcTemplate.update(updateObjectIdentity,
new Object[] {parentId, ownerSid, new Boolean(acl.isEntriesInheriting()), acl.getId()});
if (count != 1) {
throw new NotFoundException("Unable to locate ACL to update");
}
}
}
@@ -1,44 +1,43 @@
/* 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.acegisecurity.acls.jdbc;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.sid.Sid;
import java.util.Map;
/* 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.acegisecurity.acls.jdbc;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.sid.Sid;
import java.util.Map;
/**
* Performs optimised lookups for {@link JdbcAclService}.
*
* @author Ben Alex
* @version $Id$
*/
public interface LookupStrategy {
//~ Methods ========================================================================================================
/**
* Perform database-specific optimized lookup.
*
* @param objects the identities to lookup (required)
* @param sids the SIDs for which identities are required (may be <code>null</code> - implementations may elect not
* to provide SID optimisations)
*
* @return the <code>Map</code> pursuant to the interface contract for {@link
* org.acegisecurity.acls.AclService#readAclsById(ObjectIdentity[], Sid[])}
*/
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids);
}
*/
public interface LookupStrategy {
//~ Methods ========================================================================================================
/**
* Perform database-specific optimized lookup.
*
* @param objects the identities to lookup (required)
* @param sids the SIDs for which identities are required (may be <code>null</code> - implementations may elect not
* to provide SID optimisations)
*
* @return the <code>Map</code> pursuant to the interface contract for {@link
* org.acegisecurity.acls.AclService#readAclsById(ObjectIdentity[], Sid[])}
*/
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids);
}
@@ -0,0 +1,5 @@
<html>
<body>
JDBC-based persistence of ACL information.
</body>
</html>
@@ -1,23 +1,22 @@
/* 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.acegisecurity.acls.objectidentity;
import java.io.Serializable;
/* 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.acegisecurity.acls.objectidentity;
import java.io.Serializable;
/**
* Interface representing the identity of an individual domain object instance.
*
@@ -32,40 +31,40 @@ import java.io.Serializable;
*
* @author Ben Alex
* @version $Id$
*/
public interface ObjectIdentity extends Serializable {
//~ Methods ========================================================================================================
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface contract.
*
* @param obj to be compared
*
* @return <code>true</code> if the objects are equal, <code>false</code> otherwise
*/
public boolean equals(Object obj);
/**
* Obtains the actual identifier. This identifier must not be reused to represent other domain objects with
* the same <code>javaType</code>.<p>Because ACLs are largely immutable, it is strongly recommended to use
* a synthetic identifier (such as a database sequence number for the primary key). Do not use an identifier with
* business meaning, as that business meaning may change.</p>
*
* @return the identifier (unique within this <code>javaType</code>
*/
public Serializable getIdentifier();
/**
* Obtains the Java type represented by the domain object.
*
* @return the Java type of the domain object
*/
public Class getJavaType();
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface contract.
*
* @return a hash code representation of this object
*/
public int hashCode();
}
*/
public interface ObjectIdentity extends Serializable {
//~ Methods ========================================================================================================
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface contract.
*
* @param obj to be compared
*
* @return <code>true</code> if the objects are equal, <code>false</code> otherwise
*/
public boolean equals(Object obj);
/**
* Obtains the actual identifier. This identifier must not be reused to represent other domain objects with
* the same <code>javaType</code>.<p>Because ACLs are largely immutable, it is strongly recommended to use
* a synthetic identifier (such as a database sequence number for the primary key). Do not use an identifier with
* business meaning, as that business meaning may change.</p>
*
* @return the identifier (unique within this <code>javaType</code>
*/
public Serializable getIdentifier();
/**
* Obtains the Java type represented by the domain object.
*
* @return the Java type of the domain object
*/
public Class getJavaType();
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface contract.
*
* @return a hash code representation of this object
*/
public int hashCode();
}
@@ -1,146 +1,147 @@
/* 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.acegisecurity.acls.objectidentity;
import org.acegisecurity.acl.basic.AclObjectIdentity;
import org.acegisecurity.acls.IdentityUnavailableException;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import java.io.Serializable;
import java.lang.reflect.Method;
/* 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.acegisecurity.acls.objectidentity;
import org.acegisecurity.acl.basic.AclObjectIdentity;
import org.acegisecurity.acls.IdentityUnavailableException;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import java.io.Serializable;
import java.lang.reflect.Method;
/**
* Simple implementation of {@link AclObjectIdentity}.<P>Uses <code>String</code>s to store the identity of the
* domain object instance. Also offers a constructor that uses reflection to build the identity information.</p>
*/
public class ObjectIdentityImpl implements ObjectIdentity {
//~ Instance fields ================================================================================================
private Class javaType;
private Serializable identifier;
//~ Constructors ===================================================================================================
public ObjectIdentityImpl(String javaType, Serializable identifier) {
Assert.hasText(javaType, "Java Type required");
Assert.notNull(identifier, "identifier required");
try {
this.javaType = Class.forName(javaType);
} catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
this.identifier = identifier;
}
public ObjectIdentityImpl(Class javaType, Serializable identifier) {
Assert.notNull(javaType, "Java Type required");
Assert.notNull(identifier, "identifier required");
this.javaType = javaType;
this.identifier = identifier;
}
/**
* Simple implementation of {@link AclObjectIdentity}.<P>Uses <code>String</code>s to store the identity of the
* domain object instance. Also offers a constructor that uses reflection to build the identity information.</p>
*/
public class ObjectIdentityImpl implements ObjectIdentity {
//~ Instance fields ================================================================================================
private Class javaType;
private Serializable identifier;
//~ Constructors ===================================================================================================
public ObjectIdentityImpl(String javaType, Serializable identifier) {
Assert.hasText(javaType, "Java Type required");
Assert.notNull(identifier, "identifier required");
try {
this.javaType = Class.forName(javaType);
} catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
this.identifier = identifier;
}
public ObjectIdentityImpl(Class javaType, Serializable identifier) {
Assert.notNull(javaType, "Java Type required");
Assert.notNull(identifier, "identifier required");
this.javaType = javaType;
this.identifier = identifier;
}
/**
* Creates the <code>NamedEntityObjectIdentity</code> based on the passed
* Creates the <code>ObjectIdentityImpl</code> based on the passed
* object instance. The passed object must provide a <code>getId()</code>
* method, otherwise an exception will be thrown.
* method, otherwise an exception will be thrown. The object passed will
* be considered the {@link #javaType}, so if more control is required,
* an alternate constructor should be used instead.
*
* @param object the domain object instance to create an identity for
*
* @throws IdentityUnavailableException if identity could not be extracted
*/
public ObjectIdentityImpl(Object object) throws IdentityUnavailableException {
Assert.notNull(object, "object cannot be null");
this.javaType = object.getClass();
Object result;
try {
Method method = this.javaType.getMethod("getId", new Class[] {});
result = method.invoke(object, new Object[] {});
} catch (Exception e) {
throw new IdentityUnavailableException("Could not extract identity from object " + object, e);
}
Assert.isInstanceOf(Serializable.class, result, "Getter must provide a return value of type Serializable");
this.identifier = (Serializable) result;
}
//~ Methods ========================================================================================================
/**
* Important so caching operates properly.<P>Considers an object of the same class equal if it has the same
* <code>classname</code> and <code>id</code> properties.</p>
*
* @param arg0 object to compare
*
* @return <code>true</code> if the presented object matches this object
*/
public boolean equals(Object arg0) {
if (arg0 == null) {
return false;
}
if (!(arg0 instanceof ObjectIdentityImpl)) {
return false;
}
ObjectIdentityImpl other = (ObjectIdentityImpl) arg0;
if (this.getIdentifier().equals(other.getIdentifier()) && this.getJavaType().equals(other.getJavaType())) {
return true;
}
return false;
}
public Serializable getIdentifier() {
return identifier;
}
public Class getJavaType() {
return javaType;
}
/**
* Important so caching operates properly.
*
* @return the hash
*/
public int hashCode() {
int code = 31;
code ^= this.javaType.hashCode();
code ^= this.identifier.hashCode();
return code;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(this.getClass().getName()).append("[");
sb.append("Java Type: ").append(this.javaType);
sb.append("; Identifier: ").append(this.identifier).append("]");
return sb.toString();
}
}
*/
public ObjectIdentityImpl(Object object) throws IdentityUnavailableException {
Assert.notNull(object, "object cannot be null");
this.javaType = object.getClass();
Object result;
try {
Method method = this.javaType.getMethod("getId", new Class[] {});
result = method.invoke(object, new Object[] {});
} catch (Exception e) {
throw new IdentityUnavailableException("Could not extract identity from object " + object, e);
}
Assert.isInstanceOf(Serializable.class, result, "Getter must provide a return value of type Serializable");
this.identifier = (Serializable) result;
}
//~ Methods ========================================================================================================
/**
* Important so caching operates properly.<P>Considers an object of the same class equal if it has the same
* <code>classname</code> and <code>id</code> properties.</p>
*
* @param arg0 object to compare
*
* @return <code>true</code> if the presented object matches this object
*/
public boolean equals(Object arg0) {
if (arg0 == null) {
return false;
}
if (!(arg0 instanceof ObjectIdentityImpl)) {
return false;
}
ObjectIdentityImpl other = (ObjectIdentityImpl) arg0;
if (this.getIdentifier().equals(other.getIdentifier()) && this.getJavaType().equals(other.getJavaType())) {
return true;
}
return false;
}
public Serializable getIdentifier() {
return identifier;
}
public Class getJavaType() {
return javaType;
}
/**
* Important so caching operates properly.
*
* @return the hash
*/
public int hashCode() {
int code = 31;
code ^= this.javaType.hashCode();
code ^= this.identifier.hashCode();
return code;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(this.getClass().getName()).append("[");
sb.append("Java Type: ").append(this.javaType);
sb.append("; Identifier: ").append(this.identifier).append("]");
return sb.toString();
}
}
@@ -13,32 +13,18 @@
* limitations under the License.
*/
package org.acegisecurity.domain.dao;
import org.acegisecurity.domain.PersistableEntity;
package org.acegisecurity.acls.objectidentity;
/**
* Indicates an implementation capable of evicting {@link
* org.acegisecurity.domain.PersistableEntity}s.
* Strategy interface that provides the ability to determine which {@link ObjectIdentity}
* will be returned for a particular domain object
*
* <p>
* Structured as a separate interface (rather than a subclass of
* <code>Dao</code>), as it is not required for all persistence strategies.
* </p>
*
* @author Ben Alex
* @version $Id$
*
*/
public interface EvictionCapable {
public interface ObjectIdentityRetrievalStrategy {
//~ Methods ========================================================================================================
/**
* Removes the indicated persistent instance from the DAO's internal map/session.<p>If the passed object
* does not exist in the internal map/session, the invocation has no effect.</p>
* <p>May throw an exception if the implementation so desires.</p>
*
* @param entity to remove from the internal map/session
*/
public void evict(PersistableEntity entity);
public ObjectIdentity getObjectIdentity(Object domainObject);
}
@@ -13,17 +13,19 @@
* limitations under the License.
*/
package org.acegisecurity.domain.impl;
package org.acegisecurity.acls.objectidentity;
/**
* A <i>value object</i>, which means a persistable business object that does not have its own persistence
* identity.<p>Every value object belongs to a single {@link
* org.acegisecurity.domain.impl.AbstractPersistableEntity}. This is necessary so that the value object has some sort
* of persistence relationship/ownership.</p>
* <P>In addition, a value object cannot be referenced from more than one <code>PersistableEntity</code>. Use a
* <code>PersistableEntity</code> instead of a <code>PersistableValue</code> if this is a design constraint.</p>
* Basic implementation of {@link ObjectIdentityRetrievalStrategy} that uses the constructor of {@link
* ObjectIdentityImpl} to create the {@link ObjectIdentity}.
*
* @author Ben Alex
* @version $Id$
*/
public abstract class PersistableValue extends BusinessObject {}
public class ObjectIdentityRetrievalStrategyImpl implements ObjectIdentityRetrievalStrategy {
//~ Methods ========================================================================================================
public ObjectIdentity getObjectIdentity(Object domainObject) {
return new ObjectIdentityImpl(domainObject);
}
}
@@ -0,0 +1,5 @@
<html>
<body>
Provides indirection between ACL packages and domain objects.
</body>
</html>
@@ -0,0 +1,5 @@
<html>
<body>
Interfaces and shared classes to manage access control lists (ACLs) for domain object instances.
</body>
</html>
@@ -1,68 +1,67 @@
/* 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.acegisecurity.acls.sid;
import org.acegisecurity.GrantedAuthority;
import org.springframework.util.Assert;
/**
* Represents a <code>GrantedAuthority</code> as a <code>Sid</code>.<p>This is a basic implementation that simply
* uses the <code>String</code>-based principal for <code>Sid</code> comparison. More complex principal objects may
* wish to provide an alternative <code>Sid</code> implementation that uses some other identifier.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class GrantedAuthoritySid implements Sid {
//~ Instance fields ================================================================================================
private String grantedAuthority;
//~ Constructors ===================================================================================================
public GrantedAuthoritySid(String grantedAuthority) {
Assert.hasText(grantedAuthority, "GrantedAuthority required");
this.grantedAuthority = grantedAuthority;
}
public GrantedAuthoritySid(GrantedAuthority grantedAuthority) {
Assert.notNull(grantedAuthority, "GrantedAuthority required");
Assert.notNull(grantedAuthority.getAuthority(),
"This Sid is only compatible with GrantedAuthoritys that provide a non-null getAuthority()");
this.grantedAuthority = grantedAuthority.getAuthority();
}
//~ Methods ========================================================================================================
public boolean equals(Object object) {
if ((object == null) || !(object instanceof GrantedAuthoritySid)) {
return false;
}
// Delegate to getGrantedAuthority() to perform actual comparison (both should be identical)
return ((GrantedAuthoritySid) object).getGrantedAuthority().equals(this.getGrantedAuthority());
}
public String getGrantedAuthority() {
return grantedAuthority;
}
public String toString() {
return "GrantedAuthoritySid[" + this.grantedAuthority + "]";
}
}
/* 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.acegisecurity.acls.sid;
import org.acegisecurity.GrantedAuthority;
import org.springframework.util.Assert;
/**
* Represents a <code>GrantedAuthority</code> as a <code>Sid</code>.<p>This is a basic implementation that simply
* uses the <code>String</code>-based principal for <code>Sid</code> comparison. More complex principal objects may
* wish to provide an alternative <code>Sid</code> implementation that uses some other identifier.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class GrantedAuthoritySid implements Sid {
//~ Instance fields ================================================================================================
private String grantedAuthority;
//~ Constructors ===================================================================================================
public GrantedAuthoritySid(String grantedAuthority) {
Assert.hasText(grantedAuthority, "GrantedAuthority required");
this.grantedAuthority = grantedAuthority;
}
public GrantedAuthoritySid(GrantedAuthority grantedAuthority) {
Assert.notNull(grantedAuthority, "GrantedAuthority required");
Assert.notNull(grantedAuthority.getAuthority(),
"This Sid is only compatible with GrantedAuthoritys that provide a non-null getAuthority()");
this.grantedAuthority = grantedAuthority.getAuthority();
}
//~ Methods ========================================================================================================
public boolean equals(Object object) {
if ((object == null) || !(object instanceof GrantedAuthoritySid)) {
return false;
}
// Delegate to getGrantedAuthority() to perform actual comparison (both should be identical)
return ((GrantedAuthoritySid) object).getGrantedAuthority().equals(this.getGrantedAuthority());
}
public String getGrantedAuthority() {
return grantedAuthority;
}
public String toString() {
return "GrantedAuthoritySid[" + this.grantedAuthority + "]";
}
}
@@ -1,73 +1,72 @@
/* 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.acegisecurity.acls.sid;
import org.acegisecurity.Authentication;
import org.acegisecurity.userdetails.UserDetails;
import org.springframework.util.Assert;
/**
* Represents an <code>Authentication.getPrincipal()</code> as a <code>Sid</code>.<p>This is a basic implementation
* that simply uses the <code>String</code>-based principal for <code>Sid</code> comparison. More complex principal
* objects may wish to provide an alternative <code>Sid</code> implementation that uses some other identifier.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class PrincipalSid implements Sid {
//~ Instance fields ================================================================================================
private String principal;
//~ Constructors ===================================================================================================
public PrincipalSid(String principal) {
Assert.hasText(principal, "Principal required");
this.principal = principal;
}
public PrincipalSid(Authentication authentication) {
Assert.notNull(authentication, "Authentication required");
Assert.notNull(authentication.getPrincipal(), "Principal required");
this.principal = authentication.getPrincipal().toString();
if (authentication.getPrincipal() instanceof UserDetails) {
this.principal = ((UserDetails) authentication.getPrincipal()).getUsername();
}
}
//~ Methods ========================================================================================================
public boolean equals(Object object) {
if ((object == null) || !(object instanceof PrincipalSid)) {
return false;
}
// Delegate to getPrincipal() to perform actual comparison (both should be identical)
return ((PrincipalSid) object).getPrincipal().equals(this.getPrincipal());
}
public String getPrincipal() {
return principal;
}
public String toString() {
return "PrincipalSid[" + this.principal + "]";
}
}
/* 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.acegisecurity.acls.sid;
import org.acegisecurity.Authentication;
import org.acegisecurity.userdetails.UserDetails;
import org.springframework.util.Assert;
/**
* Represents an <code>Authentication.getPrincipal()</code> as a <code>Sid</code>.<p>This is a basic implementation
* that simply uses the <code>String</code>-based principal for <code>Sid</code> comparison. More complex principal
* objects may wish to provide an alternative <code>Sid</code> implementation that uses some other identifier.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class PrincipalSid implements Sid {
//~ Instance fields ================================================================================================
private String principal;
//~ Constructors ===================================================================================================
public PrincipalSid(String principal) {
Assert.hasText(principal, "Principal required");
this.principal = principal;
}
public PrincipalSid(Authentication authentication) {
Assert.notNull(authentication, "Authentication required");
Assert.notNull(authentication.getPrincipal(), "Principal required");
this.principal = authentication.getPrincipal().toString();
if (authentication.getPrincipal() instanceof UserDetails) {
this.principal = ((UserDetails) authentication.getPrincipal()).getUsername();
}
}
//~ Methods ========================================================================================================
public boolean equals(Object object) {
if ((object == null) || !(object instanceof PrincipalSid)) {
return false;
}
// Delegate to getPrincipal() to perform actual comparison (both should be identical)
return ((PrincipalSid) object).getPrincipal().equals(this.getPrincipal());
}
public String getPrincipal() {
return principal;
}
public String toString() {
return "PrincipalSid[" + this.principal + "]";
}
}
@@ -1,20 +1,19 @@
/* 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.acegisecurity.acls.sid;
/* 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.acegisecurity.acls.sid;
/**
* A security identity recognised by the ACL system.
*
@@ -29,23 +28,23 @@ package org.acegisecurity.acls.sid;
*
* @author Ben Alex
* @version $Id$
*/
public interface Sid {
//~ Methods ========================================================================================================
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface contract.
*
* @param obj to be compared
*
* @return <code>true</code> if the objects are equal, <code>false</code> otherwise
*/
public boolean equals(Object obj);
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface contract.
*
* @return a hash code representation of this object
*/
public int hashCode();
}
*/
public interface Sid {
//~ Methods ========================================================================================================
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface contract.
*
* @param obj to be compared
*
* @return <code>true</code> if the objects are equal, <code>false</code> otherwise
*/
public boolean equals(Object obj);
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface contract.
*
* @return a hash code representation of this object
*/
public int hashCode();
}
@@ -0,0 +1,32 @@
/* 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.acegisecurity.acls.sid;
import org.acegisecurity.Authentication;
/**
* Strategy interface that provides an ability to determine the {@link Sid} instances applicable
* for an {@link Authentication}.
*
* @author Ben Alex
* @version $Id$
*/
public interface SidRetrievalStrategy {
//~ Methods ========================================================================================================
public Sid[] getSids(Authentication authentication);
}
@@ -0,0 +1,48 @@
/* 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.acegisecurity.acls.sid;
import org.acegisecurity.Authentication;
import org.acegisecurity.GrantedAuthority;
import java.util.List;
import java.util.Vector;
/**
* Basic implementation of {@link SidRetrievalStrategy} that creates a {@link Sid} for the principal, as well as
* every granted authority the principal holds.<p>The returned array will always contain the {@link PrincipalSid}
* before any {@link GrantedAuthoritySid} elements.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class SidRetrievalStrategyImpl implements SidRetrievalStrategy {
//~ Methods ========================================================================================================
public Sid[] getSids(Authentication authentication) {
List list = new Vector();
list.add(new PrincipalSid(authentication));
GrantedAuthority[] authorities = authentication.getAuthorities();
for (int i = 0; i < authorities.length; i++) {
list.add(new GrantedAuthoritySid(authorities[i]));
}
return (Sid[]) list.toArray(new Sid[] {});
}
}
@@ -0,0 +1,5 @@
<html>
<body>
Provides indirection between ACL packages and security identities, such as principals and GrantedAuthority[]s.
</body>
</html>
@@ -0,0 +1,130 @@
/* 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.acegisecurity.afterinvocation;
import org.acegisecurity.Authentication;
import org.acegisecurity.ConfigAttribute;
import org.acegisecurity.acls.Acl;
import org.acegisecurity.acls.AclService;
import org.acegisecurity.acls.NotFoundException;
import org.acegisecurity.acls.Permission;
import org.acegisecurity.acls.domain.BasePermission;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategy;
import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategyImpl;
import org.acegisecurity.acls.sid.Sid;
import org.acegisecurity.acls.sid.SidRetrievalStrategy;
import org.acegisecurity.acls.sid.SidRetrievalStrategyImpl;
import org.springframework.util.Assert;
/**
* DOCUMENT ME!
*
* @author $author$
* @version $Revision$
*/
public abstract class AbstractAclProvider implements AfterInvocationProvider {
//~ Instance fields ================================================================================================
private AclService aclService;
private Class processDomainObjectClass = Object.class;
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private String processConfigAttribute;
private Permission[] requirePermission = {BasePermission.READ};
//~ Constructors ===================================================================================================
public AbstractAclProvider(AclService aclService, String processConfigAttribute, Permission[] requirePermission) {
Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory");
Assert.notNull(aclService, "An AclService is mandatory");
if ((requirePermission == null) || (requirePermission.length == 0)) {
throw new IllegalArgumentException("One or more requirePermission entries is mandatory");
}
this.aclService = aclService;
this.processConfigAttribute = processConfigAttribute;
this.requirePermission = requirePermission;
}
//~ Methods ========================================================================================================
protected Class getProcessDomainObjectClass() {
return processDomainObjectClass;
}
protected boolean hasPermission(Authentication authentication, Object domainObject) {
// Obtain the OID applicable to the domain object
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
// Obtain the SIDs applicable to the principal
Sid[] sids = sidRetrievalStrategy.getSids(authentication);
Acl acl = null;
try {
// Lookup only ACLs for SIDs we're interested in
acl = aclService.readAclById(objectIdentity, sids);
return acl.isGranted(requirePermission, sids, false);
} catch (NotFoundException ignore) {
return false;
}
}
public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
Assert.notNull(objectIdentityRetrievalStrategy, "ObjectIdentityRetrievalStrategy required");
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
}
protected void setProcessConfigAttribute(String processConfigAttribute) {
Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory");
this.processConfigAttribute = processConfigAttribute;
}
public void setProcessDomainObjectClass(Class processDomainObjectClass) {
Assert.notNull(processDomainObjectClass, "processDomainObjectClass cannot be set to null");
this.processDomainObjectClass = processDomainObjectClass;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null) && attribute.getAttribute().equals(this.processConfigAttribute)) {
return true;
} else {
return false;
}
}
/**
* This implementation supports any type of class, because it does not query the presented secure object.
*
* @param clazz the secure object
*
* @return always <code>true</code>
*/
public boolean supports(Class clazz) {
return true;
}
}
@@ -0,0 +1,133 @@
/* 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.acegisecurity.afterinvocation;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthorizationServiceException;
import org.acegisecurity.ConfigAttribute;
import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.acls.Acl;
import org.acegisecurity.acls.AclService;
import org.acegisecurity.acls.Permission;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.Collection;
import java.util.Iterator;
/**
* <p>Given a <code>Collection</code> of domain object instances returned from a secure object invocation, remove
* any <code>Collection</code> elements the principal does not have appropriate permission to access as defined by the
* {@link AclService}.</p>
* <p>The <code>AclService</code> is used to retrieve the access control list (ACL) permissions associated with
* each <code>Collection</code> domain object instance element for the current <code>Authentication</code> object.</p>
* <p>This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()} matches the {@link
* #processConfigAttribute}. The provider will then lookup the ACLs from the <code>AclService</code> and ensure the
* principal is {@link Acl#isGranted(org.acegisecurity.acls.Permission[], org.acegisecurity.acls.sid.Sid[], boolean)}
* when presenting the {@link #requirePermission} array to that method.</p>
* <p>If the principal does not have permission, that element will not be included in the returned
* <code>Collection</code>.</p>
* <p>Often users will setup a <code>BasicAclEntryAfterInvocationProvider</code> with a {@link
* #processConfigAttribute} of <code>AFTER_ACL_COLLECTION_READ</code> and a {@link #requirePermission} of
* <code>BasePermission.READ</code>. These are also the defaults.</p>
* <p>If the provided <code>returnObject</code> is <code>null</code>, a <code>null</code><code>Collection</code>
* will be returned. If the provided <code>returnObject</code> is not a <code>Collection</code>, an {@link
* AuthorizationServiceException} will be thrown.</p>
* <p>All comparisons and prefixes are case sensitive.</p>
*
* @author Ben Alex
* @author Paulo Neves
* @version $Id$
*/
public class AclEntryAfterInvocationCollectionFilteringProvider extends AbstractAclProvider {
//~ Static fields/initializers =====================================================================================
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class);
//~ Constructors ===================================================================================================
public AclEntryAfterInvocationCollectionFilteringProvider(AclService aclService, Permission[] requirePermission) {
super(aclService, "AFTER_ACL_COLLECTION_READ", requirePermission);
}
//~ Methods ========================================================================================================
public Object decide(Authentication authentication, Object object, ConfigAttributeDefinition config,
Object returnedObject) throws AccessDeniedException {
Iterator iter = config.getConfigAttributes();
while (iter.hasNext()) {
ConfigAttribute attr = (ConfigAttribute) iter.next();
if (this.supports(attr)) {
// Need to process the Collection for this invocation
if (returnedObject == null) {
if (logger.isDebugEnabled()) {
logger.debug("Return object is null, skipping");
}
return null;
}
Filterer filterer = null;
if (returnedObject instanceof Collection) {
Collection collection = (Collection) returnedObject;
filterer = new CollectionFilterer(collection);
} else if (returnedObject.getClass().isArray()) {
Object[] array = (Object[]) returnedObject;
filterer = new ArrayFilterer(array);
} else {
throw new AuthorizationServiceException(
"A Collection or an array (or null) was required as the returnedObject, but the returnedObject was: "
+ returnedObject);
}
// Locate unauthorised Collection elements
Iterator collectionIter = filterer.iterator();
while (collectionIter.hasNext()) {
Object domainObject = collectionIter.next();
boolean hasPermission = false;
if (domainObject == null) {
hasPermission = true;
} else if (!getProcessDomainObjectClass().isAssignableFrom(domainObject.getClass())) {
hasPermission = true;
} else {
hasPermission = hasPermission(authentication, domainObject);
if (!hasPermission) {
filterer.remove(domainObject);
if (logger.isDebugEnabled()) {
logger.debug("Principal is NOT authorised for element: " + domainObject);
}
}
}
}
return filterer.getFilteredObject();
}
}
return returnedObject;
}
}
@@ -0,0 +1,119 @@
/* 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.acegisecurity.afterinvocation;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.AcegiMessageSource;
import org.acegisecurity.Authentication;
import org.acegisecurity.ConfigAttribute;
import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.acls.Acl;
import org.acegisecurity.acls.AclService;
import org.acegisecurity.acls.Permission;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import java.util.Iterator;
/**
* <p>Given a domain object instance returned from a secure object invocation, ensures the principal has
* appropriate permission as defined by the {@link AclService}.</p>
* <p>The <code>AclService</code> is used to retrieve the access control list (ACL) permissions associated with a
* domain object instance for the current <code>Authentication</code> object.</p>
* <p>This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()} matches the {@link
* #processConfigAttribute}. The provider will then lookup the ACLs from the <code>AclService</code> and ensure the
* principal is {@link Acl#isGranted(org.acegisecurity.acls.Permission[], org.acegisecurity.acls.sid.Sid[], boolean)}
* when presenting the {@link #requirePermission} array to that method.</p>
* <p>Often users will setup an <code>AclEntryAfterInvocationProvider</code> with a {@link
* #processConfigAttribute} of <code>AFTER_ACL_READ</code> and a {@link #requirePermission} of
* <code>BasePermission.READ</code>. These are also the defaults.</p>
* <p>If the principal does not have sufficient permissions, an <code>AccessDeniedException</code> will be thrown.</p>
* <p>If the provided <code>returnObject</code> is <code>null</code>, permission will always be granted and
* <code>null</code> will be returned.</p>
* <p>All comparisons and prefixes are case sensitive.</p>
*/
public class AclEntryAfterInvocationProvider extends AbstractAclProvider implements MessageSourceAware {
//~ Static fields/initializers =====================================================================================
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationProvider.class);
//~ Instance fields ================================================================================================
protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor();
//~ Constructors ===================================================================================================
public AclEntryAfterInvocationProvider(AclService aclService, Permission[] requirePermission) {
super(aclService, "AFTER_ACL_READ", requirePermission);
}
//~ Methods ========================================================================================================
public Object decide(Authentication authentication, Object object, ConfigAttributeDefinition config,
Object returnedObject) throws AccessDeniedException {
Iterator iter = config.getConfigAttributes();
while (iter.hasNext()) {
ConfigAttribute attr = (ConfigAttribute) iter.next();
if (this.supports(attr)) {
// Need to make an access decision on this invocation
if (returnedObject == null) {
// AclManager interface contract prohibits nulls
// As they have permission to null/nothing, grant access
if (logger.isDebugEnabled()) {
logger.debug("Return object is null, skipping");
}
return null;
}
if (!getProcessDomainObjectClass().isAssignableFrom(returnedObject.getClass())) {
if (logger.isDebugEnabled()) {
logger.debug("Return object is not applicable for this provider, skipping");
}
return null;
}
if (hasPermission(authentication, returnedObject)) {
return returnedObject;
} else {
if (logger.isDebugEnabled()) {
logger.debug("Denying access");
}
throw new AccessDeniedException(messages.getMessage(
"BasicAclEntryAfterInvocationProvider.noPermission",
new Object[] {authentication.getName(), returnedObject},
"Authentication {0} has NO permissions to the domain object {1}"));
}
}
}
return returnedObject;
}
public void setMessageSource(MessageSource messages) {
this.messages = new MessageSourceAccessor(messages);
}
}
@@ -0,0 +1,101 @@
/* 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.acegisecurity.afterinvocation;
import org.apache.commons.collections.iterators.ArrayIterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.lang.reflect.Array;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* A filter used to filter arrays.
*
* @author Ben Alex
* @author Paulo Neves
* @version $Id$
*/
class ArrayFilterer implements Filterer {
//~ Static fields/initializers =====================================================================================
protected static final Log logger = LogFactory.getLog(BasicAclEntryAfterInvocationCollectionFilteringProvider.class);
//~ Instance fields ================================================================================================
private Set removeList;
private Object[] list;
//~ Constructors ===================================================================================================
ArrayFilterer(Object[] list) {
this.list = list;
// Collect the removed objects to a HashSet so that
// it is fast to lookup them when a filtered array
// is constructed.
removeList = new HashSet();
}
//~ Methods ========================================================================================================
/**
*
* @see org.acegisecurity.afterinvocation.Filterer#getFilteredObject()
*/
public Object getFilteredObject() {
// Recreate an array of same type and filter the removed objects.
int originalSize = list.length;
int sizeOfResultingList = originalSize - removeList.size();
Object[] filtered = (Object[]) Array.newInstance(list.getClass().getComponentType(), sizeOfResultingList);
for (int i = 0, j = 0; i < list.length; i++) {
Object object = list[i];
if (!removeList.contains(object)) {
filtered[j] = object;
j++;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Original array contained " + originalSize + " elements; now contains " + sizeOfResultingList
+ " elements");
}
return filtered;
}
/**
*
* @see org.acegisecurity.afterinvocation.Filterer#iterator()
*/
public Iterator iterator() {
return new ArrayIterator(list);
}
/**
*
* @see org.acegisecurity.afterinvocation.Filterer#remove(java.lang.Object)
*/
public void remove(Object object) {
removeList.add(object);
}
}
@@ -12,7 +12,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.acegisecurity.afterinvocation;
import org.acegisecurity.AccessDeniedException;
@@ -26,7 +25,6 @@ import org.acegisecurity.acl.AclManager;
import org.acegisecurity.acl.basic.BasicAclEntry;
import org.acegisecurity.acl.basic.SimpleAclEntry;
import org.apache.commons.collections.iterators.ArrayIterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -34,12 +32,8 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
@@ -135,42 +129,40 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider implements
boolean hasPermission = false;
AclEntry[] acls = null;
if (domainObject == null) {
hasPermission = true;
} else if (!processDomainObjectClass.isAssignableFrom(domainObject.getClass())) {
hasPermission = true;
} else {
acls = aclManager.getAcls(domainObject, authentication);
}
AclEntry[] acls = aclManager.getAcls(domainObject, authentication);
if ((acls != null) && (acls.length != 0)) {
for (int i = 0; i < acls.length; i++) {
// Locate processable AclEntrys
if (acls[i] instanceof BasicAclEntry) {
BasicAclEntry processableAcl = (BasicAclEntry) acls[i];
if ((acls != null) && (acls.length != 0)) {
for (int i = 0; i < acls.length; i++) {
// Locate processable AclEntrys
if (acls[i] instanceof BasicAclEntry) {
BasicAclEntry processableAcl = (BasicAclEntry) acls[i];
// See if principal has any of the required permissions
for (int y = 0; y < requirePermission.length; y++) {
if (processableAcl.isPermitted(requirePermission[y])) {
hasPermission = true;
// See if principal has any of the required permissions
for (int y = 0; y < requirePermission.length; y++) {
if (processableAcl.isPermitted(requirePermission[y])) {
hasPermission = true;
if (logger.isDebugEnabled()) {
logger.debug("Principal is authorised for element: " + domainObject
+ " due to ACL: " + processableAcl.toString());
if (logger.isDebugEnabled()) {
logger.debug("Principal is authorised for element: " + domainObject
+ " due to ACL: " + processableAcl.toString());
}
}
}
}
}
}
}
if (!hasPermission) {
filterer.remove(domainObject);
if (!hasPermission) {
filterer.remove(domainObject);
if (logger.isDebugEnabled()) {
logger.debug("Principal is NOT authorised for element: " + domainObject);
if (logger.isDebugEnabled()) {
logger.debug("Principal is NOT authorised for element: " + domainObject);
}
}
}
}
@@ -212,9 +204,11 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider implements
}
/**
* Allow setting permissions with String literals instead of integers as {@link #setRequirePermission(int[])}
*
* Allow setting permissions with String literals instead of integers as {@link
* #setRequirePermission(int[])}
*
* @param requirePermission permission literals
*
* @see SimpleAclEntry#parsePermissions(String[]) for valid values
*/
public void setRequirePermissionFromString(String[] requirePermission) {
@@ -240,179 +234,3 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider implements
return true;
}
}
/**
* Filter strategy interface.
*/
interface Filterer {
//~ Methods ========================================================================================================
/**
* Gets the filtered collection or array.
*
* @return the filtered collection or array
*/
public Object getFilteredObject();
/**
* Returns an iterator over the filtered collection or array.
*
* @return an Iterator
*/
public Iterator iterator();
/**
* Removes the the given object from the resulting list.
*
* @param object the object to be removed
*/
public void remove(Object object);
}
/**
* A filter used to filter Collections.
*/
class CollectionFilterer implements Filterer {
//~ Static fields/initializers =====================================================================================
protected static final Log logger = LogFactory.getLog(BasicAclEntryAfterInvocationCollectionFilteringProvider.class);
//~ Instance fields ================================================================================================
private Collection collection;
// collectionIter offers significant performance optimisations (as
// per acegisecurity-developer mailing list conversation 19/5/05)
private Iterator collectionIter;
private Set removeList;
//~ Constructors ===================================================================================================
CollectionFilterer(Collection collection) {
this.collection = collection;
// We create a Set of objects to be removed from the Collection,
// as ConcurrentModificationException prevents removal during
// iteration, and making a new Collection to be returned is
// problematic as the original Collection implementation passed
// to the method may not necessarily be re-constructable (as
// the Collection(collection) constructor is not guaranteed and
// manually adding may lose sort order or other capabilities)
removeList = new HashSet();
}
//~ Methods ========================================================================================================
/**
*
* @see org.acegisecurity.afterinvocation.Filterer#getFilteredObject()
*/
public Object getFilteredObject() {
// Now the Iterator has ended, remove Objects from Collection
Iterator removeIter = removeList.iterator();
int originalSize = collection.size();
while (removeIter.hasNext()) {
collection.remove(removeIter.next());
}
if (logger.isDebugEnabled()) {
logger.debug("Original collection contained " + originalSize + " elements; now contains "
+ collection.size() + " elements");
}
return collection;
}
/**
*
* @see org.acegisecurity.afterinvocation.Filterer#iterator()
*/
public Iterator iterator() {
collectionIter = collection.iterator();
return collectionIter;
}
/**
*
* @see org.acegisecurity.afterinvocation.Filterer#remove(java.lang.Object)
*/
public void remove(Object object) {
removeList.add(object);
}
}
/**
* A filter used to filter arrays.
*/
class ArrayFilterer implements Filterer {
//~ Static fields/initializers =====================================================================================
protected static final Log logger = LogFactory.getLog(BasicAclEntryAfterInvocationCollectionFilteringProvider.class);
//~ Instance fields ================================================================================================
private Set removeList;
private Object[] list;
//~ Constructors ===================================================================================================
ArrayFilterer(Object[] list) {
this.list = list;
// Collect the removed objects to a HashSet so that
// it is fast to lookup them when a filtered array
// is constructed.
removeList = new HashSet();
}
//~ Methods ========================================================================================================
/**
*
* @see org.acegisecurity.afterinvocation.Filterer#getFilteredObject()
*/
public Object getFilteredObject() {
// Recreate an array of same type and filter the removed objects.
int originalSize = list.length;
int sizeOfResultingList = originalSize - removeList.size();
Object[] filtered = (Object[]) Array.newInstance(list.getClass().getComponentType(), sizeOfResultingList);
for (int i = 0, j = 0; i < list.length; i++) {
Object object = list[i];
if (!removeList.contains(object)) {
filtered[j] = object;
j++;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Original array contained " + originalSize + " elements; now contains " + sizeOfResultingList
+ " elements");
}
return filtered;
}
/**
*
* @see org.acegisecurity.afterinvocation.Filterer#iterator()
*/
public Iterator iterator() {
return new ArrayIterator(list);
}
/**
*
* @see org.acegisecurity.afterinvocation.Filterer#remove(java.lang.Object)
*/
public void remove(Object object) {
removeList.add(object);
}
}
@@ -0,0 +1,104 @@
/* 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.acegisecurity.afterinvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* A filter used to filter Collections.
*
* @author Ben Alex
* @author Paulo Neves
* @version $Id$
*/
class CollectionFilterer implements Filterer {
//~ Static fields/initializers =====================================================================================
protected static final Log logger = LogFactory.getLog(BasicAclEntryAfterInvocationCollectionFilteringProvider.class);
//~ Instance fields ================================================================================================
private Collection collection;
// collectionIter offers significant performance optimisations (as
// per acegisecurity-developer mailing list conversation 19/5/05)
private Iterator collectionIter;
private Set removeList;
//~ Constructors ===================================================================================================
CollectionFilterer(Collection collection) {
this.collection = collection;
// We create a Set of objects to be removed from the Collection,
// as ConcurrentModificationException prevents removal during
// iteration, and making a new Collection to be returned is
// problematic as the original Collection implementation passed
// to the method may not necessarily be re-constructable (as
// the Collection(collection) constructor is not guaranteed and
// manually adding may lose sort order or other capabilities)
removeList = new HashSet();
}
//~ Methods ========================================================================================================
/**
*
* @see org.acegisecurity.afterinvocation.Filterer#getFilteredObject()
*/
public Object getFilteredObject() {
// Now the Iterator has ended, remove Objects from Collection
Iterator removeIter = removeList.iterator();
int originalSize = collection.size();
while (removeIter.hasNext()) {
collection.remove(removeIter.next());
}
if (logger.isDebugEnabled()) {
logger.debug("Original collection contained " + originalSize + " elements; now contains "
+ collection.size() + " elements");
}
return collection;
}
/**
*
* @see org.acegisecurity.afterinvocation.Filterer#iterator()
*/
public Iterator iterator() {
collectionIter = collection.iterator();
return collectionIter;
}
/**
*
* @see org.acegisecurity.afterinvocation.Filterer#remove(java.lang.Object)
*/
public void remove(Object object) {
removeList.add(object);
}
}
@@ -13,39 +13,39 @@
* limitations under the License.
*/
package org.acegisecurity.domain.impl;
package org.acegisecurity.afterinvocation;
import java.io.Serializable;
import java.util.Iterator;
/**
* A persistable entity that uses a <code>Integer</code> based identity.
*
* Filter strategy interface.
*
* @author Ben Alex
* @author Paulo Neves
* @version $Id$
*/
public abstract class PersistableEntityInteger extends AbstractPersistableEntity {
//~ Instance fields ================================================================================================
private Integer id;
interface Filterer {
//~ Methods ========================================================================================================
/**
* Obtains the persistence identity of this instance.
* Gets the filtered collection or array.
*
* @return DOCUMENT ME!
* @return the filtered collection or array
*/
public Integer getId() {
return this.id;
}
public Object getFilteredObject();
/**
* Required solely because Hibernate
* Returns an iterator over the filtered collection or array.
*
* @return DOCUMENT ME!
* @return an Iterator
*/
public Serializable getInternalId() {
return this.id;
}
public Iterator iterator();
/**
* Removes the the given object from the resulting list.
*
* @param object the object to be removed
*/
public void remove(Object object);
}
@@ -15,12 +15,8 @@
package org.acegisecurity.context;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import java.io.IOException;
import java.lang.reflect.Method;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@@ -31,6 +27,12 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* <p>Populates the {@link SecurityContextHolder} with information obtained from the <code>HttpSession</code>.</p>
@@ -99,8 +101,28 @@ public class HttpSessionContextIntegrationFilter implements InitializingBean, Fi
* are conscious of the session creation overhead.
*/
private boolean forceEagerSessionCreation = false;
/**
* Indicates whether the <code>SecurityContext</code> will be cloned from the <code>HttpSession</code>. The
* default is to simply reference (ie the default is <code>false</code>). The default may cause issues if
* concurrent threads need to have a different security identity from other threads being concurrently processed
* that share the same <code>HttpSession</code>. In most normal environments this does not represent an issue,
* as changes to the security identity in one thread is allowed to affect the security identitiy in other
* threads associated with the same <code>HttpSession</code>. For unusual cases where this is not permitted,
* change this value to <code>true</code> and ensure the {@link #context} is set to a <code>SecurityContext</code>
* that implements {@link Cloneable} and overrides the <code>clone()</code> method.
*/
private boolean cloneFromHttpSession = false;
public HttpSessionContextIntegrationFilter() throws ServletException {
public boolean isCloneFromHttpSession() {
return cloneFromHttpSession;
}
public void setCloneFromHttpSession(boolean cloneFromHttpSession) {
this.cloneFromHttpSession = cloneFromHttpSession;
}
public HttpSessionContextIntegrationFilter() throws ServletException {
this.contextObject = generateNewContext();
}
@@ -145,7 +167,21 @@ public class HttpSessionContextIntegrationFilter implements InitializingBean, Fi
httpSessionExistedAtStartOfRequest = true;
Object contextFromSessionObject = httpSession.getAttribute(ACEGI_SECURITY_CONTEXT_KEY);
// Clone if required (see SEC-356)
if (cloneFromHttpSession) {
Assert.isInstanceOf(Cloneable.class, contextFromSessionObject, "Context must implement Clonable and provide a Object.clone() method");
try {
Method m = contextFromSessionObject.getClass().getMethod("clone", new Class[] {});
if (!m.isAccessible()) {
m.setAccessible(true);
}
contextFromSessionObject = m.invoke(contextFromSessionObject, new Object[] {});
} catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
if (contextFromSessionObject != null) {
if (contextFromSessionObject instanceof SecurityContext) {
if (logger.isDebugEnabled()) {
@@ -15,13 +15,19 @@
package org.acegisecurity.event.authorization;
import org.acegisecurity.AccessDecisionManager;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.AfterInvocationManager;
import org.acegisecurity.Authentication;
import org.acegisecurity.ConfigAttributeDefinition;
/**
* Indicates a secure object invocation failed because the principal could not be authorized for the request.
* Indicates a secure object invocation failed because the principal could not
* be authorized for the request.
*
* <p>This event might be thrown as a result of either an
* {@link AccessDecisionManager} or an {@link AfterInvocationManager}.
*
* @author Ben Alex
* @version $Id$
@@ -148,8 +148,17 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
}
if (afterInvocationManager != null) {
returnedObject = afterInvocationManager.decide(token.getAuthentication(), token.getSecureObject(),
token.getAttr(), returnedObject);
// Attempt after invocation handling
try {
returnedObject = afterInvocationManager.decide(token.getAuthentication(), token.getSecureObject(),
token.getAttr(), returnedObject);
} catch (AccessDeniedException accessDeniedException) {
AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(),
token.getAttr(), token.getAuthentication(), accessDeniedException);
publishEvent(event);
throw accessDeniedException;
}
}
return returnedObject;
@@ -15,18 +15,18 @@
package org.acegisecurity.intercept.web;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.beans.PropertyEditorSupport;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import org.acegisecurity.util.StringSplitUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;
/**
* Property editor to assist with the setup of a {@link FilterInvocationDefinitionSource}.<p>The class creates and
@@ -50,10 +50,10 @@ public class FilterInvocationDefinitionSourceEditor extends PropertyEditorSuppor
public void setAsText(String s) throws IllegalArgumentException {
FilterInvocationDefinitionDecorator source = new FilterInvocationDefinitionDecorator();
source.setDecorated(new RegExpBasedFilterInvocationDefinitionMap());
if ((s == null) || "".equals(s)) {
// Leave target object empty
source.setDecorated(new RegExpBasedFilterInvocationDefinitionMap());
} else {
// Check if we need to override the default definition map
if (s.lastIndexOf(DIRECTIVE_PATTERN_TYPE_APACHE_ANT) != -1) {
@@ -63,6 +63,8 @@ public class FilterInvocationDefinitionSourceEditor extends PropertyEditorSuppor
logger.debug(("Detected " + DIRECTIVE_PATTERN_TYPE_APACHE_ANT
+ " directive; using Apache Ant style path expressions"));
}
} else {
source.setDecorated(new RegExpBasedFilterInvocationDefinitionMap());
}
if (s.lastIndexOf(DIRECTIVE_CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON) != -1) {
@@ -131,10 +133,10 @@ public class FilterInvocationDefinitionSourceEditor extends PropertyEditorSuppor
// Tokenize the line into its name/value tokens
// As per SEC-219, use the LAST equals as the delimiter between LHS and RHS
String name = StringUtils.substringBeforeLast(line, "=");
String value = StringUtils.substringAfterLast(line, "=");
String name = StringSplitUtils.substringBeforeLast(line, "=");
String value = StringSplitUtils.substringAfterLast(line, "=");
if (StringUtils.isBlank(name) || StringUtils.isBlank(value)) {
if (!StringUtils.hasText(name) || !StringUtils.hasText(value)) {
throw new IllegalArgumentException("Failed to parse a valid name/value pair from " + line);
}
@@ -273,17 +273,8 @@ public class ProviderManager extends AbstractAuthenticationManager implements In
Iterator iter = newList.iterator();
while (iter.hasNext()) {
Object currentObject = null;
try {
currentObject = iter.next();
//TODO bad idea, should use assignable from or instance of
AuthenticationProvider attemptToCast = (AuthenticationProvider) currentObject;
} catch (ClassCastException cce) {
throw new IllegalArgumentException("AuthenticationProvider " + currentObject.getClass().getName()
+ " must implement AuthenticationProvider");
}
Object currentObject = iter.next();
Assert.isInstanceOf(AuthenticationProvider.class, currentObject, "Can only provide AuthenticationProvider instances");
}
this.providers = newList;
@@ -36,7 +36,7 @@ import org.springframework.util.Assert;
public abstract class AbstractTicketValidator implements TicketValidator, InitializingBean {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(CasProxyTicketValidator.class);
private static final Log logger = LogFactory.getLog(AbstractTicketValidator.class);
//~ Instance fields ================================================================================================
@@ -44,6 +44,7 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
private PasswordEncoder passwordEncoder = new PlaintextPasswordEncoder();
private SaltSource saltSource;
private UserDetailsService userDetailsService;
private boolean includeDetailsObject = true;
//~ Methods ========================================================================================================
@@ -58,7 +59,7 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
if (!passwordEncoder.isPasswordValid(userDetails.getPassword(), authentication.getCredentials().toString(), salt)) {
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), includeDetailsObject ? userDetails : null);
}
}
@@ -120,4 +121,12 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
public boolean isIncludeDetailsObject() {
return includeDetailsObject;
}
public void setIncludeDetailsObject(boolean includeDetailsObject) {
this.includeDetailsObject = includeDetailsObject;
}
}
@@ -120,10 +120,11 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
private LdapAuthenticator authenticator;
private LdapAuthoritiesPopulator authoritiesPopulator;
private boolean includeDetailsObject = true;
//~ Constructors ===================================================================================================
/**
/**
* Create an initialized instance to the values passed as arguments
*
* @param authenticator
@@ -159,7 +160,7 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
throws AuthenticationException {
if (!userDetails.getPassword().equals(authentication.getCredentials().toString())) {
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), includeDetailsObject ? userDetails : null);
}
}
@@ -220,4 +221,13 @@ public class LdapAuthenticationProvider extends AbstractUserDetailsAuthenticatio
throw new AuthenticationServiceException(ldapAccessFailure.getMessage(), ldapAccessFailure);
}
}
public boolean isIncludeDetailsObject() {
return includeDetailsObject;
}
public void setIncludeDetailsObject(boolean includeDetailsObject) {
this.includeDetailsObject = includeDetailsObject;
}
}
@@ -0,0 +1,223 @@
/* 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.acegisecurity.taglibs.authz;
import org.acegisecurity.acls.Acl;
import org.acegisecurity.acls.AclService;
import org.acegisecurity.acls.NotFoundException;
import org.acegisecurity.acls.Permission;
import org.acegisecurity.acls.domain.BasePermission;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategy;
import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategyImpl;
import org.acegisecurity.acls.sid.Sid;
import org.acegisecurity.acls.sid.SidRetrievalStrategy;
import org.acegisecurity.acls.sid.SidRetrievalStrategyImpl;
import org.acegisecurity.context.SecurityContextHolder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.util.ExpressionEvaluationUtils;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import javax.servlet.ServletContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
/**
* An implementation of {@link javax.servlet.jsp.tagext.Tag} that allows its body through if some authorizations
* are granted to the request's principal.<p>One or more comma separate numeric are specified via the
* <code>hasPermission</code> attribute. Those permissions are then converted into {@link Permission} instances. These
* instances are then presented as an array to the {@link Acl#isGranted(Permission[],
* org.acegisecurity.acls.sid.Sid[], boolean)} method. The {@link Sid} presented is determined by the {@link
* SidRetrievalStrategy}.</p>
* <p>For this class to operate it must be able to access the application context via the
* <code>WebApplicationContextUtils</code> and locate an {@link AclService} and {@link SidRetrievalStrategy}.
* Application contexts must provide one and only one of these Java types.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class AccessControlListTag extends TagSupport {
//~ Static fields/initializers =====================================================================================
protected static final Log logger = LogFactory.getLog(AccessControlListTag.class);
//~ Instance fields ================================================================================================
private AclService aclService;
private ApplicationContext applicationContext;
private Object domainObject;
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy;
private SidRetrievalStrategy sidRetrievalStrategy;
private String hasPermission = "";
//~ Methods ========================================================================================================
public int doStartTag() throws JspException {
initializeIfRequired();
if ((null == hasPermission) || "".equals(hasPermission)) {
return Tag.SKIP_BODY;
}
final String evaledPermissionsString = ExpressionEvaluationUtils.evaluateString("hasPermission", hasPermission,
pageContext);
Permission[] requiredPermissions = null;
try {
requiredPermissions = parsePermissionsString(evaledPermissionsString);
} catch (NumberFormatException nfe) {
throw new JspException(nfe);
}
Object resolvedDomainObject = null;
if (domainObject instanceof String) {
resolvedDomainObject = ExpressionEvaluationUtils.evaluate("domainObject", (String) domainObject,
Object.class, pageContext);
} else {
resolvedDomainObject = domainObject;
}
if (resolvedDomainObject == null) {
if (logger.isDebugEnabled()) {
logger.debug("domainObject resolved to null, so including tag body");
}
// Of course they have access to a null object!
return Tag.EVAL_BODY_INCLUDE;
}
if (SecurityContextHolder.getContext().getAuthentication() == null) {
if (logger.isDebugEnabled()) {
logger.debug(
"SecurityContextHolder did not return a non-null Authentication object, so skipping tag body");
}
return Tag.SKIP_BODY;
}
Sid[] sids = sidRetrievalStrategy.getSids(SecurityContextHolder.getContext().getAuthentication());
ObjectIdentity oid = objectIdentityRetrievalStrategy.getObjectIdentity(resolvedDomainObject);
// Obtain aclEntrys applying to the current Authentication object
try {
Acl acl = aclService.readAclById(oid, sids);
if (acl.isGranted(requiredPermissions, sids, false)) {
return Tag.EVAL_BODY_INCLUDE;
} else {
return Tag.SKIP_BODY;
}
} catch (NotFoundException nfe) {
return Tag.SKIP_BODY;
}
}
/**
* Allows test cases to override where application context obtained from.
*
* @param pageContext so the <code>ServletContext</code> can be accessed as required by Spring's
* <code>WebApplicationContextUtils</code>
*
* @return the Spring application context (never <code>null</code>)
*/
protected ApplicationContext getContext(PageContext pageContext) {
ServletContext servletContext = pageContext.getServletContext();
return WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
}
public Object getDomainObject() {
return domainObject;
}
public String getHasPermission() {
return hasPermission;
}
private void initializeIfRequired() throws JspException {
if (applicationContext == null) {
this.applicationContext = getContext(pageContext);
Map map = applicationContext.getBeansOfType(AclService.class);
if (map.size() != 1) {
throw new JspException(
"Found incorrect number of AclService instances in application context - you must have only have one!");
}
aclService = (AclService) map.values().iterator().next();
map = applicationContext.getBeansOfType(SidRetrievalStrategy.class);
if (map.size() == 0) {
sidRetrievalStrategy = new SidRetrievalStrategyImpl();
} else if (map.size() == 1) {
sidRetrievalStrategy = (SidRetrievalStrategy) map.values().iterator().next();
} else {
throw new JspException(
"Found incorrect number of SidRetrievalStrategy instances in application context - you must have only have one!");
}
map = applicationContext.getBeansOfType(ObjectIdentityRetrievalStrategy.class);
if (map.size() == 0) {
objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
} else if (map.size() == 1) {
objectIdentityRetrievalStrategy = (ObjectIdentityRetrievalStrategy) map.values().iterator().next();
} else {
throw new JspException(
"Found incorrect number of ObjectIdentityRetrievalStrategy instances in application context - you must have only have one!");
}
}
}
private Permission[] parsePermissionsString(String integersString)
throws NumberFormatException {
final Set permissions = new HashSet();
final StringTokenizer tokenizer;
tokenizer = new StringTokenizer(integersString, ",", false);
while (tokenizer.hasMoreTokens()) {
String integer = tokenizer.nextToken();
permissions.add(BasePermission.buildFromMask(new Integer(integer).intValue()));
}
return (Permission[]) permissions.toArray(new Permission[] {});
}
public void setDomainObject(Object domainObject) {
this.domainObject = domainObject;
}
public void setHasPermission(String hasPermission) {
this.hasPermission = hasPermission;
}
}
@@ -150,7 +150,7 @@ public class AuthorizeTag extends TagSupport {
// Remove the role's whitespace characters without depending on JDK 1.4+
// Includes space, tab, new line, carriage return and form feed.
String role = StringUtils.replace(authority, " ", "");
String role = authority.trim(); // trim, don't use spaces, as per SEC-378
role = StringUtils.replace(role, "\t", "");
role = StringUtils.replace(role, "\r", "");
role = StringUtils.replace(role, "\n", "");
@@ -428,4 +428,9 @@ public abstract class AbstractProcessingFilter implements Filter, InitializingBe
sendRedirect(request, response, failureUrl);
}
public AuthenticationDetailsSource getAuthenticationDetailsSource() {
// Required due to SEC-310
return authenticationDetailsSource;
}
}
@@ -94,10 +94,8 @@ public class LogoutFilter implements Filter {
logger.debug("Logging out user '" + auth + "' and redirecting to logout page");
}
if (auth != null) {
for (int i = 0; i < handlers.length; i++) {
handlers[i].logout(httpRequest, httpResponse, auth);
}
for (int i = 0; i < handlers.length; i++) {
handlers[i].logout(httpRequest, httpResponse, auth);
}
sendRedirect(httpRequest, httpResponse, logoutSuccessUrl);
@@ -90,6 +90,7 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial
private String parameter = DEFAULT_PARAMETER;
private UserDetailsService userDetailsService;
private long tokenValiditySeconds = 1209600; // 14 days
private boolean alwaysRemember = false;
//~ Methods ========================================================================================================
@@ -238,10 +239,18 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial
cancelCookie(request, response, "Interactive authentication attempt was unsuccessful");
}
protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
if (alwaysRemember) {
return true;
}
return RequestUtils.getBooleanParameter(request, parameter, false);
}
public void loginSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication successfulAuthentication) {
// Exit if the principal hasn't asked to be remembered
if (!RequestUtils.getBooleanParameter(request, parameter, false)) {
if (!rememberMeRequested(request, parameter)) {
if (logger.isDebugEnabled()) {
logger.debug("Did not send remember-me cookie (principal did not set parameter '" + this.parameter
+ "')");
@@ -289,7 +298,7 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial
protected Cookie makeCancelCookie(HttpServletRequest request) {
Cookie cookie = new Cookie(ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY, null);
cookie.setMaxAge(0);
cookie.setPath(request.getContextPath());
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
return cookie;
}
@@ -297,7 +306,7 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial
protected Cookie makeValidCookie(long expiryTime, String tokenValueBase64, HttpServletRequest request) {
Cookie cookie = new Cookie(ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY, tokenValueBase64);
cookie.setMaxAge(60 * 60 * 24 * 365 * 5); // 5 years
cookie.setPath(request.getContextPath());
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
return cookie;
}
@@ -322,4 +331,12 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
public boolean isAlwaysRemember() {
return alwaysRemember;
}
public void setAlwaysRemember(boolean alwaysRemember) {
this.alwaysRemember = alwaysRemember;
}
}
@@ -110,8 +110,15 @@ public class SavedRequest implements java.io.Serializable {
while (paramNames.hasNext()) {
String paramName = (String) paramNames.next();
String[] paramValues = (String[]) parameters.get(paramName);
this.addParameter(paramName, paramValues);
Object o = parameters.get(paramName);
if (o instanceof String[]) {
String[] paramValues = (String[]) o;
this.addParameter(paramName, paramValues);
} else {
if (logger.isWarnEnabled()) {
logger.warn("ServletRequest.getParameterMap() returned non-String array");
}
}
}
// Primitives
@@ -0,0 +1,30 @@
package org.acegisecurity.ui.switchuser;
import java.util.List;
import org.acegisecurity.Authentication;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.userdetails.UserDetails;
/**
* Allows subclasses to modify the {@link GrantedAuthority} list that will be assigned to the principal
* when they assume the identity of a different principal.
*
* <p>Configured against the {@link SwitchUserProcessingFilter}.
*
* @author Ben Alex
* @version $Id$
*
*/
public interface SwitchUserAuthorityChanger {
/**
* Allow subclasses to add or remove authorities that will be granted when in switch user mode.
*
* @param targetUser the UserDetails representing the identity being switched to
* @param currentAuthentication the current Authentication of the principal performing the switching
* @param authoritiesToBeGranted all {@link GrantedAuthority} instances to be granted to the user,
* excluding the special "switch user" authority that is used internally (guaranteed never null)
*/
public void modifyGrantedAuthorities(UserDetails targetUser, Authentication currentAuthentication, List authoritiesToBeGranted);
}
@@ -115,6 +115,7 @@ public class SwitchUserProcessingFilter implements Filter, InitializingBean, App
private String exitUserUrl = "/j_acegi_exit_user";
private String switchUserUrl = "/j_acegi_switch_user";
private String targetUrl;
private SwitchUserAuthorityChanger switchUserAuthorityChanger;
// ~ Instance fields
// ========================================================
@@ -277,6 +278,11 @@ public class SwitchUserProcessingFilter implements Filter, InitializingBean, App
// get the original authorities
List orig = Arrays.asList(targetUser.getAuthorities());
// Allow subclasses to change the authorities to be granted
if (switchUserAuthorityChanger != null) {
switchUserAuthorityChanger.modifyGrantedAuthorities(targetUser, currentAuth, orig);
}
// add the new switch user authority
List newAuths = new ArrayList(orig);
newAuths.add(switchAuthority);
@@ -460,4 +466,12 @@ public class SwitchUserProcessingFilter implements Filter, InitializingBean, App
return uri;
}
/**
* @param switchUserAuthorityChanger to use to fine-tune the authorities granted to subclasses (may be null if
* SwitchUserProcessingFilter shoudl not fine-tune the authorities)
*/
public void setSwitchUserAuthorityChanger(SwitchUserAuthorityChanger switchUserAuthorityChanger) {
this.switchUserAuthorityChanger = switchUserAuthorityChanger;
}
}
@@ -33,6 +33,7 @@ import org.springframework.util.Assert;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@@ -66,6 +67,7 @@ public class AuthenticationProcessingFilterEntryPoint implements AuthenticationE
private PortResolver portResolver = new PortResolverImpl();
private String loginFormUrl;
private boolean forceHttps = false;
private boolean serverSideRedirect = false;
//~ Methods ========================================================================================================
@@ -88,27 +90,69 @@ public class AuthenticationProcessingFilterEntryPoint implements AuthenticationE
boolean includePort = true;
String redirectUrl = null;
boolean doForceHttps = false;
Integer httpsPort = null;
if (inHttp && (serverPort == 80)) {
includePort = false;
} else if (inHttps && (serverPort == 443)) {
includePort = false;
}
String redirectUrl = scheme + "://" + serverName + ((includePort) ? (":" + serverPort) : "") + contextPath
+ loginFormUrl;
if (forceHttps && inHttp) {
Integer httpsPort = (Integer) portMapper.lookupHttpsPort(new Integer(serverPort));
httpsPort = (Integer) portMapper.lookupHttpsPort(new Integer(serverPort));
if (httpsPort != null) {
doForceHttps = true;
if (httpsPort.intValue() == 443) {
includePort = false;
} else {
includePort = true;
}
}
}
if ( serverSideRedirect ) {
if ( doForceHttps ) {
// before doing server side redirect, we need to do client redirect to https.
String servletPath = req.getServletPath();
String pathInfo = req.getPathInfo();
String query = req.getQueryString();
redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
+ servletPath + (pathInfo == null ? "" : pathInfo ) + (query == null ? "" : "?"+query );
} else {
if (logger.isDebugEnabled()) {
logger.debug("Server side forward to: " + loginFormUrl);
}
RequestDispatcher dispatcher = req.getRequestDispatcher( loginFormUrl );
dispatcher.forward( request, response );
return;
}
} else {
if ( doForceHttps ) {
redirectUrl = "https://" + serverName + ((includePort) ? (":" + httpsPort) : "") + contextPath
+ loginFormUrl;
} else {
redirectUrl = scheme + "://" + serverName + ((includePort) ? (":" + serverPort) : "") + contextPath
+ loginFormUrl;
}
}
@@ -135,6 +179,10 @@ public class AuthenticationProcessingFilterEntryPoint implements AuthenticationE
return portResolver;
}
public boolean isServerSideRedirect() {
return serverSideRedirect;
}
/**
* Set to true to force login form access to be via https. If this value is ture (the default is false),
* and the incoming request for the protected resource which triggered the interceptor was not already
@@ -163,4 +211,15 @@ public class AuthenticationProcessingFilterEntryPoint implements AuthenticationE
public void setPortResolver(PortResolver portResolver) {
this.portResolver = portResolver;
}
/**
* Tells if we are to do a server side include of the <code>loginFormUrl</code> instead of a 302
* redirect.
*
* @param serverSideRedirect
*/
public void setServerSideRedirect(boolean serverSideRedirect) {
this.serverSideRedirect = serverSideRedirect;
}
}
@@ -38,6 +38,12 @@ import java.io.Serializable;
* {@link org.acegisecurity.userdetails.User} for a
* reference implementation (which you might like to extend).
* </p>
*
* <p>
* Concrete implementations should be immutable (value object semantics,
* like a String). This is because the <code>UserDetails</code> will be
* stored in caches and as such multiple threads may use the same instance.
* </p>
*
* @author Ben Alex
* @version $Id$
@@ -25,7 +25,6 @@ import javax.crypto.spec.DESedeKeySpec;
import org.acegisecurity.AcegiSecurityException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.Validate;
import org.springframework.util.Assert;
/**
@@ -106,6 +105,19 @@ public class EncryptionUtils {
return byteArrayToString(Base64.encodeBase64(cipherText));
}
/**
* Encrypts the inputBytes using the key.
*
* @param key at least 24 character long key (required)
* @param inputBytes the bytes to encrypt (required)
* @return the encrypted version of the inputBytes
* @throws EncryptionException in the event of an encryption failure
*/
public static byte[] encrypt(String key, byte[] inputBytes) throws EncryptionException {
isValidKey(key);
return Base64.encodeBase64(cipher(key, inputBytes, Cipher.ENCRYPT_MODE));
}
/**
* Decrypts the inputString using the key.
*
@@ -120,9 +132,22 @@ public class EncryptionUtils {
return byteArrayToString(cipherText);
}
/**
* Decrypts the inputBytes using the key.
*
* @param key the key used to originally encrypt the string (required)
* @param inputBytes the encrypted bytes (required)
* @return the decrypted version of inputBytes
* @throws EncryptionException in the event of an encryption failure
*/
public static byte[] decrypt(String key, byte[] inputBytes) throws EncryptionException {
Assert.hasText(key, "A key is required to attempt decryption");
return cipher(key, Base64.decodeBase64(inputBytes), Cipher.DECRYPT_MODE);
}
private static void isValidKey(String key) {
Assert.hasText(key, "A key to perform the encryption is required");
Validate.isTrue(key.length() >= 24, "Key must be at least 24 characters long");
Assert.isTrue(key.length() >= 24, "Key must be at least 24 characters long");
}
public static class EncryptionException extends AcegiSecurityException {
@@ -0,0 +1,101 @@
/* 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.acegisecurity.util;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
/**
* Offers static methods for directly manipulating static fields.
*
* @author Ben Alex
* @version $Id$
*/
public class FieldUtils {
//~ Methods ========================================================================================================
public static String getAccessorName(String fieldName, Class type) {
Assert.hasText(fieldName, "FieldName required");
Assert.notNull(type, "Type required");
if (type.getName().equals("boolean")) {
return "is" + org.springframework.util.StringUtils.capitalize(fieldName);
} else {
return "get" + org.springframework.util.StringUtils.capitalize(fieldName);
}
}
/**
* Attempts to locate the specified field on the class.
*
* @param clazz the class definition containing the field
* @param fieldName the name of the field to locate
*
* @return the Field (never null)
*
* @throws IllegalStateException if field could not be found
*/
public static Field getField(Class clazz, String fieldName)
throws IllegalStateException {
Assert.notNull(clazz, "Class required");
Assert.hasText(fieldName, "Field name required");
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException nsf) {
// Try superclass
if (clazz.getSuperclass() != null) {
return getField(clazz.getSuperclass(), fieldName);
}
throw new IllegalStateException("Could not locate field '" + fieldName + "' on class " + clazz);
}
}
public static String getMutatorName(String fieldName) {
Assert.hasText(fieldName, "FieldName required");
return "set" + org.springframework.util.StringUtils.capitalize(fieldName);
}
public static Object getProtectedFieldValue(String protectedField, Object object) {
Field field = FieldUtils.getField(object.getClass(), protectedField);
try {
field.setAccessible(true);
return field.get(object);
} catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
return null; // unreachable - previous line throws exception
}
}
public static void setProtectedFieldValue(String protectedField, Object object, Object newValue) {
Field field = FieldUtils.getField(object.getClass(), protectedField);
try {
field.setAccessible(true);
field.set(object, newValue);
} catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
}
@@ -104,4 +104,31 @@ public class StringSplitUtils {
return map;
}
public static String substringBeforeLast(String str, String separator) {
if (str == null || separator == null || str.length() == 0 ||
separator.length() == 0) {
return str;
}
int pos = str.lastIndexOf(separator);
if (pos == -1) {
return str;
}
return str.substring(0, pos);
}
public static String substringAfterLast(String str, String separator) {
if (str == null || str.length() == 0) {
return str;
}
if (separator == null || separator.length() == 0) {
return "";
}
int pos = str.lastIndexOf(separator);
if (pos == -1 || pos == (str.length() - separator.length())) {
return "";
}
return str.substring(pos + separator.length());
}
}
@@ -12,13 +12,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.acegisecurity.vote;
import org.acegisecurity.AuthorizationServiceException;
import org.acegisecurity.acl.AclManager;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.JoinPoint;
@@ -28,8 +25,7 @@ import org.springframework.util.Assert;
/**
* <p>Given a domain object instance passed as a method argument, ensures the principal has appropriate permission
* as defined by the {@link AclManager}.</p>
* <p>Provides helper methods for writing domain object ACL voters. Is not bound to any particular ACL system.</p>
*
* @author Ben Alex
* @version $Id$
@@ -0,0 +1,251 @@
/* 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.acegisecurity.vote;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthorizationServiceException;
import org.acegisecurity.ConfigAttribute;
import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.acls.Acl;
import org.acegisecurity.acls.AclService;
import org.acegisecurity.acls.NotFoundException;
import org.acegisecurity.acls.Permission;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategy;
import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategyImpl;
import org.acegisecurity.acls.sid.Sid;
import org.acegisecurity.acls.sid.SidRetrievalStrategy;
import org.acegisecurity.acls.sid.SidRetrievalStrategyImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
/**
* <p>Given a domain object instance passed as a method argument, ensures the principal has appropriate permission
* as indicated by the {@link AclService}.</p>
* <p>The <code>AclService</code> is used to retrieve the access control list (ACL) permissions associated with a
* domain object instance for the current <code>Authentication</code> object.</p>
* <p>The voter will vote if any {@link ConfigAttribute#getAttribute()} matches the {@link
* #processConfigAttribute}. The provider will then locate the first method argument of type {@link
* #processDomainObjectClass}. Assuming that method argument is non-null, the provider will then lookup the ACLs from
* the <code>AclManager</code> and ensure the principal is {@link Acl#isGranted(org.acegisecurity.acls.Permission[],
* org.acegisecurity.acls.sid.Sid[], boolean)} when presenting the {@link #requirePermission} array to that method.</p>
* <p>If the method argument is <code>null</code>, the voter will abstain from voting. If the method argument
* could not be found, an {@link org.acegisecurity.AuthorizationServiceException} will be thrown.</p>
* <p>In practical terms users will typically setup a number of <code>AclEntryVoter</code>s. Each will have a
* different {@link #processDomainObjectClass}, {@link #processConfigAttribute} and {@link #requirePermission}
* combination. For example, a small application might employ the following instances of <code>AclEntryVoter</code>:
* <ul>
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
* <code>VOTE_ACL_BANK_ACCONT_READ</code>, require permission <code>BasePermission.READ</code></li>
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
* <code>VOTE_ACL_BANK_ACCOUNT_WRITE</code>, require permission list <code>BasePermission.WRITE</code> and
* <code>BasePermission.CREATE</code> (allowing the principal to have <b>either</b> of these two permissions</li>
* <li>Process domain object class <code>Customer</code>, configuration attribute
* <code>VOTE_ACL_CUSTOMER_READ</code>, require permission <code>BasePermission.READ</code></li>
* <li>Process domain object class <code>Customer</code>, configuration attribute
* <code>VOTE_ACL_CUSTOMER_WRITE</code>, require permission list <code>BasePermission.WRITE</code> and
* <code>BasePermission.CREATE</code></li>
* </ul>
* Alternatively, you could have used a common superclass or interface for the {@link #processDomainObjectClass}
* if both <code>BankAccount</code> and <code>Customer</code> had common parents.</p>
* <p>If the principal does not have sufficient permissions, the voter will vote to deny access.</p>
* <p>All comparisons and prefixes are case sensitive.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class AclEntryVoter extends AbstractAclVoter {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(AclEntryVoter.class);
//~ Instance fields ================================================================================================
private AclService aclService;
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private String internalMethod;
private String processConfigAttribute;
private Permission[] requirePermission;
//~ Constructors ===================================================================================================
public AclEntryVoter(AclService aclService, String processConfigAttribute, Permission[] requirePermission) {
Assert.notNull(processConfigAttribute, "A processConfigAttribute is mandatory");
Assert.notNull(aclService, "An AclService is mandatory");
if ((requirePermission == null) || (requirePermission.length == 0)) {
throw new IllegalArgumentException("One or more requirePermission entries is mandatory");
}
this.aclService = aclService;
this.processConfigAttribute = processConfigAttribute;
this.requirePermission = requirePermission;
}
//~ Methods ========================================================================================================
/**
* Optionally specifies a method of the domain object that will be used to obtain a contained domain
* object. That contained domain object will be used for the ACL evaluation. This is useful if a domain object
* contains a parent that an ACL evaluation should be targeted for, instead of the child domain object (which
* perhaps is being created and as such does not yet have any ACL permissions)
*
* @return <code>null</code> to use the domain object, or the name of a method (that requires no arguments) that
* should be invoked to obtain an <code>Object</code> which will be the domain object used for ACL
* evaluation
*/
public String getInternalMethod() {
return internalMethod;
}
public String getProcessConfigAttribute() {
return processConfigAttribute;
}
public void setInternalMethod(String internalMethod) {
this.internalMethod = internalMethod;
}
public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
Assert.notNull(objectIdentityRetrievalStrategy, "ObjectIdentityRetrievalStrategy required");
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getProcessConfigAttribute())) {
return true;
} else {
return false;
}
}
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
Iterator iter = config.getConfigAttributes();
while (iter.hasNext()) {
ConfigAttribute attr = (ConfigAttribute) iter.next();
if (this.supports(attr)) {
// Need to make an access decision on this invocation
// Attempt to locate the domain object instance to process
Object domainObject = getDomainObjectInstance(object);
// If domain object is null, vote to abstain
if (domainObject == null) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to abstain - domainObject is null");
}
return AccessDecisionVoter.ACCESS_ABSTAIN;
}
// Evaluate if we are required to use an inner domain object
if ((internalMethod != null) && !"".equals(internalMethod)) {
try {
Class clazz = domainObject.getClass();
Method method = clazz.getMethod(internalMethod, new Class[] {});
domainObject = method.invoke(domainObject, new Object[] {});
} catch (NoSuchMethodException nsme) {
throw new AuthorizationServiceException("Object of class '" + domainObject.getClass()
+ "' does not provide the requested internalMethod: " + internalMethod);
} catch (IllegalAccessException iae) {
if (logger.isDebugEnabled()) {
logger.debug("IllegalAccessException", iae);
if (iae.getCause() != null) {
logger.debug("Cause: " + iae.getCause().getMessage(), iae.getCause());
}
}
throw new AuthorizationServiceException("Problem invoking internalMethod: " + internalMethod
+ " for object: " + domainObject);
} catch (InvocationTargetException ite) {
if (logger.isDebugEnabled()) {
logger.debug("InvocationTargetException", ite);
if (ite.getCause() != null) {
logger.debug("Cause: " + ite.getCause().getMessage(), ite.getCause());
}
}
throw new AuthorizationServiceException("Problem invoking internalMethod: " + internalMethod
+ " for object: " + domainObject);
}
}
// Obtain the OID applicable to the domain object
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
// Obtain the SIDs applicable to the principal
Sid[] sids = sidRetrievalStrategy.getSids(authentication);
Acl acl;
try {
// Lookup only ACLs for SIDs we're interested in
acl = aclService.readAclById(objectIdentity, sids);
} catch (NotFoundException nfe) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to deny access - no ACLs apply for this principal");
}
return AccessDecisionVoter.ACCESS_DENIED;
}
try {
if (acl.isGranted(requirePermission, sids, false)) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to grant access");
}
return AccessDecisionVoter.ACCESS_GRANTED;
} else {
if (logger.isDebugEnabled()) {
logger.debug(
"Voting to deny access - ACLs returned, but insufficient permissions for this principal");
}
return AccessDecisionVoter.ACCESS_DENIED;
}
} catch (NotFoundException nfe) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to deny access - no ACLs apply for this principal");
}
return AccessDecisionVoter.ACCESS_DENIED;
}
}
}
// No configuration attribute matched, so abstain
return AccessDecisionVoter.ACCESS_ABSTAIN;
}
}
@@ -0,0 +1,74 @@
/* 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.acegisecurity.vote;
import java.lang.reflect.Method;
/**
* This is a very useful implementation of the LabelParameterStrategy. Data objects which are meant to be labeled
* should implement the LabeledData interface. This strategy will then castdown to that interface for either testing
* or retrieval of the label.
*
* @author Greg Turnquist
* @version $Id$
*/
public class InterfaceBasedLabelParameterStrategy implements LabelParameterStrategy {
//~ Instance fields ================================================================================================
private String noLabel = "";
//~ Methods ========================================================================================================
/**
* Test if the argument is labeled, and if so, downcast to LabeledData and retrieve the domain object's
* labeled value. Otherwise, return an empty string. NOTE: The default for no label is an empty string. If somehow
* the user wants to make that a label itself, he or she must inject an alternate value to the noLabel property.
*
* @param method DOCUMENT ME!
* @param arg DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public String getLabel(Method method, Object arg) {
if (isLabeled(method, arg)) {
return ((LabeledData) arg).getLabel();
} else {
return noLabel;
}
}
public String getNoLabel() {
return noLabel;
}
/**
* Test if the argument implemented the LabeledData interface. NOTE: The invoking method has no bearing for
* this strategy, only the argument itself.
*
* @param method DOCUMENT ME!
* @param arg DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public boolean isLabeled(Method method, Object arg) {
return (arg instanceof LabeledData);
}
public void setNoLabel(String noLabel) {
this.noLabel = noLabel;
}
}
@@ -0,0 +1,288 @@
/* 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.acegisecurity.vote;
import org.acegisecurity.Authentication;
import org.acegisecurity.ConfigAttribute;
import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.vote.AbstractAclVoter;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
/**
* <p>This Acl voter will evaluate methods based on labels applied to incoming arguments. It will only check
* methods that have been properly tagged in the MethodSecurityInterceptor with the value stored in
* <b>attributeIndicatingLabeledOperation</b>. If a method has been tagged, then it examines each argument, and if the
* argument implements {@link LabeledData}, then it will asses if the user's list of granted authorities matches.</p>
* <p>By default, if none of the arguments are labeled, then the access will be granted. This can be overridden by
* setting <b>allowAccessIfNoAttributesAreLabeled</b> to false in the Spring context file.</p>
* <p>In many situations, different values are linked together to define a common label, it is necessary to
* define a map in the application context that links user-assigned label access to domain object labels. This is done
* by setting up the <b>labelMap</b> in the application context.</p>
*
* @author Greg Turnquist
* @version $Id$
*
* @see org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor
*/
public class LabelBasedAclVoter extends AbstractAclVoter {
//~ Instance fields ================================================================================================
private HashMap labelMap = null;
Log logger = LogFactory.getLog(LabelBasedAclVoter.class);
private String attributeIndicatingLabeledOperation = null;
private boolean allowAccessIfNoAttributesAreLabeled = true;
//~ Methods ========================================================================================================
/**
* Set whether or not to allow the user to run methods in which none of the incoming arguments are labeled.<p>Default
* value: <b>true, users can run such methods.</b></p>
*
* @param allowAccessIfNoAttributesAreLabeled boolean
*/
public void setAllowAccessIfNoAttributesAreLabeled(boolean allowAccessIfNoAttributesAreLabeled) {
this.allowAccessIfNoAttributesAreLabeled = allowAccessIfNoAttributesAreLabeled;
}
/**
* Each method intended for evaluation by this voter must include this tag name in the definition of the
* MethodSecurityInterceptor, indicating if this voter should evaluate the arguments and compare them against the
* label map.
*
* @param attributeIndicatingLabeledOperation string
*/
public void setAttributeIndicatingLabeledOperation(String attributeIndicatingLabeledOperation) {
this.attributeIndicatingLabeledOperation = attributeIndicatingLabeledOperation;
}
/**
* Set the map that correlate a user's assigned label against domain object values that are considered data
* labels.<p></p>
*
* @param labelMap - HashMap Example application context configuration of a labelMap:
*
* <pre>
* &lt;bean id="accessDecisionManager" class="org.acegisecurity.vote.UnanimousBased"&gt;<br>
* &lt;property name="allowIfAllAbstainDecisions">&lt;value&gt;false&lt;/value&gt;&lt;/property&gt;<br>
* &lt;property name="decisionVoters"&gt;<br>
* &lt;list&gt;<br>
* &lt;bean class="org.acegisecurity.vote.RoleVoter"/&gt;<br>
* &lt;bean class="net.homelinux.scifi.LabelBasedAclVoter"&gt;<br>
* &lt;property name="attributeIndicatingLabeledOperation"&gt;&lt;value&gt;LABELED_OPERATION&lt;/value&gt;&lt;/property&gt;<br>
* &lt;property name="labelMap"&gt;<br>
* &lt;map&gt;<br>
* &lt;entry key="DATA_LABEL_BLUE"&gt;<br>
* &lt;list&gt;<br>
* &lt;value&gt;blue&lt;/value&gt;<br>
* &lt;value&gt;indigo&lt;/value&gt;<br>
* &lt;value&gt;purple&lt;/value&gt;<br>
* &lt;/list&gt;<br>
* &lt;/entry&gt;<br>
* &lt;entry key="LABEL_ORANGE"&gt;<br>
* &lt;list&gt;<br>
* &lt;value&gt;orange&lt;/value&gt;<br>
* &lt;value&gt;sunshine&lt;/value&gt;<br>
* &lt;value&gt;amber&lt;/value&gt;<br>
* &lt;/list&gt;<br>
* &lt;/entry&gt;<br>
* &lt;entry key="LABEL_ADMIN"&gt;<br>
* &lt;list&gt;<br>
* &lt;value&gt;blue&lt;/value&gt;<br>
* &lt;value&gt;indigo&lt;/value&gt;<br>
* &lt;value&gt;purple&lt;/value&gt;<br>
* &lt;value&gt;orange&lt;/value&gt;<br>
* &lt;value&gt;sunshine&lt;/value&gt;<br>
* &lt;value&gt;amber&lt;/value&gt;<br>
* &lt;/list&gt;<br>
* &lt;/entry&gt;<br>
* &lt;/map&gt;<br>
* &lt;/property&gt;<br>
* &lt;/bean&gt;<br>
* &lt;/list&gt;<br>
* &lt;/property&gt;<br>
* &lt;/bean&gt;<br>
* </pre>
*/
public void setLabelMap(HashMap labelMap) {
this.labelMap = labelMap;
}
/**
* This acl voter will only evaluate labeled methods if they are marked in the security interceptor's
* configuration with the attribute stored in attributeIndicatingLabeledOperation.
*
* @param attribute DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @see org.acegisecurity.vote.AbstractAclVoter
* @see org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor
*/
public boolean supports(ConfigAttribute attribute) {
if (attribute.getAttribute().equals(attributeIndicatingLabeledOperation)) {
logger.debug(attribute + " is supported.");
return true;
}
if (logger.isDebugEnabled()) {
logger.debug(attribute + " is unsupported.");
}
return false;
}
/**
* Vote on whether or not the user has all the labels necessary to match the method argument's labeled
* data.
*
* @param authentication DOCUMENT ME!
* @param object DOCUMENT ME!
* @param config DOCUMENT ME!
*
* @return ACCESS_ABSTAIN, ACCESS_GRANTED, or ACCESS_DENIED.
*/
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
int result = ACCESS_ABSTAIN;
if (logger.isDebugEnabled()) {
logger.debug("==========================================================");
}
if (this.supports((ConfigAttribute) config.getConfigAttributes().next())) {
result = ACCESS_DENIED;
/* Parse out the user's labels by examining the security context, and checking
* for matches against the label map.
*/
List userLabels = new Vector();
for (int i = 0; i < authentication.getAuthorities().length; i++) {
if (labelMap.containsKey(authentication.getAuthorities()[i].getAuthority())) {
String userLabel = authentication.getAuthorities()[i].getAuthority();
userLabels.add(userLabel);
logger.debug("Adding " + userLabel + " to <<<" + authentication.getName()
+ "'s>>> authorized label list");
}
}
MethodInvocation invocation = (MethodInvocation) object;
int matches = 0;
int misses = 0;
int labeledArguments = 0;
for (int j = 0; j < invocation.getArguments().length; j++) {
if (invocation.getArguments()[j] instanceof LabeledData) {
labeledArguments++;
boolean matched = false;
String argumentDataLabel = ((LabeledData) invocation.getArguments()[j]).getLabel();
logger.debug("Argument[" + j + "/" + invocation.getArguments()[j].getClass().getName()
+ "] has a data label of " + argumentDataLabel);
List validDataLabels = new Vector();
for (int i = 0; i < userLabels.size(); i++) {
validDataLabels.addAll((List) labelMap.get(userLabels.get(i)));
}
logger.debug("The valid labels for user label " + userLabels + " are " + validDataLabels);
Iterator dataLabelIter = validDataLabels.iterator();
while (dataLabelIter.hasNext()) {
String validDataLabel = (String) dataLabelIter.next();
if (argumentDataLabel.equals(validDataLabel)) {
logger.debug(userLabels + " maps to " + validDataLabel + " which matches the argument");
matched = true;
}
}
if (matched) {
logger.debug("We have a match!");
matches++;
} else {
logger.debug("We have a miss!");
misses++;
}
} /* if arguments is an ILabel */} /* loop through all arguments */
Assert.isTrue((matches + misses) == labeledArguments,
"The matches (" + matches + ") and misses (" + misses + " ) don't add up (" + labeledArguments + ")");
logger.debug("We have " + matches + " matches and " + misses + " misses and " + labeledArguments
+ " labeled arguments.");
/* The result has already been set to ACCESS_DENIED. Only if there is a proper match of
* labels will this be overturned. However, if none of the attributes are actually labeled,
* the result is dependent on allowAccessIfNoAttributesAreLabeled.
*/
if ((matches > 0) && (misses == 0)) {
result = ACCESS_GRANTED;
} else if (labeledArguments == 0) {
if (allowAccessIfNoAttributesAreLabeled) {
result = ACCESS_GRANTED;
} else {
result = ACCESS_DENIED;
}
}
}
if (logger.isDebugEnabled()) {
switch (result) {
case ACCESS_GRANTED:
if (logger.isDebugEnabled()) {
logger.debug("===== Access is granted =====");
}
break;
case ACCESS_DENIED:
if (logger.isDebugEnabled()) {
logger.debug("===== Access is denied =====");
}
break;
case ACCESS_ABSTAIN:
if (logger.isDebugEnabled()) {
logger.debug("===== Abstaining =====");
}
break;
}
}
return result;
}
}
@@ -0,0 +1,52 @@
/* 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.acegisecurity.vote;
import java.lang.reflect.Method;
/**
* This is a strategy interface for determining which parts of a method invocation
* are labeled. Not all arguments are necessarily labeled. This offers a plugabble
* mechanism to define various ways to label data.
*
* @author Greg Turnquist
* @version $Id$
*/
public interface LabelParameterStrategy {
//~ Methods ========================================================================================================
/**
* Get the actual label associated with the argument. NOTE: This currently only supports one label per
* argument.
*
* @param method
* @param arg
*
* @return string value of the label
*/
public String getLabel(Method method, Object arg);
/**
* Evaluate if one particular argument is labeled. The context of the method is also provided should that
* have bearing on the label.
*
* @param method
* @param arg
*
* @return boolean
*/
public boolean isLabeled(Method method, Object arg);
}
@@ -0,0 +1,35 @@
/* 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.acegisecurity.vote;
/**
* This interface indicates data objects that carry a label. The purpose is to support
* the {@link LabelBasedAclVoter}. When it votes, it evaluates all method arguments
* tagged with this interface, and votes if they match the user's granted authorities list.
*
* @author Greg Turnquist
*/
public interface LabeledData {
//~ Methods ========================================================================================================
/**
* Retrieve the domain object's data label. NOTE: This implementation only supports one data label per
* object.
*
* @return The label value of data object as a string.
*/
public String getLabel();
}
@@ -0,0 +1,32 @@
CREATE SCHEMA sa;
CREATE TABLE acl_object_identity (
id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY CONSTRAINT acl_object_identity_PK PRIMARY KEY,
object_identity VARCHAR(250) NOT NULL,
parent_object INTEGER,
acl_class VARCHAR(250) NOT NULL,
CONSTRAINT unique_object_identity UNIQUE(object_identity),
FOREIGN KEY (parent_object) REFERENCES acl_object_identity(id) ON DELETE CASCADE
);
CREATE TABLE acl_permission (
id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY CONSTRAINT acl_permission_PK PRIMARY KEY,
acl_object_identity INTEGER NOT NULL,
recipient VARCHAR(100) NOT NULL,
mask INTEGER NOT NULL,
CONSTRAINT unique_recipient UNIQUE(acl_object_identity, recipient),
FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity(id) ON DELETE CASCADE
);
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:1', null, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:2', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:3', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:4', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:5', 3, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:6', 3, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (1, 'ROLE_ADMIN', 1);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (2, 'ROLE_ADMIN', 0);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (2, 'marissa', 2);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (3, 'scott', 14);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (6, 'scott', 1);
@@ -0,0 +1,30 @@
CREATE TABLE acl_object_identity (
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
object_identity VARCHAR(250) NOT NULL,
parent_object INTEGER,
acl_class VARCHAR(250) NOT NULL,
CONSTRAINT unique_object_identity UNIQUE(object_identity),
FOREIGN KEY (parent_object) REFERENCES acl_object_identity(id) ON DELETE CASCADE
);
CREATE TABLE acl_permission (
id INTEGER GENERATED BY DEFAULT AS IDENTITY,
acl_object_identity INTEGER NOT NULL,
recipient VARCHAR(100) NOT NULL,
mask INTEGER NOT NULL,
CONSTRAINT unique_recipient UNIQUE(acl_object_identity, recipient),
FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity(id) ON DELETE CASCADE
);
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:1', null, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:2', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:3', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:4', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:5', 3, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:6', 3, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (1, 'ROLE_ADMIN', 1);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (2, 'ROLE_ADMIN', 0);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (2, 'marissa', 2);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (3, 'scott', 14);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (6, 'scott', 1);
@@ -0,0 +1,30 @@
CREATE TABLE acl_object_identity (
id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
object_identity VARCHAR(250) NOT NULL,
parent_object INTEGER,
acl_class VARCHAR(250) NOT NULL,
CONSTRAINT unique_object_identity UNIQUE(object_identity),
FOREIGN KEY (parent_object) REFERENCES acl_object_identity(id) ON DELETE CASCADE
);
CREATE TABLE acl_permission (
id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
acl_object_identity INTEGER NOT NULL,
recipient VARCHAR(100) NOT NULL,
mask INTEGER NOT NULL,
CONSTRAINT unique_recipient UNIQUE(acl_object_identity, recipient),
FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity(id) ON DELETE CASCADE
);
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:1', null, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:2', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:3', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:4', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:5', 3, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:6', 3, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (1, 'ROLE_ADMIN', 1);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (2, 'ROLE_ADMIN', 0);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (2, 'marissa', 2);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (3, 'scott', 14);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (6, 'scott', 1);
@@ -0,0 +1,34 @@
CREATE SEQUENCE acl_object_identity_seq;
CREATE TABLE acl_object_identity (
id INTEGER NOT NULL DEFAULT nextval('acl_object_identity_seq') CONSTRAINT acl_object_identity_PK PRIMARY KEY,
object_identity VARCHAR(250) NOT NULL,
parent_object INTEGER,
acl_class VARCHAR(250) NOT NULL,
CONSTRAINT unique_object_identity UNIQUE(object_identity),
FOREIGN KEY (parent_object) REFERENCES acl_object_identity(id) ON DELETE CASCADE
);
CREATE SEQUENCE acl_permission_seq;
CREATE TABLE acl_permission (
id INTEGER NOT NULL DEFAULT nextval('acl_permission_seq') CONSTRAINT acl_permission_PK PRIMARY KEY,
acl_object_identity INTEGER NOT NULL,
recipient VARCHAR(100) NOT NULL,
mask INTEGER NOT NULL,
CONSTRAINT unique_recipient UNIQUE(acl_object_identity, recipient),
FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity(id) ON DELETE CASCADE
);
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:1', null, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:2', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:3', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:4', 1, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:5', 3, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_object_identity (object_identity, parent_object, acl_class) VALUES ('org.mydomain.MyClass:6', 3, 'org.acegisecurity.acl.basic.SimpleAclEntry');
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (1, 'ROLE_ADMIN', 1);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (2, 'ROLE_ADMIN', 0);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (2, 'marissa', 2);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (3, 'scott', 14);
--INSERT INTO acl_permission (acl_object_identity, recipient, mask) VALUES (6, 'scott', 1);
@@ -113,4 +113,34 @@
</attribute>
</tag>
<tag>
<name>accesscontrollist</name>
<tag-class>org.acegisecurity.taglibs.authz.AccessControlListTag</tag-class>
<description>
Allows inclusion of a tag body if the current Authentication
has one of the specified permissions to the presented
domain object instance.
</description>
<attribute>
<name>hasPermission</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<description>
A comma separated list of integers, each representing a
required bit mask permission from a subclass of
org.acegisecurity.acl.basic.AbstractBasicAclEntry.
</description>
</attribute>
<attribute>
<name>domainObject</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<description>
The actual domain object instance for which permissions
are being evaluated.
</description>
</attribute>
</tag>
</taglib>
@@ -12,11 +12,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.acegisecurity.acls.domain;
import junit.framework.TestCase;
import org.acegisecurity.acls.Permission;
/**
* Tests BasePermission and CumulativePermission.
@@ -30,7 +31,18 @@ public class PermissionTests extends TestCase {
public void testExpectedIntegerValues() {
assertEquals(1, BasePermission.READ.getMask());
assertEquals(16, BasePermission.ADMINISTRATION.getMask());
assertEquals(17, new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION).getMask());
assertEquals(7,
new CumulativePermission().set(BasePermission.READ).set(BasePermission.WRITE).set(BasePermission.CREATE)
.getMask());
assertEquals(17,
new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION).getMask());
}
public void testFromInteger() {
Permission permission = BasePermission.buildFromMask(7);
System.out.println("7 = " + permission.toString());
permission = BasePermission.buildFromMask(4);
System.out.println("4 = " + permission.toString());
}
public void testStringConversion() {
@@ -1,48 +1,47 @@
/* 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.acegisecurity.acls.jdbc;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import java.io.IOException;
import javax.sql.DataSource;
/**
* DOCUMENT ME!
*
* @author $author$
* @version $Revision$
*/
public class DatabaseSeeder {
//~ Constructors ===================================================================================================
public DatabaseSeeder(DataSource dataSource, Resource resource)
throws IOException {
Assert.notNull(dataSource, "dataSource required");
Assert.notNull(resource, "resource required");
JdbcTemplate template = new JdbcTemplate(dataSource);
String sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
template.execute(sql);
}
}
/* 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.acegisecurity.acls.jdbc;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import java.io.IOException;
import javax.sql.DataSource;
/**
* Seeds the database for {@link JdbcAclServiceTests}.
*
* @author Ben Alex
* @version $Id$
*/
public class DatabaseSeeder {
//~ Constructors ===================================================================================================
public DatabaseSeeder(DataSource dataSource, Resource resource)
throws IOException {
Assert.notNull(dataSource, "dataSource required");
Assert.notNull(resource, "resource required");
JdbcTemplate template = new JdbcTemplate(dataSource);
String sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
template.execute(sql);
}
}
@@ -0,0 +1,222 @@
/* 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.acegisecurity.acls.jdbc;
import org.acegisecurity.Authentication;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.acls.AccessControlEntry;
import org.acegisecurity.acls.MutableAcl;
import org.acegisecurity.acls.NotFoundException;
import org.acegisecurity.acls.Permission;
import org.acegisecurity.acls.domain.BasePermission;
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
import org.acegisecurity.acls.objectidentity.ObjectIdentityImpl;
import org.acegisecurity.acls.sid.PrincipalSid;
import org.acegisecurity.acls.sid.Sid;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.TestingAuthenticationToken;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
import java.util.Map;
/**
* Integration tests the ACL system using an in-memory database.
*
* @author Ben Alex
* @version $Id:JdbcAclServiceTests.java 1754 2006-11-17 02:01:21Z benalex $
*/
public class JdbcAclServiceTests extends AbstractTransactionalDataSourceSpringContextTests {
//~ Instance fields ================================================================================================
private JdbcMutableAclService jdbcMutableAclService;
//~ Methods ========================================================================================================
protected String[] getConfigLocations() {
return new String[] {"classpath:org/acegisecurity/acls/jdbc/applicationContext-test.xml"};
}
public void setJdbcMutableAclService(JdbcMutableAclService jdbcAclService) {
this.jdbcMutableAclService = jdbcAclService;
}
public void testLifecycle() {
setComplete();
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ADMINISTRATOR")});
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.acegisecurity.TargetObject", new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.acegisecurity.TargetObject", new Long(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.acegisecurity.TargetObject", new Long(102));
MutableAcl topParent = jdbcMutableAclService.createAcl(topParentOid);
MutableAcl middleParent = jdbcMutableAclService.createAcl(middleParentOid);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
// Specify the inheritence hierarchy
middleParent.setParent(topParent);
child.setParent(middleParent);
// Now let's add a couple of permissions
topParent.insertAce(null, BasePermission.READ, new PrincipalSid(auth), true);
topParent.insertAce(null, BasePermission.WRITE, new PrincipalSid(auth), false);
middleParent.insertAce(null, BasePermission.DELETE, new PrincipalSid(auth), true);
child.insertAce(null, BasePermission.DELETE, new PrincipalSid(auth), false);
// Explictly save the changed ACL
jdbcMutableAclService.updateAcl(topParent);
jdbcMutableAclService.updateAcl(middleParent);
jdbcMutableAclService.updateAcl(child);
// Let's check if we can read them back correctly
Map map = jdbcMutableAclService.readAclsById(new ObjectIdentity[] {topParentOid, middleParentOid, childOid});
assertEquals(3, map.size());
// Replace our current objects with their retrieved versions
topParent = (MutableAcl) map.get(topParentOid);
middleParent = (MutableAcl) map.get(middleParentOid);
child = (MutableAcl) map.get(childOid);
// Check the retrieved versions has IDs
assertNotNull(topParent.getId());
assertNotNull(middleParent.getId());
assertNotNull(child.getId());
// Check their parents were correctly persisted
assertNull(topParent.getParentAcl());
assertEquals(topParentOid, middleParent.getParentAcl().getObjectIdentity());
assertEquals(middleParentOid, child.getParentAcl().getObjectIdentity());
// Check their ACEs were correctly persisted
assertEquals(2, topParent.getEntries().length);
assertEquals(1, middleParent.getEntries().length);
assertEquals(1, child.getEntries().length);
// Check the retrieved rights are correct
assertTrue(topParent.isGranted(new Permission[] {BasePermission.READ}, new Sid[] {new PrincipalSid(auth)}, false));
assertFalse(topParent.isGranted(new Permission[] {BasePermission.WRITE}, new Sid[] {new PrincipalSid(auth)},
false));
assertTrue(middleParent.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)},
false));
assertFalse(child.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)}, false));
try {
child.isGranted(new Permission[] {BasePermission.ADMINISTRATION}, new Sid[] {new PrincipalSid(auth)}, false);
fail("Should have thrown NotFoundException");
} catch (NotFoundException expected) {
assertTrue(true);
}
// Now check the inherited rights (when not explicitly overridden) also look OK
assertTrue(child.isGranted(new Permission[] {BasePermission.READ}, new Sid[] {new PrincipalSid(auth)}, false));
assertFalse(child.isGranted(new Permission[] {BasePermission.WRITE}, new Sid[] {new PrincipalSid(auth)}, false));
assertFalse(child.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)}, false));
// Next change the child so it doesn't inherit permissions from above
child.setEntriesInheriting(false);
jdbcMutableAclService.updateAcl(child);
child = (MutableAcl) jdbcMutableAclService.readAclById(childOid);
assertFalse(child.isEntriesInheriting());
// Check the child permissions no longer inherit
assertFalse(child.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)}, true));
try {
child.isGranted(new Permission[] {BasePermission.READ}, new Sid[] {new PrincipalSid(auth)}, true);
fail("Should have thrown NotFoundException");
} catch (NotFoundException expected) {
assertTrue(true);
}
try {
child.isGranted(new Permission[] {BasePermission.WRITE}, new Sid[] {new PrincipalSid(auth)}, true);
fail("Should have thrown NotFoundException");
} catch (NotFoundException expected) {
assertTrue(true);
}
// Let's add an identical permission to the child, but it'll appear AFTER the current permission, so has no impact
child.insertAce(null, BasePermission.DELETE, new PrincipalSid(auth), true);
// Let's also add another permission to the child
child.insertAce(null, BasePermission.CREATE, new PrincipalSid(auth), true);
// Save the changed child
jdbcMutableAclService.updateAcl(child);
child = (MutableAcl) jdbcMutableAclService.readAclById(childOid);
assertEquals(3, child.getEntries().length);
// Output permissions
for (int i = 0; i < child.getEntries().length; i++) {
System.out.println(child.getEntries()[i]);
}
// Check the permissions are as they should be
assertFalse(child.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)}, true)); // as earlier permission overrode
assertTrue(child.isGranted(new Permission[] {BasePermission.CREATE}, new Sid[] {new PrincipalSid(auth)}, true));
// Now check the first ACE (index 0) really is DELETE for our Sid and is non-granting
AccessControlEntry entry = child.getEntries()[0];
assertEquals(BasePermission.DELETE.getMask(), entry.getPermission().getMask());
assertEquals(new PrincipalSid(auth), entry.getSid());
assertFalse(entry.isGranting());
assertNotNull(entry.getId());
// Now delete that first ACE
child.deleteAce(entry.getId());
// Save and check it worked
child = jdbcMutableAclService.updateAcl(child);
assertEquals(2, child.getEntries().length);
assertTrue(child.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)}, false));
SecurityContextHolder.clearContext();
}
/* public void testCumulativePermissions() {
setComplete();
Authentication auth = new TestingAuthenticationToken("ben", "ignored", new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ADMINISTRATOR")});
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.acegisecurity.TargetObject", new Long(110));
MutableAcl topParent = jdbcMutableAclService.createAcl(topParentOid);
// Add an ACE permission entry
CumulativePermission cm = new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION);
assertEquals(17, cm.getMask());
topParent.insertAce(null, cm, new PrincipalSid(auth), true);
assertEquals(1, topParent.getEntries().length);
// Explictly save the changed ACL
topParent = jdbcMutableAclService.updateAcl(topParent);
// Check the mask was retrieved correctly
assertEquals(17, topParent.getEntries()[0].getPermission().getMask());
assertTrue(topParent.isGranted(new Permission[] {cm}, new Sid[] {new PrincipalSid(auth)}, true));
SecurityContextHolder.clearContext();
}
*/
}
@@ -247,7 +247,7 @@ public class FilterInvocationDefinitionSourceEditorTests extends TestCase {
assertEquals(expected, returned);
}
public void testSingleUrlParsing() throws Exception {
public void testSingleUrlParsingWithRegularExpressions() throws Exception {
FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
editor.setAsText("\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE,ANOTHER_ROLE");
@@ -266,6 +266,25 @@ public class FilterInvocationDefinitionSourceEditorTests extends TestCase {
assertEquals(expected, returned);
}
public void testSingleUrlParsingWithAntPaths() throws Exception {
FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
editor.setAsText("PATTERN_TYPE_APACHE_ANT\r\n/secure/super/**=ROLE_WE_DONT_HAVE,ANOTHER_ROLE");
PathBasedFilterInvocationDefinitionMap map = (PathBasedFilterInvocationDefinitionMap) editor.getValue();
MockHttpServletRequest httpRequest = new MockHttpServletRequest(null, null);
httpRequest.setServletPath("/secure/super/very_secret.html");
ConfigAttributeDefinition returned = map.getAttributes(new FilterInvocation(httpRequest,
new MockHttpServletResponse(), new MockFilterChain()));
ConfigAttributeDefinition expected = new ConfigAttributeDefinition();
expected.addConfigAttribute(new SecurityConfig("ROLE_WE_DONT_HAVE"));
expected.addConfigAttribute(new SecurityConfig("ANOTHER_ROLE"));
assertEquals(expected, returned);
}
public void testWhitespaceAndCommentsAndLinesWithoutEqualsSignsAreIgnored() {
FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
editor.setAsText(
@@ -47,7 +47,7 @@ public class AuthorizeTagTests extends TestCase {
currentUser = new TestingAuthenticationToken("abc", "123",
new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_SUPERVISOR"), new GrantedAuthorityImpl("ROLE_TELLER"),
new GrantedAuthorityImpl("ROLE SUPERVISOR"), new GrantedAuthorityImpl("ROLE_TELLER"),
});
SecurityContextHolder.getContext().setAuthentication(currentUser);
@@ -80,7 +80,7 @@ public class AuthorizeTagTests extends TestCase {
}
public void testOutputsBodyWhenAllGranted() throws JspException {
authorizeTag.setIfAllGranted("ROLE_SUPERVISOR,ROLE_TELLER");
authorizeTag.setIfAllGranted("ROLE SUPERVISOR,ROLE_TELLER");
assertEquals("allows request - all required roles granted on principal", Tag.EVAL_BODY_INCLUDE,
authorizeTag.doStartTag());
}
@@ -107,7 +107,7 @@ public class AuthorizeTagTests extends TestCase {
public void testSkipsBodyWhenMissingAnAllGranted()
throws JspException {
authorizeTag.setIfAllGranted("ROLE_SUPERVISOR,ROLE_TELLER,ROLE_BANKER");
authorizeTag.setIfAllGranted("ROLE SUPERVISOR,ROLE_TELLER,ROLE_BANKER");
assertEquals("prevents request - missing ROLE_BANKER on principal", Tag.SKIP_BODY, authorizeTag.doStartTag());
}
@@ -35,6 +35,11 @@ public class EncryptionUtilsTests extends TestCase {
assertEquals("3YIE8sIbaEoqGZZrHamFGQ==", encryptedString);
}
public void testEncryptByteArrayUsingDESEde() {
final byte[] encryptedArray = EncryptionUtils.encrypt(ENCRYPTION_KEY, EncryptionUtils.stringToByteArray(STRING_TO_ENCRYPT));
assertEquals("3YIE8sIbaEoqGZZrHamFGQ==", EncryptionUtils.byteArrayToString(encryptedArray));
}
public void testEncryptionKeyCanContainLetters() throws EncryptionException {
final String encryptedString = EncryptionUtils.encrypt("ASDF asdf 1234 8983 jklasdf J2Jaf8", STRING_TO_ENCRYPT);
assertEquals("v4+DQoClx6qm5tJwBcRrkw==", encryptedString);
@@ -46,56 +51,62 @@ public class EncryptionUtilsTests extends TestCase {
assertEquals(STRING_TO_ENCRYPT, decryptedString);
}
public void testCantEncryptWithNullEncryptionKey() throws EncryptionException {
public void testDecryptByteArrayUsingDESEde() {
final byte[] encrypted = EncryptionUtils.stringToByteArray("3YIE8sIbaEoqGZZrHamFGQ==");
final byte[] decrypted = EncryptionUtils.decrypt(ENCRYPTION_KEY, encrypted);
assertEquals(STRING_TO_ENCRYPT, EncryptionUtils.byteArrayToString(decrypted));
}
public void testFailEncryptWithNullEncryptionKey() {
try {
EncryptionUtils.encrypt(null, "");
fail("Should have thrown IAE");
} catch (final IllegalArgumentException e) {
EncryptionUtils.encrypt(null, STRING_TO_ENCRYPT);
fail();
} catch (IllegalArgumentException e) {
assertTrue(true);
}
}
public void testCantEncryptWithEmptyEncryptionKey() throws EncryptionException {
public void testFailEncryptWithEmptyEncryptionKey() {
try {
EncryptionUtils.encrypt("", "");
fail("Should have thrown IAE");
} catch (final IllegalArgumentException e) {
EncryptionUtils.encrypt("", STRING_TO_ENCRYPT);
fail();
} catch (IllegalArgumentException e) {
assertTrue(true);
}
}
public void testCantEncryptWithShortEncryptionKey() throws EncryptionException {
public void teastFailEncryptWithShortEncryptionKey() {
try {
EncryptionUtils.encrypt("01234567890123456789012", "");
fail("Should have thrown IAE");
} catch (final IllegalArgumentException e) {
EncryptionUtils.encrypt("01234567890123456789012", STRING_TO_ENCRYPT);
fail();
} catch (IllegalArgumentException e) {
assertTrue(true);
}
}
public void testCantDecryptWithEmptyString() throws EncryptionException {
public void testFailDecryptWithEmptyString() {
try {
EncryptionUtils.decrypt(ENCRYPTION_KEY, "");
fail("Should have thrown IAE");
} catch (final IllegalArgumentException e) {
fail();
} catch (IllegalArgumentException e) {
assertTrue(true);
}
}
public void testCantEncryptWithEmptyString() throws EncryptionException {
public void testFailEncryptWithEmptyString() {
try {
EncryptionUtils.encrypt(ENCRYPTION_KEY, "");
fail("Should have thrown IAE");
} catch (final IllegalArgumentException e) {
fail();
} catch (IllegalArgumentException e) {
assertTrue(true);
}
}
public void testCantEncryptWithNullString() throws EncryptionException {
public void testFailEncryptWithNullString() {
try {
EncryptionUtils.encrypt(ENCRYPTION_KEY, null);
fail("Should have thrown IAE");
} catch (final IllegalArgumentException e) {
EncryptionUtils.encrypt(ENCRYPTION_KEY, (String) null);
fail();
} catch (IllegalArgumentException e) {
assertTrue(true);
}
}
@@ -0,0 +1,184 @@
/* 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.acegisecurity.vote;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.AuthenticationManager;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
import java.util.List;
/**
*
DOCUMENT ME!
*
* @author Greg Turnquist
* @version $Id$
*/
public class LabelBasedAclVoterTests extends AbstractDependencyInjectionSpringContextTests {
//~ Instance fields ================================================================================================
private SampleService sampleService = null;
//~ Methods ========================================================================================================
protected String[] getConfigLocations() {
return new String[] {"org/acegisecurity/vote/labelBasedSecurityApplicationContext.xml"};
}
public SampleService getSampleService() {
return sampleService;
}
public static void main(String[] args) {
junit.textui.TestRunner.run(LabelBasedAclVoterTests.class);
}
public void setSampleService(SampleService sampleService) {
this.sampleService = sampleService;
}
private void setupContext(String username, String password) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
AuthenticationManager authenticationManager = (AuthenticationManager) applicationContext.getBean(
"authenticationManager");
SecurityContextHolder.getContext().setAuthentication(authenticationManager.authenticate(token));
}
public void testDoingSomethingForBlueUser() {
setupContext("blueuser", "password");
List dataList = sampleService.getTheSampleData();
assertNotNull(dataList);
SampleBlockOfData block1 = (SampleBlockOfData) dataList.get(0);
SampleBlockOfData block2 = (SampleBlockOfData) dataList.get(1);
SampleBlockOfData block3 = (SampleBlockOfData) dataList.get(2);
sampleService.doSomethingOnThis(block1, block1);
try {
sampleService.doSomethingOnThis(block2, block2);
fail("Expected an AccessDeniedException");
} catch (AccessDeniedException e) {}
catch (RuntimeException e) {
fail("Expected an AccessDeniedException");
}
try {
sampleService.doSomethingOnThis(block1, block2);
fail("Expected an AccessDeniedException");
} catch (AccessDeniedException e) {}
catch (RuntimeException e) {
fail("Expected an AccessDeniedException");
}
try {
sampleService.doSomethingOnThis(block2, block1);
fail("Expected an AccessDeniedException");
} catch (AccessDeniedException e) {}
catch (RuntimeException e) {
fail("Expected an AccessDeniedException");
}
sampleService.doSomethingOnThis(block3, block3);
}
public void testDoingSomethingForMultiUser() {
setupContext("multiuser", "password4");
List dataList = sampleService.getTheSampleData();
assertNotNull(dataList);
SampleBlockOfData block1 = (SampleBlockOfData) dataList.get(0);
SampleBlockOfData block2 = (SampleBlockOfData) dataList.get(1);
SampleBlockOfData block3 = (SampleBlockOfData) dataList.get(2);
sampleService.doSomethingOnThis(block1, block1);
sampleService.doSomethingOnThis(block2, block2);
sampleService.doSomethingOnThis(block1, block2);
sampleService.doSomethingOnThis(block2, block1);
sampleService.doSomethingOnThis(block3, block3);
}
public void testDoingSomethingForOrangeUser() {
setupContext("orangeuser", "password3");
List dataList = sampleService.getTheSampleData();
assertNotNull(dataList);
SampleBlockOfData block1 = (SampleBlockOfData) dataList.get(0);
SampleBlockOfData block2 = (SampleBlockOfData) dataList.get(1);
SampleBlockOfData block3 = (SampleBlockOfData) dataList.get(2);
sampleService.doSomethingOnThis(block2, block2);
try {
sampleService.doSomethingOnThis(block1, block1);
fail("Expected an AccessDeniedException");
} catch (AccessDeniedException e) {}
catch (RuntimeException e) {
fail("Expected an AccessDeniedException");
}
try {
sampleService.doSomethingOnThis(block1, block2);
fail("Expected an AccessDeniedException");
} catch (AccessDeniedException e) {}
catch (RuntimeException e) {
fail("Expected an AccessDeniedException");
}
try {
sampleService.doSomethingOnThis(block2, block1);
fail("Expected an AccessDeniedException");
} catch (AccessDeniedException e) {}
catch (RuntimeException e) {
fail("Expected an AccessDeniedException");
}
sampleService.doSomethingOnThis(block3, block3);
}
public void testDoingSomethingForSuperUser() {
setupContext("superuser", "password2");
List dataList = sampleService.getTheSampleData();
assertNotNull(dataList);
SampleBlockOfData block1 = (SampleBlockOfData) dataList.get(0);
SampleBlockOfData block2 = (SampleBlockOfData) dataList.get(1);
SampleBlockOfData block3 = (SampleBlockOfData) dataList.get(2);
sampleService.doSomethingOnThis(block1, block1);
sampleService.doSomethingOnThis(block2, block2);
sampleService.doSomethingOnThis(block1, block2);
sampleService.doSomethingOnThis(block2, block1);
sampleService.doSomethingOnThis(block3, block3);
}
public void testSampleBlockOfDataPOJO() {
SampleBlockOfData block = new SampleBlockOfData();
block.setId("ID-ABC");
assertEquals(block.getId(), "ID-ABC");
}
}

Some files were not shown because too many files have changed in this diff Show More