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

Compare commits

..

1 Commits

Author SHA1 Message Date
Luke Taylor d6d9bc9d6b [maven-release-plugin] copy for tag spring-security-parent-2.0.3 2008-06-23 14:08:17 +00:00
1893 changed files with 78343 additions and 76043 deletions
+7
View File
@@ -0,0 +1,7 @@
dist
target
build.properties
*.log
.clover
*.keystore
server-work
-21
View File
@@ -1,21 +0,0 @@
target/
*/src/*/java/META-INF
*/src/META-INF/
*/src/*/java/META-INF/
.classpath
.springBeans
.project
.DS_Store
.settings/
.idea/
out/
bin/
intellij/
build/
*.log
*.log.*
*.iml
*.ipr
*.iws
.gradle/
atlassian-ide-plugin.xml
+205
View File
@@ -0,0 +1,205 @@
<?xml version="1.0"?>
<!--
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* 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.
*/
-->
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.2//EN"
"http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
<!--
Checkstyle is very configurable. Be sure to read the documentation at
http://checkstyle.sf.net (or in your downloaded distribution).
Most Checks are configurable, be sure to consult the documentation.
To completely disable a check, just comment it out or delete it from the file.
Finally, it is worth reading the documentation.
-->
<module name="Checker">
<!-- Checks that a package.html file exists for each package. -->
<!-- See http://checkstyle.sf.net/config_javadoc.html#PackageHtml -->
<!-- module name="PackageHtml"/ -->
<!-- Checks whether files end with a new line. -->
<!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
<module name="NewlineAtEndOfFile"/>
<!-- Checks that property files contain the same keys. -->
<!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
<module name="Translation"/>
<module name="TreeWalker">
<property name="cacheFile" value="${checkstyle.cache.file}"/>
<!-- Checks for Javadoc comments. -->
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
<!--
<module name="JavadocMethod"/>
<module name="JavadocType"/>
<module name="JavadocVariable"/>
<module name="JavadocStyle"/>
-->
<!-- Checks for Naming Conventions. -->
<!-- See http://checkstyle.sf.net/config_naming.html -->
<module name="ConstantName">
<!-- logger variables break normal constant syntax. We need to allow lower case too -->
<property name="format" value="^[a-zA-Z][a-zA-Z0-9]*(_[A-Z0-9]+)*$"/>
</module>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName"/>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<!-- Checks for Headers -->
<!-- See http://checkstyle.sf.net/config_header.html -->
<!-- <module name="Header"> -->
<!-- The follow property value demonstrates the ability -->
<!-- to have access to ANT properties. In this case it uses -->
<!-- the ${basedir} property to allow Checkstyle to be run -->
<!-- from any directory within a project. See property -->
<!-- expansion, -->
<!-- http://checkstyle.sf.net/config.html#properties -->
<!-- <property -->
<!-- name="headerFile" -->
<!-- value="${basedir}/java.header"/> -->
<!-- </module> -->
<!-- Following interprets the header file as regular expressions. -->
<!-- <module name="RegexpHeader"/> -->
<!-- Checks for imports -->
<!-- See http://checkstyle.sf.net/config_imports.html -->
<module name="AvoidStarImport">
<property name="excludes" value="javax.servlet,java.util"/>
</module>
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
<module name="RedundantImport"/>
<!--module name="UnusedImports"/ -->
<!-- Checks for Size Violations. -->
<!-- See http://checkstyle.sf.net/config_sizes.html -->
<module name="FileLength"/>
<module name="LineLength">
<property name="max" value="125"/>
</module>
<module name="MethodLength"/>
<module name="ParameterNumber"/>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<module name="EmptyForIteratorPad"/>
<module name="MethodParamPad"/>
<module name="NoWhitespaceAfter"/>
<module name="NoWhitespaceBefore"/>
<module name="OperatorWrap"/>
<module name="ParenPad"/>
<module name="TypecastParenPad"/>
<module name="TabCharacter"/>
<module name="WhitespaceAfter"/>
<!--
<module name="WhitespaceAround">
<property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyConstructors" value="true"/>
</module>
-->
<!-- Modifier Checks -->
<!-- See http://checkstyle.sf.net/config_modifiers.html -->
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<!-- Checks for blocks. You know, those {}'s -->
<!-- See http://checkstyle.sf.net/config_blocks.html -->
<module name="AvoidNestedBlocks"/>
<!-- module name="EmptyBlock"/ -->
<module name="LeftCurly"/>
<module name="NeedBraces"/>
<module name="RightCurly"/>
<!-- Checks for common coding problems -->
<!-- See http://checkstyle.sf.net/config_coding.html -->
<!-- module name="AvoidInlineConditionals"/ -->
<module name="DoubleCheckedLocking"/> <!-- MY FAVOURITE -->
<module name="EmptyStatement"/>
<!-- module name="EqualsHashCode"/ -->
<!--
<module name="HiddenField">
<property name="ignoreConstructorParameter" value="true"/>
<property name="ignoreSetter" value="true"/>
</module>
-->
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
<!-- module name="MagicNumber"/ -->
<module name="MissingSwitchDefault"/>
<!--
<module name="RedundantThrows">
<property name="allowUnchecked" value="true"/>
</module>
-->
<!--
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
-->
<!-- Checks for class design -->
<!-- See http://checkstyle.sf.net/config_design.html -->
<!-- module name="DesignForExtension"/ -->
<module name="FinalClass"/>
<module name="HideUtilityClassConstructor"/>
<module name="InterfaceIsType"/>
<module name="VisibilityModifier">
<!-- logger variables are often protected -->
<property name="protectedAllowed" value="true"/>
</module>
<!-- Miscellaneous other checks. -->
<!-- See http://checkstyle.sf.net/config_misc.html -->
<module name="ArrayTypeStyle"/>
<!-- module name="FinalParameters"/ -->
<!--
<module name="GenericIllegalRegexp">
<property name="format" value="\s+$"/>
<property name="message" value="Line has trailing spaces."/>
</module>
-->
<!-- module name="TrailingComment"/ -->
<!-- module name="TodoComment"/ -->
<module name="UpperEll"/>
</module>
</module>
-14
View File
@@ -1,14 +0,0 @@
// Acl Module build file
dependencies {
compile project(':spring-security-core'),
"net.sf.ehcache:ehcache:$ehcacheVersion",
"org.springframework:spring-aop:$springVersion",
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-tx:$springVersion",
"org.springframework:spring-jdbc:$springVersion"
testCompile "org.springframework:spring-beans:$springVersion",
"org.springframework:spring-context-support:$springVersion",
"org.springframework:spring-test:$springVersion"
}
+28 -23
View File
@@ -3,13 +3,14 @@
<parent>
<artifactId>spring-security-parent</artifactId>
<groupId>org.springframework.security</groupId>
<version>@pomVersion@</version>
<version>2.0.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<name>Spring Security - ACL module</name>
<packaging>jar</packaging>
<version>2.0.3</version>
<packaging>bundle</packaging>
<dependencies>
<dependency>
@@ -24,17 +25,13 @@
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<artifactId>spring-mock</artifactId>
<scope>test</scope>
</dependency>
<dependency>
@@ -47,22 +44,30 @@
<artifactId>hsqldb</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.3-603.jdbc3</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<spring.osgi.export>
org.springframework.security.*;version=${pom.version.osgi}
</spring.osgi.export>
<spring.osgi.import>
net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional,
org.springframework.security.*;version="[${pom.version.osgi},${pom.version.osgi}]",
org.springframework.context.*;version="${spring.version.osgi}",
org.springframework.dao.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.jdbc.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.transaction.support.*;version="${spring.version.osgi}";resolution:=optional,
org.springframework.util.*;version="${spring.version.osgi}",
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
javax.sql.*
</spring.osgi.import>
<build>
<plugins>
<plugin>
<groupId>com.springsource.bundlor</groupId>
<artifactId>com.springsource.bundlor.maven</artifactId>
</plugin>
</plugins>
</build>
<spring.osgi.private.pkg>
!org.springframework.security.*
</spring.osgi.private.pkg>
</project>
<spring.osgi.symbolic.name>org.springframework.security.acls</spring.osgi.symbolic.name>
</properties>
</project>
@@ -12,8 +12,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls;
import org.springframework.security.acls.sid.Sid;
import java.io.Serializable;
@@ -27,6 +28,7 @@ import java.io.Serializable;
* </p>
*
* @author Ben Alex
* @version $Id$
*
*/
public interface AccessControlEntry extends Serializable {
@@ -12,11 +12,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.sid.Sid;
import java.io.Serializable;
import java.util.List;
/**
@@ -27,34 +28,35 @@ import java.util.List;
* order to avoid needing references to the domain object itself, this
* interface handles indirection between a domain object and an ACL object
* identity via the {@link
* org.springframework.security.acls.model.ObjectIdentity} interface.
* org.springframework.security.acls.objectidentity.ObjectIdentity} interface.
* </p>
*
* <p>
* Implementing classes may elect to return instances that represent
* {@link org.springframework.security.acls.model.Permission} information for either
* some OR all {@link org.springframework.security.acls.model.Sid}
* Implementing classes may elect to return instances that represent
* {@link org.springframework.security.acls.Permission} information for either
* some OR all {@link org.springframework.security.acls.sid.Sid}
* instances. Therefore, an instance may NOT necessarily contain ALL <tt>Sid</tt>s
* for a given domain object.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface Acl extends Serializable {
/**
/**
* Returns all of the entries represented by the present <tt>Acl</tt>. Entries associated with
* the <tt>Acl</tt> parents are not returned.
*
*
* <p>This method is typically used for administrative purposes.</p>
*
*
* <p>The order that entries appear in the array is important for methods declared in the
* {@link MutableAcl} interface. Furthermore, some implementations MAY use ordering as
* part of advanced permission checking.</p>
*
*
* <p>Do <em>NOT</em> use this method for making authorization decisions. Instead use {@link
* #isGranted(List, List, boolean)}.</p>
*
* #isGranted(Permission[], Sid[], boolean)}.</p>
*
* <p>This method must operate correctly even if the <tt>Acl</tt> only represents a subset of
* <tt>Sid</tt>s. The caller is responsible for correctly handling the result if only a subset of
* <tt>Sid</tt>s is represented.</p>
@@ -62,7 +64,7 @@ public interface Acl extends Serializable {
* @return the list of entries represented by the <tt>Acl</tt>, or <tt>null</tt> if there are
* no entries presently associated with this <tt>Acl</tt>.
*/
List<AccessControlEntry> getEntries();
AccessControlEntry[] getEntries();
/**
* Obtains the domain object this <tt>Acl</tt> provides entries for. This is immutable once an
@@ -83,11 +85,11 @@ public interface Acl extends Serializable {
/**
* 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 <tt>Acl</tt> and this
* <tt>Acl</tt>. For actual inheritance to take place, the {@link #isEntriesInheriting()} must also be
* <tt>true</tt>.</p>
*
*
* <p>This method must operate correctly even if the <tt>Acl</tt> only represents a subset of
* <tt>Sid</tt>s. The caller is responsible for correctly handling the result if only a subset of
* <tt>Sid</tt>s is represented.</p>
@@ -110,13 +112,13 @@ public interface Acl extends Serializable {
/**
* This is the actual authorization logic method, and must be used whenever ACL authorization decisions are
* required.
*
*
* <p>An array of <tt>Sid</tt>s are presented, representing security identifies of the current
* principal. In addition, an array of <tt>Permission</tt>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 <tt>Permission</tt>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
@@ -126,9 +128,9 @@ public interface Acl extends Serializable {
* ACL, provided that {@link #isEntriesInheriting()} is <tt>true</tt>, 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 <tt>Acl</tt> only represents a subset of <tt>Sid</tt>s,
* although the implementation is permitted to throw one of the signature-defined exceptions if the method
* although the implementation is permitted to throw one of the signature-defined exceptions if the method
* is called requesting an authorization decision for a {@link Sid} that was never loaded in this <tt>Acl</tt>.
* </p>
*
@@ -144,7 +146,7 @@ public interface Acl extends Serializable {
* @throws UnloadedSidException thrown if the <tt>Acl</tt> does not have details for one or more of the
* <tt>Sid</tt>s passed as arguments
*/
boolean isGranted(List<Permission> permission, List<Sid> sids, boolean administrativeMode)
boolean isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode)
throws NotFoundException, UnloadedSidException;
/**
@@ -164,5 +166,5 @@ public interface Acl extends Serializable {
*
* @return <tt>true</tt> if every passed <tt>Sid</tt> is represented by this <tt>Acl</tt> instance
*/
boolean isSidLoaded(List<Sid> sids);
boolean isSidLoaded(Sid[] sids);
}
@@ -12,9 +12,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
package org.springframework.security.acls;
import org.springframework.security.acls.model.Permission;
import org.springframework.util.Assert;
@@ -22,6 +21,7 @@ import org.springframework.util.Assert;
* Utility methods for displaying ACL information.
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AclFormattingUtils {
@@ -63,11 +63,17 @@ public abstract class AclFormattingUtils {
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}.
* 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
*
@@ -96,12 +102,4 @@ public abstract class AclFormattingUtils {
return printBinary(mask, Permission.RESERVED_ON, Permission.RESERVED_OFF).replace(Permission.RESERVED_ON, code);
}
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);
}
}
@@ -1,145 +0,0 @@
package org.springframework.security.acls;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.acls.domain.DefaultPermissionFactory;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.domain.PermissionFactory;
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityGenerator;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
/**
* Used by Spring Security's expression-based access control implementation to evaluate permissions for a particular
* object using the ACL module. Similar in behaviour to
* {@link org.springframework.security.acls.AclEntryVoter AclEntryVoter}.
*
* @author Luke Taylor
* @since 3.0
*/
public class AclPermissionEvaluator implements PermissionEvaluator {
private final Log logger = LogFactory.getLog(getClass());
private AclService aclService;
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
private ObjectIdentityGenerator objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private PermissionFactory permissionFactory = new DefaultPermissionFactory();
public AclPermissionEvaluator(AclService aclService) {
this.aclService = aclService;
}
/**
* Determines whether the user has the given permission(s) on the domain object using the ACL
* configuration. If the domain object is null, returns false (this can always be overridden using a null
* check in the expression itself).
*/
public boolean hasPermission(Authentication authentication, Object domainObject, Object permission) {
if (domainObject == null) {
return false;
}
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
return checkPermission(authentication, objectIdentity, permission);
}
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
ObjectIdentity objectIdentity = objectIdentityGenerator.createObjectIdentity(targetId, targetType);
return checkPermission(authentication, objectIdentity, permission);
}
private boolean checkPermission(Authentication authentication, ObjectIdentity oid, Object permission) {
// Obtain the SIDs applicable to the principal
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
List<Permission> requiredPermission = resolvePermission(permission);
try {
// Lookup only ACLs for SIDs we're interested in
Acl acl = aclService.readAclById(oid, sids);
if (acl.isGranted(requiredPermission, sids, false)) {
if (logger.isDebugEnabled()) {
logger.debug("Access is granted");
}
return true;
}
if (logger.isDebugEnabled()) {
logger.debug("Returning false - ACLs returned, but insufficient permissions for this principal");
}
} catch (NotFoundException nfe) {
if (logger.isDebugEnabled()) {
logger.debug("Returning false - no ACLs apply for this principal");
}
}
return false;
}
List<Permission> resolvePermission(Object permission) {
if (permission instanceof Integer) {
return Arrays.asList(permissionFactory.buildFromMask(((Integer)permission).intValue()));
}
if (permission instanceof Permission) {
return Arrays.asList((Permission)permission);
}
if (permission instanceof Permission[]) {
return Arrays.asList((Permission[])permission);
}
if (permission instanceof String) {
String permString = (String)permission;
Permission p = null;
try {
p = permissionFactory.buildFromName(permString);
} catch(IllegalArgumentException notfound) {
p = permissionFactory.buildFromName(permString.toUpperCase());
}
if (p != null) {
return Arrays.asList(p);
}
}
throw new IllegalArgumentException("Unsupported permission: " + permission);
}
public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
}
public void setObjectIdentityGenerator(ObjectIdentityGenerator objectIdentityGenerator) {
this.objectIdentityGenerator = objectIdentityGenerator;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public void setPermissionFactory(PermissionFactory permissionFactory) {
this.permissionFactory = permissionFactory;
}
}
@@ -12,10 +12,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.sid.Sid;
import java.util.List;
import java.util.Map;
@@ -23,6 +24,7 @@ import java.util.Map;
* Provides retrieval of {@link Acl} instances.
*
* @author Ben Alex
* @version $Id$
*/
public interface AclService {
//~ Methods ========================================================================================================
@@ -34,13 +36,12 @@ public interface AclService {
*
* @return the children (or <tt>null</tt> if none were found)
*/
List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity);
ObjectIdentity[] findChildren(ObjectIdentity parentIdentity);
/**
* Same as {@link #readAclsById(List)} except it returns only a single Acl.
* <p>
* This method should not be called as it does not leverage the underlying implementation's potential ability to
* filter <tt>Acl</tt> entries based on a {@link Sid} parameter.</p>
* 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
* <tt>Acl</tt> entries based on a {@link Sid} parameter.</p>
*
* @param object to locate an {@link Acl} for
*
@@ -51,17 +52,18 @@ public interface AclService {
Acl readAclById(ObjectIdentity object) throws NotFoundException;
/**
* Same as {@link #readAclsById(List, List)} except it returns only a single Acl.
* Same as {@link #readAclsById(ObjectIdentity[], Sid[])} except it returns only a single Acl.
*
* @param object to locate an {@link Acl} for
* @param sids the security identities for which {@link Acl} information is required
* @param sids the security identities for which {@link Acl} information is required
* (may be <tt>null</tt> to denote all entries)
*
* @return the {@link Acl} for the requested {@link ObjectIdentity} (never <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for the requested {@link ObjectIdentity}
*/
Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException;
Acl readAclById(ObjectIdentity object, Sid[] sids)
throws NotFoundException;
/**
* Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s.<p>The returned map is
@@ -74,7 +76,7 @@ public interface AclService {
*
* @throws NotFoundException if an {@link Acl} was not found for each requested {@link ObjectIdentity}
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects) throws NotFoundException;
Map readAclsById(ObjectIdentity[] objects) throws NotFoundException;
/**
* Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s, but only for the
@@ -87,12 +89,13 @@ public interface AclService {
* not have a map key.</p>
*
* @param objects the objects to find {@link Acl} information for
* @param sids the security identities for which {@link Acl} information is required
* @param sids the security identities for which {@link Acl} information is required
* (may be <tt>null</tt> to denote all entries)
*
* @return a map with exactly one element for each {@link ObjectIdentity} passed as an argument (never <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for each requested {@link ObjectIdentity}
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids) throws NotFoundException;
Map readAclsById(ObjectIdentity[] objects, Sid[] sids)
throws NotFoundException;
}
@@ -12,18 +12,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls;
import org.springframework.security.SpringSecurityException;
/**
* Thrown if an <code>Acl</code> entry already exists for the object.
*
* @author Ben Alex
* @version $Id$
*/
public class AlreadyExistsException extends AclDataAccessException {
public class AlreadyExistsException extends SpringSecurityException {
//~ Constructors ===================================================================================================
/**
/**
* Constructs an <code>AlreadyExistsException</code> with the specified message.
*
* @param msg the detail message
@@ -32,7 +35,7 @@ public class AlreadyExistsException extends AclDataAccessException {
super(msg);
}
/**
/**
* Constructs an <code>AlreadyExistsException</code> with the specified message
* and root cause.
*
@@ -12,13 +12,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls;
/**
* Represents an ACE that provides auditing information.
*
* @author Ben Alex
* @version $Id$
*
*/
public interface AuditableAccessControlEntry extends AccessControlEntry {
@@ -12,13 +12,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls;
/**
* A mutable ACL that provides audit capabilities.
*
* @author Ben Alex
* @version $Id$
*
*/
public interface AuditableAcl extends MutableAcl {
@@ -12,18 +12,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls;
import org.springframework.security.SpringSecurityException;
/**
* Thrown if an {@link Acl} cannot be deleted because children <code>Acl</code>s exist.
*
* @author Ben Alex
* @version $Id$
*/
public class ChildrenExistException extends AclDataAccessException {
public class ChildrenExistException extends SpringSecurityException {
//~ Constructors ===================================================================================================
/**
/**
* Constructs an <code>ChildrenExistException</code> with the specified
* message.
*
@@ -33,7 +36,7 @@ public class ChildrenExistException extends AclDataAccessException {
super(msg);
}
/**
/**
* Constructs an <code>ChildrenExistException</code> with the specified
* message and root cause.
*
@@ -12,17 +12,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
package org.springframework.security.acls;
import org.springframework.security.SpringSecurityException;
/**
* Thrown if an ACL identity could not be extracted from an object.
*
* @author Ben Alex
* @version $Id$
*/
public class IdentityUnavailableException extends RuntimeException {
public class IdentityUnavailableException extends SpringSecurityException {
//~ Constructors ===================================================================================================
/**
/**
* Constructs an <code>IdentityUnavailableException</code> with the specified message.
*
* @param msg the detail message
@@ -31,7 +35,7 @@ public class IdentityUnavailableException extends RuntimeException {
super(msg);
}
/**
/**
* Constructs an <code>IdentityUnavailableException</code> with the specified message
* and root cause.
*
@@ -12,19 +12,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls;
import java.io.Serializable;
import org.springframework.security.acls.sid.Sid;
/**
* A mutable <tt>Acl</tt>.
*
* <p>
* A mutable ACL must ensure that appropriate security checks are performed
* before allowing access to its methods.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface MutableAcl extends Acl {
//~ Methods ========================================================================================================
@@ -43,7 +47,7 @@ public interface MutableAcl extends Acl {
/**
* Changes the present owner to a different owner.
*
*
* @param newOwner the new owner (mandatory; cannot be null)
*/
void setOwner(Sid newOwner);
@@ -12,14 +12,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
/**
* Provides support for creating and storing <code>Acl</code> instances.
*
* @author Ben Alex
* @version $Id$
*/
public interface MutableAclService extends AclService {
//~ Methods ========================================================================================================
@@ -53,6 +55,8 @@ public interface MutableAclService extends AclService {
*
* @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?)
@@ -12,18 +12,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls;
import org.springframework.security.SpringSecurityException;
/**
* Thrown if an ACL-related object cannot be found.
*
* @author Ben Alex
* @version $Id$
*/
public class NotFoundException extends AclDataAccessException {
public class NotFoundException extends SpringSecurityException {
//~ Constructors ===================================================================================================
/**
/**
* Constructs an <code>NotFoundException</code> with the specified message.
*
* @param msg the detail message
@@ -32,7 +35,7 @@ public class NotFoundException extends AclDataAccessException {
super(msg);
}
/**
/**
* Constructs an <code>NotFoundException</code> with the specified message
* and root cause.
*
@@ -12,8 +12,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls;
import org.springframework.security.acls.sid.Sid;
/**
@@ -22,8 +23,10 @@ package org.springframework.security.acls.model;
* <p>
* Generally the owner of an ACL is able to call any ACL mutator method, as
* well as assign a new owner.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface OwnershipAcl extends MutableAcl {
//~ Methods ========================================================================================================
@@ -12,14 +12,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls;
import java.io.Serializable;
/**
* Represents a permission granted to a <tt>Sid</tt> for a given domain object.
* Represents a permission granted to a {@link org.springframework.security.acls.sid.Sid Sid} for a given domain object.
*
* @author Ben Alex
* @version $Id$
*/
public interface Permission extends Serializable {
//~ Static fields/initializers =====================================================================================
@@ -45,11 +46,10 @@ public interface Permission extends Serializable {
* {@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>
* The returned String must be 32 characters in length.
* <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>
* <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
*/
@@ -12,7 +12,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls;
import org.springframework.security.SpringSecurityException;
/**
@@ -20,11 +22,12 @@ package org.springframework.security.acls.model;
* the caller has requested details for an unloaded <code>Sid</code>.
*
* @author Ben Alex
* @version $Id$
*/
public class UnloadedSidException extends AclDataAccessException {
public class UnloadedSidException extends SpringSecurityException {
//~ Constructors ===================================================================================================
/**
/**
* Constructs an <code>NotFoundException</code> with the specified message.
*
* @param msg the detail message
@@ -33,7 +36,7 @@ public class UnloadedSidException extends AclDataAccessException {
super(msg);
}
/**
/**
* Constructs an <code>NotFoundException</code> with the specified message
* and root cause.
*
@@ -1,6 +0,0 @@
/**
* After-invocation providers for collection and array filtering. Consider using a {@code PostFilter} annotation in
* preference.
*/
package org.springframework.security.acls.afterinvocation;
@@ -1,12 +1,15 @@
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.AclFormattingUtils;
import org.springframework.security.acls.Permission;
/**
* Provides an abstract superclass for {@link Permission} implementations.
*
*
* @author Ben Alex
* @since 2.0.3
* @see AbstractRegisteredPermission
*
*/
public abstract class AbstractPermission implements Permission {
@@ -16,23 +19,7 @@ public abstract class AbstractPermission implements Permission {
protected int mask;
//~ Constructors ===================================================================================================
/**
* Sets the permission mask and uses the '*' character to represent active bits when represented as a bit
* pattern string.
*
* @param mask the integer bit mask for the permission
*/
protected AbstractPermission(int mask) {
this.mask = mask;
this.code = '*';
}
/**
* Sets the permission mask and uses the specified character for active bits.
*
* @param mask the integer bit mask for the permission
* @param code the character to print for each active bit in the mask (see {@link Permission#getPattern()})
*/
protected AbstractPermission(int mask, char code) {
this.mask = mask;
this.code = code;
@@ -66,7 +53,7 @@ public abstract class AbstractPermission implements Permission {
return this.getClass().getSimpleName() + "[" + getPattern() + "=" + mask + "]";
}
public final int hashCode() {
return this.mask;
}
public final int hashCode() {
return this.mask;
}
}
@@ -0,0 +1,39 @@
package org.springframework.security.acls.domain;
import org.springframework.security.acls.Permission;
/**
* Provides an abstract base for standard {@link Permission} instances that wish to offer static convenience
* methods to callers via delegation to {@link DefaultPermissionFactory}.
*
* @author Ben Alex
* @since 2.0.3
*
*/
public abstract class AbstractRegisteredPermission extends AbstractPermission {
protected static DefaultPermissionFactory defaultPermissionFactory = new DefaultPermissionFactory();
protected AbstractRegisteredPermission(int mask, char code) {
super(mask, code);
}
protected final static void registerPermissionsFor(Class subClass) {
defaultPermissionFactory.registerPublicPermissions(subClass);
}
public final static Permission buildFromMask(int mask) {
return defaultPermissionFactory.buildFromMask(mask);
}
public final static Permission[] buildFromMask(int[] masks) {
return defaultPermissionFactory.buildFromMask(masks);
}
public final static Permission buildFromName(String name) {
return defaultPermissionFactory.buildFromName(name);
}
public final static Permission[] buildFromName(String[] names) {
return defaultPermissionFactory.buildFromName(names);
}
}
@@ -14,11 +14,11 @@
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AuditableAccessControlEntry;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.AccessControlEntry;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.AuditableAccessControlEntry;
import org.springframework.security.acls.Permission;
import org.springframework.security.acls.sid.Sid;
import org.springframework.util.Assert;
@@ -29,6 +29,7 @@ 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 ================================================================================================
@@ -67,47 +68,47 @@ public class AccessControlEntryImpl implements AccessControlEntry, AuditableAcce
AccessControlEntryImpl rhs = (AccessControlEntryImpl) arg0;
if (this.acl == null) {
if (rhs.getAcl() != null) {
return false;
}
// Both this.acl and rhs.acl are null and thus equal
if (rhs.getAcl() != null) {
return false;
}
// Both this.acl and rhs.acl are null and thus equal
} else {
// this.acl is non-null
if (rhs.getAcl() == null) {
return false;
}
// Both this.acl and rhs.acl are non-null, so do a comparison
if (this.acl.getObjectIdentity() == null) {
if (rhs.acl.getObjectIdentity() != null) {
return false;
}
// Both this.acl and rhs.acl are null and thus equal
} else {
// Both this.acl.objectIdentity and rhs.acl.objectIdentity are non-null
if (!this.acl.getObjectIdentity().equals(rhs.getAcl().getObjectIdentity())) {
return false;
}
}
// this.acl is non-null
if (rhs.getAcl() == null) {
return false;
}
// Both this.acl and rhs.acl are non-null, so do a comparison
if (this.acl.getObjectIdentity() == null) {
if (rhs.acl.getObjectIdentity() != null) {
return false;
}
// Both this.acl and rhs.acl are null and thus equal
} else {
// Both this.acl.objectIdentity and rhs.acl.objectIdentity are non-null
if (!this.acl.getObjectIdentity().equals(rhs.getAcl().getObjectIdentity())) {
return false;
}
}
}
if (this.id == null) {
if (rhs.id != null) {
return false;
}
// Both this.id and rhs.id are null and thus equal
if (rhs.id != null) {
return false;
}
// Both this.id and rhs.id are null and thus equal
} else {
// this.id is non-null
if (rhs.id == null) {
return false;
}
// this.id is non-null
if (rhs.id == null) {
return false;
}
// Both this.id and rhs.id are non-null
if (!this.id.equals(rhs.id)) {
return false;
}
// Both this.id and rhs.id are non-null
if (!this.id.equals(rhs.id)) {
return false;
}
}
if ((this.auditFailure != rhs.isAuditFailure()) || (this.auditSuccess != rhs.isAuditSuccess())
|| (this.granting != rhs.isGranting())
|| !this.permission.equals(rhs.getPermission()) || !this.sid.equals(rhs.getSid())) {
@@ -159,7 +160,7 @@ public class AccessControlEntryImpl implements AccessControlEntry, AuditableAcce
}
public String toString() {
StringBuilder sb = new StringBuilder();
StringBuffer sb = new StringBuffer();
sb.append("AccessControlEntryImpl[");
sb.append("id: ").append(this.id).append("; ");
sb.append("granting: ").append(this.granting).append("; ");
@@ -15,7 +15,7 @@
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.Acl;
/**
@@ -23,6 +23,7 @@ import org.springframework.security.acls.model.Acl;
* adminstrative methods on the <code>AclImpl</code>.
*
* @author Ben Alex
* @version $Id$
*/
public interface AclAuthorizationStrategy {
//~ Static fields/initializers =====================================================================================
@@ -15,28 +15,30 @@
package org.springframework.security.acls.domain;
import java.util.Arrays;
import java.util.List;
import org.springframework.security.AccessDeniedException;
import org.springframework.security.Authentication;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.Permission;
import org.springframework.security.acls.sid.PrincipalSid;
import org.springframework.security.acls.sid.Sid;
import org.springframework.security.acls.sid.SidRetrievalStrategy;
import org.springframework.security.acls.sid.SidRetrievalStrategyImpl;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.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.
* 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 ================================================================================================
@@ -48,7 +50,7 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
//~ Constructors ===================================================================================================
/**
/**
* Constructor. The only mandatory parameter relates to the system-wide {@link GrantedAuthority} instances that
* can be held to always permit ACL changes.
*
@@ -58,7 +60,8 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
* index 2 is the authority needed to change other ACL and ACE details) (required)
*/
public AclAuthorizationStrategyImpl(GrantedAuthority[] auths) {
Assert.isTrue(auths != null && auths.length == 3, "GrantedAuthority[] with three elements required");
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];
@@ -97,19 +100,23 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
}
// Iterate this principal's authorities to determine right
if (authentication.getAuthorities().contains(requiredAuthority)) {
return;
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
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
Sid[] sids = sidRetrievalStrategy.getSids(authentication);
if (acl.isGranted(Arrays.asList(BasePermission.ADMINISTRATION), sids, false)) {
if (acl.isGranted(new Permission[] {BasePermission.ADMINISTRATION}, sids, false)) {
return;
}
throw new AccessDeniedException(
"Principal does not have required ACL permissions to perform requested operation");
"Principal does not have required ACL permissions to perform requested operation");
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
@@ -15,19 +15,20 @@
package org.springframework.security.acls.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AuditableAcl;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.OwnershipAcl;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.UnloadedSidException;
import org.springframework.security.acls.AccessControlEntry;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.AuditableAcl;
import org.springframework.security.acls.MutableAcl;
import org.springframework.security.acls.NotFoundException;
import org.springframework.security.acls.OwnershipAcl;
import org.springframework.security.acls.Permission;
import org.springframework.security.acls.UnloadedSidException;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.sid.Sid;
import org.springframework.util.Assert;
@@ -35,25 +36,26 @@ import org.springframework.util.Assert;
* Base implementation of <code>Acl</code>.
*
* @author Ben Alex
* @version $Id
*/
public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
//~ Instance fields ================================================================================================
private Acl parentAcl;
private transient AclAuthorizationStrategy aclAuthorizationStrategy;
private transient AuditLogger auditLogger;
private List<AccessControlEntry> aces = new ArrayList<AccessControlEntry>();
private List aces = new Vector();
private ObjectIdentity objectIdentity;
private Serializable id;
private Sid owner; // OwnershipAcl
private List<Sid> loadedSids = null; // includes all SIDs the WHERE clause covered, even if there was no ACE for a SID
private Sid[] loadedSids = null; // includes all SIDs the WHERE clause covered, even if there was no ACE for a SID
private boolean entriesInheriting = true;
//~ Constructors ===================================================================================================
/**
/**
* Minimal constructor, which should be used {@link
* org.springframework.security.acls.model.MutableAclService#createAcl(ObjectIdentity)}.
* org.springframework.security.acls.MutableAclService#createAcl(ObjectIdentity)}.
*
* @param objectIdentity the object identity this ACL relates to (required)
* @param id the primary key assigned to this ACL (required)
@@ -61,7 +63,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
* @param auditLogger audit logger (required)
*/
public AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy,
AuditLogger auditLogger) {
AuditLogger auditLogger) {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(id, "Id required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
@@ -72,7 +74,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
this.auditLogger = auditLogger;
}
/**
/**
* Full constructor, which should be used by persistence tools that do not
* provide field-level access features.
*
@@ -88,7 +90,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
* @param owner the owner (required)
*/
public AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy,
AuditLogger auditLogger, Acl parentAcl, List<Sid> loadedSids, boolean entriesInheriting, Sid owner) {
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");
@@ -104,55 +106,35 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
this.owner = owner;
}
/**
/**
* Private no-argument constructor for use by reflection-based persistence
* tools along with field-level access.
*/
@SuppressWarnings("unused")
private AclImpl() {}
//~ Methods ========================================================================================================
private void verifyAceIndexExists(int aceIndex) {
if (aceIndex < 0) {
throw new NotFoundException("aceIndex must be greater than or equal to zero");
}
if (aceIndex > this.aces.size()) {
throw new NotFoundException("aceIndex must correctly refer to an index of the AccessControlEntry collection");
}
}
public void deleteAce(int aceIndex) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
this.aces.remove(aceIndex);
}
}
private void verifyAceIndexExists(int aceIndex) {
if (aceIndex < 0) {
throw new NotFoundException("aceIndex must be greater than or equal to zero");
}
if (aceIndex >= this.aces.size()) {
throw new NotFoundException("aceIndex must refer to an index of the AccessControlEntry list. " +
"List size is " + aces.size() + ", index was " + aceIndex);
}
}
public void insertAce(int atIndexLocation, Permission permission, Sid sid, boolean granting) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.notNull(permission, "Permission required");
Assert.notNull(sid, "Sid required");
if (atIndexLocation < 0) {
throw new NotFoundException("atIndexLocation must be greater than or equal to zero");
}
if (atIndexLocation > this.aces.size()) {
throw new NotFoundException("atIndexLocation must be less than or equal to the size of the AccessControlEntry collection");
}
AccessControlEntryImpl ace = new AccessControlEntryImpl(null, this, sid, permission, granting, false, false);
synchronized (aces) {
this.aces.add(atIndexLocation, ace);
}
}
public List<AccessControlEntry> getEntries() {
public AccessControlEntry[] getEntries() {
// Can safely return AccessControlEntry directly, as they're immutable outside the ACL package
return new ArrayList<AccessControlEntry>(aces);
return (AccessControlEntry[]) aces.toArray(new AccessControlEntry[] {});
}
public Serializable getId() {
@@ -163,6 +145,33 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
return objectIdentity;
}
public Sid getOwner() {
return this.owner;
}
public Acl getParentAcl() {
return parentAcl;
}
public void insertAce(int atIndexLocation, Permission permission, Sid sid, boolean granting)
throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.notNull(permission, "Permission required");
Assert.notNull(sid, "Sid required");
if (atIndexLocation < 0) {
throw new NotFoundException("atIndexLocation must be greater than or equal to zero");
}
if (atIndexLocation > this.aces.size()) {
throw new NotFoundException("atIndexLocation must be less than or equal to the size of the AccessControlEntry collection");
}
AccessControlEntryImpl ace = new AccessControlEntryImpl(null, this, sid, permission, granting, false, false);
synchronized (aces) {
this.aces.add(atIndexLocation, ace);
}
}
public boolean isEntriesInheriting() {
return entriesInheriting;
}
@@ -198,8 +207,8 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
* @throws UnloadedSidException if the passed SIDs are unknown to this ACL because the ACL was only loaded for a
* subset of SIDs
*/
public boolean isGranted(List<Permission> permission, List<Sid> sids, boolean administrativeMode)
throws NotFoundException, UnloadedSidException {
public boolean isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode)
throws NotFoundException, UnloadedSidException {
Assert.notEmpty(permission, "Permissions required");
Assert.notEmpty(sids, "SIDs required");
@@ -209,14 +218,16 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
AccessControlEntry firstRejection = null;
for (Permission p : permission) {
for (Sid sid: sids) {
for (int i = 0; i < permission.length; i++) {
for (int x = 0; x < sids.length; x++) {
// Attempt to find exact match for this permission mask and SID
Iterator acesIterator = aces.iterator();
boolean scanNextSid = true;
for (AccessControlEntry ace : aces ) {
while (acesIterator.hasNext()) {
AccessControlEntry ace = (AccessControlEntry) acesIterator.next();
if ((ace.getPermission().getMask() == p.getMask()) && ace.getSid().equals(sid)) {
if ((ace.getPermission().getMask() == permission[i].getMask()) && ace.getSid().equals(sids[x])) {
// Found a matching ACE, so its authorization decision will prevail
if (ace.isGranting()) {
// Success
@@ -225,19 +236,19 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
}
return true;
} else {
// Failure for this permission, so stop search
// We will see if they have a different permission
// (this permission is 100% rejected for this SID)
if (firstRejection == null) {
// Store first rejection for auditing reasons
firstRejection = ace;
}
scanNextSid = false; // helps break the loop
break; // exit "aceIterator" while loop
}
// Failure for this permission, so stop search
// We will see if they have a different permission
// (this permission is 100% rejected for this SID)
if (firstRejection == null) {
// Store first rejection for auditing reasons
firstRejection = ace;
}
scanNextSid = false; // helps break the loop
break; // exit aces loop
}
}
@@ -267,19 +278,19 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
}
}
public boolean isSidLoaded(List<Sid> sids) {
public boolean isSidLoaded(Sid[] sids) {
// If loadedSides is null, this indicates all SIDs were loaded
// Also return true if the caller didn't specify a SID to find
if ((this.loadedSids == null) || (sids == null) || (sids.size() == 0)) {
if ((this.loadedSids == null) || (sids == null) || (sids.length == 0)) {
return true;
}
// This ACL applies to a SID subset only. Iterate to check it applies.
for (Sid sid: sids) {
for (int i = 0; i < sids.length; i++) {
boolean found = false;
for (Sid loadedSid : loadedSids) {
if (sid.equals(loadedSid)) {
for (int y = 0; y < this.loadedSids.length; y++) {
if (sids[i].equals(this.loadedSids[y])) {
// this SID is OK
found = true;
@@ -306,37 +317,30 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
this.owner = newOwner;
}
public Sid getOwner() {
return this.owner;
}
public void setParent(Acl newParent) {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.isTrue(newParent == null || !newParent.equals(this), "Cannot be the parent of yourself");
this.parentAcl = newParent;
}
public Acl getParentAcl() {
return parentAcl;
}
public String toString() {
StringBuilder sb = new StringBuilder();
StringBuffer sb = new StringBuffer();
sb.append("AclImpl[");
sb.append("id: ").append(this.id).append("; ");
sb.append("objectIdentity: ").append(this.objectIdentity).append("; ");
sb.append("owner: ").append(this.owner).append("; ");
Iterator iterator = this.aces.iterator();
int count = 0;
for (AccessControlEntry ace : aces) {
while (iterator.hasNext()) {
count++;
if (count == 1) {
sb.append("\r\n");
}
sb.append(ace).append("\r\n");
sb.append(iterator.next().toString()).append("\r\n");
}
if (count == 0) {
@@ -345,7 +349,6 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
sb.append("inheriting: ").append(this.entriesInheriting).append("; ");
sb.append("parent: ").append((this.parentAcl == null) ? "Null" : this.parentAcl.getObjectIdentity().toString());
sb.append("; ");
sb.append("aclAuthorizationStrategy: ").append(this.aclAuthorizationStrategy).append("; ");
sb.append("auditLogger: ").append(this.auditLogger);
sb.append("]");
@@ -357,7 +360,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(aceIndex);
ace.setPermission(permission);
@@ -367,42 +370,42 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
public void updateAuditing(int aceIndex, boolean auditSuccess, boolean auditFailure) {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_AUDITING);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(aceIndex);
ace.setAuditSuccess(auditSuccess);
ace.setAuditFailure(auditFailure);
}
}
public boolean equals(Object obj) {
if (obj instanceof AclImpl) {
AclImpl rhs = (AclImpl) obj;
if (this.aces.equals(rhs.aces)) {
if ((this.parentAcl == null && rhs.parentAcl == null) || (this.parentAcl !=null && this.parentAcl.equals(rhs.parentAcl))) {
if ((this.objectIdentity == null && rhs.objectIdentity == null) || (this.objectIdentity != null && this.objectIdentity.equals(rhs.objectIdentity))) {
if ((this.id == null && rhs.id == null) || (this.id != null && this.id.equals(rhs.id))) {
if ((this.owner == null && rhs.owner == null) || (this.owner != null && this.owner.equals(rhs.owner))) {
if (this.entriesInheriting == rhs.entriesInheriting) {
if ((this.loadedSids == null && rhs.loadedSids == null)) {
return true;
}
if (this.loadedSids != null && (this.loadedSids.size() == rhs.loadedSids.size())) {
for (int i = 0; i < this.loadedSids.size(); i++) {
if (!this.loadedSids.get(i).equals(rhs.loadedSids.get(i))) {
return false;
}
}
return true;
}
}
}
}
}
}
}
}
return false;
}
public boolean equals(Object obj) {
if (obj instanceof AclImpl) {
AclImpl rhs = (AclImpl) obj;
if (this.aces.equals(rhs.aces)) {
if ((this.parentAcl == null && rhs.parentAcl == null) || (this.parentAcl.equals(rhs.parentAcl))) {
if ((this.objectIdentity == null && rhs.objectIdentity == null) || (this.objectIdentity.equals(rhs.objectIdentity))) {
if ((this.id == null && rhs.id == null) || (this.id.equals(rhs.id))) {
if ((this.owner == null && rhs.owner == null) || this.owner.equals(rhs.owner)) {
if (this.entriesInheriting == rhs.entriesInheriting) {
if ((this.loadedSids == null && rhs.loadedSids == null)) {
return true;
}
if (this.loadedSids.length == rhs.loadedSids.length) {
for (int i = 0; i < this.loadedSids.length; i++) {
if (!this.loadedSids[i].equals(rhs.loadedSids[i])) {
return false;
}
}
return true;
}
}
}
}
}
}
}
}
return false;
}
}
@@ -14,13 +14,14 @@
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.AccessControlEntry;
/**
* Used by <code>AclImpl</code> to log audit events.
*
* @author Ben Alex
* @version $Id$
*
*/
public interface AuditLogger {
@@ -14,31 +14,36 @@
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.Permission;
/**
* A set of standard permissions.
*
*
* <p>
* You may subclass this class to add additional permissions, or use this class as a guide
* for creating your own permission classes.
* </p>
*
*
* @author Ben Alex
* @version $Id$
*/
public class BasePermission extends AbstractPermission {
public class BasePermission extends AbstractRegisteredPermission {
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
protected BasePermission(int mask) {
super(mask);
/**
* Registers the public static permissions defined on this class. This is mandatory so
* that the static methods will operate correctly.
*/
static {
registerPermissionsFor(BasePermission.class);
}
protected BasePermission(int mask, char code) {
super(mask, code);
super(mask, code);
}
}
@@ -14,16 +14,17 @@
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.AuditableAccessControlEntry;
import org.springframework.security.acls.AccessControlEntry;
import org.springframework.security.acls.AuditableAccessControlEntry;
import org.springframework.util.Assert;
/**
* A basic implementation of {@link AuditLogger}.
* A bsaic implementation of {@link AuditLogger}.
*
* @author Ben Alex
* @version $Id$
*/
public class ConsoleAuditLogger implements AuditLogger {
//~ Methods ========================================================================================================
@@ -14,24 +14,26 @@
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.AclFormattingUtils;
import org.springframework.security.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 extends AbstractPermission {
private String pattern = THIRTY_TWO_RESERVED_OFF;
public CumulativePermission() {
super(0, ' ');
super(0, ' ');
}
public CumulativePermission clear(Permission permission) {
this.mask &= ~permission.getMask();
this.pattern = AclFormattingUtils.demergePatterns(this.pattern, permission.getPattern());
@@ -45,14 +47,14 @@ public class CumulativePermission extends AbstractPermission {
return this;
}
public CumulativePermission set(Permission permission) {
this.mask |= permission.getMask();
this.pattern = AclFormattingUtils.mergePatterns(this.pattern, permission.getPattern());
return this;
}
public String getPattern() {
return this.pattern;
}
@@ -1,68 +1,42 @@
package org.springframework.security.acls.domain;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.Permission;
import org.springframework.security.acls.jdbc.LookupStrategy;
import org.springframework.util.Assert;
/**
* Default implementation of {@link PermissionFactory}.
*
* <p>
* Used as a strategy by classes which wish to map integer masks and permission names to <tt>Permission</tt>
* instances for use with the ACL implementation.
* <p>
* Maintains a registry of permission names and masks to <tt>Permission</tt> instances.
*
* Generally this class will be used by a {@link Permission} instance, as opposed to being dependency
* injected into a {@link LookupStrategy} or similar. Nevertheless, the latter mode of operation is
* fully supported (in which case your {@link Permission} implementations probably should extend
* {@link AbstractPermission} instead of {@link AbstractRegisteredPermission}).
* </p>
*
* @author Ben Alex
* @author Luke Taylor
* @since 2.0.3
*
*/
public class DefaultPermissionFactory implements PermissionFactory {
private final Map<Integer, Permission> registeredPermissionsByInteger = new HashMap<Integer, Permission>();
private final Map<String, Permission> registeredPermissionsByName = new HashMap<String, Permission>();
private Map registeredPermissionsByInteger = new HashMap();
private Map registeredPermissionsByName = new HashMap();
/**
* Registers the <tt>Permission</tt> fields from the <tt>BasePermission</tt> class.
*/
public DefaultPermissionFactory() {
registerPublicPermissions(BasePermission.class);
}
/**
* Registers the <tt>Permission</tt> fields from the supplied class.
*/
public DefaultPermissionFactory(Class<? extends Permission> permissionClass) {
registerPublicPermissions(permissionClass);
}
/**
* Registers a map of named <tt>Permission</tt> instances.
*
* @param namedPermissions the map of <tt>Permission</tt>s, keyed by name.
*/
public DefaultPermissionFactory(Map<String, ? extends Permission> namedPermissions) {
for (String name : namedPermissions.keySet()) {
registerPermission(namedPermissions.get(name), name);
}
}
/**
* Registers the public static fields of type {@link Permission} for a give class.
* <p>
* These permissions will be registered under the name of the field. See {@link BasePermission}
* for an example.
*
* Permit registration of a {@link DefaultPermissionFactory} class. The class must provide
* public static fields of type {@link Permission} to represent the possible permissions.
*
* @param clazz a {@link Permission} class with public static fields to register
*/
protected void registerPublicPermissions(Class<? extends Permission> clazz) {
public void registerPublicPermissions(Class clazz) {
Assert.notNull(clazz, "Class required");
Field[] fields = clazz.getFields();
Assert.isAssignable(Permission.class, clazz);
Field[] fields = clazz.getFields();
for (int i = 0; i < fields.length; i++) {
try {
@@ -72,32 +46,32 @@ public class DefaultPermissionFactory implements PermissionFactory {
// Found a Permission static field
Permission perm = (Permission) fieldValue;
String permissionName = fields[i].getName();
registerPermission(perm, permissionName);
}
} catch (Exception ignore) {}
}
}
protected void registerPermission(Permission perm, String permissionName) {
Assert.notNull(perm, "Permission required");
Assert.hasText(permissionName, "Permission name required");
Integer mask = new Integer(perm.getMask());
// Ensure no existing Permission uses this integer or code
Assert.isTrue(!registeredPermissionsByInteger.containsKey(mask), "An existing Permission already provides mask " + mask);
Assert.isTrue(!registeredPermissionsByName.containsKey(permissionName), "An existing Permission already provides name '" + permissionName + "'");
// Register the new Permission
registeredPermissionsByInteger.put(mask, perm);
registeredPermissionsByName.put(permissionName, perm);
}
public void registerPermission(Permission perm, String permissionName) {
Assert.notNull(perm, "Permission required");
Assert.hasText(permissionName, "Permission name required");
Integer mask = new Integer(perm.getMask());
// Ensure no existing Permission uses this integer or code
Assert.isTrue(!registeredPermissionsByInteger.containsKey(mask), "An existing Permission already provides mask " + mask);
Assert.isTrue(!registeredPermissionsByName.containsKey(permissionName), "An existing Permission already provides name '" + permissionName + "'");
// Register the new Permission
registeredPermissionsByInteger.put(mask, perm);
registeredPermissionsByName.put(permissionName, perm);
}
public Permission buildFromMask(int mask) {
if (registeredPermissionsByInteger.containsKey(Integer.valueOf(mask))) {
// The requested mask has an exact match against a statically-defined Permission, so return it
return registeredPermissionsByInteger.get(new Integer(mask));
if (registeredPermissionsByInteger.containsKey(new Integer(mask))) {
// The requested mask has an exactly match against a statically-defined Permission, so return it
return (Permission) registeredPermissionsByInteger.get(new Integer(mask));
}
// To get this far, we have to use a CumulativePermission
@@ -107,11 +81,8 @@ public class DefaultPermissionFactory implements PermissionFactory {
int permissionToCheck = 1 << i;
if ((mask & permissionToCheck) == permissionToCheck) {
Permission p = registeredPermissionsByInteger.get(Integer.valueOf(permissionToCheck));
if (p == null) {
throw new IllegalStateException("Mask '" + permissionToCheck + "' does not have a corresponding static Permission");
}
Permission p = (Permission) registeredPermissionsByInteger.get(new Integer(permissionToCheck));
Assert.state(p != null, "Mask " + permissionToCheck + " does not have a corresponding static Permission");
permission.set(p);
}
}
@@ -119,28 +90,38 @@ public class DefaultPermissionFactory implements PermissionFactory {
return permission;
}
public Permission buildFromName(String name) {
Permission p = registeredPermissionsByName.get(name);
if (p == null) {
throw new IllegalArgumentException("Unknown permission '" + name + "'");
public Permission[] buildFromMask(int[] masks) {
if ((masks == null) || (masks.length == 0)) {
return new Permission[0];
}
return p;
}
Permission[] permissions = new Permission[masks.length];
public List<Permission> buildFromNames(List<String> names) {
if ((names == null) || (names.size() == 0)) {
return Collections.emptyList();
}
List<Permission> permissions = new ArrayList<Permission>(names.size());
for (String name : names) {
permissions.add(buildFromName(name));
for (int i = 0; i < masks.length; i++) {
permissions[i] = buildFromMask(masks[i]);
}
return permissions;
}
public Permission buildFromName(String name) {
Assert.isTrue(registeredPermissionsByName.containsKey(name), "Unknown permission '" + name + "'");
return (Permission) registeredPermissionsByName.get(name);
}
public Permission[] buildFromName(String[] names) {
if ((names == null) || (names.length == 0)) {
return new Permission[0];
}
Permission[] permissions = new Permission[names.length];
for (int i = 0; i < names.length; i++) {
permissions[i] = buildFromName(names[i]);
}
return permissions;
}
}
@@ -1,31 +1,24 @@
package org.springframework.security.acls.domain;
import java.util.List;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.Permission;
/**
* Provides a simple mechanism to retrieve {@link Permission} instances from integer masks.
*
*
* @author Ben Alex
* @since 2.0.3
*
*
*/
public interface PermissionFactory {
/**
* 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
*/
Permission buildFromMask(int mask);
/**
* 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 abstract Permission buildFromMask(int mask);
Permission buildFromName(String name);
List<Permission> buildFromNames(List<String> names);
}
}
@@ -1,65 +0,0 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;
/**
* Basic implementation of {@link SidRetrievalStrategy} that creates a {@link Sid} for the principal, as well as
* every granted authority the principal holds. Can optionally have a <tt>RoleHierarchy</tt> injected in order to
* determine the extended list of authorities that the principal is assigned.
* <p>
* The returned array will always contain the {@link PrincipalSid} before any {@link GrantedAuthoritySid} elements.
*
* @author Ben Alex
*/
public class SidRetrievalStrategyImpl implements SidRetrievalStrategy {
private RoleHierarchy roleHierarchy = new NullRoleHierarchy();
public SidRetrievalStrategyImpl() {
}
public SidRetrievalStrategyImpl(RoleHierarchy roleHierarchy) {
Assert.notNull(roleHierarchy, "RoleHierarchy must not be null");
this.roleHierarchy = roleHierarchy;
}
//~ Methods ========================================================================================================
public List<Sid> getSids(Authentication authentication) {
Collection<GrantedAuthority> authorities = roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities());
List<Sid> sids = new ArrayList<Sid>(authorities.size() + 1);
sids.add(new PrincipalSid(authentication));
for (GrantedAuthority authority : authorities) {
sids.add(new GrantedAuthoritySid(authority));
}
return sids;
}
}
@@ -1,5 +0,0 @@
/**
* Basic implementation of access control lists (ACLs) interfaces.
*/
package org.springframework.security.acls.domain;
@@ -0,0 +1,5 @@
<html>
<body>
Basic implementation of access control lists (ACLs) interfaces.
</body>
</html>
@@ -12,9 +12,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls.jdbc;
import org.springframework.security.acls.jdbc.JdbcAclService;
import org.springframework.security.acls.MutableAcl;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import java.io.Serializable;
@@ -23,6 +24,7 @@ import java.io.Serializable;
* A caching layer for {@link JdbcAclService}.
*
* @author Ben Alex
* @version $Id$
*
*/
public interface AclCache {
@@ -37,6 +39,4 @@ public interface AclCache {
MutableAcl getFromCache(Serializable pk);
void putInCache(MutableAcl acl);
void clearCache();
}
@@ -14,15 +14,13 @@
*/
package org.springframework.security.acls.jdbc;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -33,95 +31,47 @@ import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.security.acls.AccessControlEntry;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.MutableAcl;
import org.springframework.security.acls.NotFoundException;
import org.springframework.security.acls.Permission;
import org.springframework.security.acls.UnloadedSidException;
import org.springframework.security.acls.domain.AccessControlEntryImpl;
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
import org.springframework.security.acls.domain.AclImpl;
import org.springframework.security.acls.domain.AuditLogger;
import org.springframework.security.acls.domain.DefaultPermissionFactory;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PermissionFactory;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.UnloadedSidException;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.objectidentity.ObjectIdentityImpl;
import org.springframework.security.acls.sid.GrantedAuthoritySid;
import org.springframework.security.acls.sid.PrincipalSid;
import org.springframework.security.acls.sid.Sid;
import org.springframework.security.util.FieldUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Performs lookups in a manner that is compatible with ANSI SQL.
* <p>
* NB: This implementation does attempt to provide reasonably optimised lookups - within the constraints of a normalised
* database and standard ANSI SQL features. If you are willing to sacrifice either of these constraints
* (e.g. use a particular database feature such as hierarchical queries or materalized views, or reduce normalisation)
* you are likely to achieve better performance. In such situations you will need to provide your own custom
* <code>LookupStrategy</code>. This class does not support subclassing, as it is likely to change in future releases
* and therefore subclassing is unsupported.
* <p>
* There are two SQL queries executed, one in the <tt>lookupPrimaryKeys</tt> method and one in
* <tt>lookupObjectIdentities</tt>. These are built from the same select and "order by" clause, using a different
* where clause in each case. In order to use custom schema or column names, each of these SQL clauses can be
* customized, but they must be consistent with each other and with the expected result set
* generated by the the default values.
* Performs lookups in a manner that is compatible with ANSI SQL.<p>NB: This implementation does attempt to provide
* reasonably optimised lookups - within the constraints of a normalised database and standard ANSI SQL features. If
* you are willing to sacrifice either of these constraints (eg use a particular database feature such as hierarchical
* queries or materalized views, or reduce normalisation) you are likely to achieve better performance. In such
* situations you will need to provide your own custom <code>LookupStrategy</code>. This class does not support
* subclassing, as it is likely to change in future releases and therefore subclassing is unsupported.</p>
*
* @author Ben Alex
* @version $Id$
*/
public final class BasicLookupStrategy implements LookupStrategy {
public final static String DEFAULT_SELECT_CLAUSE = "select acl_object_identity.object_id_identity, "
+ "acl_entry.ace_order, "
+ "acl_object_identity.id as acl_id, "
+ "acl_object_identity.parent_object, "
+ "acl_object_identity.entries_inheriting, "
+ "acl_entry.id as ace_id, "
+ "acl_entry.mask, "
+ "acl_entry.granting, "
+ "acl_entry.audit_success, "
+ "acl_entry.audit_failure, "
+ "acl_sid.principal as ace_principal, "
+ "acl_sid.sid as ace_sid, "
+ "acli_sid.principal as acl_principal, "
+ "acli_sid.sid as acl_sid, "
+ "acl_class.class "
+ "from acl_object_identity "
+ "left join acl_sid acli_sid on acli_sid.id = acl_object_identity.owner_sid "
+ "left join acl_class on acl_class.id = acl_object_identity.object_id_class "
+ "left join acl_entry on acl_object_identity.id = acl_entry.acl_object_identity "
+ "left join acl_sid on acl_entry.sid = acl_sid.id "
+ "where ( ";
private final static String DEFAULT_LOOKUP_KEYS_WHERE_CLAUSE = "(acl_object_identity.id = ?)";
private final static String DEFAULT_LOOKUP_IDENTITIES_WHERE_CLAUSE = "(acl_object_identity.object_id_identity = ? and acl_class.class = ?)";
public final static String DEFAULT_ORDER_BY_CLAUSE = ") order by acl_object_identity.object_id_identity"
+ " asc, acl_entry.ace_order asc";
//~ Instance fields ================================================================================================
private AclAuthorizationStrategy aclAuthorizationStrategy;
private PermissionFactory permissionFactory = new DefaultPermissionFactory();
private AclCache aclCache;
private AuditLogger auditLogger;
private JdbcTemplate jdbcTemplate;
private int batchSize = 50;
private final Field fieldAces = FieldUtils.getField(AclImpl.class, "aces");
private final Field fieldAcl = FieldUtils.getField(AccessControlEntryImpl.class, "acl");
// SQL Customization fields
private String selectClause = DEFAULT_SELECT_CLAUSE;
private String lookupPrimaryKeysWhereClause = DEFAULT_LOOKUP_KEYS_WHERE_CLAUSE;
private String lookupObjectIdentitiesWhereClause = DEFAULT_LOOKUP_IDENTITIES_WHERE_CLAUSE;
private String orderByClause = DEFAULT_ORDER_BY_CLAUSE;
//~ Constructors ===================================================================================================
/**
@@ -137,234 +87,56 @@ public final class BasicLookupStrategy implements LookupStrategy {
Assert.notNull(aclCache, "AclCache required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
Assert.notNull(auditLogger, "AuditLogger required");
jdbcTemplate = new JdbcTemplate(dataSource);
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.aclCache = aclCache;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.auditLogger = auditLogger;
fieldAces.setAccessible(true);
fieldAcl.setAccessible(true);
}
//~ Methods ========================================================================================================
private String computeRepeatingSql(String repeatingSql, int requiredRepetitions) {
assert requiredRepetitions > 0 : "requiredRepetitions must be > 0";
private static String computeRepeatingSql(String repeatingSql, int requiredRepetitions) {
Assert.isTrue(requiredRepetitions >= 1, "Must be => 1");
final String startSql = selectClause;
String startSql = "select acl_object_identity.object_id_identity, "
+ "acl_entry.ace_order, "
+ "acl_object_identity.id as acl_id, "
+ "acl_object_identity.parent_object, "
+ "acl_object_identity.entries_inheriting, "
+ "acl_entry.id as ace_id, "
+ "acl_entry.mask, "
+ "acl_entry.granting, "
+ "acl_entry.audit_success, "
+ "acl_entry.audit_failure, "
+ "acl_sid.principal as ace_principal, "
+ "acl_sid.sid as ace_sid, "
+ "acli_sid.principal as acl_principal, "
+ "acli_sid.sid as acl_sid, "
+ "acl_class.class "
+ "from acl_object_identity "
+ "left join acl_sid acli_sid on acli_sid.id = acl_object_identity.owner_sid "
+ "left join acl_class on acl_class.id = acl_object_identity.object_id_class "
+ "left join acl_entry on acl_object_identity.id = acl_entry.acl_object_identity "
+ "left join acl_sid on acl_entry.sid = acl_sid.id "
+ "where ( ";
final String endSql = orderByClause;
String endSql = ") order by acl_object_identity.object_id_identity"
+ " asc, acl_entry.ace_order asc";
StringBuilder sqlStringBldr =
new StringBuilder(startSql.length() + endSql.length() + requiredRepetitions * (repeatingSql.length() + 4));
sqlStringBldr.append(startSql);
StringBuffer sqlStringBuffer = new StringBuffer();
sqlStringBuffer.append(startSql);
for (int i = 1; i <= requiredRepetitions; i++) {
sqlStringBldr.append(repeatingSql);
sqlStringBuffer.append(repeatingSql);
if (i != requiredRepetitions) {
sqlStringBldr.append(" or ");
sqlStringBuffer.append(" or ");
}
}
sqlStringBldr.append(endSql);
sqlStringBuffer.append(endSql);
return sqlStringBldr.toString();
}
@SuppressWarnings("unchecked")
private List<AccessControlEntryImpl> readAces(AclImpl acl) {
try {
return (List<AccessControlEntryImpl>) fieldAces.get(acl);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not obtain AclImpl.aces field", e);
}
}
private void setAclOnAce(AccessControlEntryImpl ace, AclImpl acl) {
try {
fieldAcl.set(ace, acl);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not or set AclImpl on AccessControlEntryImpl fields", e);
}
}
private void setAces(AclImpl acl, List<AccessControlEntryImpl> aces) {
try {
fieldAces.set(acl, aces);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not set AclImpl entries", e);
}
}
/**
* Locates the primary key IDs specified in "findNow", adding AclImpl instances with StubAclParents to the
* "acls" Map.
*
* @param acls the AclImpls (with StubAclParents)
* @param findNow Long-based primary keys to retrieve
* @param sids
*/
private void lookupPrimaryKeys(final Map<Serializable, Acl> acls, final Set<Long> findNow, final List<Sid> sids) {
Assert.notNull(acls, "ACLs are required");
Assert.notEmpty(findNow, "Items to find now required");
String sql = computeRepeatingSql(lookupPrimaryKeysWhereClause, findNow.size());
Set<Long> parentsToLookup = jdbcTemplate.query(sql,
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
int i = 0;
for (Long toFind : findNow) {
i++;
ps.setLong(i, toFind);
}
}
}, new ProcessResultSet(acls, sids));
// Lookup the parents, now that our JdbcTemplate has released the database connection (SEC-547)
if (parentsToLookup.size() > 0) {
lookupPrimaryKeys(acls, parentsToLookup, sids);
}
}
/**
* The main method.
* <p>
* WARNING: This implementation completely disregards the "sids" argument! Every item in the cache is expected to
* contain all SIDs. If you have serious performance needs (e.g. a very large number of
* SIDs per object identity), you'll probably want to develop a custom {@link LookupStrategy} implementation
* instead.
* <p>
* The implementation works in batch sizes specified by {@link #batchSize}.
*
* @param objects the identities to lookup (required)
* @param sids the SIDs for which identities are required (ignored by this implementation)
*
* @return a <tt>Map</tt> where keys represent the {@link ObjectIdentity} of the located {@link Acl} and values
* are the located {@link Acl} (never <tt>null</tt> although some entries may be missing; this method
* should not throw {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used
* to automatically create entries if required)
*/
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids) {
Assert.isTrue(batchSize >= 1, "BatchSize must be >= 1");
Assert.notEmpty(objects, "Objects to lookup required");
// Map<ObjectIdentity,Acl>
Map<ObjectIdentity, Acl> result = new HashMap<ObjectIdentity, Acl>(); // contains FULLY loaded Acl objects
Set<ObjectIdentity> currentBatchToLoad = new HashSet<ObjectIdentity>();
for (int i = 0; i < objects.size(); i++) {
final ObjectIdentity oid = objects.get(i);
boolean aclFound = false;
// Check we don't already have this ACL in the results
if (result.containsKey(oid)) {
aclFound = true;
}
// Check cache for the present ACL entry
if (!aclFound) {
Acl acl = aclCache.getFromCache(oid);
// Ensure any cached element supports all the requested SIDs
// (they should always, as our base impl doesn't filter on SID)
if (acl != null) {
if (acl.isSidLoaded(sids)) {
result.put(acl.getObjectIdentity(), acl);
aclFound = true;
} else {
throw new IllegalStateException(
"Error: SID-filtered element detected when implementation does not perform SID filtering "
+ "- have you added something to the cache manually?");
}
}
}
// Load the ACL from the database
if (!aclFound) {
currentBatchToLoad.add(oid);
}
// Is it time to load from JDBC the currentBatchToLoad?
if ((currentBatchToLoad.size() == this.batchSize) || ((i + 1) == objects.size())) {
if (currentBatchToLoad.size() > 0) {
Map<ObjectIdentity, Acl> loadedBatch = lookupObjectIdentities(currentBatchToLoad, sids);
// Add loaded batch (all elements 100% initialized) to results
result.putAll(loadedBatch);
// Add the loaded batch to the cache
for (Acl loadedAcl : loadedBatch.values()) {
aclCache.putInCache((AclImpl) loadedAcl);
}
currentBatchToLoad.clear();
}
}
}
return result;
}
/**
* Looks up a batch of <code>ObjectIdentity</code>s directly from the database.
* <p>
* The caller is responsible for optimization issues, such as selecting the identities to lookup, ensuring the
* cache doesn't contain them already, and adding the returned elements to the cache etc.
* <p>
* This subclass is required to return fully valid <code>Acl</code>s, including properly-configured
* parent ACLs.
*
*/
private Map<ObjectIdentity, Acl> lookupObjectIdentities(final Collection<ObjectIdentity> objectIdentities, List<Sid> sids) {
Assert.notEmpty(objectIdentities, "Must provide identities to lookup");
final Map<Serializable, Acl> acls = new HashMap<Serializable, Acl>(); // contains Acls with StubAclParents
// Make the "acls" map contain all requested objectIdentities
// (including markers to each parent in the hierarchy)
String sql = computeRepeatingSql(lookupObjectIdentitiesWhereClause, objectIdentities.size());
Set<Long> parentsToLookup = jdbcTemplate.query(sql,
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
int i = 0;
for (ObjectIdentity oid : objectIdentities) {
// Determine prepared statement values for this iteration
String type = oid.getType();
// No need to check for nulls, as guaranteed non-null by ObjectIdentity.getIdentifier() interface contract
String identifier = oid.getIdentifier().toString();
long id = (Long.valueOf(identifier)).longValue();
// Inject values
ps.setLong((2 * i) + 1, id);
ps.setString((2 * i) + 2, type);
i++;
}
}
}, new ProcessResultSet(acls, sids));
// Lookup the parents, now that our JdbcTemplate has released the database connection (SEC-547)
if (parentsToLookup.size() > 0) {
lookupPrimaryKeys(acls, parentsToLookup, sids);
}
// Finally, convert our "acls" containing StubAclParents into true Acls
Map<ObjectIdentity, Acl> resultMap = new HashMap<ObjectIdentity, Acl>();
for (Acl inputAcl : acls.values()) {
Assert.isInstanceOf(AclImpl.class, inputAcl, "Map should have contained an AclImpl");
Assert.isInstanceOf(Long.class, ((AclImpl) inputAcl).getId(), "Acl.getId() must be Long");
Acl result = convert(acls, (Long) ((AclImpl) inputAcl).getId());
resultMap.put(result.getObjectIdentity(), result);
}
return resultMap;
return sqlStringBuffer.toString();
}
/**
@@ -374,13 +146,16 @@ public final class BasicLookupStrategy implements LookupStrategy {
* @param inputMap the unconverted <code>AclImpl</code>s
* @param currentIdentity the current<code>Acl</code> that we wish to convert (this may be
*
* @return
*
* @throws IllegalStateException DOCUMENT ME!
*/
private AclImpl convert(Map<Serializable, Acl> inputMap, Long currentIdentity) {
private AclImpl convert(Map inputMap, Long currentIdentity) {
Assert.notEmpty(inputMap, "InputMap required");
Assert.notNull(currentIdentity, "CurrentIdentity required");
// Retrieve this Acl from the InputMap
Acl uncastAcl = inputMap.get(currentIdentity);
Acl uncastAcl = (Acl) inputMap.get(currentIdentity);
Assert.isInstanceOf(AclImpl.class, uncastAcl, "The inputMap contained a non-AclImpl");
AclImpl inputAcl = (AclImpl) uncastAcl;
@@ -398,78 +173,288 @@ public final class BasicLookupStrategy implements LookupStrategy {
auditLogger, parent, null, inputAcl.isEntriesInheriting(), inputAcl.getOwner());
// Copy the "aces" from the input to the destination
Field field = FieldUtils.getField(AclImpl.class, "aces");
// Obtain the "aces" from the input ACL
List<AccessControlEntryImpl> aces = readAces(inputAcl);
// Create a list in which to store the "aces" for the "result" AclImpl instance
List<AccessControlEntryImpl> acesNew = new ArrayList<AccessControlEntryImpl>();
// Iterate over the "aces" input and replace each nested AccessControlEntryImpl.getAcl() with the new "result" AclImpl instance
// This ensures StubAclParent instances are removed, as per SEC-951
for (AccessControlEntryImpl ace : aces) {
setAclOnAce(ace, result);
acesNew.add(ace);
try {
field.setAccessible(true);
field.set(result, field.get(inputAcl));
} catch (IllegalAccessException ex) {
throw new IllegalStateException("Could not obtain or set AclImpl.ace field");
}
// Finally, now that the "aces" have been converted to have the "result" AclImpl instance, modify the "result" AclImpl instance
setAces(result, acesNew);
return result;
}
/**
* Sets the {@code PermissionFactory} instance which will be used to convert loaded permission
* data values to {@code Permission}s. A {@code DefaultPermissionFactory} will be used by default.
* Accepts the current <code>ResultSet</code> row, and converts it into an <code>AclImpl</code> that
* contains a <code>StubAclParent</code>
*
* @param permissionFactory
* @param acls the Map we should add the converted Acl to
* @param rs the ResultSet focused on a current row
*
* @throws SQLException if something goes wrong converting values
* @throws IllegalStateException DOCUMENT ME!
*/
public void setPermissionFactory(PermissionFactory permissionFactory) {
this.permissionFactory = permissionFactory;
private void convertCurrentResultIntoObject(Map acls, ResultSet rs)
throws SQLException {
Long id = new Long(rs.getLong("acl_id"));
// If we already have an ACL for this ID, just create the ACE
AclImpl acl = (AclImpl) acls.get(id);
if (acl == null) {
// Make an AclImpl and pop it into the Map
ObjectIdentity objectIdentity = new ObjectIdentityImpl(rs.getString("class"),
new Long(rs.getLong("object_id_identity")));
Acl parentAcl = null;
long parentAclId = rs.getLong("parent_object");
if (parentAclId != 0) {
parentAcl = new StubAclParent(new Long(parentAclId));
}
boolean entriesInheriting = rs.getBoolean("entries_inheriting");
Sid owner;
if (rs.getBoolean("acl_principal")) {
owner = new PrincipalSid(rs.getString("acl_sid"));
} else {
owner = new GrantedAuthoritySid(rs.getString("acl_sid"));
}
acl = new AclImpl(objectIdentity, id, aclAuthorizationStrategy, auditLogger, parentAcl, null,
entriesInheriting, owner);
acls.put(id, acl);
}
// Add an extra ACE to the ACL (ORDER BY maintains the ACE list order)
// It is permissable to have no ACEs in an ACL (which is detected by a null ACE_SID)
if (rs.getString("ace_sid") != null) {
Long aceId = new Long(rs.getLong("ace_id"));
Sid recipient;
if (rs.getBoolean("ace_principal")) {
recipient = new PrincipalSid(rs.getString("ace_sid"));
} else {
recipient = new GrantedAuthoritySid(rs.getString("ace_sid"));
}
int mask = rs.getInt("mask");
Permission permission = convertMaskIntoPermission(mask);
boolean granting = rs.getBoolean("granting");
boolean auditSuccess = rs.getBoolean("audit_success");
boolean auditFailure = rs.getBoolean("audit_failure");
AccessControlEntryImpl ace = new AccessControlEntryImpl(aceId, acl, recipient, permission, granting,
auditSuccess, auditFailure);
Field acesField = FieldUtils.getField(AclImpl.class, "aces");
List aces;
try {
acesField.setAccessible(true);
aces = (List) acesField.get(acl);
} catch (IllegalAccessException ex) {
throw new IllegalStateException("Could not obtain AclImpl.ace field: cause[" + ex.getMessage() + "]");
}
// Add the ACE if it doesn't already exist in the ACL.aces field
if (!aces.contains(ace)) {
aces.add(ace);
}
}
}
protected Permission convertMaskIntoPermission(int mask) {
return BasePermission.buildFromMask(mask);
}
/**
* Looks up a batch of <code>ObjectIdentity</code>s directly from the database.<p>The caller is responsible
* for optimization issues, such as selecting the identities to lookup, ensuring the cache doesn't contain them
* already, and adding the returned elements to the cache etc.</p>
* <p>This subclass is required to return fully valid <code>Acl</code>s, including properly-configured
* parent ACLs.</p>
*
* @param objectIdentities DOCUMENT ME!
* @param sids DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
private Map lookupObjectIdentities(final ObjectIdentity[] objectIdentities, Sid[] sids) {
Assert.notEmpty(objectIdentities, "Must provide identities to lookup");
final Map acls = new HashMap(); // contains Acls with StubAclParents
// Make the "acls" map contain all requested objectIdentities
// (including markers to each parent in the hierarchy)
String sql = computeRepeatingSql("(acl_object_identity.object_id_identity = ? and acl_class.class = ?)",
objectIdentities.length);
Set parentsToLookup = (Set) jdbcTemplate.query(sql,
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps)
throws SQLException {
for (int i = 0; i < objectIdentities.length; i++) {
// Determine prepared statement values for this iteration
String javaType = objectIdentities[i].getJavaType().getName();
// No need to check for nulls, as guaranteed non-null by ObjectIdentity.getIdentifier() interface contract
String identifier = objectIdentities[i].getIdentifier().toString();
long id = (Long.valueOf(identifier)).longValue();
// Inject values
ps.setLong((2 * i) + 1, id);
ps.setString((2 * i) + 2, javaType);
}
}
}, new ProcessResultSet(acls, sids));
// Lookup the parents, now that our JdbcTemplate has released the database connection (SEC-547)
if (parentsToLookup.size() > 0) {
lookupPrimaryKeys(acls, parentsToLookup, sids);
}
// Finally, convert our "acls" containing StubAclParents into true Acls
Map resultMap = new HashMap();
Iterator iter = acls.values().iterator();
while (iter.hasNext()) {
Acl inputAcl = (Acl) iter.next();
Assert.isInstanceOf(AclImpl.class, inputAcl, "Map should have contained an AclImpl");
Assert.isInstanceOf(Long.class, ((AclImpl) inputAcl).getId(), "Acl.getId() must be Long");
Acl result = convert(acls, (Long) ((AclImpl) inputAcl).getId());
resultMap.put(result.getObjectIdentity(), result);
}
return resultMap;
}
/**
* Locates the primary key IDs specified in "findNow", adding AclImpl instances with StubAclParents to the
* "acls" Map.
*
* @param acls the AclImpls (with StubAclParents)
* @param findNow Long-based primary keys to retrieve
* @param sids DOCUMENT ME!
*/
private void lookupPrimaryKeys(final Map acls, final Set findNow, final Sid[] sids) {
Assert.notNull(acls, "ACLs are required");
Assert.notEmpty(findNow, "Items to find now required");
String sql = computeRepeatingSql("(acl_object_identity.id = ?)", findNow.size());
Set parentsToLookup = (Set) jdbcTemplate.query(sql,
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps)
throws SQLException {
Iterator iter = findNow.iterator();
int i = 0;
while (iter.hasNext()) {
i++;
ps.setLong(i, ((Long) iter.next()).longValue());
}
}
}, new ProcessResultSet(acls, sids));
// Lookup the parents, now that our JdbcTemplate has released the database connection (SEC-547)
if (parentsToLookup.size() > 0) {
lookupPrimaryKeys(acls, parentsToLookup, sids);
}
}
/**
* The main method.<p>WARNING: This implementation completely disregards the "sids" argument! Every item
* in the cache is expected to contain all SIDs. If you have serious performance needs (eg a very large number of
* SIDs per object identity), you'll probably want to develop a custom {@link LookupStrategy} implementation
* instead.</p>
* <p>The implementation works in batch sizes specfied by {@link #batchSize}.</p>
*
* @param objects the identities to lookup (required)
* @param sids the SIDs for which identities are required (ignored by this implementation)
*
* @return a <tt>Map</tt> where keys represent the {@link ObjectIdentity} of the located {@link Acl} and values
* are the located {@link Acl} (never <tt>null</tt> although some entries may be missing; this method
* should not throw {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used
* to automatically create entries if required)
*/
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids) {
Assert.isTrue(batchSize >= 1, "BatchSize must be >= 1");
Assert.notEmpty(objects, "Objects to lookup required");
// Map<ObjectIdentity,Acl>
Map result = new HashMap(); // contains FULLY loaded Acl objects
Set currentBatchToLoad = new HashSet(); // contains ObjectIdentitys
for (int i = 0; i < objects.length; i++) {
boolean aclFound = false;
// Check we don't already have this ACL in the results
if (result.containsKey(objects[i])) {
aclFound = true;
}
// Check cache for the present ACL entry
if (!aclFound) {
Acl acl = aclCache.getFromCache(objects[i]);
// Ensure any cached element supports all the requested SIDs
// (they should always, as our base impl doesn't filter on SID)
if (acl != null) {
if (acl.isSidLoaded(sids)) {
result.put(acl.getObjectIdentity(), acl);
aclFound = true;
} else {
throw new IllegalStateException(
"Error: SID-filtered element detected when implementation does not perform SID filtering "
+ "- have you added something to the cache manually?");
}
}
}
// Load the ACL from the database
if (!aclFound) {
currentBatchToLoad.add(objects[i]);
}
// Is it time to load from JDBC the currentBatchToLoad?
if ((currentBatchToLoad.size() == this.batchSize) || ((i + 1) == objects.length)) {
if (currentBatchToLoad.size() > 0) {
Map loadedBatch = lookupObjectIdentities((ObjectIdentity[]) currentBatchToLoad.toArray(new ObjectIdentity[] {}), sids);
// Add loaded batch (all elements 100% initialized) to results
result.putAll(loadedBatch);
// Add the loaded batch to the cache
Iterator loadedAclIterator = loadedBatch.values().iterator();
while (loadedAclIterator.hasNext()) {
aclCache.putInCache((AclImpl) loadedAclIterator.next());
}
currentBatchToLoad.clear();
}
}
}
return result;
}
public void setBatchSize(int batchSize) {
this.batchSize = batchSize;
}
/**
* The SQL for the select clause. If customizing in order to modify
* column names, schema etc, the other SQL customization fields must also be set to match.
*
* @param selectClause the select clause, which defaults to {@link #DEFAULT_SELECT_CLAUSE}.
*/
public void setSelectClause(String selectClause) {
this.selectClause = selectClause;
}
/**
* The SQL for the where clause used in the <tt>lookupPrimaryKey</tt> method.
*/
public void setLookupPrimaryKeysWhereClause(String lookupPrimaryKeysWhereClause) {
this.lookupPrimaryKeysWhereClause = lookupPrimaryKeysWhereClause;
}
/**
* The SQL for the where clause used in the <tt>lookupObjectIdentities</tt> method.
*/
public void setLookupObjectIdentitiesWhereClause(String lookupObjectIdentitiesWhereClause) {
this.lookupObjectIdentitiesWhereClause = lookupObjectIdentitiesWhereClause;
}
/**
* The SQL for the "order by" clause used in both queries.
*/
public void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}
//~ Inner Classes ==================================================================================================
private class ProcessResultSet implements ResultSetExtractor<Set<Long>> {
private Map<Serializable, Acl> acls;
private List<Sid> sids;
private class ProcessResultSet implements ResultSetExtractor {
private Map acls;
private Sid[] sids;
public ProcessResultSet(Map<Serializable, Acl> acls, List<Sid> sids) {
public ProcessResultSet(Map acls, Sid[] sids) {
Assert.notNull(acls, "ACLs cannot be null");
this.acls = acls;
this.sids = sids; // can be null
@@ -486,8 +471,8 @@ public final class BasicLookupStrategy implements LookupStrategy {
* @throws SQLException
* @throws DataAccessException
*/
public Set<Long> extractData(ResultSet rs) throws SQLException, DataAccessException {
Set<Long> parentIdsToLookup = new HashSet<Long>(); // Set of parent_id Longs
public Object extractData(ResultSet rs) throws SQLException, DataAccessException {
Set parentIdsToLookup = new HashSet(); // Set of parent_id Longs
while (rs.next()) {
// Convert current row into an Acl (albeit with a StubAclParent)
@@ -515,82 +500,9 @@ public final class BasicLookupStrategy implements LookupStrategy {
}
}
// Return the parents left to lookup to the caller
// Return the parents left to lookup to the calller
return parentIdsToLookup;
}
/**
* Accepts the current <code>ResultSet</code> row, and converts it into an <code>AclImpl</code> that
* contains a <code>StubAclParent</code>
*
* @param acls the Map we should add the converted Acl to
* @param rs the ResultSet focused on a current row
*
* @throws SQLException if something goes wrong converting values
*/
private void convertCurrentResultIntoObject(Map<Serializable, Acl> acls, ResultSet rs) throws SQLException {
Long id = new Long(rs.getLong("acl_id"));
// If we already have an ACL for this ID, just create the ACE
Acl acl = acls.get(id);
if (acl == null) {
// Make an AclImpl and pop it into the Map
ObjectIdentity objectIdentity = new ObjectIdentityImpl(rs.getString("class"),
Long.valueOf(rs.getLong("object_id_identity")));
Acl parentAcl = null;
long parentAclId = rs.getLong("parent_object");
if (parentAclId != 0) {
parentAcl = new StubAclParent(Long.valueOf(parentAclId));
}
boolean entriesInheriting = rs.getBoolean("entries_inheriting");
Sid owner;
if (rs.getBoolean("acl_principal")) {
owner = new PrincipalSid(rs.getString("acl_sid"));
} else {
owner = new GrantedAuthoritySid(rs.getString("acl_sid"));
}
acl = new AclImpl(objectIdentity, id, aclAuthorizationStrategy, auditLogger, parentAcl, null,
entriesInheriting, owner);
acls.put(id, acl);
}
// Add an extra ACE to the ACL (ORDER BY maintains the ACE list order)
// It is permissible to have no ACEs in an ACL (which is detected by a null ACE_SID)
if (rs.getString("ace_sid") != null) {
Long aceId = new Long(rs.getLong("ace_id"));
Sid recipient;
if (rs.getBoolean("ace_principal")) {
recipient = new PrincipalSid(rs.getString("ace_sid"));
} else {
recipient = new GrantedAuthoritySid(rs.getString("ace_sid"));
}
int mask = rs.getInt("mask");
Permission permission = permissionFactory.buildFromMask(mask);
boolean granting = rs.getBoolean("granting");
boolean auditSuccess = rs.getBoolean("audit_success");
boolean auditFailure = rs.getBoolean("audit_failure");
AccessControlEntryImpl ace = new AccessControlEntryImpl(aceId, acl, recipient, permission, granting,
auditSuccess, auditFailure);
//Field acesField = FieldUtils.getField(AclImpl.class, "aces");
List<AccessControlEntryImpl> aces = readAces((AclImpl)acl);
// Add the ACE if it doesn't already exist in the ACL.aces field
if (!aces.contains(ace)) {
aces.add(ace);
}
}
}
}
private class StubAclParent implements Acl {
@@ -600,7 +512,7 @@ public final class BasicLookupStrategy implements LookupStrategy {
this.id = id;
}
public List<AccessControlEntry> getEntries() {
public AccessControlEntry[] getEntries() {
throw new UnsupportedOperationException("Stub only");
}
@@ -624,12 +536,12 @@ public final class BasicLookupStrategy implements LookupStrategy {
throw new UnsupportedOperationException("Stub only");
}
public boolean isGranted(List<Permission> permission, List<Sid> sids, boolean administrativeMode)
public boolean isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode)
throws NotFoundException, UnloadedSidException {
throw new UnsupportedOperationException("Stub only");
}
public boolean isSidLoaded(List<Sid> sids) {
public boolean isSidLoaded(Sid[] sids) {
throw new UnsupportedOperationException("Stub only");
}
}
@@ -12,7 +12,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
package org.springframework.security.acls.jdbc;
import java.io.Serializable;
@@ -20,22 +20,25 @@ import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.MutableAcl;
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
import org.springframework.security.acls.domain.AclImpl;
import org.springframework.security.acls.domain.AuditLogger;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.util.FieldUtils;
import org.springframework.util.Assert;
/**
* Simple implementation of {@link AclCache} that delegates to EH-CACHE.
*
*
* <p>
* Designed to handle the transient fields in {@link AclImpl}. Note that this implementation assumes all
* {@link AclImpl} instances share the same {@link AuditLogger} and {@link AclAuthorizationStrategy} instance.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class EhCacheBasedAclCache implements AclCache {
//~ Instance fields ================================================================================================
@@ -91,7 +94,7 @@ public class EhCacheBasedAclCache implements AclCache {
return initializeTransientFields((MutableAcl)element.getValue());
}
public MutableAcl getFromCache(Serializable pk) {
public MutableAcl getFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
Element element = null;
@@ -114,11 +117,11 @@ public class EhCacheBasedAclCache implements AclCache {
if (this.aclAuthorizationStrategy == null) {
if (acl instanceof AclImpl) {
this.aclAuthorizationStrategy = (AclAuthorizationStrategy) FieldUtils.getProtectedFieldValue("aclAuthorizationStrategy", acl);
this.auditLogger = (AuditLogger) FieldUtils.getProtectedFieldValue("auditLogger", acl);
this.aclAuthorizationStrategy = (AclAuthorizationStrategy) FieldUtils.getProtectedFieldValue("aclAuthorizationStrategy", acl);
this.auditLogger = (AuditLogger) FieldUtils.getProtectedFieldValue("auditLogger", acl);
}
}
if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
putInCache((MutableAcl) acl.getParentAcl());
}
@@ -128,18 +131,10 @@ public class EhCacheBasedAclCache implements AclCache {
}
private MutableAcl initializeTransientFields(MutableAcl value) {
if (value instanceof AclImpl) {
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value, this.aclAuthorizationStrategy);
FieldUtils.setProtectedFieldValue("auditLogger", value, this.auditLogger);
}
if (value.getParentAcl() != null) {
initializeTransientFields((MutableAcl) value.getParentAcl());
}
return value;
}
public void clearCache() {
cache.removeAll();
}
if (value instanceof AclImpl) {
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value, this.aclAuthorizationStrategy);
FieldUtils.setProtectedFieldValue("auditLogger", value, this.auditLogger);
}
return value;
}
}
@@ -14,41 +14,46 @@
*/
package org.springframework.security.acls.jdbc;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.AclService;
import org.springframework.security.acls.NotFoundException;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.objectidentity.ObjectIdentityImpl;
import org.springframework.security.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 org.springframework.util.StringUtils;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
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.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Sid;
import org.springframework.util.Assert;
/**
* Simple JDBC-based implementation of <code>AclService</code>.
* <p>
* Requires the "dirty" flags in {@link org.springframework.security.acls.domain.AclImpl} and
* Requires the "dirty" flags in {@link org.springframework.security.acls.domain.AclImpl} and
* {@link org.springframework.security.acls.domain.AccessControlEntryImpl} to be set, so that the implementation can
* detect changed parameters easily.
*
* @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 DEFAULT_SELECT_ACL_WITH_PARENT_SQL = "select obj.object_id_identity as obj_id, class.class as class "
private static final String selectAclObjectWithParent = "select obj.object_id_identity as obj_id, class.class as 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 = ("
@@ -58,7 +63,6 @@ public class JdbcAclService implements AclService {
protected JdbcTemplate jdbcTemplate;
private LookupStrategy lookupStrategy;
private String findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL;
//~ Constructors ===================================================================================================
@@ -71,11 +75,12 @@ public class JdbcAclService implements AclService {
//~ Methods ========================================================================================================
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
Object[] args = {parentIdentity.getIdentifier(), parentIdentity.getType()};
List<ObjectIdentity> objects = jdbcTemplate.query(findChildrenSql, args,
new RowMapper<ObjectIdentity>() {
public ObjectIdentity mapRow(ResultSet rs, int rowNum) throws SQLException {
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");
Long identifier = new Long(rs.getLong("obj_id"));
@@ -84,14 +89,14 @@ public class JdbcAclService implements AclService {
});
if (objects.size() == 0) {
return null;
return null;
}
return objects;
return (ObjectIdentityImpl[]) objects.toArray(new ObjectIdentityImpl[objects.size()]);
}
public Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException {
Map<ObjectIdentity, Acl> map = readAclsById(Arrays.asList(object), sids);
public Acl readAclById(ObjectIdentity object, Sid[] sids) throws NotFoundException {
Map map = readAclsById(new ObjectIdentity[] {object}, sids);
Assert.isTrue(map.containsKey(object), "There should have been an Acl entry for ObjectIdentity " + object);
return (Acl) map.get(object);
@@ -101,30 +106,21 @@ public class JdbcAclService implements AclService {
return readAclById(object, null);
}
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects) throws NotFoundException {
public Map readAclsById(ObjectIdentity[] objects) throws NotFoundException {
return readAclsById(objects, null);
}
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids) throws NotFoundException {
Map<ObjectIdentity, Acl> result = lookupStrategy.readAclsById(objects, sids);
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids) throws NotFoundException {
Map result = lookupStrategy.readAclsById(objects, sids);
// Check every requested object identity was found (throw NotFoundException if needed)
for (int i = 0; i < objects.size(); i++) {
if (!result.containsKey(objects.get(i))) {
for (int i = 0; i < objects.length; i++) {
if (!result.containsKey(objects[i])) {
throw new NotFoundException("Unable to find ACL information for object identity '"
+ objects.get(i) + "'");
+ objects[i].toString() + "'");
}
}
return result;
}
/**
* Allows customization of the SQL query used to find child object identities.
*
* @param findChildrenSql
*/
public void setFindChildrenQuery(String findChildrenSql) {
this.findChildrenSql = findChildrenSql;
}
}
@@ -14,58 +14,58 @@
*/
package org.springframework.security.acls.jdbc;
import org.springframework.security.Authentication;
import org.springframework.security.acls.AccessControlEntry;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.AlreadyExistsException;
import org.springframework.security.acls.ChildrenExistException;
import org.springframework.security.acls.MutableAcl;
import org.springframework.security.acls.MutableAclService;
import org.springframework.security.acls.NotFoundException;
import org.springframework.security.acls.domain.AccessControlEntryImpl;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.objectidentity.ObjectIdentityImpl;
import org.springframework.security.acls.sid.GrantedAuthoritySid;
import org.springframework.security.acls.sid.PrincipalSid;
import org.springframework.security.acls.sid.Sid;
import org.springframework.security.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;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.security.acls.domain.AccessControlEntryImpl;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.AlreadyExistsException;
import org.springframework.security.acls.model.ChildrenExistException;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.MutableAclService;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
/**
* Provides a base JDBC implementation of {@link MutableAclService}.
* <p>
* The default settings are for HSQLDB. If you are using a different database you
* will probably need to set the {@link #setSidIdentityQuery(String) sidIdentityQuery} and
* {@link #setClassIdentityQuery(String) classIdentityQuery} properties appropriately. The other queries,
* SQL inserts and updates can also be customized to accomodate schema variations, but must produce results
* consistent with those expected by the defaults.
* <p>
* See the appendix of the Spring Security reference manual for more information on the expected schema
* and how it is used. Information on using PostgreSQL is also included.
* 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 boolean foreignKeysInDatabase = true;
private boolean foreignKeysInDatabase = true;
private AclCache aclCache;
private String deleteEntryByObjectIdentityForeignKey = "delete from acl_entry where acl_object_identity=?";
private String deleteObjectIdentityByPrimaryKey = "delete from acl_object_identity where id=?";
private String classIdentityQuery = "call identity()";
private String sidIdentityQuery = "call identity()";
private String classIdentityQuery = "call identity()"; // should be overridden for postgres : select currval('acl_class_seq')
private String sidIdentityQuery = "call identity()"; // should be overridden for postgres : select currval('acl_siq_seq')
private String insertClass = "insert into acl_class (class) values (?)";
private String insertEntry = "insert into acl_entry "
+ "(acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)"
@@ -91,7 +91,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
//~ Methods ========================================================================================================
public MutableAcl createAcl(ObjectIdentity objectIdentity) throws AlreadyExistsException {
public MutableAcl createAcl(ObjectIdentity objectIdentity)
throws AlreadyExistsException {
Assert.notNull(objectIdentity, "Object Identity required");
// Check this object identity hasn't already been persisted
@@ -122,12 +123,14 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
jdbcTemplate.batchUpdate(insertEntry,
new BatchPreparedStatementSetter() {
public int getBatchSize() {
return acl.getEntries().size();
return acl.getEntries().length;
}
public void setValues(PreparedStatement stmt, int i) throws SQLException {
AccessControlEntry entry_ = acl.getEntries().get(i);
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());
@@ -150,34 +153,37 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
*/
protected void createObjectIdentity(ObjectIdentity object, Sid owner) {
Long sidId = createOrRetrieveSidPrimaryKey(owner, true);
Long classId = createOrRetrieveClassPrimaryKey(object.getType(), true);
jdbcTemplate.update(insertObjectIdentity, classId, object.getIdentifier(), sidId, Boolean.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 {@code acl_class}, creating a new row if needed and the
* {@code allowCreate} property is {@code true}.
* Retrieves the primary key from acl_class, creating a new row if needed and the allowCreate property is
* true.
*
* @param type to find or create an entry for (often the fully-qualified class name)
* @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(String type, boolean allowCreate) {
List<Long> classIds = jdbcTemplate.queryForList(selectClassPrimaryKey, new Object[] {type}, Long.class);
protected Long createOrRetrieveClassPrimaryKey(Class clazz, boolean allowCreate) {
List classIds = jdbcTemplate.queryForList(selectClassPrimaryKey, new Object[] {clazz.getName()}, Long.class);
Long classId = null;
if (!classIds.isEmpty()) {
return classIds.get(0);
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(classIdentityQuery));
}
} else {
classId = (Long) classIds.iterator().next();
}
if (allowCreate) {
jdbcTemplate.update(insertClass, type);
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(),
"Transaction must be running");
return new Long(jdbcTemplate.queryForLong(classIdentityQuery));
}
return null;
return classId;
}
/**
@@ -189,64 +195,68 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
*
* @return the primary key or null if not found
*
* @throws IllegalArgumentException if the <tt>Sid</tt> is not a recognized implementation.
* @throws IllegalArgumentException DOCUMENT ME!
*/
protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
Assert.notNull(sid, "Sid required");
String sidName = null;
boolean sidIsPrincipal = true;
boolean principal = true;
if (sid instanceof PrincipalSid) {
sidName = ((PrincipalSid) sid).getPrincipal();
} else if (sid instanceof GrantedAuthoritySid) {
sidName = ((GrantedAuthoritySid) sid).getGrantedAuthority();
sidIsPrincipal = false;
principal = false;
} else {
throw new IllegalArgumentException("Unsupported implementation of Sid");
}
List<Long> sidIds = jdbcTemplate.queryForList(selectSidPrimaryKey,
new Object[] {Boolean.valueOf(sidIsPrincipal), sidName}, Long.class);
List sidIds = jdbcTemplate.queryForList(selectSidPrimaryKey, new Object[] {new Boolean(principal), sidName},
Long.class);
Long sidId = null;
if (!sidIds.isEmpty()) {
return sidIds.get(0);
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(sidIdentityQuery));
}
} else {
sidId = (Long) sidIds.iterator().next();
}
if (allowCreate) {
jdbcTemplate.update(insertSid, new Object[] {Boolean.valueOf(sidIsPrincipal), sidName});
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), "Transaction must be running");
return new Long(jdbcTemplate.queryForLong(sidIdentityQuery));
}
return null;
return sidId;
}
public void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren) throws ChildrenExistException {
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");
if (deleteChildren) {
List<ObjectIdentity> children = findChildren(objectIdentity);
if (children != null) {
for (int i = 0; i < children.size(); i++) {
deleteAcl(children.get(i), true);
ObjectIdentity[] children = findChildren(objectIdentity);
if (children != null) {
for (int i = 0; i < children.length; i++) {
deleteAcl(children[i], true);
}
}
}
} else {
if (!foreignKeysInDatabase) {
// We need to perform a manual verification for what a FK would normally do
// We generally don't do this, in the interests of deadlock management
List<ObjectIdentity> children = findChildren(objectIdentity);
if (children != null) {
throw new ChildrenExistException("Cannot delete '" + objectIdentity + "' (has " + children.size()
if (!foreignKeysInDatabase) {
// We need to perform a manual verification for what a FK would normally do
// We generally don't do this, in the interests of deadlock management
ObjectIdentity[] children = findChildren(objectIdentity);
if (children != null) {
throw new ChildrenExistException("Cannot delete '" + objectIdentity + "' (has " + children.length
+ " children)");
}
}
}
}
}
Long oidPrimaryKey = retrieveObjectIdentityPrimaryKey(objectIdentity);
// Delete this ACL's ACEs in the acl_entry table
deleteEntries(oidPrimaryKey);
@@ -263,15 +273,17 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* @param oidPrimaryKey the rows in acl_entry to delete
*/
protected void deleteEntries(Long oidPrimaryKey) {
jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey,
jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey,
new Object[] {oidPrimaryKey});
}
/**
* Deletes a single row from acl_object_identity that is associated with the presented ObjectIdentity primary key.
*
* <p>
* We do not delete any entries from acl_class, even if no classes are using that class any longer. This is a
* deadlock avoidance approach.
* </p>
*
* @param oidPrimaryKey to delete the acl_object_identity
*/
@@ -292,7 +304,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
protected Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
try {
return new Long(jdbcTemplate.queryForLong(selectObjectIdentityPrimaryKey,
new Object[] {oid.getType(), oid.getIdentifier()}));
new Object[] {oid.getJavaType().getName(), oid.getIdentifier()}));
} catch (DataAccessException notFound) {
return null;
}
@@ -302,6 +314,12 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* 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");
@@ -321,14 +339,14 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
// Retrieve the ACL via superclass (ensures cache registration, proper retrieval etc)
return (MutableAcl) super.readAclById(acl.getObjectIdentity());
}
private void clearCacheIncludingChildren(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
List<ObjectIdentity> children = findChildren(objectIdentity);
Assert.notNull(objectIdentity, "ObjectIdentity required");
ObjectIdentity[] children = findChildren(objectIdentity);
if (children != null) {
for (int i = 0; i < children.size(); i++) {
clearCacheIncludingChildren(children.get(i));
}
for (int i = 0; i < children.length; i++) {
clearCacheIncludingChildren(children[i]);
}
}
aclCache.evictFromCache(objectIdentity);
}
@@ -339,7 +357,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
*
* @param acl to modify (a row must already exist in acl_object_identity)
*
* @throws NotFoundException if the ACL could not be found to update.
* @throws NotFoundException DOCUMENT ME!
*/
protected void updateObjectIdentity(MutableAcl acl) {
Long parentId = null;
@@ -363,73 +381,20 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
}
}
/**
* Sets the query that will be used to retrieve the identity of a newly created row in the <tt>acl_class</tt>
* table.
*
* @param classIdentityQuery the query, which should return the identifier. Defaults to <tt>call identity()</tt>
*/
public void setClassIdentityQuery(String classIdentityQuery) {
Assert.hasText(classIdentityQuery, "New classIdentityQuery query is required");
this.classIdentityQuery = classIdentityQuery;
}
public void setClassIdentityQuery(String identityQuery) {
Assert.hasText(identityQuery, "New identity query is required");
this.classIdentityQuery = identityQuery;
}
/**
* Sets the query that will be used to retrieve the identity of a newly created row in the <tt>acl_sid</tt>
* table.
*
* @param sidIdentityQuery the query, which should return the identifier. Defaults to <tt>call identity()</tt>
*/
public void setSidIdentityQuery(String sidIdentityQuery) {
Assert.hasText(sidIdentityQuery, "New sidIdentityQuery query is required");
this.sidIdentityQuery = sidIdentityQuery;
}
public void setDeleteEntryByObjectIdentityForeignKeySql(String deleteEntryByObjectIdentityForeignKey) {
this.deleteEntryByObjectIdentityForeignKey = deleteEntryByObjectIdentityForeignKey;
}
public void setDeleteObjectIdentityByPrimaryKeySql(String deleteObjectIdentityByPrimaryKey) {
this.deleteObjectIdentityByPrimaryKey = deleteObjectIdentityByPrimaryKey;
}
public void setInsertClassSql(String insertClass) {
this.insertClass = insertClass;
}
public void setInsertEntrySql(String insertEntry) {
this.insertEntry = insertEntry;
}
public void setInsertObjectIdentitySql(String insertObjectIdentity) {
this.insertObjectIdentity = insertObjectIdentity;
}
public void setInsertSidSql(String insertSid) {
this.insertSid = insertSid;
}
public void setClassPrimaryKeyQuery(String selectClassPrimaryKey) {
this.selectClassPrimaryKey = selectClassPrimaryKey;
}
public void setObjectIdentityPrimaryKeyQuery(String selectObjectIdentityPrimaryKey) {
this.selectObjectIdentityPrimaryKey = selectObjectIdentityPrimaryKey;
}
public void setSidPrimaryKeyQuery(String selectSidPrimaryKey) {
this.selectSidPrimaryKey = selectSidPrimaryKey;
}
public void setUpdateObjectIdentity(String updateObjectIdentity) {
this.updateObjectIdentity = updateObjectIdentity;
}
/**
* @param foreignKeysInDatabase if false this class will perform additional FK constrain checking, which may
* cause deadlocks (the default is true, so deadlocks are avoided but the database is expected to enforce FKs)
*/
public void setForeignKeysInDatabase(boolean foreignKeysInDatabase) {
this.foreignKeysInDatabase = foreignKeysInDatabase;
}
public void setSidIdentityQuery(String identityQuery) {
Assert.hasText(identityQuery, "New identity query is required");
this.sidIdentityQuery = identityQuery;
}
/**
* @param foreignKeysInDatabase if false this class will perform additional FK constrain checking, which may
* cause deadlocks (the default is true, so deadlocks are avoided but the database is expected to enforce FKs)
*/
public void setForeignKeysInDatabase(boolean foreignKeysInDatabase) {
this.foreignKeysInDatabase = foreignKeysInDatabase;
}
}
@@ -14,19 +14,18 @@
*/
package org.springframework.security.acls.jdbc;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.NotFoundException;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.sid.Sid;
import java.util.List;
import java.util.Map;
/**
* Performs lookups for {@link org.springframework.security.acls.model.AclService}.
*
* Performs lookups for {@link org.springframework.security.acls.AclService}.
*
* @author Ben Alex
* @version $Id$
*/
public interface LookupStrategy {
//~ Methods ========================================================================================================
@@ -41,7 +40,7 @@ public interface LookupStrategy {
* @return a <tt>Map</tt> where keys represent the {@link ObjectIdentity} of the located {@link Acl} and values
* are the located {@link Acl} (never <tt>null</tt> although some entries may be missing; this method
* should not throw {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used
* to automatically create entries if required)
* to automatically create entries if required)
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids);
Map readAclsById(ObjectIdentity[] objects, Sid[] sids);
}
@@ -1,5 +0,0 @@
/**
* JDBC-based persistence of ACL information
*/
package org.springframework.security.acls.jdbc;
@@ -0,0 +1,5 @@
<html>
<body>
JDBC-based persistence of ACL information.
</body>
</html>
@@ -1,31 +0,0 @@
package org.springframework.security.acls.model;
/**
* Abstract base class for Acl data operations.
*
* @author Luke Taylor
* @since 3.0
*/
public abstract class AclDataAccessException extends RuntimeException {
/**
* Constructs an <code>AclDataAccessException</code> with the specified
* message and root cause.
*
* @param msg the detail message
* @param cause the root cause
*/
public AclDataAccessException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Constructs an <code>AclDataAccessException</code> with the specified
* message and no root cause.
*
* @param msg the detail message
*/
public AclDataAccessException(String msg) {
super(msg);
}
}
@@ -1,26 +0,0 @@
package org.springframework.security.acls.model;
import java.io.Serializable;
/**
* Strategy which creates an {@link ObjectIdentity} from an object identifier (such as a primary key)
* and type information.
* <p>
* Differs from {@link ObjectIdentityRetrievalStrategy} in that it is used in situations when the actual object
* instance isn't available.
*
* @author Luke Taylor
* @since 3.0
*/
public interface ObjectIdentityGenerator {
/**
*
* @param id the identifier of the domain object, not null
* @param type the type of the object (often a class name), not null
* @return the identity constructed using the supplied identifier and type information.
*/
ObjectIdentity createObjectIdentity(Serializable id, String type);
}
@@ -1,5 +0,0 @@
/**
* Interfaces and shared classes to manage access control lists (ACLs) for domain object instances.
*/
package org.springframework.security.acls.model;
@@ -12,7 +12,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls.objectidentity;
import java.io.Serializable;
@@ -21,15 +21,16 @@ import java.io.Serializable;
* Represents the identity of an individual domain object instance.
*
* <p>
* As implementations of <tt>ObjectIdentity</tt> are used as the key to represent
* As implementations of <tt>ObjectIdentity</tt> are used as the key to represent
* domain objects in the ACL subsystem, it is essential that implementations provide
* methods so that object-equality rather than reference-equality can be relied upon
* reliably. In other words, the ACL subsystem can consider two
* <tt>ObjectIdentity</tt>s equal if <tt>identity1.equals(identity2)</tt>, rather than
* reliably. In other words, the ACL subsystem can consider two
* <tt>ObjectIdentity</tt>s equal if <tt>identity1.equals(identity2)</tt>, rather than
* reference-equality of <tt>identity1==identity2</tt>.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface ObjectIdentity extends Serializable {
//~ Methods ========================================================================================================
@@ -45,23 +46,23 @@ public interface ObjectIdentity extends Serializable {
/**
* Obtains the actual identifier. This identifier must not be reused to represent other domain objects with
* the same <tt>javaType</tt>.
*
*
* <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 in the future such change will cascade to the ACL
* business meaning, as that business meaning may change in the future such change will cascade to the ACL
* subsystem data.</p>
*
* @return the identifier (unique within this <tt>type</tt>; never <tt>null</tt>)
* @return the identifier (unique within this <tt>javaType</tt>; never <tt>null</tt>)
*/
Serializable getIdentifier();
/**
* Obtains the "type" metadata for the domain object. This will often be a Java type name (an interface or a class)
* &ndash; traditionally it is the name of the domain object implementation class.
* Obtains the Java type represented by the domain object. The Java type can be an interface or a class, but is
* most often the domain object implementation class.
*
* @return the "type" of the domain object (never <tt>null</tt>).
* @return the Java type of the domain object (never <tt>null</tt>)
*/
String getType();
Class getJavaType();
/**
* @return a hash code representation of the <tt>ObjectIdentity</tt>
@@ -12,14 +12,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
package org.springframework.security.acls.objectidentity;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.springframework.security.acls.IdentityUnavailableException;
import org.springframework.security.acls.jdbc.LookupStrategy;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import java.io.Serializable;
import java.lang.reflect.Method;
/**
@@ -29,55 +33,56 @@ import org.springframework.util.ClassUtils;
* reflection to build the identity information.
*
* @author Ben Alex
* @version $Id$
*/
public class ObjectIdentityImpl implements ObjectIdentity {
//~ Instance fields ================================================================================================
private final String type;
private Class javaType;
private Serializable identifier;
//~ Constructors ===================================================================================================
public ObjectIdentityImpl(String type, Serializable identifier) {
Assert.hasText(type, "Type required");
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;
this.type = type;
}
/**
* Constructor which uses the name of the supplied class as the <tt>type</tt> property.
*/
public ObjectIdentityImpl(Class<?> javaType, Serializable identifier) {
public ObjectIdentityImpl(Class javaType, Serializable identifier) {
Assert.notNull(javaType, "Java Type required");
Assert.notNull(identifier, "identifier required");
this.type = javaType.getName();
this.javaType = javaType;
this.identifier = identifier;
}
/**
/**
* 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.
* <p>
* The class name of the object passed will be considered the {@link #type}, so if more control is required,
* a different constructor should be used.
* 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.
* @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");
Class<?> typeClass = ClassUtils.getUserClass(object.getClass());
type = typeClass.getName();
this.javaType = ClassUtils.getUserClass(object.getClass());
Object result;
try {
Method method = typeClass.getMethod("getId", new Class[] {});
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);
@@ -91,46 +96,43 @@ public class ObjectIdentityImpl implements ObjectIdentity {
//~ Methods ========================================================================================================
/**
* Important so caching operates properly.
* 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>
*
* <p>
* Considers an object of the same class equal if it has the same <code>classname</code> and
* <code>id</code> properties.
* <p>
* Numeric identities (Integer and Long values) are considered equal if they are numerically equal. Other
* serializable types are evaluated using a simple equality.
* Note that this class uses string equality for the identifier field, which ensures it better supports
* differences between {@link LookupStrategy} requirements and the domain object represented by this
* <code>ObjectIdentityImpl</code>.
* </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 || !(arg0 instanceof ObjectIdentityImpl)) {
if (arg0 == null) {
return false;
}
if (!(arg0 instanceof ObjectIdentityImpl)) {
return false;
}
ObjectIdentityImpl other = (ObjectIdentityImpl) arg0;
if (identifier instanceof Number && other.identifier instanceof Number) {
// Integers and Longs with same value should be considered equal
if (((Number)identifier).longValue() != ((Number)other.identifier).longValue()) {
return false;
}
} else {
// Use plain equality for other serializable types
if (!identifier.equals(other.identifier)) {
return false;
}
if (this.getIdentifier().toString().equals(other.getIdentifier().toString()) && this.getJavaType().equals(other.getJavaType())) {
return true;
}
return type.equals(other.type);
return false;
}
public Serializable getIdentifier() {
return identifier;
}
public String getType() {
return type;
public Class getJavaType() {
return javaType;
}
/**
@@ -140,16 +142,16 @@ public class ObjectIdentityImpl implements ObjectIdentity {
*/
public int hashCode() {
int code = 31;
code ^= this.type.hashCode();
code ^= this.javaType.hashCode();
code ^= this.identifier.hashCode();
return code;
}
public String toString() {
StringBuilder sb = new StringBuilder();
StringBuffer sb = new StringBuffer();
sb.append(this.getClass().getName()).append("[");
sb.append("Type: ").append(this.type);
sb.append("Java Type: ").append(this.javaType.getName());
sb.append("; Identifier: ").append(this.identifier).append("]");
return sb.toString();
@@ -13,14 +13,14 @@
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls.objectidentity;
/**
* Strategy interface that provides the ability to determine which {@link ObjectIdentity}
* will be returned for a particular domain object
*
* @author Ben Alex
* @version $Id$
*
*/
public interface ObjectIdentityRetrievalStrategy {
@@ -13,28 +13,19 @@
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import java.io.Serializable;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityGenerator;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
package org.springframework.security.acls.objectidentity;
/**
* Basic implementation of {@link ObjectIdentityRetrievalStrategy} and <tt>ObjectIdentityGenerator</tt>
* that uses the constructors of {@link ObjectIdentityImpl} to create the {@link ObjectIdentity}.
* Basic implementation of {@link ObjectIdentityRetrievalStrategy} that uses the constructor of {@link
* ObjectIdentityImpl} to create the {@link ObjectIdentity}.
*
* @author Ben Alex
* @version $Id$
*/
public class ObjectIdentityRetrievalStrategyImpl implements ObjectIdentityRetrievalStrategy, ObjectIdentityGenerator {
public class ObjectIdentityRetrievalStrategyImpl implements ObjectIdentityRetrievalStrategy {
//~ Methods ========================================================================================================
public ObjectIdentity getObjectIdentity(Object domainObject) {
return new ObjectIdentityImpl(domainObject);
}
public ObjectIdentity createObjectIdentity(Serializable id, String type) {
return new ObjectIdentityImpl(type, id);
}
}
@@ -0,0 +1,5 @@
<html>
<body>
Provides indirection between ACL packages and domain objects.
</body>
</html>
@@ -1,9 +0,0 @@
/**
* The Spring Security ACL package which implements instance-based security for domain objects.
* <p>
* Consider using the annotation based approach ({@code @PreAuthorize}, {@code @PostFilter} annotations) combined
* with a {@link org.springframework.security.acls.AclPermissionEvaluator} in preference to the older and more verbose
* attribute/voter/after-invocation approach from versions before Spring Security 3.0.
*/
package org.springframework.security.acls;
@@ -0,0 +1,5 @@
<html>
<body>
Interfaces and shared classes to manage access control lists (ACLs) for domain object instances.
</body>
</html>
@@ -12,10 +12,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
package org.springframework.security.acls.sid;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.GrantedAuthority;
import org.springframework.util.Assert;
@@ -26,6 +25,7 @@ import org.springframework.util.Assert;
* 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 ================================================================================================
@@ -12,12 +12,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
package org.springframework.security.acls.sid;
import org.springframework.security.Authentication;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.util.Assert;
@@ -28,6 +27,7 @@ import org.springframework.util.Assert;
* 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 ================================================================================================
@@ -12,7 +12,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls.sid;
import java.io.Serializable;
@@ -29,6 +29,7 @@ import java.io.Serializable;
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public interface Sid extends Serializable {
//~ Methods ========================================================================================================
@@ -13,11 +13,9 @@
* limitations under the License.
*/
package org.springframework.security.acls.model;
package org.springframework.security.acls.sid;
import java.util.List;
import org.springframework.security.core.Authentication;
import org.springframework.security.Authentication;
/**
@@ -25,9 +23,10 @@ import org.springframework.security.core.Authentication;
* for an {@link Authentication}.
*
* @author Ben Alex
* @version $Id$
*/
public interface SidRetrievalStrategy {
//~ Methods ========================================================================================================
List<Sid> getSids(Authentication authentication);
Sid[] getSids(Authentication authentication);
}
@@ -0,0 +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.springframework.security.acls.sid;
import org.springframework.security.Authentication;
import org.springframework.security.GrantedAuthority;
/**
* 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.
*
* @author Ben Alex
* @version $Id$
*/
public class SidRetrievalStrategyImpl implements SidRetrievalStrategy {
//~ Methods ========================================================================================================
public Sid[] getSids(Authentication authentication) {
GrantedAuthority[] authorities = authentication.getAuthorities();
Sid[] sids = new Sid[authorities.length + 1];
sids[0] = new PrincipalSid(authentication);
for (int i = 1; i <= authorities.length; i++) {
sids[i] = new GrantedAuthoritySid(authorities[i - 1]);
}
return sids;
}
}
@@ -0,0 +1,5 @@
<html>
<body>
Provides indirection between ACL packages and security identities, such as principals and GrantedAuthority[]s.
</body>
</html>
@@ -13,25 +13,23 @@
* limitations under the License.
*/
package org.springframework.security.acls.afterinvocation;
package org.springframework.security.afterinvocation;
import java.util.Arrays;
import java.util.List;
import org.springframework.security.Authentication;
import org.springframework.security.ConfigAttribute;
import org.springframework.security.access.AfterInvocationProvider;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.AclService;
import org.springframework.security.acls.NotFoundException;
import org.springframework.security.acls.Permission;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.objectidentity.ObjectIdentityRetrievalStrategy;
import org.springframework.security.acls.objectidentity.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.sid.Sid;
import org.springframework.security.acls.sid.SidRetrievalStrategy;
import org.springframework.security.acls.sid.SidRetrievalStrategyImpl;
import org.springframework.util.Assert;
@@ -39,24 +37,25 @@ import org.springframework.util.Assert;
* Abstract {@link AfterInvocationProvider} which provides commonly-used ACL-related services.
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AbstractAclProvider implements AfterInvocationProvider {
//~ Instance fields ================================================================================================
protected AclService aclService;
protected Class<?> processDomainObjectClass = Object.class;
protected Class processDomainObjectClass = Object.class;
protected ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
protected SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
protected String processConfigAttribute;
protected List<Permission> requirePermission = Arrays.asList(BasePermission.READ);
protected Permission[] requirePermission = {BasePermission.READ};
//~ Constructors ===================================================================================================
public AbstractAclProvider(AclService aclService, String processConfigAttribute, List<Permission> requirePermission) {
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.isEmpty()) {
if ((requirePermission == null) || (requirePermission.length == 0)) {
throw new IllegalArgumentException("One or more requirePermission entries is mandatory");
}
@@ -67,7 +66,7 @@ public abstract class AbstractAclProvider implements AfterInvocationProvider {
//~ Methods ========================================================================================================
protected Class<?> getProcessDomainObjectClass() {
protected Class getProcessDomainObjectClass() {
return processDomainObjectClass;
}
@@ -76,7 +75,7 @@ public abstract class AbstractAclProvider implements AfterInvocationProvider {
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
// Obtain the SIDs applicable to the principal
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
Sid[] sids = sidRetrievalStrategy.getSids(authentication);
Acl acl = null;
@@ -100,7 +99,7 @@ public abstract class AbstractAclProvider implements AfterInvocationProvider {
this.processConfigAttribute = processConfigAttribute;
}
public void setProcessDomainObjectClass(Class<?> processDomainObjectClass) {
public void setProcessDomainObjectClass(Class processDomainObjectClass) {
Assert.notNull(processDomainObjectClass, "processDomainObjectClass cannot be set to null");
this.processDomainObjectClass = processDomainObjectClass;
}
@@ -121,7 +120,7 @@ public abstract class AbstractAclProvider implements AfterInvocationProvider {
*
* @return always <code>true</code>
*/
public boolean supports(Class<?> clazz) {
public boolean supports(Class clazz) {
return true;
}
}
@@ -12,19 +12,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.afterinvocation;
package org.springframework.security.afterinvocation;
import java.util.Collection;
import java.util.List;
import org.springframework.security.AccessDeniedException;
import org.springframework.security.Authentication;
import org.springframework.security.AuthorizationServiceException;
import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.acls.AclService;
import org.springframework.security.acls.Permission;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.AuthorizationServiceException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.core.Authentication;
import java.util.Collection;
import java.util.Iterator;
/**
@@ -38,7 +41,8 @@ import org.springframework.security.core.Authentication;
* <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 org.springframework.security.acls.model.Acl#isGranted(List, List, boolean) Acl.isGranted()}
* principal is {@link org.springframework.security.acls.Acl#isGranted(org.springframework.security.acls.Permission[],
* org.springframework.security.acls.sid.Sid[], boolean) Acl.isGranted(Permission[], Sid[], boolean)}
* when presenting the {@link #requirePermission} array to that method.
* <p>
* If the principal does not have permission, that element will not be included in the returned
@@ -56,6 +60,7 @@ import org.springframework.security.core.Authentication;
*
* @author Ben Alex
* @author Paulo Neves
* @version $Id$
*/
public class AclEntryAfterInvocationCollectionFilteringProvider extends AbstractAclProvider {
//~ Static fields/initializers =====================================================================================
@@ -64,14 +69,13 @@ public class AclEntryAfterInvocationCollectionFilteringProvider extends Abstract
//~ Constructors ===================================================================================================
public AclEntryAfterInvocationCollectionFilteringProvider(AclService aclService, List<Permission> requirePermission) {
public AclEntryAfterInvocationCollectionFilteringProvider(AclService aclService, Permission[] requirePermission) {
super(aclService, "AFTER_ACL_COLLECTION_READ", requirePermission);
}
//~ Methods ========================================================================================================
@SuppressWarnings("unchecked")
public Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,
public Object decide(Authentication authentication, Object object, ConfigAttributeDefinition config,
Object returnedObject) throws AccessDeniedException {
if (returnedObject == null) {
@@ -82,7 +86,11 @@ public class AclEntryAfterInvocationCollectionFilteringProvider extends Abstract
return null;
}
for (ConfigAttribute attr : config) {
Iterator iter = config.getConfigAttributes().iterator();
while (iter.hasNext()) {
ConfigAttribute attr = (ConfigAttribute) iter.next();
if (!this.supports(attr)) {
continue;
}
@@ -100,7 +108,11 @@ public class AclEntryAfterInvocationCollectionFilteringProvider extends Abstract
}
// Locate unauthorised Collection elements
for (Object domainObject : filterer) {
Iterator collectionIter = filterer.iterator();
while (collectionIter.hasNext()) {
Object domainObject = collectionIter.next();
// Ignore nulls or entries which aren't instances of the configured domain object class
if (domainObject == null || !getProcessDomainObjectClass().isAssignableFrom(domainObject.getClass())) {
continue;
@@ -12,22 +12,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.afterinvocation;
package org.springframework.security.afterinvocation;
import java.util.Collection;
import java.util.List;
import org.springframework.security.AccessDeniedException;
import org.springframework.security.SpringSecurityMessageSource;
import org.springframework.security.Authentication;
import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.acls.AclService;
import org.springframework.security.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 org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityMessageSource;
import java.util.Iterator;
/**
@@ -39,10 +42,11 @@ import org.springframework.security.core.SpringSecurityMessageSource;
* <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 <tt>AclService</tt> and ensure the
* principal is {@link org.springframework.security.acls.model.Acl#isGranted(List, List, boolean)
* Acl.isGranted(List, List, boolean)} when presenting the {@link #requirePermission} array to that method.
* principal is {@link org.springframework.security.acls.Acl#isGranted(org.springframework.security.acls.Permission[],
org.springframework.security.acls.sid.Sid[], boolean) Acl.isGranted(Permission[], Sid[], boolean)}
* when presenting the {@link #requirePermission} array to that method.
* <p>
* Often users will set up an <code>AclEntryAfterInvocationProvider</code> with a {@link
* 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>
@@ -64,20 +68,17 @@ public class AclEntryAfterInvocationProvider extends AbstractAclProvider impleme
//~ Constructors ===================================================================================================
public AclEntryAfterInvocationProvider(AclService aclService, List<Permission> requirePermission) {
public AclEntryAfterInvocationProvider(AclService aclService, Permission[] requirePermission) {
super(aclService, "AFTER_ACL_READ", requirePermission);
}
public AclEntryAfterInvocationProvider(AclService aclService, String processConfigAttribute,
List<Permission> requirePermission) {
super(aclService, processConfigAttribute, requirePermission);
}
//~ Methods ========================================================================================================
public Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,
public Object decide(Authentication authentication, Object object, ConfigAttributeDefinition config,
Object returnedObject) throws AccessDeniedException {
Iterator iter = config.getConfigAttributes().iterator();
if (returnedObject == null) {
// AclManager interface contract prohibits nulls
// As they have permission to null/nothing, grant access
@@ -94,9 +95,11 @@ public class AclEntryAfterInvocationProvider extends AbstractAclProvider impleme
}
return returnedObject;
}
}
while (iter.hasNext()) {
ConfigAttribute attr = (ConfigAttribute) iter.next();
for (ConfigAttribute attr : config) {
if (!this.supports(attr)) {
continue;
}
@@ -108,7 +111,7 @@ public class AclEntryAfterInvocationProvider extends AbstractAclProvider impleme
logger.debug("Denying access");
throw new AccessDeniedException(messages.getMessage("AclEntryAfterInvocationProvider.noPermission",
throw new AccessDeniedException(messages.getMessage("BasicAclEntryAfterInvocationProvider.noPermission",
new Object[] {authentication.getName(), returnedObject},
"Authentication {0} has NO permissions to the domain object {1}"));
}
@@ -12,30 +12,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls;
package org.springframework.security.vote;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Iterator;
import org.springframework.security.Authentication;
import org.springframework.security.AuthorizationServiceException;
import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.AclService;
import org.springframework.security.acls.NotFoundException;
import org.springframework.security.acls.Permission;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.objectidentity.ObjectIdentityRetrievalStrategy;
import org.springframework.security.acls.objectidentity.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.sid.Sid;
import org.springframework.security.acls.sid.SidRetrievalStrategy;
import org.springframework.security.acls.sid.SidRetrievalStrategyImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.AuthorizationServiceException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.vote.AbstractAclVoter;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -51,17 +49,16 @@ import org.springframework.util.StringUtils;
* 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(List,
* List, boolean)} when presenting the {@link #requirePermission} array to that
* principal is {@link Acl#isGranted(org.springframework.security.acls.Permission[],
* org.springframework.security.acls.sid.Sid[], boolean)} when presenting the {@link #requirePermission} array to that
* method.
* <p>
* If the method argument is <tt>null</tt>, the voter will abstain from voting. If the method argument
* could not be found, an {@link AuthorizationServiceException} will be thrown.
* could not be found, an {@link org.springframework.security.AuthorizationServiceException} will be thrown.
* <p>
* In practical terms users will typically setup a number of <tt>AclEntryVoter</tt>s. Each will have a
* different {@link #setProcessDomainObjectClass processDomainObjectClass}, {@link #processConfigAttribute} and
* {@link #requirePermission} combination. For example, a small application might employ the following instances of
* <tt>AclEntryVoter</tt>:
* different {@link #processDomainObjectClass}, {@link #processConfigAttribute} and {@link #requirePermission}
* combination. For example, a small application might employ the following instances of <tt>AclEntryVoter</tt>:
* <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>
@@ -80,6 +77,7 @@ import org.springframework.util.StringUtils;
* <p>All comparisons and prefixes are case sensitive.</p>
*
* @author Ben Alex
* @version $Id$
*/
public class AclEntryVoter extends AbstractAclVoter {
//~ Static fields/initializers =====================================================================================
@@ -93,7 +91,7 @@ public class AclEntryVoter extends AbstractAclVoter {
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private String internalMethod;
private String processConfigAttribute;
private List<Permission> requirePermission;
private Permission[] requirePermission;
//~ Constructors ===================================================================================================
@@ -107,7 +105,7 @@ public class AclEntryVoter extends AbstractAclVoter {
this.aclService = aclService;
this.processConfigAttribute = processConfigAttribute;
this.requirePermission = Arrays.asList(requirePermission);
this.requirePermission = requirePermission;
}
//~ Methods ========================================================================================================
@@ -152,9 +150,11 @@ public class AclEntryVoter extends AbstractAclVoter {
}
}
public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
Iterator iter = config.getConfigAttributes().iterator();
for(ConfigAttribute attr : attributes) {
while (iter.hasNext()) {
ConfigAttribute attr = (ConfigAttribute) iter.next();
if (!this.supports(attr)) {
continue;
@@ -169,13 +169,13 @@ public class AclEntryVoter extends AbstractAclVoter {
logger.debug("Voting to abstain - domainObject is null");
}
return ACCESS_ABSTAIN;
return AccessDecisionVoter.ACCESS_ABSTAIN;
}
// Evaluate if we are required to use an inner domain object
if (StringUtils.hasText(internalMethod)) {
try {
Class<?> clazz = domainObject.getClass();
Class clazz = domainObject.getClass();
Method method = clazz.getMethod(internalMethod, new Class[0]);
domainObject = method.invoke(domainObject, new Object[0]);
} catch (NoSuchMethodException nsme) {
@@ -198,7 +198,7 @@ public class AclEntryVoter extends AbstractAclVoter {
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
// Obtain the SIDs applicable to the principal
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
Sid[] sids = sidRetrievalStrategy.getSids(authentication);
Acl acl;
@@ -210,7 +210,7 @@ public class AclEntryVoter extends AbstractAclVoter {
logger.debug("Voting to deny access - no ACLs apply for this principal");
}
return ACCESS_DENIED;
return AccessDecisionVoter.ACCESS_DENIED;
}
try {
@@ -219,25 +219,25 @@ public class AclEntryVoter extends AbstractAclVoter {
logger.debug("Voting to grant access");
}
return ACCESS_GRANTED;
return AccessDecisionVoter.ACCESS_GRANTED;
} else {
if (logger.isDebugEnabled()) {
logger.debug(
"Voting to deny access - ACLs returned, but insufficient permissions for this principal");
}
return ACCESS_DENIED;
return AccessDecisionVoter.ACCESS_DENIED;
}
} catch (NotFoundException nfe) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to deny access - no ACLs apply for this principal");
}
return ACCESS_DENIED;
return AccessDecisionVoter.ACCESS_DENIED;
}
}
// No configuration attribute matched, so abstain
return ACCESS_ABSTAIN;
return AccessDecisionVoter.ACCESS_ABSTAIN;
}
}
@@ -1,46 +0,0 @@
-- ACL schema sql used in HSQLDB
-- drop table acl_entry;
-- drop table acl_object_identity;
-- drop table acl_class;
-- drop table acl_sid;
create table acl_sid(
id bigint generated by default as identity(start with 100) not null primary key,
principal boolean not null,
sid varchar_ignorecase(100) not null,
constraint unique_uk_1 unique(sid,principal));
create table acl_class(
id bigint generated by default as identity(start with 100) not null primary key,
class varchar_ignorecase(100) not null,
constraint unique_uk_2 unique(class)
);
create table acl_object_identity(
id bigint generated by default as identity(start with 100) not null primary key,
object_id_class bigint not null,
object_id_identity bigint not null,
parent_object bigint,
owner_sid bigint,
entries_inheriting boolean not null,
constraint unique_uk_3 unique(object_id_class,object_id_identity),
constraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id),
constraint foreign_fk_2 foreign key(object_id_class)references acl_class(id),
constraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id)
);
create table acl_entry(
id bigint generated by default as identity(start with 100) not null primary key,
acl_object_identity bigint not null,
ace_order int not null,
sid bigint not null,
mask integer not null,
granting boolean not null,
audit_success boolean not null,
audit_failure boolean not null,
constraint unique_uk_4 unique(acl_object_identity,ace_order),
constraint foreign_fk_4 foreign key(acl_object_identity) references acl_object_identity(id),
constraint foreign_fk_5 foreign key(sid) references acl_sid(id)
);
@@ -1,46 +0,0 @@
-- ACL Schema SQL for PostgreSQL
-- drop table acl_entry;
-- drop table acl_object_identity;
-- drop table acl_class;
-- drop table acl_sid;
create table acl_sid(
id bigserial not null primary key,
principal boolean not null,
sid varchar(100) not null,
constraint unique_uk_1 unique(sid,principal)
);
create table acl_class(
id bigserial not null primary key,
class varchar(100) not null,
constraint unique_uk_2 unique(class)
);
create table acl_object_identity(
id bigserial primary key,
object_id_class bigint not null,
object_id_identity bigint not null,
parent_object bigint,
owner_sid bigint,
entries_inheriting boolean not null,
constraint unique_uk_3 unique(object_id_class,object_id_identity),
constraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id),
constraint foreign_fk_2 foreign key(object_id_class)references acl_class(id),
constraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id)
);
create table acl_entry(
id bigserial primary key,
acl_object_identity bigint not null,
ace_order int not null,
sid bigint not null,
mask integer not null,
granting boolean not null,
audit_success boolean not null,
audit_failure boolean not null,
constraint unique_uk_4 unique(acl_object_identity,ace_order),
constraint foreign_fk_4 foreign key(acl_object_identity) references acl_object_identity(id),
constraint foreign_fk_5 foreign key(sid) references acl_sid(id)
);
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
- Application context containing business beans.
-
- Used by all artifacts.
-
- $Id:applicationContext-test.xml 1754 2006-11-17 02:01:21Z benalex $
-->
<beans>
<bean id="databaseSeeder" class="org.springframework.security.acls.jdbc.DatabaseSeeder">
<constructor-arg ref="dataSource"/>
<constructor-arg value="classpath:org/springframework/security/acls/jdbc/testData.sql"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="aclCache" class="org.springframework.security.acls.jdbc.EhCacheBasedAclCache">
<constructor-arg>
<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
</property>
<property name="cacheName" value="aclCache"/>
</bean>
</constructor-arg>
</bean>
<bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
<constructor-arg ref="dataSource"/>
<constructor-arg ref="aclCache"/>
<constructor-arg ref="aclAuthorizationStrategy"/>
<constructor-arg>
<bean class="org.springframework.security.acls.domain.ConsoleAuditLogger"/>
</constructor-arg>
</bean>
<bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
<constructor-arg>
<list>
<bean class="org.springframework.security.GrantedAuthorityImpl">
<constructor-arg value="ROLE_ADMINISTRATOR"/>
</bean>
<bean class="org.springframework.security.GrantedAuthorityImpl">
<constructor-arg value="ROLE_ADMINISTRATOR"/>
</bean>
<bean class="org.springframework.security.GrantedAuthorityImpl">
<constructor-arg value="ROLE_ADMINISTRATOR"/>
</bean>
</list>
</constructor-arg>
</bean>
<bean id="aclService" class="org.springframework.security.acls.jdbc.JdbcMutableAclService">
<constructor-arg ref="dataSource"/>
<constructor-arg ref="lookupStrategy"/>
<constructor-arg ref="aclCache"/>
</bean>
<bean id="dataSource" class="org.springframework.security.TestDataSource">
<constructor-arg value="acltest" />
</bean>
</beans>
@@ -0,0 +1,27 @@
-- Not required. Just shows the sort of queries being sent to DB.
select ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY, ACL_ENTRY.ACE_ORDER,
ACL_OBJECT_IDENTITY.ID as ACL_ID,
ACL_OBJECT_IDENTITY.PARENT_OBJECT,
ACL_OBJECT_IDENTITY,ENTRIES_INHERITING,
ACL_ENTRY.ID as ACE_ID, ACL_ENTRY.MASK, ACL_ENTRY.GRANTING, ACL_ENTRY.AUDIT_SUCCESS, ACL_ENTRY.AUDIT_FAILURE,
ACL_SID.PRINCIPAL as ACE_PRINCIPAL, ACL_SID.SID as ACE_SID,
ACLI_SID.PRINCIPAL as ACL_PRINCIPAL, ACLI_SID.SID as ACL_SID,
ACL_CLASS.CLASS
from ACL_OBJECT_IDENTITY, ACL_SID ACLI_SID, ACL_CLASS
LEFT JOIN ACL_ENTRY ON ACL_OBJECT_IDENTITY.ID = ACL_ENTRY.ACL_OBJECT_IDENTITY
LEFT JOIN ACL_SID ON ACL_ENTRY.SID = ACL_SID.ID
where
ACLI_SID.ID = ACL_OBJECT_IDENTITY.OWNER_SID
and ACL_CLASS.ID = ACL_OBJECT_IDENTITY.OBJECT_ID_CLASS
and (
(ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY = 1
and ACL_CLASS.CLASS = 'sample.contact.Contact')
or
(ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY = 2000
and ACL_CLASS.CLASS = 'sample.contact.Contact')
) order by ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY asc, ACL_ENTRY.ACE_ORDER asc
@@ -0,0 +1,45 @@
-- Injected into DatabaseSeeder via applicationContext-test.xml (see test case JdbcAclServiceTests)
-- DROP TABLE ACL_ENTRY;
-- DROP TABLE ACL_OBJECT_IDENTITY;
-- DROP TABLE ACL_CLASS;
-- DROP TABLE ACL_SID;
CREATE TABLE ACL_SID(
ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,
PRINCIPAL BOOLEAN NOT NULL,
SID VARCHAR_IGNORECASE(100) NOT NULL,
CONSTRAINT UNIQUE_UK_1 UNIQUE(SID,PRINCIPAL));
CREATE TABLE ACL_CLASS(
ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,
CLASS VARCHAR_IGNORECASE(100) NOT NULL,
CONSTRAINT UNIQUE_UK_2 UNIQUE(CLASS));
INSERT INTO ACL_CLASS VALUES (1, 'sample.contact.Contact');
CREATE TABLE ACL_OBJECT_IDENTITY(
ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,
OBJECT_ID_CLASS BIGINT NOT NULL,
OBJECT_ID_IDENTITY BIGINT NOT NULL,
PARENT_OBJECT BIGINT,
OWNER_SID BIGINT,
ENTRIES_INHERITING BOOLEAN NOT NULL,
CONSTRAINT UNIQUE_UK_3 UNIQUE(OBJECT_ID_CLASS,OBJECT_ID_IDENTITY),
CONSTRAINT FOREIGN_FK_1 FOREIGN KEY(PARENT_OBJECT)REFERENCES ACL_OBJECT_IDENTITY(ID),
CONSTRAINT FOREIGN_FK_2 FOREIGN KEY(OBJECT_ID_CLASS)REFERENCES ACL_CLASS(ID),
CONSTRAINT FOREIGN_FK_3 FOREIGN KEY(OWNER_SID)REFERENCES ACL_SID(ID));
CREATE TABLE ACL_ENTRY(
ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,
ACL_OBJECT_IDENTITY BIGINT NOT NULL,
ACE_ORDER INT NOT NULL,
SID BIGINT NOT NULL,
MASK INTEGER NOT NULL,
GRANTING BOOLEAN NOT NULL,
AUDIT_SUCCESS BOOLEAN NOT NULL,
AUDIT_FAILURE BOOLEAN NOT NULL,
CONSTRAINT UNIQUE_UK_4 UNIQUE(ACL_OBJECT_IDENTITY,ACE_ORDER),
CONSTRAINT FOREIGN_FK_4 FOREIGN KEY(ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY(ID),
CONSTRAINT FOREIGN_FK_5 FOREIGN KEY(SID) REFERENCES ACL_SID(ID));
-39
View File
@@ -1,39 +0,0 @@
-- Not required. Just shows the sort of queries being sent to DB.
select acl_object_identity.object_id_identity,
acl_entry.ace_order,
acl_object_identity.id as acl_id,
acl_object_identity.parent_object,
acl_object_identity,
entries_inheriting,
acl_entry.id as ace_id,
acl_entry.mask,
acl_entry.granting,
acl_entry.audit_success,
acl_entry.audit_failure,
acl_sid.principal as ace_principal,
acl_sid.sid as ace_sid,
acli_sid.principal as acl_principal,
acli_sid.sid as acl_sid,
acl_class.class
from acl_object_identity,
acl_sid acli_sid,
acl_class
left join acl_entry on acl_object_identity.id = acl_entry.acl_object_identity
left join acl_sid on acl_entry.sid = acl_sid.id
where
acli_sid.id = acl_object_identity.owner_sid
and acl_class.id = acl_object_identity.object_id_class
and (
(acl_object_identity.object_id_identity = 1 and acl_class.class = 'sample.contact.contact')
or
(acl_object_identity.object_id_identity = 2000 and acl_class.class = 'sample.contact.contact')
) order by acl_object_identity.object_id_identity asc, acl_entry.ace_order asc
@@ -1,127 +1,124 @@
package org.springframework.security.acls;
import org.springframework.security.acls.domain.AclFormattingUtils;
import org.springframework.security.acls.model.Permission;
import junit.framework.Assert;
import junit.framework.TestCase;
/**
* Tests for {@link AclFormattingUtils}.
*
* @author Andrei Stefan
*/
public class AclFormattingUtilsTests extends TestCase {
//~ Methods ========================================================================================================
public final void testDemergePatternsParametersConstraints() throws Exception {
try {
AclFormattingUtils.demergePatterns(null, "SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", null);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", "LONGER SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", "SAME LENGTH");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
public final void testDemergePatterns() throws Exception {
String original = "...........................A...R";
String removeBits = "...............................R";
Assert.assertEquals("...........................A....", AclFormattingUtils
.demergePatterns(original, removeBits));
Assert.assertEquals("ABCDEF", AclFormattingUtils.demergePatterns("ABCDEF", "......"));
Assert.assertEquals("......", AclFormattingUtils.demergePatterns("ABCDEF", "GHIJKL"));
}
public final void testMergePatternsParametersConstraints() throws Exception {
try {
AclFormattingUtils.mergePatterns(null, "SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", null);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", "LONGER SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", "SAME LENGTH");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
public final void testMergePatterns() throws Exception {
String original = "...............................R";
String extraBits = "...........................A....";
Assert.assertEquals("...........................A...R", AclFormattingUtils
.mergePatterns(original, extraBits));
Assert.assertEquals("ABCDEF", AclFormattingUtils.mergePatterns("ABCDEF", "......"));
Assert.assertEquals("GHIJKL", AclFormattingUtils.mergePatterns("ABCDEF", "GHIJKL"));
}
public final void testBinaryPrints() throws Exception {
Assert.assertEquals("............................****", AclFormattingUtils.printBinary(15));
try {
AclFormattingUtils.printBinary(15, Permission.RESERVED_ON);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException notExpected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.printBinary(15, Permission.RESERVED_OFF);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException notExpected) {
Assert.assertTrue(true);
}
Assert.assertEquals("............................xxxx", AclFormattingUtils.printBinary(15, 'x'));
}
}
package org.springframework.security.acls;
import junit.framework.Assert;
import junit.framework.TestCase;
/**
* Tests for {@link AclFormattingUtils}.
*
* @author Andrei Stefan
*/
public class AclFormattingUtilsTests extends TestCase {
//~ Methods ========================================================================================================
public final void testDemergePatternsParametersConstraints() throws Exception {
try {
AclFormattingUtils.demergePatterns(null, "SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", null);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", "LONGER SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", "SAME LENGTH");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
public final void testDemergePatterns() throws Exception {
String original = "...........................A...R";
String removeBits = "...............................R";
Assert.assertEquals("...........................A....", AclFormattingUtils
.demergePatterns(original, removeBits));
Assert.assertEquals("ABCDEF", AclFormattingUtils.demergePatterns("ABCDEF", "......"));
Assert.assertEquals("......", AclFormattingUtils.demergePatterns("ABCDEF", "GHIJKL"));
}
public final void testMergePatternsParametersConstraints() throws Exception {
try {
AclFormattingUtils.mergePatterns(null, "SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", null);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", "LONGER SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", "SAME LENGTH");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
public final void testMergePatterns() throws Exception {
String original = "...............................R";
String extraBits = "...........................A....";
Assert.assertEquals("...........................A...R", AclFormattingUtils
.mergePatterns(original, extraBits));
Assert.assertEquals("ABCDEF", AclFormattingUtils.mergePatterns("ABCDEF", "......"));
Assert.assertEquals("GHIJKL", AclFormattingUtils.mergePatterns("ABCDEF", "GHIJKL"));
}
public final void testBinaryPrints() throws Exception {
Assert.assertEquals("............................****", AclFormattingUtils.printBinary(15));
try {
AclFormattingUtils.printBinary(15, Permission.RESERVED_ON);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException notExpected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.printBinary(15, Permission.RESERVED_OFF);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException notExpected) {
Assert.assertTrue(true);
}
Assert.assertEquals("............................xxxx", AclFormattingUtils.printBinary(15, 'x'));
}
}
@@ -1,39 +0,0 @@
package org.springframework.security.acls;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
/**
*
* @author Luke Taylor
* @since 3.0
*/
public class AclPermissionEvaluatorTests {
@Test
@SuppressWarnings("unchecked")
public void hasPermissionReturnsTrueIfAclGrantsPermission() throws Exception {
AclService service = mock(AclService.class);
AclPermissionEvaluator pe = new AclPermissionEvaluator(service);
ObjectIdentity oid = mock(ObjectIdentity.class);
ObjectIdentityRetrievalStrategy oidStrategy = mock(ObjectIdentityRetrievalStrategy.class);
when(oidStrategy.getObjectIdentity(anyObject())).thenReturn(oid);
pe.setObjectIdentityRetrievalStrategy(oidStrategy);
pe.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
Acl acl = mock(Acl.class);
when(service.readAclById(any(ObjectIdentity.class), anyList())).thenReturn(acl);
when(acl.isGranted(anyList(), anyList(), eq(false))).thenReturn(true);
assertTrue(pe.hasPermission(mock(Authentication.class), new Object(), "READ"));
}
}
@@ -1,10 +0,0 @@
package org.springframework.security.acls;
/**
* Dummy domain object class
*
* @author Luke Taylor
*/
public final class TargetObject {
}
@@ -0,0 +1,136 @@
package org.springframework.security.acls.domain;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.springframework.security.acls.AccessControlEntry;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.AuditableAccessControlEntry;
import org.springframework.security.acls.NotFoundException;
import org.springframework.security.acls.Permission;
import org.springframework.security.acls.UnloadedSidException;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.sid.PrincipalSid;
import org.springframework.security.acls.sid.Sid;
/**
* Test class for {@link AccessControlEntryImpl}
*
* @author Andrei Stefan
*/
public class AccessControlEntryTests extends TestCase {
//~ Methods ========================================================================================================
public void testConstructorRequiredFields() throws Exception {
// Check Acl field is present
try {
AccessControlEntry ace = new AccessControlEntryImpl(null, null, new PrincipalSid("johndoe"),
BasePermission.ADMINISTRATION, true, true, true);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
// Check Sid field is present
try {
AccessControlEntry ace = new AccessControlEntryImpl(null, new MockAcl(), null,
BasePermission.ADMINISTRATION, true, true, true);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
// Check Permission field is present
try {
AccessControlEntry ace = new AccessControlEntryImpl(null, new MockAcl(), new PrincipalSid("johndoe"), null,
true, true, true);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
}
public void testAccessControlEntryImplGetters() throws Exception {
Acl mockAcl = new MockAcl();
Sid sid = new PrincipalSid("johndoe");
// Create a sample entry
AccessControlEntry ace = new AccessControlEntryImpl(new Long(1), mockAcl, sid, BasePermission.ADMINISTRATION,
true, true, true);
// and check every get() method
Assert.assertEquals(new Long(1), ace.getId());
Assert.assertEquals(mockAcl, ace.getAcl());
Assert.assertEquals(sid, ace.getSid());
Assert.assertTrue(ace.isGranting());
Assert.assertEquals(BasePermission.ADMINISTRATION, ace.getPermission());
Assert.assertTrue(((AuditableAccessControlEntry) ace).isAuditFailure());
Assert.assertTrue(((AuditableAccessControlEntry) ace).isAuditSuccess());
}
public void testEquals() throws Exception {
Acl mockAcl = new MockAcl();
Sid sid = new PrincipalSid("johndoe");
AccessControlEntry ace = new AccessControlEntryImpl(new Long(1), mockAcl, sid, BasePermission.ADMINISTRATION,
true, true, true);
Assert.assertFalse(ace.equals(null));
Assert.assertFalse(ace.equals(new Long(100)));
Assert.assertTrue(ace.equals(ace));
Assert.assertTrue(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, true)));
Assert.assertFalse(ace.equals(new AccessControlEntryImpl(new Long(2), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, true)));
Assert.assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, new PrincipalSid("scott"),
BasePermission.ADMINISTRATION, true, true, true)));
Assert.assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid, BasePermission.WRITE, true,
true, true)));
Assert.assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid,
BasePermission.ADMINISTRATION, false, true, true)));
Assert.assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, false, true)));
Assert.assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, false)));
}
//~ Inner Classes ==================================================================================================
private class MockAcl implements Acl {
public AccessControlEntry[] getEntries() {
return null;
}
public ObjectIdentity getObjectIdentity() {
return null;
}
public Sid getOwner() {
return null;
}
public Acl getParentAcl() {
return null;
}
public boolean isEntriesInheriting() {
return false;
}
public boolean isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode)
throws NotFoundException, UnloadedSidException {
return false;
}
public boolean isSidLoaded(Sid[] sids) {
return false;
}
}
}
@@ -1,104 +0,0 @@
package org.springframework.security.acls.domain;
import static org.junit.Assert.*;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.Test;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AuditableAccessControlEntry;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Sid;
/**
* Tests for {@link AccessControlEntryImpl}.
*
* @author Andrei Stefan
*/
public class AccessControlImplEntryTests {
Mockery jmock = new JUnit4Mockery();
//~ Methods ========================================================================================================
@Test
public void testConstructorRequiredFields() {
// Check Acl field is present
try {
new AccessControlEntryImpl(null, null, new PrincipalSid("johndoe"),
BasePermission.ADMINISTRATION, true, true, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// Check Sid field is present
try {
new AccessControlEntryImpl(null, jmock.mock(Acl.class), null,
BasePermission.ADMINISTRATION, true, true, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// Check Permission field is present
try {
new AccessControlEntryImpl(null, jmock.mock(Acl.class), new PrincipalSid("johndoe"), null,
true, true, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
public void testAccessControlEntryImplGetters() {
Acl mockAcl = jmock.mock(Acl.class);
Sid sid = new PrincipalSid("johndoe");
// Create a sample entry
AccessControlEntry ace = new AccessControlEntryImpl(new Long(1), mockAcl, sid, BasePermission.ADMINISTRATION,
true, true, true);
// and check every get() method
assertEquals(new Long(1), ace.getId());
assertEquals(mockAcl, ace.getAcl());
assertEquals(sid, ace.getSid());
assertTrue(ace.isGranting());
assertEquals(BasePermission.ADMINISTRATION, ace.getPermission());
assertTrue(((AuditableAccessControlEntry) ace).isAuditFailure());
assertTrue(((AuditableAccessControlEntry) ace).isAuditSuccess());
}
@Test
public void testEquals() {
final Acl mockAcl = jmock.mock(Acl.class);
final ObjectIdentity oid = jmock.mock(ObjectIdentity.class);
jmock.checking(new Expectations() {{
allowing(mockAcl).getObjectIdentity(); will(returnValue(oid));
}});
Sid sid = new PrincipalSid("johndoe");
AccessControlEntry ace = new AccessControlEntryImpl(new Long(1), mockAcl, sid, BasePermission.ADMINISTRATION,
true, true, true);
assertFalse(ace.equals(null));
assertFalse(ace.equals(new Long(100)));
assertTrue(ace.equals(ace));
assertTrue(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(new Long(2), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, new PrincipalSid("scott"),
BasePermission.ADMINISTRATION, true, true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid, BasePermission.WRITE, true,
true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid,
BasePermission.ADMINISTRATION, false, true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, false, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(new Long(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, false)));
}
}
File diff suppressed because it is too large Load Diff
@@ -1,260 +1,296 @@
package org.springframework.security.acls.domain;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* Test class for {@link AclAuthorizationStrategyImpl} and {@link AclImpl}
* security checks.
*
* @author Andrei Stefan
*/
public class AclImplementationSecurityCheckTests extends TestCase {
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
//~ Methods ========================================================================================================
protected void setUp() throws Exception {
SecurityContextHolder.clearContext();
}
protected void tearDown() throws Exception {
SecurityContextHolder.clearContext();
}
public void testSecurityCheckNoACEs() throws Exception {
Authentication auth = new TestingAuthenticationToken("user", "password","ROLE_GENERAL","ROLE_AUDITING","ROLE_OWNERSHIP");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
Acl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL);
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_AUDITING);
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
// Create another authorization strategy
AclAuthorizationStrategy aclAuthorizationStrategy2 = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO"),
new GrantedAuthorityImpl("ROLE_THREE") });
Acl acl2 = new AclImpl(identity, new Long(1), aclAuthorizationStrategy2, new ConsoleAuditLogger());
// Check access in case the principal has no authorization rights
try {
aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
try {
aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
try {
aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
}
public void testSecurityCheckWithMultipleACEs() throws Exception {
// Create a simple authentication with ROLE_GENERAL
Authentication auth = new TestingAuthenticationToken("user", "password",
new GrantedAuthority[] { new GrantedAuthorityImpl("ROLE_GENERAL") });
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
// Authorization strategy will require a different role for each access
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
// Let's give the principal the ADMINISTRATION permission, without
// granting access
MutableAcl aclFirstDeny = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
aclFirstDeny.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false);
// The CHANGE_GENERAL test should pass as the principal has ROLE_GENERAL
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_GENERAL);
// The CHANGE_AUDITING and CHANGE_OWNERSHIP should fail since the
// principal doesn't have these authorities,
// nor granting access
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
}
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
}
// Add granting access to this principal
aclFirstDeny.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
// and try again for CHANGE_AUDITING - the first ACE's granting flag
// (false) will deny this access
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
}
// Create another ACL and give the principal the ADMINISTRATION
// permission, with granting access
MutableAcl aclFirstAllow = new AclImpl(identity, new Long(1), aclAuthorizationStrategy,
new ConsoleAuditLogger());
aclFirstAllow.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
// The CHANGE_AUDITING test should pass as there is one ACE with
// granting access
aclAuthorizationStrategy.securityCheck(aclFirstAllow, AclAuthorizationStrategy.CHANGE_AUDITING);
// Add a deny ACE and test again for CHANGE_AUDITING
aclFirstAllow.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false);
try {
aclAuthorizationStrategy.securityCheck(aclFirstAllow, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.assertTrue(true);
}
catch (AccessDeniedException notExpected) {
Assert.fail("It shouldn't have thrown AccessDeniedException");
}
// Create an ACL with no ACE
MutableAcl aclNoACE = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
try {
aclAuthorizationStrategy.securityCheck(aclNoACE, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
Assert.assertTrue(true);
}
// and still grant access for CHANGE_GENERAL
try {
aclAuthorizationStrategy.securityCheck(aclNoACE, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.assertTrue(true);
}
catch (NotFoundException expected) {
Assert.fail("It shouldn't have thrown NotFoundException");
}
}
public void testSecurityCheckWithInheritableACEs() throws Exception {
// Create a simple authentication with ROLE_GENERAL
Authentication auth = new TestingAuthenticationToken("user", "password",
new GrantedAuthority[] { new GrantedAuthorityImpl("ROLE_GENERAL") });
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
// Authorization strategy will require a different role for each access
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
// Let's give the principal an ADMINISTRATION permission, with granting
// access
MutableAcl parentAcl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
parentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
MutableAcl childAcl = new AclImpl(identity, new Long(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
// Check against the 'child' acl, which doesn't offer any authorization
// rights on CHANGE_OWNERSHIP
try {
aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
Assert.assertTrue(true);
}
// Link the child with its parent and test again against the
// CHANGE_OWNERSHIP right
childAcl.setParent(parentAcl);
childAcl.setEntriesInheriting(true);
try {
aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.assertTrue(true);
}
catch (NotFoundException expected) {
Assert.fail("It shouldn't have thrown NotFoundException");
}
// Create a root parent and link it to the middle parent
MutableAcl rootParentAcl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy,
new ConsoleAuditLogger());
parentAcl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
rootParentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
parentAcl.setEntriesInheriting(true);
parentAcl.setParent(rootParentAcl);
childAcl.setParent(parentAcl);
try {
aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.assertTrue(true);
}
catch (NotFoundException expected) {
Assert.fail("It shouldn't have thrown NotFoundException");
}
}
public void testSecurityCheckPrincipalOwner() throws Exception {
Authentication auth = new TestingAuthenticationToken("user", "password", new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_ONE"),
new GrantedAuthorityImpl("ROLE_ONE") });
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
Acl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger(), null, null,
false, new PrincipalSid(auth));
try {
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.assertTrue(true);
}
catch (AccessDeniedException notExpected) {
Assert.fail("It shouldn't have thrown AccessDeniedException");
}
try {
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.fail("It shouldn't have thrown AccessDeniedException");
}
catch (NotFoundException expected) {
Assert.assertTrue(true);
}
try {
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.assertTrue(true);
}
catch (AccessDeniedException notExpected) {
Assert.fail("It shouldn't have thrown AccessDeniedException");
}
}
}
package org.springframework.security.acls.domain;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.springframework.security.AccessDeniedException;
import org.springframework.security.Authentication;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.MutableAcl;
import org.springframework.security.acls.NotFoundException;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.objectidentity.ObjectIdentityImpl;
import org.springframework.security.acls.sid.PrincipalSid;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.TestingAuthenticationToken;
/**
* Test class for {@link AclAuthorizationStrategyImpl} and {@link AclImpl}
* security checks.
*
* @author Andrei Stefan
*/
public class AclImplementationSecurityCheckTests extends TestCase {
//~ Methods ========================================================================================================
protected void setUp() throws Exception {
SecurityContextHolder.clearContext();
}
protected void tearDown() throws Exception {
SecurityContextHolder.clearContext();
}
public void testSecurityCheckNoACEs() throws Exception {
Authentication auth = new TestingAuthenticationToken("user", "password", new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_GENERAL"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_OWNERSHIP") });
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
Acl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
try {
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.assertTrue(true);
}
catch (AccessDeniedException notExpected) {
Assert.fail("It shouldn't have thrown AccessDeniedException");
}
try {
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.assertTrue(true);
}
catch (AccessDeniedException notExpected) {
Assert.fail("It shouldn't have thrown AccessDeniedException");
}
try {
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.assertTrue(true);
}
catch (AccessDeniedException notExpected) {
Assert.fail("It shouldn't have thrown AccessDeniedException");
}
// Create another authorization strategy
AclAuthorizationStrategy aclAuthorizationStrategy2 = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO"),
new GrantedAuthorityImpl("ROLE_THREE") });
Acl acl2 = new AclImpl(identity, new Long(1), aclAuthorizationStrategy2, new ConsoleAuditLogger());
// Check access in case the principal has no authorization rights
try {
aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
Assert.assertTrue(true);
}
try {
aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
Assert.assertTrue(true);
}
try {
aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
Assert.assertTrue(true);
}
}
public void testSecurityCheckWithMultipleACEs() throws Exception {
// Create a simple authentication with ROLE_GENERAL
Authentication auth = new TestingAuthenticationToken("user", "password",
new GrantedAuthority[] { new GrantedAuthorityImpl("ROLE_GENERAL") });
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
// Authorization strategy will require a different role for each access
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
// Let's give the principal the ADMINISTRATION permission, without
// granting access
MutableAcl aclFirstDeny = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
aclFirstDeny.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false);
// The CHANGE_GENERAL test should pass as the principal has ROLE_GENERAL
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.assertTrue(true);
}
catch (AccessDeniedException notExpected) {
Assert.fail("It shouldn't have thrown AccessDeniedException");
}
// The CHANGE_AUDITING and CHANGE_OWNERSHIP should fail since the
// principal doesn't have these authorities,
// nor granting access
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
Assert.assertTrue(true);
}
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
Assert.assertTrue(true);
}
// Add granting access to this principal
aclFirstDeny.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
// and try again for CHANGE_AUDITING - the first ACE's granting flag
// (false) will deny this access
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
Assert.assertTrue(true);
}
// Create another ACL and give the principal the ADMINISTRATION
// permission, with granting access
MutableAcl aclFirstAllow = new AclImpl(identity, new Long(1), aclAuthorizationStrategy,
new ConsoleAuditLogger());
aclFirstAllow.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
// The CHANGE_AUDITING test should pass as there is one ACE with
// granting access
try {
aclAuthorizationStrategy.securityCheck(aclFirstAllow, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.assertTrue(true);
}
catch (AccessDeniedException notExpected) {
Assert.fail("It shouldn't have thrown AccessDeniedException");
}
// Add a deny ACE and test again for CHANGE_AUDITING
aclFirstAllow.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false);
try {
aclAuthorizationStrategy.securityCheck(aclFirstAllow, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.assertTrue(true);
}
catch (AccessDeniedException notExpected) {
Assert.fail("It shouldn't have thrown AccessDeniedException");
}
// Create an ACL with no ACE
MutableAcl aclNoACE = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
try {
aclAuthorizationStrategy.securityCheck(aclNoACE, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
Assert.assertTrue(true);
}
// and still grant access for CHANGE_GENERAL
try {
aclAuthorizationStrategy.securityCheck(aclNoACE, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.assertTrue(true);
}
catch (NotFoundException expected) {
Assert.fail("It shouldn't have thrown NotFoundException");
}
}
public void testSecurityCheckWithInheritableACEs() throws Exception {
// Create a simple authentication with ROLE_GENERAL
Authentication auth = new TestingAuthenticationToken("user", "password",
new GrantedAuthority[] { new GrantedAuthorityImpl("ROLE_GENERAL") });
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
// Authorization strategy will require a different role for each access
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
// Let's give the principal an ADMINISTRATION permission, with granting
// access
MutableAcl parentAcl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
parentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
MutableAcl childAcl = new AclImpl(identity, new Long(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
// Check against the 'child' acl, which doesn't offer any authorization
// rights on CHANGE_OWNERSHIP
try {
aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
Assert.assertTrue(true);
}
// Link the child with its parent and test again against the
// CHANGE_OWNERSHIP right
childAcl.setParent(parentAcl);
childAcl.setEntriesInheriting(true);
try {
aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.assertTrue(true);
}
catch (NotFoundException expected) {
Assert.fail("It shouldn't have thrown NotFoundException");
}
// Create a root parent and link it to the middle parent
MutableAcl rootParentAcl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy,
new ConsoleAuditLogger());
parentAcl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
rootParentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
parentAcl.setEntriesInheriting(true);
parentAcl.setParent(rootParentAcl);
childAcl.setParent(parentAcl);
try {
aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.assertTrue(true);
}
catch (NotFoundException expected) {
Assert.fail("It shouldn't have thrown NotFoundException");
}
}
public void testSecurityCheckPrincipalOwner() throws Exception {
Authentication auth = new TestingAuthenticationToken("user", "password", new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_ONE"),
new GrantedAuthorityImpl("ROLE_ONE") });
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
Acl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger(), null, null,
false, new PrincipalSid(auth));
try {
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.assertTrue(true);
}
catch (AccessDeniedException notExpected) {
Assert.fail("It shouldn't have thrown AccessDeniedException");
}
try {
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_AUDITING);
Assert.fail("It shouldn't have thrown AccessDeniedException");
}
catch (NotFoundException expected) {
Assert.assertTrue(true);
}
try {
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.assertTrue(true);
}
catch (AccessDeniedException notExpected) {
Assert.fail("It shouldn't have thrown AccessDeniedException");
}
}
}
@@ -1,91 +1,137 @@
package org.springframework.security.acls.domain;
import static org.junit.Assert.*;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.AuditableAccessControlEntry;
/**
* Test class for {@link ConsoleAuditLogger}.
*
* @author Andrei Stefan
*/
public class AuditLoggerTests {
//~ Instance fields ================================================================================================
private Mockery jmock = new JUnit4Mockery();
private PrintStream console;
private ByteArrayOutputStream bytes = new ByteArrayOutputStream();
private ConsoleAuditLogger logger;
private AuditableAccessControlEntry ace;
private Expectations aceRequiresAudit;
private Expectations aceDoesntRequireAudit;
//~ Methods ========================================================================================================
@Before
public void setUp() throws Exception {
logger = new ConsoleAuditLogger();
ace = jmock.mock(AuditableAccessControlEntry.class);
aceRequiresAudit = new Expectations() {{
allowing(ace).isAuditSuccess(); will(returnValue(true));
allowing(ace).isAuditFailure(); will(returnValue(true));
}};
aceDoesntRequireAudit = new Expectations() {{
allowing(ace).isAuditSuccess(); will(returnValue(false));
allowing(ace).isAuditFailure(); will(returnValue(false));
}};
console = System.out;
System.setOut(new PrintStream(bytes));
}
@After
public void tearDown() throws Exception {
System.setOut(console);
bytes.reset();
}
@Test
public void nonAuditableAceIsIgnored() {
AccessControlEntry ace = jmock.mock(AccessControlEntry.class);
logger.logIfNeeded(true, ace);
assertEquals(0, bytes.size());
}
@Test
public void successIsNotLoggedIfAceDoesntRequireSuccessAudit() throws Exception {
jmock.checking(aceDoesntRequireAudit);
logger.logIfNeeded(true, ace);
assertEquals(0, bytes.size());
}
@Test
public void successIsLoggedIfAceRequiresSuccessAudit() throws Exception {
jmock.checking(aceRequiresAudit);
logger.logIfNeeded(true, ace);
assertTrue(bytes.toString().startsWith("GRANTED due to ACE"));
}
@Test
public void failureIsntLoggedIfAceDoesntRequireFailureAudit() throws Exception {
jmock.checking(aceDoesntRequireAudit);
logger.logIfNeeded(false, ace);
assertEquals(0, bytes.size());
}
@Test
public void failureIsLoggedIfAceRequiresFailureAudit() throws Exception {
jmock.checking(aceRequiresAudit);
logger.logIfNeeded(false, ace);
assertTrue(bytes.toString().startsWith("DENIED due to ACE"));
}
}
package org.springframework.security.acls.domain;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.Serializable;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.springframework.security.acls.AccessControlEntry;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.AuditableAccessControlEntry;
import org.springframework.security.acls.Permission;
import org.springframework.security.acls.sid.Sid;
/**
* Test class for {@link ConsoleAuditLogger}.
*
* @author Andrei Stefan
*/
public class AuditLoggerTests extends TestCase {
//~ Instance fields ================================================================================================
private PrintStream console;
private ByteArrayOutputStream bytes = new ByteArrayOutputStream();
//~ Methods ========================================================================================================
public void setUp() throws Exception {
console = System.out;
System.setOut(new PrintStream(bytes));
}
public void tearDown() throws Exception {
System.setOut(console);
}
public void testLoggingTests() throws Exception {
ConsoleAuditLogger logger = new ConsoleAuditLogger();
MockAccessControlEntryImpl auditableAccessControlEntry = new MockAccessControlEntryImpl();
logger.logIfNeeded(true, auditableAccessControlEntry);
Assert.assertTrue(bytes.size() == 0);
bytes.reset();
logger.logIfNeeded(false, auditableAccessControlEntry);
Assert.assertTrue(bytes.size() == 0);
auditableAccessControlEntry.setAuditSuccess(true);
bytes.reset();
logger.logIfNeeded(true, auditableAccessControlEntry);
Assert.assertTrue(bytes.toString().length() > 0);
Assert.assertTrue(bytes.toString().startsWith("GRANTED due to ACE"));
auditableAccessControlEntry.setAuditFailure(true);
bytes.reset();
logger.logIfNeeded(false, auditableAccessControlEntry);
Assert.assertTrue(bytes.toString().length() > 0);
Assert.assertTrue(bytes.toString().startsWith("DENIED due to ACE"));
MockAccessControlEntry accessControlEntry = new MockAccessControlEntry();
bytes.reset();
logger.logIfNeeded(true, accessControlEntry);
Assert.assertTrue(bytes.size() == 0);
}
//~ Inner Classes ==================================================================================================
private class MockAccessControlEntryImpl implements AuditableAccessControlEntry {
private boolean auditFailure = false;
private boolean auditSuccess = false;
public boolean isAuditFailure() {
return auditFailure;
}
public boolean isAuditSuccess() {
return auditSuccess;
}
public Acl getAcl() {
return null;
}
public Serializable getId() {
return null;
}
public Permission getPermission() {
return null;
}
public Sid getSid() {
return null;
}
public boolean isGranting() {
return false;
}
public void setAuditFailure(boolean auditFailure) {
this.auditFailure = auditFailure;
}
public void setAuditSuccess(boolean auditSuccess) {
this.auditSuccess = auditSuccess;
}
}
private class MockAccessControlEntry implements AccessControlEntry {
public Acl getAcl() {
return null;
}
public Serializable getId() {
return null;
}
public Permission getPermission() {
return null;
}
public Sid getSid() {
return null;
}
public boolean isGranting() {
return false;
}
}
}
@@ -1,189 +0,0 @@
package org.springframework.security.acls.domain;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.security.acls.domain.IdentityUnavailableException;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.model.ObjectIdentity;
/**
* Tests for {@link ObjectIdentityImpl}.
*
* @author Andrei Stefan
*/
@SuppressWarnings("unused")
public class ObjectIdentityImplTests {
private static final String DOMAIN_CLASS =
"org.springframework.security.acls.domain.ObjectIdentityImplTests$MockIdDomainObject";
//~ Methods ========================================================================================================
@Test
public void constructorsRespectRequiredFields() throws Exception {
// Check one-argument constructor required field
try {
new ObjectIdentityImpl(null);
fail("It should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
// Check String-Serializable constructor required field
try {
new ObjectIdentityImpl("", Long.valueOf(1));
fail("It should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
// Check Serializable parameter is not null
try {
new ObjectIdentityImpl(DOMAIN_CLASS, null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// The correct way of using String-Serializable constructor
try {
new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
}
catch (IllegalArgumentException notExpected) {
fail("It shouldn't have thrown IllegalArgumentException");
}
// Check the Class-Serializable constructor
try {
new ObjectIdentityImpl(MockIdDomainObject.class, null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
public void gettersReturnExpectedValues() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
assertEquals(Long.valueOf(1), obj.getIdentifier());
assertEquals(MockIdDomainObject.class.getName(), obj.getType());
}
@Test
public void testGetIdMethodConstraints() throws Exception {
// Check the getId() method is present
try {
new ObjectIdentityImpl("A_STRING_OBJECT");
fail("It should have thrown IdentityUnavailableException");
}
catch (IdentityUnavailableException expected) {
}
// getId() should return a non-null value
MockIdDomainObject mockId = new MockIdDomainObject();
try {
new ObjectIdentityImpl(mockId);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// getId() should return a Serializable object
mockId.setId(new MockIdDomainObject());
try {
new ObjectIdentityImpl(mockId);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// getId() should return a Serializable object
mockId.setId(new Long(100));
try {
new ObjectIdentityImpl(mockId);
}
catch (IllegalArgumentException expected) {
}
}
@Test(expected=IllegalArgumentException.class)
public void constructorRejectsInvalidTypeParameter() throws Exception {
new ObjectIdentityImpl("", Long.valueOf(1));
}
@Test
public void testEquals() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
MockIdDomainObject mockObj = new MockIdDomainObject();
mockObj.setId(Long.valueOf(1));
String string = "SOME_STRING";
assertNotSame(obj, string);
assertFalse(obj.equals(null));
assertFalse(obj.equals("DIFFERENT_OBJECT_TYPE"));
assertFalse(obj.equals(new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(2))));
assertFalse(obj.equals(new ObjectIdentityImpl(
"org.springframework.security.acls.domain.ObjectIdentityImplTests$MockOtherIdDomainObject",
Long.valueOf(1))));
assertEquals(new ObjectIdentityImpl(DOMAIN_CLASS,Long.valueOf(1)), obj);
assertEquals(obj, new ObjectIdentityImpl(mockObj));
}
@Test
public void hashcodeIsDifferentForDifferentJavaTypes() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, Long.valueOf(1));
ObjectIdentity obj2 = new ObjectIdentityImpl(String.class, Long.valueOf(1));
assertFalse(obj.hashCode() == obj2.hashCode());
}
@Test
public void longAndIntegerIdsWithSameValueAreEqualAndHaveSameHashcode() {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, new Long(5));
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, new Integer(5));
assertEquals(obj, obj2);
assertEquals(obj.hashCode(), obj2.hashCode());
}
@Test
public void equalStringIdsAreEqualAndHaveSameHashcode() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, "1000");
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, "1000");
assertEquals(obj, obj2);
assertEquals(obj.hashCode(), obj2.hashCode());
}
@Test
public void stringAndNumericIdsAreNotEqual() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, "1000");
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, Long.valueOf(1000));
assertFalse(obj.equals(obj2));
}
//~ Inner Classes ==================================================================================================
private class MockIdDomainObject {
private Object id;
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
}
private class MockOtherIdDomainObject {
private Object id;
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
}
}
@@ -14,32 +14,23 @@
*/
package org.springframework.security.acls.domain;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.Permission;
/**
* Tests classes associated with Permission.
*
* @author Ben Alex
* @version $Id${date}
*/
public class PermissionTests {
private static final Log LOGGER = LogFactory.getLog(PermissionTests.class);
private DefaultPermissionFactory permissionFactory;
@Before
public void createPermissionfactory() {
permissionFactory = new DefaultPermissionFactory();
}
@Test
public void basePermissionTest() {
Permission p = permissionFactory.buildFromName("WRITE");
assertNotNull(p);
}
//~ Methods ========================================================================================================
@Test
public void expectedIntegerValues() {
@@ -54,16 +45,14 @@ public class PermissionTests {
@Test
public void fromInteger() {
Permission permission = permissionFactory.buildFromMask(7);
Permission permission = BasePermission.buildFromMask(7);
System.out.println("7 = " + permission.toString());
permission = permissionFactory.buildFromMask(4);
permission = BasePermission.buildFromMask(4);
System.out.println("4 = " + permission.toString());
}
@Test
public void stringConversion() {
permissionFactory.registerPublicPermissions(SpecialPermission.class);
System.out.println("R = " + BasePermission.READ.toString());
assertEquals("BasePermission[...............................R=1]", BasePermission.READ.toString());
@@ -14,19 +14,27 @@
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.Permission;
/**
* A test permission.
*
*
* @author Ben Alex
* @version $Id$
*/
public class SpecialPermission extends BasePermission {
public static final Permission ENTER = new SpecialPermission(1 << 5, 'E'); // 32
public static final Permission LEAVE = new SpecialPermission(1 << 6, 'L');
/**
* Registers the public static permissions defined on this class. This is mandatory so
* that the static methods will operate correctly.
*/
static {
registerPermissionsFor(SpecialPermission.class);
}
protected SpecialPermission(int mask, char code) {
super(mask, code);
super(mask, code);
}
}
@@ -0,0 +1,209 @@
package org.springframework.security.acls.jdbc;
import java.io.IOException;
import junit.framework.TestCase;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import org.springframework.cache.ehcache.EhCacheFactoryBean;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.security.Authentication;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.acls.MutableAcl;
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
import org.springframework.security.acls.domain.AclImpl;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.domain.ConsoleAuditLogger;
import org.springframework.security.acls.objectidentity.ObjectIdentityImpl;
import org.springframework.security.acls.sid.GrantedAuthoritySid;
import org.springframework.security.acls.sid.PrincipalSid;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
public class AclPermissionInheritanceTests extends TestCase {
private JdbcMutableAclService aclService;
private JdbcTemplate jdbcTemplate;
private DriverManagerDataSource dataSource;
private DataSourceTransactionManager txManager;
private TransactionStatus txStatus;
protected void setUp() throws Exception {
dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:mem:permissiontest");
dataSource.setUsername("sa");
dataSource.setPassword("");
jdbcTemplate = new JdbcTemplate(dataSource);
txManager = new DataSourceTransactionManager();
txManager.setDataSource(dataSource);
txStatus = txManager.getTransaction(new DefaultTransactionDefinition());
aclService = createAclService(dataSource);
Authentication auth = new UsernamePasswordAuthenticationToken(
"system", "secret", new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_IGNORED")});
SecurityContextHolder.getContext().setAuthentication(auth);
}
protected void tearDown() throws Exception {
txManager.rollback(txStatus);
SecurityContextHolder.clearContext();
}
public void test1() throws Exception {
createAclSchema(jdbcTemplate);
ObjectIdentityImpl rootObject =
new ObjectIdentityImpl(TestDomainObject.class, new Long(1));
MutableAcl parent = aclService.createAcl(rootObject);
MutableAcl child = aclService.createAcl(new ObjectIdentityImpl(TestDomainObject.class, new Long(2)));
child.setParent(parent);
aclService.updateAcl(child);
parent = (AclImpl) aclService.readAclById(rootObject);
parent.insertAce(0, BasePermission.READ,
new PrincipalSid("john"), true);
aclService.updateAcl(parent);
parent = (AclImpl) aclService.readAclById(rootObject);
parent.insertAce(1, BasePermission.READ,
new PrincipalSid("joe"), true);
aclService.updateAcl(parent);
child = (MutableAcl) aclService.readAclById(
new ObjectIdentityImpl(TestDomainObject.class, new Long(2)));
parent = (MutableAcl) child.getParentAcl();
assertEquals("Fails because child has a stale reference to its parent",
2, parent.getEntries().length);
assertEquals(1, parent.getEntries()[0].getPermission().getMask());
assertEquals(new PrincipalSid("john"), parent.getEntries()[0].getSid());
assertEquals(1, parent.getEntries()[1].getPermission().getMask());
assertEquals(new PrincipalSid("joe"), parent.getEntries()[1].getSid());
}
public void test2() throws Exception {
createAclSchema(jdbcTemplate);
ObjectIdentityImpl rootObject =
new ObjectIdentityImpl(TestDomainObject.class, new Long(1));
MutableAcl parent = aclService.createAcl(rootObject);
MutableAcl child = aclService.createAcl(new ObjectIdentityImpl(TestDomainObject.class, new Long(2)));
child.setParent(parent);
aclService.updateAcl(child);
parent.insertAce(0, BasePermission.ADMINISTRATION,
new GrantedAuthoritySid("ROLE_ADMINISTRATOR"), true);
aclService.updateAcl(parent);
parent.insertAce(1, BasePermission.DELETE, new PrincipalSid("terry"), true);
aclService.updateAcl(parent);
child = (MutableAcl) aclService.readAclById(
new ObjectIdentityImpl(TestDomainObject.class, new Long(2)));
parent = (MutableAcl) child.getParentAcl();
assertEquals(2, parent.getEntries().length);
assertEquals(16, parent.getEntries()[0].getPermission().getMask());
assertEquals(new GrantedAuthoritySid("ROLE_ADMINISTRATOR"), parent.getEntries()[0].getSid());
assertEquals(8, parent.getEntries()[1].getPermission().getMask());
assertEquals(new PrincipalSid("terry"), parent.getEntries()[1].getSid());
}
private JdbcMutableAclService createAclService(DriverManagerDataSource ds)
throws IOException {
GrantedAuthorityImpl adminAuthority = new GrantedAuthorityImpl("ROLE_ADMINISTRATOR");
AclAuthorizationStrategyImpl authStrategy = new AclAuthorizationStrategyImpl(
new GrantedAuthorityImpl[]{adminAuthority,adminAuthority,adminAuthority});
EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ehCacheManagerFactoryBean.afterPropertiesSet();
CacheManager cacheManager = (CacheManager) ehCacheManagerFactoryBean.getObject();
EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
ehCacheFactoryBean.setCacheName("aclAche");
ehCacheFactoryBean.setCacheManager(cacheManager);
ehCacheFactoryBean.afterPropertiesSet();
Ehcache ehCache = (Ehcache) ehCacheFactoryBean.getObject();
AclCache aclAche = new EhCacheBasedAclCache(ehCache);
BasicLookupStrategy lookupStrategy =
new BasicLookupStrategy(ds, aclAche, authStrategy, new ConsoleAuditLogger());
return new JdbcMutableAclService(ds,lookupStrategy, aclAche);
}
private void createAclSchema(JdbcTemplate jdbcTemplate) {
jdbcTemplate.execute("DROP TABLE ACL_ENTRY IF EXISTS;");
jdbcTemplate.execute("DROP TABLE ACL_OBJECT_IDENTITY IF EXISTS;");
jdbcTemplate.execute("DROP TABLE ACL_CLASS IF EXISTS");
jdbcTemplate.execute("DROP TABLE ACL_SID IF EXISTS");
jdbcTemplate.execute(
"CREATE TABLE ACL_SID(" +
"ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," +
"PRINCIPAL BOOLEAN NOT NULL," +
"SID VARCHAR_IGNORECASE(100) NOT NULL," +
"CONSTRAINT UNIQUE_UK_1 UNIQUE(SID,PRINCIPAL));");
jdbcTemplate.execute(
"CREATE TABLE ACL_CLASS(" +
"ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," +
"CLASS VARCHAR_IGNORECASE(100) NOT NULL," +
"CONSTRAINT UNIQUE_UK_2 UNIQUE(CLASS));");
jdbcTemplate.execute(
"CREATE TABLE ACL_OBJECT_IDENTITY(" +
"ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," +
"OBJECT_ID_CLASS BIGINT NOT NULL," +
"OBJECT_ID_IDENTITY BIGINT NOT NULL," +
"PARENT_OBJECT BIGINT," +
"OWNER_SID BIGINT," +
"ENTRIES_INHERITING BOOLEAN NOT NULL," +
"CONSTRAINT UNIQUE_UK_3 UNIQUE(OBJECT_ID_CLASS,OBJECT_ID_IDENTITY)," +
"CONSTRAINT FOREIGN_FK_1 FOREIGN KEY(PARENT_OBJECT)REFERENCES ACL_OBJECT_IDENTITY(ID)," +
"CONSTRAINT FOREIGN_FK_2 FOREIGN KEY(OBJECT_ID_CLASS)REFERENCES ACL_CLASS(ID)," +
"CONSTRAINT FOREIGN_FK_3 FOREIGN KEY(OWNER_SID)REFERENCES ACL_SID(ID));");
jdbcTemplate.execute(
"CREATE TABLE ACL_ENTRY(" +
"ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY," +
"ACL_OBJECT_IDENTITY BIGINT NOT NULL,ACE_ORDER INT NOT NULL,SID BIGINT NOT NULL," +
"MASK INTEGER NOT NULL,GRANTING BOOLEAN NOT NULL,AUDIT_SUCCESS BOOLEAN NOT NULL," +
"AUDIT_FAILURE BOOLEAN NOT NULL,CONSTRAINT UNIQUE_UK_4 UNIQUE(ACL_OBJECT_IDENTITY,ACE_ORDER)," +
"CONSTRAINT FOREIGN_FK_4 FOREIGN KEY(ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY(ID)," +
"CONSTRAINT FOREIGN_FK_5 FOREIGN KEY(SID) REFERENCES ACL_SID(ID));");
}
public static class TestDomainObject {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
}
@@ -1,312 +1,294 @@
package org.springframework.security.acls.jdbc;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import junit.framework.Assert;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.domain.ConsoleAuditLogger;
import org.springframework.security.acls.domain.DefaultPermissionFactory;
import org.springframework.security.acls.domain.EhCacheBasedAclCache;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AuditableAccessControlEntry;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.util.FileCopyUtils;
/**
* Tests {@link BasicLookupStrategy}
*
* @author Andrei Stefan
*/
public class BasicLookupStrategyTests {
private static final Sid BEN_SID = new PrincipalSid("ben");
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
//~ Instance fields ================================================================================================
private static JdbcTemplate jdbcTemplate;
private BasicLookupStrategy strategy;
private static SingleConnectionDataSource dataSource;
private static CacheManager cacheManager;
//~ Methods ========================================================================================================
@BeforeClass
public static void initCacheManaer() {
cacheManager = new CacheManager();
cacheManager.addCache(new Cache("basiclookuptestcache", 500, false, false, 30, 30));
}
@BeforeClass
public static void createDatabase() throws Exception {
dataSource = new SingleConnectionDataSource("jdbc:hsqldb:mem:lookupstrategytest", "sa", "", true);
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
jdbcTemplate = new JdbcTemplate(dataSource);
Resource resource = new ClassPathResource("createAclSchema.sql");
String sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
jdbcTemplate.execute(sql);
}
@AfterClass
public static void dropDatabase() throws Exception {
dataSource.destroy();
}
@AfterClass
public static void shutdownCacheManager() {
cacheManager.removalAll();
cacheManager.shutdown();
}
@Before
public void populateDatabase() {
String query = "INSERT INTO acl_sid(ID,PRINCIPAL,SID) VALUES (1,1,'ben');"
+ "INSERT INTO acl_class(ID,CLASS) VALUES (2,'" + TARGET_CLASS + "');"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (1,2,100,null,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (2,2,101,1,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (3,2,102,2,1,1);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (1,1,0,1,1,1,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (2,1,1,1,2,0,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (3,2,0,1,8,1,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (4,3,0,1,8,0,0,0);";
jdbcTemplate.execute(query);
}
@Before
public void initializeBeans() {
EhCacheBasedAclCache cache = new EhCacheBasedAclCache(getCache());
AclAuthorizationStrategy authorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_ADMINISTRATOR"), new GrantedAuthorityImpl("ROLE_ADMINISTRATOR"),
new GrantedAuthorityImpl("ROLE_ADMINISTRATOR") });
strategy = new BasicLookupStrategy(dataSource, cache, authorizationStrategy, new ConsoleAuditLogger());
strategy.setPermissionFactory(new DefaultPermissionFactory());
}
@After
public void emptyDatabase() {
String query = "DELETE FROM acl_entry;" + "DELETE FROM acl_object_identity WHERE ID = 7;"
+ "DELETE FROM acl_object_identity WHERE ID = 6;" + "DELETE FROM acl_object_identity WHERE ID = 5;"
+ "DELETE FROM acl_object_identity WHERE ID = 4;" + "DELETE FROM acl_object_identity WHERE ID = 3;"
+ "DELETE FROM acl_object_identity WHERE ID = 2;" + "DELETE FROM acl_object_identity WHERE ID = 1;"
+ "DELETE FROM acl_class;" + "DELETE FROM acl_sid;";
jdbcTemplate.execute(query);
}
private Ehcache getCache() {
Ehcache cache = cacheManager.getCache("basiclookuptestcache");
cache.removeAll();
return cache;
}
@Test
public void testAclsRetrievalWithDefaultBatchSize() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(101));
// Deliberately use an integer for the child, to reproduce bug report in SEC-819
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Integer(102));
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
@Test
public void testAclsRetrievalFromCacheOnly() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Integer(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(101));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102));
// Objects were put in cache
strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
// Let's empty the database to force acls retrieval from cache
emptyDatabase();
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
@Test
public void testAclsRetrievalWithCustomBatchSize() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Integer(101));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102));
// Set a batch size to allow multiple database queries in order to retrieve all acls
((BasicLookupStrategy) this.strategy).setBatchSize(1);
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
private void checkEntries(ObjectIdentity topParentOid, ObjectIdentity middleParentOid, ObjectIdentity childOid,
Map<ObjectIdentity, Acl> map) throws Exception {
Assert.assertEquals(3, map.size());
MutableAcl topParent = (MutableAcl) map.get(topParentOid);
MutableAcl middleParent = (MutableAcl) map.get(middleParentOid);
MutableAcl child = (MutableAcl) map.get(childOid);
// Check the retrieved versions has IDs
Assert.assertNotNull(topParent.getId());
Assert.assertNotNull(middleParent.getId());
Assert.assertNotNull(child.getId());
// Check their parents were correctly retrieved
Assert.assertNull(topParent.getParentAcl());
Assert.assertEquals(topParentOid, middleParent.getParentAcl().getObjectIdentity());
Assert.assertEquals(middleParentOid, child.getParentAcl().getObjectIdentity());
// Check their ACEs were correctly retrieved
Assert.assertEquals(2, topParent.getEntries().size());
Assert.assertEquals(1, middleParent.getEntries().size());
Assert.assertEquals(1, child.getEntries().size());
// Check object identities were correctly retrieved
Assert.assertEquals(topParentOid, topParent.getObjectIdentity());
Assert.assertEquals(middleParentOid, middleParent.getObjectIdentity());
Assert.assertEquals(childOid, child.getObjectIdentity());
// Check each entry
Assert.assertTrue(topParent.isEntriesInheriting());
Assert.assertEquals(topParent.getId(), new Long(1));
Assert.assertEquals(topParent.getOwner(), new PrincipalSid("ben"));
Assert.assertEquals(topParent.getEntries().get(0).getId(), new Long(1));
Assert.assertEquals(topParent.getEntries().get(0).getPermission(), BasePermission.READ);
Assert.assertEquals(topParent.getEntries().get(0).getSid(), new PrincipalSid("ben"));
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditSuccess());
Assert.assertTrue(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isGranting());
Assert.assertEquals(topParent.getEntries().get(1).getId(), new Long(2));
Assert.assertEquals(topParent.getEntries().get(1).getPermission(), BasePermission.WRITE);
Assert.assertEquals(topParent.getEntries().get(1).getSid(), new PrincipalSid("ben"));
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditSuccess());
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isGranting());
Assert.assertTrue(middleParent.isEntriesInheriting());
Assert.assertEquals(middleParent.getId(), new Long(2));
Assert.assertEquals(middleParent.getOwner(), new PrincipalSid("ben"));
Assert.assertEquals(middleParent.getEntries().get(0).getId(), new Long(3));
Assert.assertEquals(middleParent.getEntries().get(0).getPermission(), BasePermission.DELETE);
Assert.assertEquals(middleParent.getEntries().get(0).getSid(), new PrincipalSid("ben"));
Assert.assertFalse(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditSuccess());
Assert.assertTrue(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isGranting());
Assert.assertTrue(child.isEntriesInheriting());
Assert.assertEquals(child.getId(), new Long(3));
Assert.assertEquals(child.getOwner(), new PrincipalSid("ben"));
Assert.assertEquals(child.getEntries().get(0).getId(), new Long(4));
Assert.assertEquals(child.getEntries().get(0).getPermission(), BasePermission.DELETE);
Assert.assertEquals(child.getEntries().get(0).getSid(), new PrincipalSid("ben"));
Assert.assertFalse(((AuditableAccessControlEntry) child.getEntries().get(0)).isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) child.getEntries().get(0)).isAuditSuccess());
Assert.assertFalse((child.getEntries().get(0)).isGranting());
}
@Test
public void testAllParentsAreRetrievedWhenChildIsLoaded() throws Exception {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,103,1,1,1);";
jdbcTemplate.execute(query);
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Integer(101));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102));
ObjectIdentity middleParent2Oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(103));
// Retrieve the child
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(Arrays.asList(childOid), null);
// Check that the child and all its parents were retrieved
Assert.assertNotNull(map.get(childOid));
Assert.assertEquals(childOid, ((Acl) map.get(childOid)).getObjectIdentity());
Assert.assertNotNull(map.get(middleParentOid));
Assert.assertEquals(middleParentOid, ((Acl) map.get(middleParentOid)).getObjectIdentity());
Assert.assertNotNull(map.get(topParentOid));
Assert.assertEquals(topParentOid, ((Acl) map.get(topParentOid)).getObjectIdentity());
// The second parent shouldn't have been retrieved
Assert.assertNull(map.get(middleParent2Oid));
}
/**
* Test created from SEC-590.
*/
@Test
public void testReadAllObjectIdentitiesWhenLastElementIsAlreadyCached() throws Exception {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,104,null,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (5,2,105,4,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (6,2,106,4,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (7,2,107,5,1,1);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (5,4,0,1,1,1,0,0)";
jdbcTemplate.execute(query);
ObjectIdentity grandParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(104));
ObjectIdentity parent1Oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(105));
ObjectIdentity parent2Oid = new ObjectIdentityImpl(TARGET_CLASS, new Integer(106));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Integer(107));
// First lookup only child, thus populating the cache with grandParent, parent1 and child
List<Permission> checkPermission = Arrays.asList(BasePermission.READ);
List<Sid> sids = Arrays.asList(BEN_SID);
List<ObjectIdentity> childOids = Arrays.asList(childOid);
strategy.setBatchSize(6);
Map<ObjectIdentity, Acl> foundAcls = strategy.readAclsById(childOids, sids);
Acl foundChildAcl = (Acl) foundAcls.get(childOid);
Assert.assertNotNull(foundChildAcl);
Assert.assertTrue(foundChildAcl.isGranted(checkPermission, sids, false));
// Search for object identities has to be done in the following order: last element have to be one which
// is already in cache and the element before it must not be stored in cache
List<ObjectIdentity> allOids = Arrays.asList(grandParentOid, parent1Oid, parent2Oid, childOid);
try {
foundAcls = strategy.readAclsById(allOids, sids);
Assert.assertTrue(true);
} catch (NotFoundException notExpected) {
Assert.fail("It shouldn't have thrown NotFoundException");
}
Acl foundParent2Acl = (Acl) foundAcls.get(parent2Oid);
Assert.assertNotNull(foundParent2Acl);
Assert.assertTrue(foundParent2Acl.isGranted(checkPermission, sids, false));
}
@Test(expected=IllegalArgumentException.class)
public void nullOwnerIsNotSupported() {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,104,null,null,1);";
jdbcTemplate.execute(query);
ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(104));
strategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID));
}
}
package org.springframework.security.acls.jdbc;
import java.util.Map;
import junit.framework.Assert;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.TestDataSource;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.AuditableAccessControlEntry;
import org.springframework.security.acls.MutableAcl;
import org.springframework.security.acls.NotFoundException;
import org.springframework.security.acls.Permission;
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.domain.ConsoleAuditLogger;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.objectidentity.ObjectIdentityImpl;
import org.springframework.security.acls.sid.PrincipalSid;
import org.springframework.security.acls.sid.Sid;
import org.springframework.util.FileCopyUtils;
/**
* Tests {@link BasicLookupStrategy}
*
* @author Andrei Stefan
*/
public class BasicLookupStrategyTests {
//~ Instance fields ================================================================================================
private static JdbcTemplate jdbcTemplate;
private LookupStrategy strategy;
private static TestDataSource dataSource;
private static CacheManager cacheManager;
//~ Methods ========================================================================================================
@BeforeClass
public static void initCacheManaer() {
cacheManager = new CacheManager();
cacheManager.addCache(new Cache("basiclookuptestcache", 500, false, false, 30, 30));
}
@BeforeClass
public static void createDatabase() throws Exception {
dataSource = new TestDataSource("lookupstrategytest");
jdbcTemplate = new JdbcTemplate(dataSource);
Resource resource = new ClassPathResource("org/springframework/security/acls/jdbc/testData.sql");
String sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
jdbcTemplate.execute(sql);
}
@AfterClass
public static void dropDatabase() throws Exception {
dataSource.destroy();
}
@AfterClass
public static void shutdownCacheManager() {
cacheManager.removalAll();
cacheManager.shutdown();
}
@Before
public void populateDatabase() {
String query = "INSERT INTO acl_sid(ID,PRINCIPAL,SID) VALUES (1,1,'ben');"
+ "INSERT INTO acl_class(ID,CLASS) VALUES (2,'org.springframework.security.TargetObject');"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (1,2,100,null,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (2,2,101,1,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (3,2,102,2,1,1);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (1,1,0,1,1,1,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (2,1,1,1,2,0,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (3,2,0,1,8,1,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (4,3,0,1,8,0,0,0);";
jdbcTemplate.execute(query);
}
@Before
public void initializeBeans() {
EhCacheBasedAclCache cache = new EhCacheBasedAclCache(getCache());
AclAuthorizationStrategy authorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_ADMINISTRATOR"), new GrantedAuthorityImpl("ROLE_ADMINISTRATOR"),
new GrantedAuthorityImpl("ROLE_ADMINISTRATOR") });
strategy = new BasicLookupStrategy(dataSource, cache, authorizationStrategy, new ConsoleAuditLogger());
}
@After
public void emptyDatabase() {
String query = "DELETE FROM acl_entry;" + "DELETE FROM acl_object_identity WHERE ID = 7;"
+ "DELETE FROM acl_object_identity WHERE ID = 6;" + "DELETE FROM acl_object_identity WHERE ID = 5;"
+ "DELETE FROM acl_object_identity WHERE ID = 4;" + "DELETE FROM acl_object_identity WHERE ID = 3;"
+ "DELETE FROM acl_object_identity WHERE ID = 2;" + "DELETE FROM acl_object_identity WHERE ID = 1;"
+ "DELETE FROM acl_class;" + "DELETE FROM acl_sid;";
jdbcTemplate.execute(query);
}
private Ehcache getCache() {
Ehcache cache = cacheManager.getCache("basiclookuptestcache");
cache.removeAll();
return cache;
}
@Test
public void testAclsRetrievalWithDefaultBatchSize() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
// Deliberately use an integer for the child, to reproduce bug report in SEC-819
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(102));
Map map = this.strategy.readAclsById(new ObjectIdentity[] { topParentOid, middleParentOid, childOid }, null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
@Test
public void testAclsRetrievalFromCacheOnly() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(102));
// Objects were put in cache
strategy.readAclsById(new ObjectIdentity[] { topParentOid, middleParentOid, childOid }, null);
// Let's empty the database to force acls retrieval from cache
emptyDatabase();
Map map = this.strategy.readAclsById(new ObjectIdentity[] { topParentOid, middleParentOid, childOid }, null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
@Test
public void testAclsRetrievalWithCustomBatchSize() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(102));
// Set a batch size to allow multiple database queries in order to retrieve all acls
((BasicLookupStrategy) this.strategy).setBatchSize(1);
Map map = this.strategy.readAclsById(new ObjectIdentity[] { topParentOid, middleParentOid, childOid }, null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
private void checkEntries(ObjectIdentity topParentOid, ObjectIdentity middleParentOid, ObjectIdentity childOid, Map map)
throws Exception {
Assert.assertEquals(3, map.size());
MutableAcl topParent = (MutableAcl) map.get(topParentOid);
MutableAcl middleParent = (MutableAcl) map.get(middleParentOid);
MutableAcl child = (MutableAcl) map.get(childOid);
// Check the retrieved versions has IDs
Assert.assertNotNull(topParent.getId());
Assert.assertNotNull(middleParent.getId());
Assert.assertNotNull(child.getId());
// Check their parents were correctly retrieved
Assert.assertNull(topParent.getParentAcl());
Assert.assertEquals(topParentOid, middleParent.getParentAcl().getObjectIdentity());
Assert.assertEquals(middleParentOid, child.getParentAcl().getObjectIdentity());
// Check their ACEs were correctly retrieved
Assert.assertEquals(2, topParent.getEntries().length);
Assert.assertEquals(1, middleParent.getEntries().length);
Assert.assertEquals(1, child.getEntries().length);
// Check object identities were correctly retrieved
Assert.assertEquals(topParentOid, topParent.getObjectIdentity());
Assert.assertEquals(middleParentOid, middleParent.getObjectIdentity());
Assert.assertEquals(childOid, child.getObjectIdentity());
// Check each entry
Assert.assertTrue(topParent.isEntriesInheriting());
Assert.assertEquals(topParent.getId(), new Long(1));
Assert.assertEquals(topParent.getOwner(), new PrincipalSid("ben"));
Assert.assertEquals(topParent.getEntries()[0].getId(), new Long(1));
Assert.assertEquals(topParent.getEntries()[0].getPermission(), BasePermission.READ);
Assert.assertEquals(topParent.getEntries()[0].getSid(), new PrincipalSid("ben"));
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries()[0]).isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries()[0]).isAuditSuccess());
Assert.assertTrue(((AuditableAccessControlEntry) topParent.getEntries()[0]).isGranting());
Assert.assertEquals(topParent.getEntries()[1].getId(), new Long(2));
Assert.assertEquals(topParent.getEntries()[1].getPermission(), BasePermission.WRITE);
Assert.assertEquals(topParent.getEntries()[1].getSid(), new PrincipalSid("ben"));
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries()[1]).isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries()[1]).isAuditSuccess());
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries()[1]).isGranting());
Assert.assertTrue(middleParent.isEntriesInheriting());
Assert.assertEquals(middleParent.getId(), new Long(2));
Assert.assertEquals(middleParent.getOwner(), new PrincipalSid("ben"));
Assert.assertEquals(middleParent.getEntries()[0].getId(), new Long(3));
Assert.assertEquals(middleParent.getEntries()[0].getPermission(), BasePermission.DELETE);
Assert.assertEquals(middleParent.getEntries()[0].getSid(), new PrincipalSid("ben"));
Assert.assertFalse(((AuditableAccessControlEntry) middleParent.getEntries()[0]).isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) middleParent.getEntries()[0]).isAuditSuccess());
Assert.assertTrue(((AuditableAccessControlEntry) middleParent.getEntries()[0]).isGranting());
Assert.assertTrue(child.isEntriesInheriting());
Assert.assertEquals(child.getId(), new Long(3));
Assert.assertEquals(child.getOwner(), new PrincipalSid("ben"));
Assert.assertEquals(child.getEntries()[0].getId(), new Long(4));
Assert.assertEquals(child.getEntries()[0].getPermission(), BasePermission.DELETE);
Assert.assertEquals(child.getEntries()[0].getSid(), new PrincipalSid("ben"));
Assert.assertFalse(((AuditableAccessControlEntry) child.getEntries()[0]).isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) child.getEntries()[0]).isAuditSuccess());
Assert.assertFalse((child.getEntries()[0]).isGranting());
}
@Test
public void testAllParentsAreRetrievedWhenChildIsLoaded() throws Exception {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,103,1,1,1);";
jdbcTemplate.execute(query);
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(102));
ObjectIdentity middleParent2Oid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(103));
// Retrieve the child
Map map = this.strategy.readAclsById(new ObjectIdentity[] { childOid }, null);
// Check that the child and all its parents were retrieved
Assert.assertNotNull(map.get(childOid));
Assert.assertEquals(childOid, ((Acl) map.get(childOid)).getObjectIdentity());
Assert.assertNotNull(map.get(middleParentOid));
Assert.assertEquals(middleParentOid, ((Acl) map.get(middleParentOid)).getObjectIdentity());
Assert.assertNotNull(map.get(topParentOid));
Assert.assertEquals(topParentOid, ((Acl) map.get(topParentOid)).getObjectIdentity());
// The second parent shouldn't have been retrieved
Assert.assertNull(map.get(middleParent2Oid));
}
/**
* Test created from SEC-590.
*/
@Test
public void testReadAllObjectIdentitiesWhenLastElementIsAlreadyCached() throws Exception {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,104,null,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (5,2,105,4,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (6,2,106,4,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (7,2,107,5,1,1);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (5,4,0,1,1,1,0,0)";
jdbcTemplate.execute(query);
ObjectIdentity grandParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(104));
ObjectIdentity parent1Oid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(105));
ObjectIdentity parent2Oid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(106));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(107));
// First lookup only child, thus populating the cache with grandParent, parent1 and child
Permission[] checkPermission = new Permission[] { BasePermission.READ };
Sid[] sids = new Sid[] { new PrincipalSid("ben") };
ObjectIdentity[] childOids = new ObjectIdentity[] { childOid };
((BasicLookupStrategy) this.strategy).setBatchSize(6);
Map foundAcls = strategy.readAclsById(childOids, sids);
Acl foundChildAcl = (Acl) foundAcls.get(childOid);
Assert.assertNotNull(foundChildAcl);
Assert.assertTrue(foundChildAcl.isGranted(checkPermission, sids, false));
// Search for object identities has to be done in the following order: last element have to be one which
// is already in cache and the element before it must not be stored in cache
ObjectIdentity[] allOids = new ObjectIdentity[] { grandParentOid, parent1Oid, parent2Oid, childOid };
try {
foundAcls = strategy.readAclsById(allOids, sids);
Assert.assertTrue(true);
} catch (NotFoundException notExpected) {
Assert.fail("It shouldn't have thrown NotFoundException");
}
Acl foundParent2Acl = (Acl) foundAcls.get(parent2Oid);
Assert.assertNotNull(foundParent2Acl);
Assert.assertTrue(foundParent2Acl.isGranted(checkPermission, sids, false));
}
}
@@ -27,9 +27,10 @@ import javax.sql.DataSource;
/**
* Seeds the database for {@link JdbcMutableAclServiceTests}.
* Seeds the database for {@link JdbcAclServiceTests}.
*
* @author Ben Alex
* @version $Id$
*/
public class DatabaseSeeder {
//~ Constructors ===================================================================================================
@@ -1,266 +1,248 @@
package org.springframework.security.acls.jdbc;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Map;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
import org.springframework.security.acls.domain.AclImpl;
import org.springframework.security.acls.domain.ConsoleAuditLogger;
import org.springframework.security.acls.domain.EhCacheBasedAclCache;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.util.FieldUtils;
/**
* Tests {@link EhCacheBasedAclCache}
*
* @author Andrei Stefan
*/
public class EhCacheBasedAclCacheTests {
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
private static CacheManager cacheManager;
@BeforeClass
public static void initCacheManaer() {
cacheManager = new CacheManager();
// Use disk caching immediately (to test for serialization issue reported in SEC-527)
cacheManager.addCache(new Cache("ehcachebasedacltests", 0, true, false, 600, 300));
}
@AfterClass
public static void shutdownCacheManager() {
cacheManager.removalAll();
cacheManager.shutdown();
}
@After
public void clearContext() {
SecurityContextHolder.clearContext();
}
private Ehcache getCache() {
Ehcache cache = cacheManager.getCache("ehcachebasedacltests");
cache.removeAll();
return cache;
}
@Test(expected=IllegalArgumentException.class)
public void constructorRejectsNullParameters() throws Exception {
new EhCacheBasedAclCache(null);
}
@Test
public void methodsRejectNullParameters() throws Exception {
Ehcache cache = new MockEhcache();
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
try {
Serializable id = null;
myCache.evictFromCache(id);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
ObjectIdentity obj = null;
myCache.evictFromCache(obj);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
Serializable id = null;
myCache.getFromCache(id);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
ObjectIdentity obj = null;
myCache.getFromCache(obj);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
MutableAcl acl = null;
myCache.putInCache(acl);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
// SEC-527
@Test
public void testDiskSerializationOfMutableAclObjectInstance() throws Exception {
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
MutableAcl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
// Serialization test
File file = File.createTempFile("SEC_TEST", ".object");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(acl);
oos.close();
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
MutableAcl retrieved = (MutableAcl) ois.readObject();
ois.close();
assertEquals(acl, retrieved);
Object retrieved1 = FieldUtils.getProtectedFieldValue("aclAuthorizationStrategy", retrieved);
assertEquals(null, retrieved1);
Object retrieved2 = FieldUtils.getProtectedFieldValue("auditLogger", retrieved);
assertEquals(null, retrieved2);
}
@Test
public void cacheOperationsAclWithoutParent() throws Exception {
Ehcache cache = getCache();
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
MutableAcl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
assertEquals(0, cache.getDiskStoreSize());
myCache.putInCache(acl);
assertEquals(cache.getSize(), 2);
assertEquals(2, cache.getDiskStoreSize());
assertTrue(cache.isElementOnDisk(acl.getObjectIdentity()));
assertFalse(cache.isElementInMemory(acl.getObjectIdentity()));
// Check we can get from cache the same objects we put in
assertEquals(myCache.getFromCache(new Long(1)), acl);
assertEquals(myCache.getFromCache(identity), acl);
// Put another object in cache
ObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, new Long(101));
MutableAcl acl2 = new AclImpl(identity2, new Long(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
myCache.putInCache(acl2);
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
// Try to evict an entry that doesn't exist
myCache.evictFromCache(new Long(3));
myCache.evictFromCache(new ObjectIdentityImpl(TARGET_CLASS, new Long(102)));
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
myCache.evictFromCache(new Long(1));
assertEquals(cache.getSize(), 2);
assertEquals(2, cache.getDiskStoreSize());
// Check the second object inserted
assertEquals(myCache.getFromCache(new Long(2)), acl2);
assertEquals(myCache.getFromCache(identity2), acl2);
myCache.evictFromCache(identity2);
assertEquals(cache.getSize(), 0);
}
@SuppressWarnings("unchecked")
@Test
public void cacheOperationsAclWithParent() throws Exception {
Ehcache cache = getCache();
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
Authentication auth = new TestingAuthenticationToken("user", "password", new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_GENERAL") });
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(1));
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS, new Long(2));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
MutableAcl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
MutableAcl parentAcl = new AclImpl(identityParent, new Long(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
acl.setParent(parentAcl);
assertEquals(0, cache.getDiskStoreSize());
myCache.putInCache(acl);
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
assertTrue(cache.isElementOnDisk(acl.getObjectIdentity()));
assertTrue(cache.isElementOnDisk(Long.valueOf(1)));
assertFalse(cache.isElementInMemory(acl.getObjectIdentity()));
assertFalse(cache.isElementInMemory(Long.valueOf(1)));
cache.flush();
// Wait for the spool to be written to disk (it's asynchronous)
Map spool = (Map) FieldUtils.getFieldValue(cache, "diskStore.spool");
while(spool.size() > 0) {
Thread.sleep(50);
}
// Check we can get from cache the same objects we put in
AclImpl aclFromCache = (AclImpl) myCache.getFromCache(new Long(1));
// For the checks on transient fields, we need to be sure that the object is being loaded from the cache,
// not from the ehcache spool or elsewhere...
assertFalse(acl == aclFromCache);
assertEquals(acl, aclFromCache);
// SEC-951 check transient fields are set on parent
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "aclAuthorizationStrategy"));
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "auditLogger"));
assertEquals(acl, myCache.getFromCache(identity));
assertNotNull(FieldUtils.getFieldValue(aclFromCache, "aclAuthorizationStrategy"));
AclImpl parentAclFromCache = (AclImpl) myCache.getFromCache(new Long(2));
assertEquals(parentAcl, parentAclFromCache);
assertNotNull(FieldUtils.getFieldValue(parentAclFromCache, "aclAuthorizationStrategy"));
assertEquals(parentAcl, myCache.getFromCache(identityParent));
}
//~ Inner Classes ==================================================================================================
private class MockEhcache extends Cache {
public MockEhcache() {
super("cache", 0, true, true, 0, 0);
}
}
}
package org.springframework.security.acls.jdbc;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.security.Authentication;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.acls.MutableAcl;
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
import org.springframework.security.acls.domain.AclImpl;
import org.springframework.security.acls.domain.ConsoleAuditLogger;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.objectidentity.ObjectIdentityImpl;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.TestingAuthenticationToken;
import org.springframework.security.util.FieldUtils;
/**
* Tests {@link EhCacheBasedAclCache}
*
* @author Andrei Stefan
*/
public class EhCacheBasedAclCacheTests {
//~ Instance fields ================================================================================================
private static CacheManager cacheManager;
//~ Methods ========================================================================================================
@BeforeClass
public static void initCacheManaer() {
cacheManager = new CacheManager();
// Use disk caching immediately (to test for serialization issue reported in SEC-527)
cacheManager.addCache(new Cache("ehcachebasedacltests", 0, true, false, 30, 30));
}
@AfterClass
public static void shutdownCacheManager() {
cacheManager.removalAll();
cacheManager.shutdown();
}
@After
public void clearContext() {
SecurityContextHolder.clearContext();
}
private Ehcache getCache() {
Ehcache cache = cacheManager.getCache("ehcachebasedacltests");
cache.removeAll();
return cache;
}
@Test(expected=IllegalArgumentException.class)
public void constructorRejectsNullParameters() throws Exception {
AclCache aclCache = new EhCacheBasedAclCache(null);
fail("It should have thrown IllegalArgumentException");
}
@Test
public void methodsRejectNullParameters() throws Exception {
Ehcache cache = new MockEhcache();
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
try {
Serializable id = null;
myCache.evictFromCache(id);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
ObjectIdentity obj = null;
myCache.evictFromCache(obj);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
Serializable id = null;
myCache.getFromCache(id);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
ObjectIdentity obj = null;
myCache.getFromCache(obj);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
MutableAcl acl = null;
myCache.putInCache(acl);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
// SEC-527
@Test
public void testDiskSerializationOfMutableAclObjectInstance() throws Exception {
ObjectIdentity identity = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
MutableAcl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
// Serialization test
File file = File.createTempFile("SEC_TEST", ".object");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(acl);
oos.close();
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
MutableAcl retrieved = (MutableAcl) ois.readObject();
ois.close();
assertEquals(acl, retrieved);
Object retrieved1 = FieldUtils.getProtectedFieldValue("aclAuthorizationStrategy", retrieved);
assertEquals(null, retrieved1);
Object retrieved2 = FieldUtils.getProtectedFieldValue("auditLogger", retrieved);
assertEquals(null, retrieved2);
}
@Test
public void cacheOperationsAclWithoutParent() throws Exception {
Ehcache cache = getCache();
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
ObjectIdentity identity = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
MutableAcl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
assertEquals(0, cache.getDiskStoreSize());
myCache.putInCache(acl);
assertEquals(cache.getSize(), 2);
assertEquals(2, cache.getDiskStoreSize());
assertTrue(cache.isElementOnDisk(acl.getObjectIdentity()));
assertFalse(cache.isElementInMemory(acl.getObjectIdentity()));
// Check we can get from cache the same objects we put in
assertEquals(myCache.getFromCache(new Long(1)), acl);
assertEquals(myCache.getFromCache(identity), acl);
// Put another object in cache
ObjectIdentity identity2 = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
MutableAcl acl2 = new AclImpl(identity2, new Long(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
myCache.putInCache(acl2);
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
// Try to evict an entry that doesn't exist
myCache.evictFromCache(new Long(3));
myCache.evictFromCache(new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(102)));
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
myCache.evictFromCache(new Long(1));
assertEquals(cache.getSize(), 2);
assertEquals(2, cache.getDiskStoreSize());
// Check the second object inserted
assertEquals(myCache.getFromCache(new Long(2)), acl2);
assertEquals(myCache.getFromCache(identity2), acl2);
myCache.evictFromCache(identity2);
assertEquals(cache.getSize(), 0);
}
@Test
public void cacheOperationsAclWithParent() throws Exception {
Ehcache cache = getCache();
EhCacheBasedAclCache myCache = new EhCacheBasedAclCache(cache);
Authentication auth = new TestingAuthenticationToken("user", "password", new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_GENERAL") });
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
ObjectIdentity identityParent = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_OWNERSHIP"), new GrantedAuthorityImpl("ROLE_AUDITING"),
new GrantedAuthorityImpl("ROLE_GENERAL") });
MutableAcl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
MutableAcl parentAcl = new AclImpl(identityParent, new Long(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
acl.setParent(parentAcl);
assertEquals(0, cache.getDiskStoreSize());
myCache.putInCache(acl);
assertEquals(cache.getSize(), 4);
assertEquals(4, cache.getDiskStoreSize());
assertTrue(cache.isElementOnDisk(acl.getObjectIdentity()));
assertFalse(cache.isElementInMemory(acl.getObjectIdentity()));
// Check we can get from cache the same objects we put in
assertEquals(myCache.getFromCache(new Long(1)), acl);
assertEquals(myCache.getFromCache(identity), acl);
assertEquals(myCache.getFromCache(new Long(2)), parentAcl);
assertEquals(myCache.getFromCache(identityParent), parentAcl);
}
//~ Inner Classes ==================================================================================================
private class MockEhcache extends Cache {
public MockEhcache() {
super("cache", 0, true, true, 0, 0);
}
}
}
@@ -0,0 +1,415 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.jdbc;
import java.util.Map;
import org.springframework.security.Authentication;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.acls.AccessControlEntry;
import org.springframework.security.acls.Acl;
import org.springframework.security.acls.AlreadyExistsException;
import org.springframework.security.acls.ChildrenExistException;
import org.springframework.security.acls.MutableAcl;
import org.springframework.security.acls.NotFoundException;
import org.springframework.security.acls.Permission;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.objectidentity.ObjectIdentity;
import org.springframework.security.acls.objectidentity.ObjectIdentityImpl;
import org.springframework.security.acls.sid.PrincipalSid;
import org.springframework.security.acls.sid.Sid;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.TestingAuthenticationToken;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
/**
* Integration tests the ACL system using an in-memory database.
*
* @author Ben Alex
* @author Andrei Stefan
* @version $Id:JdbcAclServiceTests.java 1754 2006-11-17 02:01:21Z benalex $
*/
public class JdbcAclServiceTests extends AbstractTransactionalDataSourceSpringContextTests {
//~ Constant fields ================================================================================================
public static final String SELECT_ALL_CLASSES = "SELECT * FROM acl_class WHERE class = ?";
public static final String SELECT_ALL_OBJECT_IDENTITIES = "SELECT * FROM acl_object_identity";
public static final String SELECT_OBJECT_IDENTITY = "SELECT * FROM acl_object_identity WHERE object_id_identity = ?";
public static final String SELECT_ACL_ENTRY = "SELECT * FROM acl_entry, acl_object_identity WHERE " +
"acl_object_identity.id = acl_entry.acl_object_identity " +
"AND acl_object_identity.object_id_identity <= ?";
//~ Instance fields ================================================================================================
private JdbcMutableAclService jdbcMutableAclService;
private AclCache aclCache;
private LookupStrategy lookupStrategy;
//~ Methods ========================================================================================================
protected String[] getConfigLocations() {
return new String[] {"classpath:org/springframework/security/acls/jdbc/applicationContext-test.xml"};
}
public void setJdbcMutableAclService(JdbcMutableAclService jdbcAclService) {
this.jdbcMutableAclService = jdbcAclService;
}
public void setAclCache(AclCache aclCache) {
this.aclCache = aclCache;
}
public void setLookupStrategy(LookupStrategy lookupStrategy) {
this.lookupStrategy = lookupStrategy;
}
protected void onTearDown() throws Exception {
super.onTearDown();
SecurityContextHolder.clearContext();
}
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.springframework.security.TargetObject", new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(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(0, BasePermission.READ, new PrincipalSid(auth), true);
topParent.insertAce(1, BasePermission.WRITE, new PrincipalSid(auth), false);
middleParent.insertAce(0, BasePermission.DELETE, new PrincipalSid(auth), true);
child.insertAce(0, 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(1, BasePermission.DELETE, new PrincipalSid(auth), true);
// Let's also add another permission to the child
child.insertAce(2, 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(0);
// 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();
}
/**
* Test method that demonstrates eviction failure from cache - SEC-676
*/
public void testDeleteAclAlsoDeletesChildren() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(102));
// Check the childOid really is a child of middleParentOid
Acl childAcl = jdbcMutableAclService.readAclById(childOid);
assertEquals(middleParentOid, childAcl.getParentAcl().getObjectIdentity());
// Delete the mid-parent and test if the child was deleted, as well
jdbcMutableAclService.deleteAcl(middleParentOid, true);
try {
Acl acl = jdbcMutableAclService.readAclById(middleParentOid);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
try {
Acl acl = jdbcMutableAclService.readAclById(childOid);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
Acl acl = jdbcMutableAclService.readAclById(topParentOid);
assertNotNull(acl);
assertEquals(((MutableAcl) acl).getObjectIdentity(), topParentOid);
}
public void testConstructorRejectsNullParameters() throws Exception {
try {
JdbcAclService service = new JdbcMutableAclService(null, lookupStrategy, aclCache);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
JdbcAclService service = new JdbcMutableAclService(this.getJdbcTemplate().getDataSource(), null, aclCache);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
JdbcAclService service = new JdbcMutableAclService(this.getJdbcTemplate().getDataSource(), lookupStrategy, null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
public void testCreateAclRejectsNullParameter() throws Exception {
try {
jdbcMutableAclService.createAcl(null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
public void testCreateAclForADuplicateDomainObject() throws Exception {
ObjectIdentity duplicateOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
// Try to add the same object second time
try {
jdbcMutableAclService.createAcl(duplicateOid);
fail("It should have thrown AlreadyExistsException");
}
catch (AlreadyExistsException expected) {
assertTrue(true);
}
}
public void testDeleteAclRejectsNullParameters() throws Exception {
try {
jdbcMutableAclService.deleteAcl(null, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
public void testDeleteAclWithChildrenThrowsException() throws Exception {
try {
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
jdbcMutableAclService.setForeignKeysInDatabase(false); // switch on FK checking in the class, not database
jdbcMutableAclService.deleteAcl(topParentOid, false);
fail("It should have thrown ChildrenExistException");
}
catch (ChildrenExistException expected) {
assertTrue(true);
} finally {
jdbcMutableAclService.setForeignKeysInDatabase(true); // restore to the default
}
}
public void testDeleteAclRemovesRowsFromDatabase() throws Exception {
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ADMINISTRATOR")});
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity topParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(101));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Integer(102));
// Remove the child and check all related database rows were removed accordingly
jdbcMutableAclService.deleteAcl(childOid, false);
assertEquals(1, getJdbcTemplate().queryForList(SELECT_ALL_CLASSES, new Object[] {"org.springframework.security.TargetObject"} ).size());
assertEquals(0, getJdbcTemplate().queryForList(SELECT_OBJECT_IDENTITY, new Object[] {new Long(102)}).size());
assertEquals(2, getJdbcTemplate().queryForList(SELECT_ALL_OBJECT_IDENTITIES).size());
assertEquals(3, getJdbcTemplate().queryForList(SELECT_ACL_ENTRY, new Object[] {new Long(103)} ).size());
// Check the cache
assertNull(aclCache.getFromCache(childOid));
assertNull(aclCache.getFromCache(new Long(102)));
}
/**
* SEC-655
*/
/* public void testClearChildrenFromCacheWhenParentIsUpdated() throws Exception {
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ADMINISTRATOR")});
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity parentOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(104));
ObjectIdentity childOid = new ObjectIdentityImpl("org.springframework.security.TargetObject", new Long(105));
MutableAcl parent = jdbcMutableAclService.createAcl(parentOid);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
child.setParent(parent);
jdbcMutableAclService.updateAcl(child);
parent = (AclImpl) jdbcMutableAclService.readAclById(parentOid);
parent.insertAce(null, BasePermission.READ, new PrincipalSid("ben"), true);
jdbcMutableAclService.updateAcl(parent);
parent = (AclImpl) jdbcMutableAclService.readAclById(parentOid);
parent.insertAce(null, BasePermission.READ, new PrincipalSid("scott"), true);
jdbcMutableAclService.updateAcl(parent);
child = (MutableAcl) jdbcMutableAclService.readAclById(childOid);
parent = (MutableAcl) child.getParentAcl();
assertEquals("Fails because child has a stale reference to its parent", 2, parent.getEntries().length);
assertEquals(1, parent.getEntries()[0].getPermission().getMask());
assertEquals(new PrincipalSid("ben"), parent.getEntries()[0].getSid());
assertEquals(1, parent.getEntries()[1].getPermission().getMask());
assertEquals(new PrincipalSid("scott"), parent.getEntries()[1].getSid());
}*/
/* 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.springframework.security.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();
}
*/
}
@@ -1,493 +0,0 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.jdbc;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.acls.TargetObject;
import org.springframework.security.acls.domain.AclImpl;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.domain.CumulativePermission;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.AlreadyExistsException;
import org.springframework.security.acls.model.ChildrenExistException;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.transaction.annotation.Transactional;
/**
* Integration tests the ACL system using an in-memory database.
*
* @author Ben Alex
* @author Andrei Stefan
*/
@ContextConfiguration(locations={"/jdbcMutableAclServiceTests-context.xml"})
public class JdbcMutableAclServiceTests extends AbstractTransactionalJUnit4SpringContextTests {
//~ Constant fields ================================================================================================
private static final String TARGET_CLASS = TargetObject.class.getName();
private final Authentication auth = new TestingAuthenticationToken("ben", "ignored","ROLE_ADMINISTRATOR");
public static final String SELECT_ALL_CLASSES = "SELECT * FROM acl_class WHERE class = ?";
//~ Instance fields ================================================================================================
private final ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
private final ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
private final ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102));
@Autowired
private JdbcMutableAclService jdbcMutableAclService;
@Autowired
private AclCache aclCache;
@Autowired
private LookupStrategy lookupStrategy;
@Autowired
private DataSource dataSource;
@Autowired
private JdbcTemplate jdbcTemplate;
//~ Methods ========================================================================================================
@BeforeTransaction
public void createTables() throws Exception {
try {
new DatabaseSeeder(dataSource, new ClassPathResource("createAclSchema.sql"));
// new DatabaseSeeder(dataSource, new ClassPathResource("createAclSchemaPostgres.sql"));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@AfterTransaction
public void clearContextAndData() throws Exception {
SecurityContextHolder.clearContext();
jdbcTemplate.execute("drop table acl_entry");
jdbcTemplate.execute("drop table acl_object_identity");
jdbcTemplate.execute("drop table acl_class");
jdbcTemplate.execute("drop table acl_sid");
}
@Test
@Transactional
public void testLifecycle() {
SecurityContextHolder.getContext().setAuthentication(auth);
MutableAcl topParent = jdbcMutableAclService.createAcl(topParentOid);
MutableAcl middleParent = jdbcMutableAclService.createAcl(middleParentOid);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
// Specify the inheritance hierarchy
middleParent.setParent(topParent);
child.setParent(middleParent);
// Now let's add a couple of permissions
topParent.insertAce(0, BasePermission.READ, new PrincipalSid(auth), true);
topParent.insertAce(1, BasePermission.WRITE, new PrincipalSid(auth), false);
middleParent.insertAce(0, BasePermission.DELETE, new PrincipalSid(auth), true);
child.insertAce(0, BasePermission.DELETE, new PrincipalSid(auth), false);
// Explicitly save the changed ACL
jdbcMutableAclService.updateAcl(topParent);
jdbcMutableAclService.updateAcl(middleParent);
jdbcMutableAclService.updateAcl(child);
// Let's check if we can read them back correctly
Map<ObjectIdentity, Acl> map = jdbcMutableAclService.readAclsById(Arrays.asList(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().size());
assertEquals(1, middleParent.getEntries().size());
assertEquals(1, child.getEntries().size());
// Check the retrieved rights are correct
List<Permission> read = Arrays.asList(BasePermission.READ);
List<Permission> write = Arrays.asList(BasePermission.WRITE);
List<Permission> delete = Arrays.asList(BasePermission.DELETE);
List<Sid> pSid = Arrays.asList((Sid)new PrincipalSid(auth));
assertTrue(topParent.isGranted(read, pSid, false));
assertFalse(topParent.isGranted(write, pSid, false));
assertTrue(middleParent.isGranted(delete, pSid, false));
assertFalse(child.isGranted(delete, pSid, false));
try {
child.isGranted(Arrays.asList(BasePermission.ADMINISTRATION), pSid, 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(read, pSid, false));
assertFalse(child.isGranted(write, pSid, false));
assertFalse(child.isGranted(delete, pSid, 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(delete, pSid, true));
try {
child.isGranted(read, pSid, true);
fail("Should have thrown NotFoundException");
} catch (NotFoundException expected) {
assertTrue(true);
}
try {
child.isGranted(write, pSid, 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(1, BasePermission.DELETE, new PrincipalSid(auth), true);
// Let's also add another permission to the child
child.insertAce(2, BasePermission.CREATE, new PrincipalSid(auth), true);
// Save the changed child
jdbcMutableAclService.updateAcl(child);
child = (MutableAcl) jdbcMutableAclService.readAclById(childOid);
assertEquals(3, child.getEntries().size());
// Output permissions
for (int i = 0; i < child.getEntries().size(); i++) {
System.out.println(child.getEntries().get(i));
}
// Check the permissions are as they should be
assertFalse(child.isGranted(delete, pSid, true)); // as earlier permission overrode
assertTrue(child.isGranted(Arrays.asList(BasePermission.CREATE), pSid, true));
// Now check the first ACE (index 0) really is DELETE for our Sid and is non-granting
AccessControlEntry entry = child.getEntries().get(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(0);
// Save and check it worked
child = jdbcMutableAclService.updateAcl(child);
assertEquals(2, child.getEntries().size());
assertTrue(child.isGranted(delete, pSid, false));
SecurityContextHolder.clearContext();
}
/**
* Test method that demonstrates eviction failure from cache - SEC-676
*/
@Test
@Transactional
public void deleteAclAlsoDeletesChildren() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
jdbcMutableAclService.createAcl(topParentOid);
MutableAcl middleParent = jdbcMutableAclService.createAcl(middleParentOid);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
child.setParent(middleParent);
jdbcMutableAclService.updateAcl(middleParent);
jdbcMutableAclService.updateAcl(child);
// Check the childOid really is a child of middleParentOid
Acl childAcl = jdbcMutableAclService.readAclById(childOid);
assertEquals(middleParentOid, childAcl.getParentAcl().getObjectIdentity());
// Delete the mid-parent and test if the child was deleted, as well
jdbcMutableAclService.deleteAcl(middleParentOid, true);
try {
jdbcMutableAclService.readAclById(middleParentOid);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
try {
jdbcMutableAclService.readAclById(childOid);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
Acl acl = jdbcMutableAclService.readAclById(topParentOid);
assertNotNull(acl);
assertEquals(((MutableAcl) acl).getObjectIdentity(), topParentOid);
}
@Test
public void constructorRejectsNullParameters() throws Exception {
try {
new JdbcMutableAclService(null, lookupStrategy, aclCache);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
new JdbcMutableAclService(dataSource, null, aclCache);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
new JdbcMutableAclService(dataSource, lookupStrategy, null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
public void createAclRejectsNullParameter() throws Exception {
try {
jdbcMutableAclService.createAcl(null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
@Transactional
public void createAclForADuplicateDomainObject() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity duplicateOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
jdbcMutableAclService.createAcl(duplicateOid);
// Try to add the same object second time
try {
jdbcMutableAclService.createAcl(duplicateOid);
fail("It should have thrown AlreadyExistsException");
}
catch (AlreadyExistsException expected) {
}
}
@Test
@Transactional
public void deleteAclRejectsNullParameters() throws Exception {
try {
jdbcMutableAclService.deleteAcl(null, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
@Transactional
public void deleteAclWithChildrenThrowsException() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
MutableAcl parent = jdbcMutableAclService.createAcl(topParentOid);
MutableAcl child = jdbcMutableAclService.createAcl(middleParentOid);
// Specify the inheritance hierarchy
child.setParent(parent);
jdbcMutableAclService.updateAcl(child);
try {
jdbcMutableAclService.setForeignKeysInDatabase(false); // switch on FK checking in the class, not database
jdbcMutableAclService.deleteAcl(topParentOid, false);
fail("It should have thrown ChildrenExistException");
}
catch (ChildrenExistException expected) {
} finally {
jdbcMutableAclService.setForeignKeysInDatabase(true); // restore to the default
}
}
@Test
@Transactional
public void deleteAclRemovesRowsFromDatabase() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
child.insertAce(0, BasePermission.DELETE, new PrincipalSid(auth), false);
jdbcMutableAclService.updateAcl(child);
// Remove the child and check all related database rows were removed accordingly
jdbcMutableAclService.deleteAcl(childOid, false);
assertEquals(1, jdbcTemplate.queryForList(SELECT_ALL_CLASSES, new Object[] {TARGET_CLASS} ).size());
assertEquals(0, jdbcTemplate.queryForList("select * from acl_object_identity").size());
assertEquals(0, jdbcTemplate.queryForList("select * from acl_entry").size());
// Check the cache
assertNull(aclCache.getFromCache(childOid));
assertNull(aclCache.getFromCache(Long.valueOf(102)));
}
/** SEC-1107 */
@Test
@Transactional
public void identityWithIntegerIdIsSupportedByCreateAcl() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(101));
jdbcMutableAclService.createAcl(oid);
assertNotNull(jdbcMutableAclService.readAclById(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101))));
}
/**
* SEC-655
*/
@Test
@Transactional
public void childrenAreClearedFromCacheWhenParentIsUpdated() throws Exception {
Authentication auth = new TestingAuthenticationToken("ben", "ignored","ROLE_ADMINISTRATOR");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity parentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(104));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(105));
MutableAcl parent = jdbcMutableAclService.createAcl(parentOid);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
child.setParent(parent);
jdbcMutableAclService.updateAcl(child);
parent = (AclImpl) jdbcMutableAclService.readAclById(parentOid);
parent.insertAce(0, BasePermission.READ, new PrincipalSid("ben"), true);
jdbcMutableAclService.updateAcl(parent);
parent = (AclImpl) jdbcMutableAclService.readAclById(parentOid);
parent.insertAce(1, BasePermission.READ, new PrincipalSid("scott"), true);
jdbcMutableAclService.updateAcl(parent);
child = (MutableAcl) jdbcMutableAclService.readAclById(childOid);
parent = (MutableAcl) child.getParentAcl();
assertEquals("Fails because child has a stale reference to its parent", 2, parent.getEntries().size());
assertEquals(1, parent.getEntries().get(0).getPermission().getMask());
assertEquals(new PrincipalSid("ben"), parent.getEntries().get(0).getSid());
assertEquals(1, parent.getEntries().get(1).getPermission().getMask());
assertEquals(new PrincipalSid("scott"), parent.getEntries().get(1).getSid());
}
/**
* SEC-655
*/
@Test
@Transactional
public void childrenAreClearedFromCacheWhenParentisUpdated2() throws Exception {
Authentication auth = new TestingAuthenticationToken("system", "secret","ROLE_IGNORED");
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentityImpl rootObject = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(1));
MutableAcl parent = jdbcMutableAclService.createAcl(rootObject);
MutableAcl child = jdbcMutableAclService.createAcl(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(2)));
child.setParent(parent);
jdbcMutableAclService.updateAcl(child);
parent.insertAce(0, BasePermission.ADMINISTRATION, new GrantedAuthoritySid("ROLE_ADMINISTRATOR"), true);
jdbcMutableAclService.updateAcl(parent);
parent.insertAce(1, BasePermission.DELETE, new PrincipalSid("terry"), true);
jdbcMutableAclService.updateAcl(parent);
child = (MutableAcl) jdbcMutableAclService.readAclById(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(2)));
parent = (MutableAcl) child.getParentAcl();
assertEquals(2, parent.getEntries().size());
assertEquals(16, parent.getEntries().get(0).getPermission().getMask());
assertEquals(new GrantedAuthoritySid("ROLE_ADMINISTRATOR"), parent.getEntries().get(0).getSid());
assertEquals(8, parent.getEntries().get(1).getPermission().getMask());
assertEquals(new PrincipalSid("terry"), parent.getEntries().get(1).getSid());
}
@Test
@Transactional
public void cumulativePermissions() {
Authentication auth = new TestingAuthenticationToken("ben", "ignored", "ROLE_ADMINISTRATOR");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(110));
MutableAcl topParent = jdbcMutableAclService.createAcl(topParentOid);
// Add an ACE permission entry
Permission cm = new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION);
assertEquals(17, cm.getMask());
Sid benSid = new PrincipalSid(auth);
topParent.insertAce(0, cm, benSid, true);
assertEquals(1, topParent.getEntries().size());
// Explicitly save the changed ACL
topParent = jdbcMutableAclService.updateAcl(topParent);
// Check the mask was retrieved correctly
assertEquals(17, topParent.getEntries().get(0).getPermission().getMask());
assertTrue(topParent.isGranted(Arrays.asList(cm), Arrays.asList(benSid), true));
SecurityContextHolder.clearContext();
}
}
@@ -1,42 +1,37 @@
package org.springframework.security.acls.domain;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
import junit.framework.TestCase;
/**
* Tests for {@link ObjectIdentityRetrievalStrategyImpl}
*
* @author Andrei Stefan
*/
public class ObjectIdentityRetrievalStrategyImplTests extends TestCase {
//~ Methods ========================================================================================================
public void testObjectIdentityCreation() throws Exception {
MockIdDomainObject domain = new MockIdDomainObject();
domain.setId(new Integer(1));
ObjectIdentityRetrievalStrategy retStrategy = new ObjectIdentityRetrievalStrategyImpl();
ObjectIdentity identity = retStrategy.getObjectIdentity(domain);
assertNotNull(identity);
assertEquals(identity, new ObjectIdentityImpl(domain));
}
//~ Inner Classes ==================================================================================================
@SuppressWarnings("unused")
private class MockIdDomainObject {
private Object id;
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
}
}
package org.springframework.security.acls.objectidentity;
import junit.framework.TestCase;
/**
* Tests for {@link ObjectIdentityRetrievalStrategyImpl}
*
* @author Andrei Stefan
*/
public class ObjectIdentityRetrievalStrategyImplTests extends TestCase {
//~ Methods ========================================================================================================
public void testObjectIdentityCreation() throws Exception {
MockIdDomainObject domain = new MockIdDomainObject();
domain.setId(new Integer(1));
ObjectIdentityRetrievalStrategy retStrategy = new ObjectIdentityRetrievalStrategyImpl();
ObjectIdentity identity = retStrategy.getObjectIdentity(domain);
assertNotNull(identity);
assertEquals(identity, new ObjectIdentityImpl(domain));
}
//~ Inner Classes ==================================================================================================
private class MockIdDomainObject {
private Object id;
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
}
}
@@ -0,0 +1,204 @@
package org.springframework.security.acls.objectidentity;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.springframework.security.acls.IdentityUnavailableException;
/**
* Tests for {@link ObjectIdentityImpl}.
*
* @author Andrei Stefan
*/
public class ObjectIdentityTests extends TestCase {
//~ Methods ========================================================================================================
public void testConstructorsRequiredFields() throws Exception {
// Check one-argument constructor required field
try {
ObjectIdentity obj = new ObjectIdentityImpl(null);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
// Check String-Serializable constructor required field
try {
ObjectIdentity obj = new ObjectIdentityImpl("", new Long(1));
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
// Check Serializable parameter is not null
try {
ObjectIdentity obj = new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject", null);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
// The correct way of using String-Serializable constructor
try {
ObjectIdentity obj = new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject",
new Long(1));
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
// Check the Class-Serializable constructor
try {
ObjectIdentity obj = new ObjectIdentityImpl(MockIdDomainObject.class, null);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
}
public void testGetIdMethodConstraints() throws Exception {
// Check the getId() method is present
try {
ObjectIdentity obj = new ObjectIdentityImpl("A_STRING_OBJECT");
Assert.fail("It should have thrown IdentityUnavailableException");
}
catch (IdentityUnavailableException expected) {
Assert.assertTrue(true);
}
// getId() should return a non-null value
MockIdDomainObject mockId = new MockIdDomainObject();
try {
ObjectIdentity obj = new ObjectIdentityImpl(mockId);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
// getId() should return a Serializable object
mockId.setId(new MockIdDomainObject());
try {
ObjectIdentity obj = new ObjectIdentityImpl(mockId);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
// getId() should return a Serializable object
mockId.setId(new Long(100));
try {
ObjectIdentity obj = new ObjectIdentityImpl(mockId);
Assert.assertTrue(true);
}
catch (IllegalArgumentException expected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
public void testConstructorInvalidClassParameter() throws Exception {
try {
ObjectIdentity obj = new ObjectIdentityImpl("not.a.Class", new Long(1));
}
catch (IllegalStateException expected) {
return;
}
Assert.fail("It should have thrown IllegalStateException");
}
public void testEquals() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject", new Long(1));
MockIdDomainObject mockObj = new MockIdDomainObject();
mockObj.setId(new Long(1));
String string = "SOME_STRING";
Assert.assertNotSame(obj, string);
Assert.assertTrue(!obj.equals(null));
Assert.assertTrue(!obj.equals("DIFFERENT_OBJECT_TYPE"));
Assert.assertTrue(!obj
.equals(new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject",
new Long(2))));
Assert.assertTrue(!obj.equals(new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockOtherIdDomainObject",
new Long(1))));
Assert.assertEquals(
new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject",
new Long(1)), obj);
Assert.assertTrue(new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject", new Long(1))
.equals(obj));
Assert.assertTrue(new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject", new Long(1))
.equals(new ObjectIdentityImpl(mockObj)));
}
public void testHashCode() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject", new Long(1));
Assert.assertEquals(new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject", new Long(1))
.hashCode(), obj.hashCode());
Assert.assertTrue(new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockOtherIdDomainObject",
new Long(1)).hashCode() != obj.hashCode());
Assert.assertTrue(new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject", new Long(2))
.hashCode() != obj.hashCode());
}
/* public void testHashCodeDifferentSerializableTypes() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject", new Long(1));
Assert.assertEquals(new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject", "1")
.hashCode(), obj.hashCode());
Assert.assertEquals(new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject",
new Integer(1)).hashCode(), obj.hashCode());
}*/
public void testGetters() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(
"org.springframework.security.acls.objectidentity.ObjectIdentityTests$MockIdDomainObject", new Long(1));
Assert.assertEquals(new Long(1), obj.getIdentifier());
Assert.assertEquals(MockIdDomainObject.class, obj.getJavaType());
}
//~ Inner Classes ==================================================================================================
private class MockIdDomainObject {
private Object id;
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
}
private class MockOtherIdDomainObject {
private Object id;
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
}
}
@@ -1,66 +1,40 @@
package org.springframework.security.acls.sid;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.util.List;
import org.junit.Test;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
/**
* Tests for {@link SidRetrievalStrategyImpl}
*
* @author Andrei Stefan
* @author Luke Taylor
*/
@SuppressWarnings("unchecked")
public class SidRetrievalStrategyTests {
Authentication authentication = new TestingAuthenticationToken("scott", "password", "A", "B", "C");
//~ Methods ========================================================================================================
@Test
public void correctSidsAreRetrieved() throws Exception {
SidRetrievalStrategy retrStrategy = new SidRetrievalStrategyImpl();
List<Sid> sids = retrStrategy.getSids(authentication);
assertNotNull(sids);
assertEquals(4, sids.size());
assertNotNull(sids.get(0));
assertTrue(sids.get(0) instanceof PrincipalSid);
for (int i = 1; i < sids.size(); i++) {
assertTrue(sids.get(i) instanceof GrantedAuthoritySid);
}
assertEquals("scott", ((PrincipalSid) sids.get(0)).getPrincipal());
assertEquals("A", ((GrantedAuthoritySid) sids.get(1)).getGrantedAuthority());
assertEquals("B", ((GrantedAuthoritySid) sids.get(2)).getGrantedAuthority());
assertEquals("C", ((GrantedAuthoritySid) sids.get(3)).getGrantedAuthority());
}
@Test
public void roleHierarchyIsUsedWhenSet() throws Exception {
RoleHierarchy rh = mock(RoleHierarchy.class);
List<GrantedAuthority> rhAuthorities = AuthorityUtils.createAuthorityList("D");
when(rh.getReachableGrantedAuthorities(anyList())).thenReturn(rhAuthorities);
SidRetrievalStrategy strat = new SidRetrievalStrategyImpl(rh);
List<Sid> sids = strat.getSids(authentication);
assertEquals(2, sids.size());
assertNotNull(sids.get(0));
assertTrue(sids.get(0) instanceof PrincipalSid);
assertEquals("D", ((GrantedAuthoritySid) sids.get(1)).getGrantedAuthority());
}
}
package org.springframework.security.acls.sid;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.springframework.security.Authentication;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.providers.TestingAuthenticationToken;
/**
* Tests for {@link SidRetrievalStrategyImpl}
*
* @author Andrei Stefan
*/
public class SidRetrievalStrategyTests extends TestCase {
//~ Methods ========================================================================================================
public void testSidsRetrieval() throws Exception {
Authentication authentication = new TestingAuthenticationToken("scott", "password", new GrantedAuthority[] {
new GrantedAuthorityImpl("ROLE_1"), new GrantedAuthorityImpl("ROLE_2"), new GrantedAuthorityImpl("ROLE_3") });
SidRetrievalStrategy retrStrategy = new SidRetrievalStrategyImpl();
Sid[] sids = retrStrategy.getSids(authentication);
assertNotNull(sids);
assertEquals(4, sids.length);
assertNotNull(sids[0]);
assertTrue(sids[0] instanceof PrincipalSid);
for (int i = 1; i < sids.length; i++) {
assertTrue(sids[i] instanceof GrantedAuthoritySid);
}
Assert.assertEquals("scott", ((PrincipalSid) sids[0]).getPrincipal());
Assert.assertEquals("ROLE_1", ((GrantedAuthoritySid) sids[1]).getGrantedAuthority());
Assert.assertEquals("ROLE_2", ((GrantedAuthoritySid) sids[2]).getGrantedAuthority());
Assert.assertEquals("ROLE_3", ((GrantedAuthoritySid) sids[3]).getGrantedAuthority());
}
}
@@ -1,190 +1,191 @@
package org.springframework.security.acls.sid;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
public class SidTests extends TestCase {
//~ Methods ========================================================================================================
public void testPrincipalSidConstructorsRequiredFields() throws Exception {
// Check one String-argument constructor
try {
String string = null;
new PrincipalSid(string);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
new PrincipalSid("");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
new PrincipalSid("johndoe");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
// Check one Authentication-argument constructor
try {
Authentication authentication = null;
new PrincipalSid(authentication);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
Authentication authentication = new TestingAuthenticationToken(null, "password");
new PrincipalSid(authentication);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
new PrincipalSid(authentication);
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
public void testGrantedAuthoritySidConstructorsRequiredFields() throws Exception {
// Check one String-argument constructor
try {
String string = null;
new GrantedAuthoritySid(string);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
new GrantedAuthoritySid("");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
new GrantedAuthoritySid("ROLE_TEST");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
// Check one GrantedAuthority-argument constructor
try {
GrantedAuthority ga = null;
new GrantedAuthoritySid(ga);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
GrantedAuthority ga = new GrantedAuthorityImpl(null);
new GrantedAuthoritySid(ga);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_TEST");
new GrantedAuthoritySid(ga);
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
public void testPrincipalSidEquals() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
Sid principalSid = new PrincipalSid(authentication);
Assert.assertFalse(principalSid.equals(null));
Assert.assertFalse(principalSid.equals("DIFFERENT_TYPE_OBJECT"));
Assert.assertTrue(principalSid.equals(principalSid));
Assert.assertTrue(principalSid.equals(new PrincipalSid(authentication)));
Assert.assertTrue(principalSid.equals(new PrincipalSid(new TestingAuthenticationToken("johndoe", null))));
Assert.assertFalse(principalSid.equals(new PrincipalSid(new TestingAuthenticationToken("scott", null))));
Assert.assertTrue(principalSid.equals(new PrincipalSid("johndoe")));
Assert.assertFalse(principalSid.equals(new PrincipalSid("scott")));
}
public void testGrantedAuthoritySidEquals() throws Exception {
GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_TEST");
Sid gaSid = new GrantedAuthoritySid(ga);
Assert.assertFalse(gaSid.equals(null));
Assert.assertFalse(gaSid.equals("DIFFERENT_TYPE_OBJECT"));
Assert.assertTrue(gaSid.equals(gaSid));
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid(ga)));
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid(new GrantedAuthorityImpl("ROLE_TEST"))));
Assert.assertFalse(gaSid.equals(new GrantedAuthoritySid(new GrantedAuthorityImpl("ROLE_NOT_EQUAL"))));
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid("ROLE_TEST")));
Assert.assertFalse(gaSid.equals(new GrantedAuthoritySid("ROLE_NOT_EQUAL")));
}
public void testPrincipalSidHashCode() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
Sid principalSid = new PrincipalSid(authentication);
Assert.assertTrue(principalSid.hashCode() == new String("johndoe").hashCode());
Assert.assertTrue(principalSid.hashCode() == new PrincipalSid("johndoe").hashCode());
Assert.assertTrue(principalSid.hashCode() != new PrincipalSid("scott").hashCode());
Assert.assertTrue(principalSid.hashCode() != new PrincipalSid(new TestingAuthenticationToken("scott", "password")).hashCode());
}
public void testGrantedAuthoritySidHashCode() throws Exception {
GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_TEST");
Sid gaSid = new GrantedAuthoritySid(ga);
Assert.assertTrue(gaSid.hashCode() == new String("ROLE_TEST").hashCode());
Assert.assertTrue(gaSid.hashCode() == new GrantedAuthoritySid("ROLE_TEST").hashCode());
Assert.assertTrue(gaSid.hashCode() != new GrantedAuthoritySid("ROLE_TEST_2").hashCode());
Assert.assertTrue(gaSid.hashCode() != new GrantedAuthoritySid(new GrantedAuthorityImpl("ROLE_TEST_2")).hashCode());
}
public void testGetters() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
PrincipalSid principalSid = new PrincipalSid(authentication);
GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_TEST");
GrantedAuthoritySid gaSid = new GrantedAuthoritySid(ga);
Assert.assertTrue("johndoe".equals(principalSid.getPrincipal()));
Assert.assertFalse("scott".equals(principalSid.getPrincipal()));
Assert.assertTrue("ROLE_TEST".equals(gaSid.getGrantedAuthority()));
Assert.assertFalse("ROLE_TEST2".equals(gaSid.getGrantedAuthority()));
}
}
package org.springframework.security.acls.sid;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.springframework.security.Authentication;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.acls.sid.GrantedAuthoritySid;
import org.springframework.security.acls.sid.PrincipalSid;
import org.springframework.security.acls.sid.Sid;
import org.springframework.security.providers.TestingAuthenticationToken;
public class SidTests extends TestCase {
//~ Methods ========================================================================================================
public void testPrincipalSidConstructorsRequiredFields() throws Exception {
// Check one String-argument constructor
try {
String string = null;
Sid principalSid = new PrincipalSid(string);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
Sid principalSid = new PrincipalSid("");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
Sid principalSid = new PrincipalSid("johndoe");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
// Check one Authentication-argument constructor
try {
Authentication authentication = null;
Sid principalSid = new PrincipalSid(authentication);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
Authentication authentication = new TestingAuthenticationToken(null, "password", null);
Sid principalSid = new PrincipalSid(authentication);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password", null);
Sid principalSid = new PrincipalSid(authentication);
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
public void testGrantedAuthoritySidConstructorsRequiredFields() throws Exception {
// Check one String-argument constructor
try {
String string = null;
Sid gaSid = new GrantedAuthoritySid(string);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
Sid gaSid = new GrantedAuthoritySid("");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
Sid gaSid = new GrantedAuthoritySid("ROLE_TEST");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
// Check one GrantedAuthority-argument constructor
try {
GrantedAuthority ga = null;
Sid gaSid = new GrantedAuthoritySid(ga);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
GrantedAuthority ga = new GrantedAuthorityImpl(null);
Sid gaSid = new GrantedAuthoritySid(ga);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_TEST");
Sid gaSid = new GrantedAuthoritySid(ga);
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
public void testPrincipalSidEquals() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password", null);
Sid principalSid = new PrincipalSid(authentication);
Assert.assertFalse(principalSid.equals(null));
Assert.assertFalse(principalSid.equals("DIFFERENT_TYPE_OBJECT"));
Assert.assertTrue(principalSid.equals(principalSid));
Assert.assertTrue(principalSid.equals(new PrincipalSid(authentication)));
Assert.assertTrue(principalSid.equals(new PrincipalSid(new TestingAuthenticationToken("johndoe", null, null))));
Assert.assertFalse(principalSid.equals(new PrincipalSid(new TestingAuthenticationToken("scott", null, null))));
Assert.assertTrue(principalSid.equals(new PrincipalSid("johndoe")));
Assert.assertFalse(principalSid.equals(new PrincipalSid("scott")));
}
public void testGrantedAuthoritySidEquals() throws Exception {
GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_TEST");
Sid gaSid = new GrantedAuthoritySid(ga);
Assert.assertFalse(gaSid.equals(null));
Assert.assertFalse(gaSid.equals("DIFFERENT_TYPE_OBJECT"));
Assert.assertTrue(gaSid.equals(gaSid));
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid(ga)));
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid(new GrantedAuthorityImpl("ROLE_TEST"))));
Assert.assertFalse(gaSid.equals(new GrantedAuthoritySid(new GrantedAuthorityImpl("ROLE_NOT_EQUAL"))));
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid("ROLE_TEST")));
Assert.assertFalse(gaSid.equals(new GrantedAuthoritySid("ROLE_NOT_EQUAL")));
}
public void testPrincipalSidHashCode() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password", null);
Sid principalSid = new PrincipalSid(authentication);
Assert.assertTrue(principalSid.hashCode() == new String("johndoe").hashCode());
Assert.assertTrue(principalSid.hashCode() == new PrincipalSid("johndoe").hashCode());
Assert.assertTrue(principalSid.hashCode() != new PrincipalSid("scott").hashCode());
Assert.assertTrue(principalSid.hashCode() != new PrincipalSid(new TestingAuthenticationToken("scott", "password",
null)).hashCode());
}
public void testGrantedAuthoritySidHashCode() throws Exception {
GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_TEST");
Sid gaSid = new GrantedAuthoritySid(ga);
Assert.assertTrue(gaSid.hashCode() == new String("ROLE_TEST").hashCode());
Assert.assertTrue(gaSid.hashCode() == new GrantedAuthoritySid("ROLE_TEST").hashCode());
Assert.assertTrue(gaSid.hashCode() != new GrantedAuthoritySid("ROLE_TEST_2").hashCode());
Assert.assertTrue(gaSid.hashCode() != new GrantedAuthoritySid(new GrantedAuthorityImpl("ROLE_TEST_2")).hashCode());
}
public void testGetters() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password", null);
PrincipalSid principalSid = new PrincipalSid(authentication);
GrantedAuthority ga = new GrantedAuthorityImpl("ROLE_TEST");
GrantedAuthoritySid gaSid = new GrantedAuthoritySid(ga);
Assert.assertTrue("johndoe".equals(principalSid.getPrincipal()));
Assert.assertFalse("scott".equals(principalSid.getPrincipal()));
Assert.assertTrue("ROLE_TEST".equals(gaSid.getGrantedAuthority()));
Assert.assertFalse("ROLE_TEST2".equals(gaSid.getGrantedAuthority()));
}
}
@@ -1,83 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
- Application context containing business beans.
-
- Used by all artifacts.
-
-->
<beans>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="aclCache" class="org.springframework.security.acls.domain.EhCacheBasedAclCache">
<constructor-arg>
<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
</property>
<property name="cacheName" value="aclCache"/>
</bean>
</constructor-arg>
</bean>
<bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
<constructor-arg ref="dataSource"/>
<constructor-arg ref="aclCache"/>
<constructor-arg ref="aclAuthorizationStrategy"/>
<constructor-arg>
<bean class="org.springframework.security.acls.domain.ConsoleAuditLogger"/>
</constructor-arg>
</bean>
<bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
<constructor-arg>
<list>
<bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
<constructor-arg value="ROLE_ADMINISTRATOR"/>
</bean>
<bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
<constructor-arg value="ROLE_ADMINISTRATOR"/>
</bean>
<bean class="org.springframework.security.core.authority.GrantedAuthorityImpl">
<constructor-arg value="ROLE_ADMINISTRATOR"/>
</bean>
</list>
</constructor-arg>
</bean>
<bean id="aclService" class="org.springframework.security.acls.jdbc.JdbcMutableAclService">
<constructor-arg ref="dataSource"/>
<constructor-arg ref="lookupStrategy"/>
<constructor-arg ref="aclCache"/>
<!-- Uncomment to use PostgreSQL
<property name="classIdentityQuery" value="select currval(pg_get_serial_sequence('acl_class', 'id'))"/>
<property name="sidIdentityQuery" value="select currval(pg_get_serial_sequence('acl_sid', 'id'))"/>
-->
</bean>
<!-- PostgreSQL DataSource configuration
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.postgresql.Driver"/>
<property name="url" value="jdbc:postgresql://localhost:5432/acltest"/>
<property name="username" value="acltest"/>
<property name="password" value="acltest"/>
</bean>
-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:mem:acltest"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
-23
View File
@@ -1,23 +0,0 @@
Implementation-Title: org.springframework.security.acls
Implementation-Version: ${version}
Bundle-SymbolicName: org.springframework.security.acls
Bundle-Name: Spring Security Acls
Bundle-Vendor: SpringSource
Bundle-Version: ${version}
Bundle-ManifestVersion: 2
Ignored-Existing-Headers:
Import-Package,
Export-Package
Import-Template:
org.aopalliance.*;version="${aopAllianceRange}",
org.apache.commons.logging.*;version="${cloggingRange}",
org.springframework.security.core.*;version="${secRange}",
org.springframework.security.access.*;version="${secRange}",
org.springframework.security.util.*;version="${secRange}",
org.springframework.context.*;version="${springRange}";resolution:=optional,
org.springframework.dao.*;version="${springRange}";resolution:=optional,
org.springframework.jdbc.core.*;version="${springRange}";resolution:=optional,
org.springframework.transaction.support.*;version="${springRange}";resolution:=optional,
org.springframework.util.*;version="${springRange}";resolution:=optional,
net.sf.ehcache.*;version="${ehcacheRange}";resolution:=optional,
javax.sql.*;version="0";resolution:=optional
+1
View File
@@ -0,0 +1 @@
target
+5
View File
@@ -0,0 +1,5 @@
target
.settings
.classpath
.project
.wtpmodules
+22
View File
@@ -0,0 +1,22 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-adapters</artifactId>
<version>2.0.3</version>
</parent>
<artifactId>spring-security-catalina</artifactId>
<name>Spring Security - Catalina adapter</name>
<dependencies>
<dependency>
<groupId>tomcat</groupId>
<artifactId>catalina</artifactId>
<version>4.1.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,236 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.adapters.catalina;
import org.springframework.security.Authentication;
import org.springframework.security.AuthenticationException;
import org.springframework.security.AuthenticationManager;
import org.springframework.security.adapters.PrincipalSpringSecurityUserToken;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
import org.apache.catalina.Container;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.realm.RealmBase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import java.io.File;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.Map;
/**
* Adapter to enable Catalina (Tomcat) to authenticate via the Spring Security.<p>Returns a {@link
* PrincipalSpringSecurityUserToken} to Catalina's authentication system, which is subsequently available via
* <code>HttpServletRequest.getUserPrincipal()</code>.</p>
*
* @author Ben Alex
* @version $Id:CatalinaSpringSecurityUserRealm.java 2151 2007-09-22 11:54:13Z luke_t $
*/
public class CatalinaSpringSecurityUserRealm extends RealmBase {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(CatalinaSpringSecurityUserRealm.class);
//~ Instance fields ================================================================================================
private AuthenticationManager authenticationManager;
private Container container;
private String appContextLocation;
private String key;
protected final String name = "CatalinaSpringUserRealm / $Id:CatalinaSpringSecurityUserRealm.java 2151 2007-09-22 11:54:13Z luke_t $";
//~ Methods ========================================================================================================
public Principal authenticate(String username, String credentials) {
if (username == null) {
return null;
}
if (credentials == null) {
credentials = "";
}
Authentication request = new UsernamePasswordAuthenticationToken(username, credentials);
Authentication response = null;
try {
response = authenticationManager.authenticate(request);
} catch (AuthenticationException failed) {
if (logger.isDebugEnabled()) {
logger.debug("Authentication request for user: " + username + " failed: " + failed.toString());
}
return null;
}
return new PrincipalSpringSecurityUserToken(this.key, response.getPrincipal().toString(),
response.getCredentials().toString(), response.getAuthorities(), response.getPrincipal());
}
public Principal authenticate(String username, byte[] credentials) {
return authenticate(username, new String(credentials));
}
/**
* Not supported, returns null
*
* @param username DOCUMENT ME!
* @param digest DOCUMENT ME!
* @param nonce DOCUMENT ME!
* @param nc DOCUMENT ME!
* @param cnonce DOCUMENT ME!
* @param qop DOCUMENT ME!
* @param realm DOCUMENT ME!
* @param md5a2 DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public java.security.Principal authenticate(java.lang.String username, java.lang.String digest,
java.lang.String nonce, java.lang.String nc, java.lang.String cnonce, java.lang.String qop,
java.lang.String realm, java.lang.String md5a2) {
return null;
}
/**
* Not supported, returns null
*
* @param x509Certificates DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public Principal authenticate(X509Certificate[] x509Certificates) {
return null;
}
public String getAppContextLocation() {
return appContextLocation;
}
public String getKey() {
return key;
}
protected String getName() {
return this.name;
}
/**
* Always returns null (we override authenticate methods)
*
* @param arg0 DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
protected String getPassword(String arg0) {
return null;
}
/**
* Always returns null (we override authenticate methods)
*
* @param arg0 DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
protected Principal getPrincipal(String arg0) {
return null;
}
public boolean hasRole(Principal principal, String role) {
if ((principal == null) || (role == null)) {
return false;
}
if (!(principal instanceof PrincipalSpringSecurityUserToken)) {
logger.warn("Expected passed principal to be of type PrincipalSpringSecurityUserToken but was "
+ principal.getClass().getName());
return false;
}
PrincipalSpringSecurityUserToken test = (PrincipalSpringSecurityUserToken) principal;
return test.isUserInRole(role);
}
public void setAppContextLocation(String appContextLocation) {
this.appContextLocation = appContextLocation;
}
public void setKey(String key) {
this.key = key;
}
/**
* Provides the method that Catalina will use to start the container.
*
* @throws LifecycleException if a problem is detected
*/
public void start() throws LifecycleException {
this.start(true);
}
private void start(boolean startParent) throws LifecycleException {
if (startParent) {
super.start();
}
if ((appContextLocation == null) || "".equals(appContextLocation)) {
throw new LifecycleException("appContextLocation must be defined");
}
if ((key == null) || "".equals(key)) {
throw new LifecycleException("key must be defined");
}
File xml = new File(System.getProperty("catalina.base"), appContextLocation);
if (!xml.exists()) {
throw new LifecycleException("appContextLocation does not seem to exist in " + xml.toString());
}
FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext("file:" + xml.getAbsolutePath());
Map beans = ctx.getBeansOfType(AuthenticationManager.class, true, true);
if (beans.size() == 0) {
throw new IllegalArgumentException(
"Bean context must contain at least one bean of type AuthenticationManager");
}
String beanName = (String) beans.keySet().iterator().next();
authenticationManager = (AuthenticationManager) beans.get(beanName);
logger.info("CatalinaSpringSecurityUserRealm Started");
}
/**
* Provides a method to load the container adapter without delegating to the superclass, which cannot
* operate outside the Catalina container.
*
* @throws LifecycleException if a problem is detected
*/
protected void startForTest() throws LifecycleException {
this.start(false);
}
}
@@ -0,0 +1,7 @@
<html>
<body>
Adapter to Catalina web container (Tomcat).
<p>
</body>
</html>
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!--
* Copyright 2004 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* $Id$
-->
<beans>
<!-- Data access object which stores authentication information -->
<bean id="inMemoryDaoImpl" class="org.springframework.security.userdetails.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
rod=koala,ROLE_TELLER,ROLE_SUPERVISOR
dianne=emu,ROLE_TELLER
scott=wombat,ROLE_TELLER
peter=opal,disabled,ROLE_TELLER
</value>
</property>
</bean>
<!-- The authentication manager is deliberately missing in order to test error detection -->
</beans>

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