Initial commit of ACL capabilities.
This commit is contained in:
parent
b749e83b1d
commit
56829872b6
|
@ -0,0 +1,25 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl;
|
||||
|
||||
/**
|
||||
* Marker interface representing an access control list entry associated with a
|
||||
* specific domain object instance.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface AclEntry {}
|
|
@ -0,0 +1,58 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl;
|
||||
|
||||
import net.sf.acegisecurity.Authentication;
|
||||
|
||||
|
||||
/**
|
||||
* Obtains the <code>AclEntry</code> instances that apply to a particular
|
||||
* domain object instance.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface AclManager {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Obtains the ACLs that apply to the specified domain instance.
|
||||
*
|
||||
* @param domainInstance the instance for which ACL information is required
|
||||
* (never <code>null</code>)
|
||||
*
|
||||
* @return the ACLs that apply, or <code>null</code> if no ACLs apply to
|
||||
* the specified domain instance
|
||||
*/
|
||||
public AclEntry[] getAcls(Object domainInstance);
|
||||
|
||||
/**
|
||||
* Obtains the ACLs that apply to the specified domain instance, but only
|
||||
* including those ACLs which have been granted to the presented
|
||||
* <code>Authentication</code> object
|
||||
*
|
||||
* @param domainInstance the instance for which ACL information is required
|
||||
* (never <code>null</code>)
|
||||
* @param authentication the prncipal for which ACL information should be
|
||||
* filtered (never <code>null</code>)
|
||||
*
|
||||
* @return only those ACLs applying to the domain instance that have been
|
||||
* granted to the principal (or <code>null</code>) if no such ACLs
|
||||
* are found
|
||||
*/
|
||||
public AclEntry[] getAcls(Object domainInstance,
|
||||
Authentication authentication);
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl;
|
||||
|
||||
import net.sf.acegisecurity.Authentication;
|
||||
|
||||
/**
|
||||
* Indicates a class can process a given domain object instance and
|
||||
* authoritatively return the ACLs that apply.
|
||||
*
|
||||
* <P>
|
||||
* Implementations are typically called from the {@link AclProviderManager}.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface AclProvider {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Obtains the ACLs that apply to the specified domain instance.
|
||||
*
|
||||
* <P>
|
||||
* Will never be called unless the {@link #supports(Object)} method
|
||||
* returned <code>true</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param domainInstance the instance for which ACL information is required
|
||||
* (never <code>null</code>)
|
||||
*
|
||||
* @return the ACLs that apply, or <code>null</code> if no ACLs apply to
|
||||
* the specified domain instance
|
||||
*/
|
||||
public AclEntry[] getAcls(Object domainInstance);
|
||||
|
||||
/**
|
||||
* Obtains the ACLs that apply to the specified domain instance
|
||||
* and presented <code>Authentication</code> object.
|
||||
*
|
||||
* <P>
|
||||
* Will never be called unless the {@link #supports(Object)} method
|
||||
* returned <code>true</code>.
|
||||
* </p>
|
||||
*
|
||||
* @param domainInstance the instance for which ACL information is required
|
||||
* (never <code>null</code>)
|
||||
* @param authentication the prncipal for which ACL information should be
|
||||
* filtered (never <code>null</code>)
|
||||
*
|
||||
* @return only those ACLs applying to the domain instance that have been
|
||||
* granted to the principal (or <code>null</code>) if no such ACLs
|
||||
* are found
|
||||
*/
|
||||
public AclEntry[] getAcls(Object domainInstance,
|
||||
Authentication authentication);
|
||||
|
||||
/**
|
||||
* Indicates whether this <code>AclProvider</code> can authoritatively
|
||||
* return ACL information for the specified domain object instance.
|
||||
*
|
||||
* @param domainInstance the instance for which ACL information is required
|
||||
* (never <code>null</code>)
|
||||
*
|
||||
* @return <code>true</code> if this provider is authoritative for the
|
||||
* specified domain object instance, <code>false</code> otherwise
|
||||
*/
|
||||
public boolean supports(Object domainInstance);
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl;
|
||||
|
||||
import net.sf.acegisecurity.Authentication;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Iterates through a list of {@link AclProvider}s to locate the ACLs that
|
||||
* apply to a given domain object instance.
|
||||
*
|
||||
* <P>
|
||||
* If no compatible provider is found, it is assumed that no ACLs apply for the
|
||||
* specified domain object instance and <code>null</code> is returned.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AclProviderManager implements AclManager, InitializingBean {
|
||||
//~ Static fields/initializers =============================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AclProviderManager.class);
|
||||
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private List providers;
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public AclEntry[] getAcls(Object domainInstance) {
|
||||
if (domainInstance == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"domainInstance is null - violating interface contract");
|
||||
}
|
||||
|
||||
Iterator iter = providers.iterator();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
AclProvider provider = (AclProvider) iter.next();
|
||||
|
||||
if (provider.supports(domainInstance)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("ACL lookup using "
|
||||
+ provider.getClass().getName());
|
||||
}
|
||||
|
||||
return provider.getAcls(domainInstance);
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No AclProvider found for "
|
||||
+ domainInstance.toString());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public AclEntry[] getAcls(Object domainInstance,
|
||||
Authentication authentication) {
|
||||
if (domainInstance == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"domainInstance is null - violating interface contract");
|
||||
}
|
||||
if (authentication == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"authentication is null - violating interface contract");
|
||||
}
|
||||
|
||||
Iterator iter = providers.iterator();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
AclProvider provider = (AclProvider) iter.next();
|
||||
|
||||
if (provider.supports(domainInstance)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("ACL lookup using "
|
||||
+ provider.getClass().getName());
|
||||
}
|
||||
|
||||
return provider.getAcls(domainInstance, authentication);
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No AclProvider found for "
|
||||
+ domainInstance.toString());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AclProvider} objects to be used for ACL determinations.
|
||||
*
|
||||
* @param newList that should be used for ACL determinations
|
||||
*
|
||||
* @throws IllegalArgumentException if an invalid provider was included in
|
||||
* the list
|
||||
*/
|
||||
public void setProviders(List newList) {
|
||||
checkIfValidList(newList);
|
||||
|
||||
Iterator iter = newList.iterator();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
Object currentObject = null;
|
||||
|
||||
try {
|
||||
currentObject = iter.next();
|
||||
|
||||
AclProvider attemptToCast = (AclProvider) currentObject;
|
||||
} catch (ClassCastException cce) {
|
||||
throw new IllegalArgumentException("AclProvider "
|
||||
+ currentObject.getClass().getName()
|
||||
+ " must implement AclProvider");
|
||||
}
|
||||
}
|
||||
|
||||
this.providers = newList;
|
||||
}
|
||||
|
||||
public List getProviders() {
|
||||
return this.providers;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
checkIfValidList(this.providers);
|
||||
}
|
||||
|
||||
private void checkIfValidList(List listToCheck) {
|
||||
if ((listToCheck == null) || (listToCheck.size() == 0)) {
|
||||
throw new IllegalArgumentException(
|
||||
"A list of AclManagers is required");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link BasicAclEntry}.
|
||||
*
|
||||
* <P>
|
||||
* Provides core bit mask handling methods.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public abstract class AbstractBasicAclEntry implements BasicAclEntry {
|
||||
//~ Static fields/initializers =============================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AbstractBasicAclEntry.class);
|
||||
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private AclObjectIdentity aclObjectIdentity;
|
||||
private AclObjectIdentity aclObjectParentIdentity;
|
||||
private Object recipient;
|
||||
private int[] validPermissions;
|
||||
private int mask = 0; // default means no permissions
|
||||
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public AbstractBasicAclEntry(Object recipient,
|
||||
AclObjectIdentity aclObjectIdentity,
|
||||
AclObjectIdentity aclObjectParentIdentity, int mask) {
|
||||
if (recipient == null) {
|
||||
throw new IllegalArgumentException("recipient cannot be null");
|
||||
}
|
||||
|
||||
if (aclObjectIdentity == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"aclObjectIdentity cannot be null");
|
||||
}
|
||||
|
||||
validPermissions = getValidPermissions();
|
||||
Arrays.sort(validPermissions);
|
||||
|
||||
for (int i = 0; i < validPermissions.length; i++) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Valid permission: "
|
||||
+ printPermissionsBlock(validPermissions[i]) + " "
|
||||
+ printBinary(validPermissions[i]) + " ("
|
||||
+ validPermissions[i] + ")");
|
||||
}
|
||||
}
|
||||
|
||||
this.recipient = recipient;
|
||||
this.aclObjectIdentity = aclObjectIdentity;
|
||||
this.aclObjectParentIdentity = aclObjectParentIdentity;
|
||||
this.mask = mask;
|
||||
}
|
||||
|
||||
protected AbstractBasicAclEntry() {}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public void setAclObjectIdentity(AclObjectIdentity aclObjectIdentity) {
|
||||
this.aclObjectIdentity = aclObjectIdentity;
|
||||
}
|
||||
|
||||
public AclObjectIdentity getAclObjectIdentity() {
|
||||
return this.aclObjectIdentity;
|
||||
}
|
||||
|
||||
public void setAclObjectParentIdentity(
|
||||
AclObjectIdentity aclObjectParentIdentity) {
|
||||
this.aclObjectParentIdentity = aclObjectParentIdentity;
|
||||
}
|
||||
|
||||
public AclObjectIdentity getAclObjectParentIdentity() {
|
||||
return this.aclObjectParentIdentity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must indicate the permissions they support. Each base
|
||||
* permission should be an integer with a base 2. ie: the first permission
|
||||
* is 2^^0 (1), the second permission is 2^^1 (1), the third permission is
|
||||
* 2^^2 (4) etc. Each base permission should be exposed by the subclass as
|
||||
* a <code>public static final int</code>. It is further recommended that
|
||||
* valid combinations of permissions are also exposed as <code>public
|
||||
* static final int</code>s.
|
||||
*
|
||||
* <P>
|
||||
* This method returns all permission integers that are allowed to be used
|
||||
* together. <B>This must include any combinations of valid
|
||||
* permissions</b>. So if the permissions indicated by 2^^2 (4) and 2^^1
|
||||
* (2) can be used together, one of the integers returned by this method
|
||||
* must be 6 (4 + 2). Otherwise attempts to set the permission will be
|
||||
* rejected, as the final resulting mask will be rejected.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* Whilst it may seem unduly time onerous to return every valid permission
|
||||
* <B>combination</B>, doing so delivers maximum flexibility in ensuring
|
||||
* ACLs only reflect logical combinations. For example, it would be
|
||||
* inappropriate to grant a "read" and "write" permission along with an
|
||||
* "unrestricted" permission, as the latter implies the former
|
||||
* permissions.
|
||||
* </p>
|
||||
*
|
||||
* @return <b>every</b> valid combination of permissions
|
||||
*/
|
||||
public abstract int[] getValidPermissions();
|
||||
|
||||
/**
|
||||
* Outputs the permissions in a human-friendly format. For example, this
|
||||
* method may return "CR-D" to indicate the passed integer permits create,
|
||||
* permits read, does not permit update, and permits delete.
|
||||
*
|
||||
* @param i the integer containing the mask which should be printed
|
||||
*
|
||||
* @return the human-friend formatted block
|
||||
*/
|
||||
public abstract String printPermissionsBlock(int i);
|
||||
|
||||
public void setMask(int mask) {
|
||||
this.mask = mask;
|
||||
}
|
||||
|
||||
public int getMask() {
|
||||
return this.mask;
|
||||
}
|
||||
|
||||
public boolean isPermitted(int permissionToCheck) {
|
||||
return isPermitted(this.mask, permissionToCheck);
|
||||
}
|
||||
|
||||
public void setRecipient(Object recipient) {
|
||||
this.recipient = recipient;
|
||||
}
|
||||
|
||||
public Object getRecipient() {
|
||||
return this.recipient;
|
||||
}
|
||||
|
||||
public int addPermission(int permissionToAdd) {
|
||||
return addPermissions(new int[] {permissionToAdd});
|
||||
}
|
||||
|
||||
public int addPermissions(int[] permissionsToAdd) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("BEFORE Permissions: " + printPermissionsBlock(mask)
|
||||
+ " " + printBinary(mask) + " (" + mask + ")");
|
||||
}
|
||||
|
||||
for (int i = 0; i < permissionsToAdd.length; i++) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Add permission: "
|
||||
+ printPermissionsBlock(permissionsToAdd[i]) + " "
|
||||
+ printBinary(permissionsToAdd[i]) + " ("
|
||||
+ permissionsToAdd[i] + ")");
|
||||
}
|
||||
|
||||
this.mask |= permissionsToAdd[i];
|
||||
}
|
||||
|
||||
if (Arrays.binarySearch(validPermissions, this.mask) < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Resulting permission set will be invalid.");
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("AFTER Permissions: "
|
||||
+ printPermissionsBlock(mask) + " " + printBinary(mask)
|
||||
+ " (" + mask + ")");
|
||||
}
|
||||
|
||||
return this.mask;
|
||||
}
|
||||
}
|
||||
|
||||
public int deletePermission(int permissionToDelete) {
|
||||
return deletePermissions(new int[] {permissionToDelete});
|
||||
}
|
||||
|
||||
public int deletePermissions(int[] permissionsToDelete) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("BEFORE Permissions: " + printPermissionsBlock(mask)
|
||||
+ " " + printBinary(mask) + " (" + mask + ")");
|
||||
}
|
||||
|
||||
for (int i = 0; i < permissionsToDelete.length; i++) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Delete permission: "
|
||||
+ printPermissionsBlock(permissionsToDelete[i]) + " "
|
||||
+ printBinary(permissionsToDelete[i]) + " ("
|
||||
+ permissionsToDelete[i] + ")");
|
||||
}
|
||||
|
||||
this.mask &= ~permissionsToDelete[i];
|
||||
}
|
||||
|
||||
if (Arrays.binarySearch(validPermissions, this.mask) < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Resulting permission set will be invalid.");
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("AFTER Permissions: "
|
||||
+ printPermissionsBlock(mask) + " " + printBinary(mask)
|
||||
+ " (" + mask + ")");
|
||||
}
|
||||
|
||||
return this.mask;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the permissions in human-friendly format for the current
|
||||
* <code>AbstractBasicAclEntry</code>'s mask.
|
||||
*
|
||||
* @return the human-friendly formatted block for this instance
|
||||
*/
|
||||
public String printPermissionsBlock() {
|
||||
return printPermissionsBlock(this.mask);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(getClass().getName());
|
||||
sb.append("[").append(aclObjectIdentity).append(",").append(recipient);
|
||||
sb.append("=").append(printPermissionsBlock(mask)).append(" ");
|
||||
sb.append(printBinary(mask)).append(" (");
|
||||
sb.append(mask).append(")").append("]");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public int togglePermission(int permissionToToggle) {
|
||||
this.mask ^= permissionToToggle;
|
||||
|
||||
if (Arrays.binarySearch(validPermissions, this.mask) < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Resulting permission set will be invalid.");
|
||||
} else {
|
||||
return this.mask;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isPermitted(int maskToCheck, int permissionToCheck) {
|
||||
return ((maskToCheck & permissionToCheck) == permissionToCheck);
|
||||
}
|
||||
|
||||
private String printBinary(int i) {
|
||||
String s = Integer.toString(i, 2);
|
||||
|
||||
String pattern = "................................";
|
||||
|
||||
String temp1 = pattern.substring(0, pattern.length() - s.length());
|
||||
|
||||
String temp2 = temp1 + s;
|
||||
|
||||
return temp2.replace('0', '.');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* Interface representing the identity of an individual domain object instance.
|
||||
*
|
||||
* <P>
|
||||
* It should be noted that <code>AclObjectIdentity</code> instances are created
|
||||
* in various locations throughout the package. As
|
||||
* <code>AclObjectIdentity</code>s are used as the key for caching, it is
|
||||
* essential that implementations provide methods so that object-equality
|
||||
* rather than reference-equality can be relied upon by caches. In other
|
||||
* words, a cache can consider two <code>AclObjectIdentity</code>s equal if
|
||||
* <code>identity1.equals(identity2)</code>, rather than reference-equality of
|
||||
* <code>identity1==identity2</code>.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* In practical terms this means you must implement the standard
|
||||
* <code>java.lang.Object</code> methods shown below. Depending on your
|
||||
* cache's internal structure, you may also need to implement special
|
||||
* interfaces such as <code>java.util.Comparator</code> or
|
||||
* <code>java.lang.Comparable</code>.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface AclObjectIdentity extends Serializable {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Refer to the <code>java.lang.Object</code> documentation for the
|
||||
* interface contract.
|
||||
*
|
||||
* @param obj to be compared
|
||||
*
|
||||
* @return <code>true</code> if the objects are equal, <code>false</code>
|
||||
* otherwise
|
||||
*/
|
||||
public boolean equals(Object obj);
|
||||
|
||||
/**
|
||||
* Refer to the <code>java.lang.Object</code> documentation for the
|
||||
* interface contract.
|
||||
*
|
||||
* @return a hash code representation of this object
|
||||
*/
|
||||
public int hashCode();
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
/**
|
||||
* Indicates a domain object instance is able to provide {@link
|
||||
* AclObjectIdentity} information.
|
||||
*
|
||||
* <P>
|
||||
* Domain objects must implement this interface if they wish to provide an
|
||||
* <code>AclObjectIdentity</code> rather than it being determined by relying
|
||||
* classes. Specifically, the {@link BasicAclProvider} detects and uses this
|
||||
* interface.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface AclObjectIdentityAware {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Retrieves the <code>AclObjectIdentity</code> for this instance.
|
||||
*
|
||||
* @return the ACL object identity for this instance (can never be
|
||||
* <code>null</code>)
|
||||
*/
|
||||
public AclObjectIdentity getAclObjectIdentity();
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
/**
|
||||
* Represents a data access object that can return the {@link BasicAclEntry}s
|
||||
* applying to a given ACL object identity.
|
||||
*
|
||||
* <P>
|
||||
* <code>BasicAclDao</code> implementations are responsible for interpreting a
|
||||
* given {@link AclObjectIdentity} and being able to lookup and return the
|
||||
* corresponding {@link BasicAclEntry}[]s.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* <code>BasicAclDao</code>s many, but are not required to, allow the backend
|
||||
* ACL repository to specify the class of <code>BasicAclEntry</code>
|
||||
* implementations that should be returned.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface BasicAclDao {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Obtains the ACLs that apply to the specified domain instance.
|
||||
*
|
||||
* <P>
|
||||
* Does <b>not</b> perform caching, include ACLs from any inheritance
|
||||
* hierarchy or filter returned objects based on effective permissions.
|
||||
* Implementations are solely responsible for returning ACLs found in the
|
||||
* ACL repository for the specified object identity.
|
||||
* </p>
|
||||
*
|
||||
* @param aclObjectIdentity the domain object instance that ACL information
|
||||
* is being requested for (never <code>null</code>)
|
||||
*
|
||||
* @return the ACLs that apply (no <code>null</code>s are permitted in the
|
||||
* array), or <code>null</code> if no ACLs could be found for the
|
||||
* specified ACL object identity
|
||||
*/
|
||||
public BasicAclEntry[] getAcls(AclObjectIdentity aclObjectIdentity);
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
import net.sf.acegisecurity.acl.AclEntry;
|
||||
|
||||
|
||||
/**
|
||||
* Represents an entry in an access control list.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface BasicAclEntry extends AclEntry {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* This setter should <B>only</B> be used by DAO implementations.
|
||||
*
|
||||
* @param aclObjectIdentity an object which can be used to uniquely
|
||||
* identify the domain object instance subject of this ACL entry
|
||||
*/
|
||||
public void setAclObjectIdentity(AclObjectIdentity aclObjectIdentity);
|
||||
|
||||
/**
|
||||
* Indicates the domain object instance that is subject of this
|
||||
* <code>BasicAclEntry</code>. This information may be of interest to
|
||||
* relying classes (voters and business methods) that wish to know the
|
||||
* actual origination of the ACL entry (so as to distinguish individual
|
||||
* ACL entries from others contributed by the inheritance hierarchy).
|
||||
*
|
||||
* @return the ACL object identity that is subject of this ACL entry (never
|
||||
* <code>null</code>)
|
||||
*/
|
||||
public AclObjectIdentity getAclObjectIdentity();
|
||||
|
||||
/**
|
||||
* This setter should <B>only</B> be used by DAO implementations.
|
||||
*
|
||||
* @param aclObjectParentIdentity an object which represents the parent of
|
||||
* the domain object instance subject of this ACL entry, or
|
||||
* <code>null</code> if either the domain object instance has no
|
||||
* parent or its parent should be not used to compute an
|
||||
* inheritance hierarchy
|
||||
*/
|
||||
public void setAclObjectParentIdentity(
|
||||
AclObjectIdentity aclObjectParentIdentity);
|
||||
|
||||
/**
|
||||
* Indicates any ACL parent of the domain object instance. This is used by
|
||||
* <code>BasicAclProvider</code> to walk the inheritance hierarchy. An
|
||||
* domain object instance need <b>not</b> have a parent.
|
||||
*
|
||||
* @return the ACL object identity that is the parent of this ACL entry
|
||||
* (may be <code>null</code> if no parent should be consulted)
|
||||
*/
|
||||
public AclObjectIdentity getAclObjectParentIdentity();
|
||||
|
||||
/**
|
||||
* This setter should <B>only</B> be used by DAO implementations.
|
||||
*
|
||||
* @param mask the integer representing the permissions bit mask
|
||||
*/
|
||||
public void setMask(int mask);
|
||||
|
||||
/**
|
||||
* Access control lists in this package are based on bit masking. The
|
||||
* integer value of the bit mask can be obtained from this method.
|
||||
*
|
||||
* @return the bit mask applicable to this ACL entry (zero indicates a bit
|
||||
* mask where no permissions have been granted)
|
||||
*/
|
||||
public int getMask();
|
||||
|
||||
/**
|
||||
* This setter should <B>only</B> be used by DAO implementations.
|
||||
*
|
||||
* @param recipient a representation of the recipient of this ACL entry
|
||||
* that makes sense to an <code>EffectiveAclsResolver</code>
|
||||
* implementation
|
||||
*/
|
||||
public void setRecipient(Object recipient);
|
||||
|
||||
/**
|
||||
* A domain object instance will usually have multiple
|
||||
* <code>BasicAclEntry</code>s. Each separate <code>BasicAclEntry</code>
|
||||
* applies to a particular "recipient". Typical examples of recipients
|
||||
* include (but do not necessarily have to include) usernames, role names,
|
||||
* complex granted authorities etc.
|
||||
*
|
||||
* <P>
|
||||
* <B>It is essential that only one <code>BasicAclEntry</code> exists for a
|
||||
* given recipient</B>. Otherwise conflicts as to the mask that should
|
||||
* apply to a given recipient will occur.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* This method indicates which recipient this <code>BasicAclEntry</code>
|
||||
* applies to. The returned object type will vary depending on the type of
|
||||
* recipient. For instance, it might be a <code>String</code> containing a
|
||||
* username, or a <code>GrantedAuthorityImpl</code> containing a complex
|
||||
* granted authority that is being granted the permissions contained in
|
||||
* this access control entry. The {@link EffectiveAclsResolver} and {@link
|
||||
* BasicAclProvider#getAcls(Object, Authentication)} can process the
|
||||
* different recipient types and return only those that apply to a
|
||||
* specified <code>Authentication</code> object.
|
||||
* </p>
|
||||
*
|
||||
* @return the recipient of this access control list entry (never
|
||||
* <code>null</code>)
|
||||
*/
|
||||
public Object getRecipient();
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
/**
|
||||
* Provides a cache of {@link BasicAclEntry} objects.
|
||||
*
|
||||
* <P>
|
||||
* Implementations should provide appropriate methods to set their cache
|
||||
* parameters (eg time-to-live) and/or force removal of entities before their
|
||||
* normal expiration. These are not part of the
|
||||
* <code>BasicAclEntryCache</code> interface contract because they vary
|
||||
* depending on the type of caching system used (eg in-memory vs disk vs
|
||||
* cluster vs hybrid).
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface BasicAclEntryCache {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Obtains an array of {@link BasicAclEntry}s from the cache.
|
||||
*
|
||||
* @param aclObjectIdentity which should be obtained from the cache
|
||||
*
|
||||
* @return any applicable <code>BasicAclEntry</code>s (no
|
||||
* <code>null</code>s are permitted in the returned array) or
|
||||
* <code>null</code> if the object identity could not be found or
|
||||
* if the cache entry has expired
|
||||
*/
|
||||
public BasicAclEntry[] getEntriesFromCache(
|
||||
AclObjectIdentity aclObjectIdentity);
|
||||
|
||||
/**
|
||||
* Places an array of {@link BasicAclEntry}s in the cache.
|
||||
*
|
||||
* <P>
|
||||
* No <code>null</code>s are allowed in the passed array. If any
|
||||
* <code>null</code> is passed, the implementation may throw an exception.
|
||||
* </p>
|
||||
*
|
||||
* @param basicAclEntry the ACL entries to cache (the key will be extracted
|
||||
* from the {@link BasicAclEntry#getAclObjectIdentity()} method
|
||||
*/
|
||||
public void putEntriesInCache(BasicAclEntry[] basicAclEntry);
|
||||
}
|
|
@ -0,0 +1,341 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
import net.sf.acegisecurity.Authentication;
|
||||
import net.sf.acegisecurity.acl.AclEntry;
|
||||
import net.sf.acegisecurity.acl.AclProvider;
|
||||
import net.sf.acegisecurity.acl.basic.cache.NullAclEntryCache;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* <P>
|
||||
* Retrieves access control lists (ACL) entries for domain object instances
|
||||
* from a data access object (DAO).
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* This implementation will provide ACL lookup services for any object that it
|
||||
* can determine the {@link AclObjectIdentity} for by calling the {@link
|
||||
* #obtainIdentity(Object)} method. Subclasses can override this method if
|
||||
* they only want the <code>BasicAclProvider</code> responding to particular
|
||||
* domain object instances.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* <code>BasicAclProvider</code> will walk an inheritance hierarchy if a
|
||||
* <code>BasicAclEntry</code> returned by the DAO indicates it has a parent.
|
||||
* NB: inheritance occurs at a <I>domain instance object</I> level. It does
|
||||
* not occur at an ACL recipient level. This means
|
||||
* <B>all</B><code>BasicAclEntry</code>s for a given domain instance object
|
||||
* <B>must</B> have the <B>same</B> parent identity, or
|
||||
* <B>all</B><code>BasicAclEntry</code>s must have <code>null</code> as their
|
||||
* parent identity.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* A cache should be used. This is provided by the {@link BasicAclEntryCache}.
|
||||
* <code>BasicAclProvider</code> by default is setup to use the {@link
|
||||
* NullAclEntryCache}, which performs no caching.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* To implement the {@link #getAcls(Object, Authentication)} method,
|
||||
* <code>BasicAclProvider</code> requires a {@link EffectiveAclsResolver} to
|
||||
* be configured against it. By default the {@link
|
||||
* GrantedAuthorityEffectiveAclsResolver} is used.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class BasicAclProvider implements AclProvider, InitializingBean {
|
||||
//~ Static fields/initializers =============================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(BasicAclProvider.class);
|
||||
|
||||
/**
|
||||
* Marker added to the cache to indicate an AclObjectIdentity has no
|
||||
* corresponding BasicAclEntry[]s
|
||||
*/
|
||||
private static String RECIPIENT_FOR_CACHE_EMPTY = "RESERVED_RECIPIENT_NOBODY";
|
||||
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
/**
|
||||
* Must be set to an appropriate data access object. Defaults to
|
||||
* <code>null</code>.
|
||||
*/
|
||||
private BasicAclDao basicAclDao;
|
||||
private BasicAclEntryCache basicAclEntryCache = new NullAclEntryCache();
|
||||
private Class defaultAclObjectIdentityClass = NamedEntityObjectIdentity.class;
|
||||
private EffectiveAclsResolver effectiveAclsResolver = new GrantedAuthorityEffectiveAclsResolver();
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public AclEntry[] getAcls(Object domainInstance) {
|
||||
Map map = new HashMap();
|
||||
|
||||
AclObjectIdentity aclIdentity = obtainIdentity(domainInstance);
|
||||
|
||||
if (aclIdentity == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"domainInstance is not supported by this provider");
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking up: " + aclIdentity.toString());
|
||||
}
|
||||
|
||||
BasicAclEntry[] instanceAclEntries = lookup(aclIdentity);
|
||||
|
||||
// Exit if there is no ACL information or parent for this instance
|
||||
if (instanceAclEntries == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add the leaf objects to the Map, keyed on recipient
|
||||
for (int i = 0; i < instanceAclEntries.length; i++) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Explicit add: "
|
||||
+ instanceAclEntries[i].toString());
|
||||
}
|
||||
|
||||
map.put(instanceAclEntries[i].getRecipient(), instanceAclEntries[i]);
|
||||
}
|
||||
|
||||
AclObjectIdentity parent = instanceAclEntries[0]
|
||||
.getAclObjectParentIdentity();
|
||||
|
||||
while (parent != null) {
|
||||
BasicAclEntry[] parentAclEntries = lookup(parent);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Parent lookup: " + parent.toString());
|
||||
}
|
||||
|
||||
// Exit loop if parent couldn't be found (unexpected condition)
|
||||
if (parentAclEntries == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Parent could not be found in ACL repository");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Now add each _NEW_ recipient to the list
|
||||
for (int i = 0; i < parentAclEntries.length; i++) {
|
||||
if (!map.containsKey(parentAclEntries[i].getRecipient())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Added parent to map: "
|
||||
+ parentAclEntries[i].toString());
|
||||
}
|
||||
|
||||
map.put(parentAclEntries[i].getRecipient(),
|
||||
parentAclEntries[i]);
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Did NOT add parent to map: "
|
||||
+ parentAclEntries[i].toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare for next iteration of while loop
|
||||
parent = parentAclEntries[0].getAclObjectParentIdentity();
|
||||
}
|
||||
|
||||
Collection collection = map.values();
|
||||
|
||||
return (AclEntry[]) collection.toArray(new AclEntry[] {});
|
||||
}
|
||||
|
||||
public AclEntry[] getAcls(Object domainInstance,
|
||||
Authentication authentication) {
|
||||
AclEntry[] allAcls = (AclEntry[]) this.getAcls(domainInstance);
|
||||
|
||||
return this.effectiveAclsResolver.resolveEffectiveAcls(allAcls,
|
||||
authentication);
|
||||
}
|
||||
|
||||
public void setBasicAclDao(BasicAclDao basicAclDao) {
|
||||
this.basicAclDao = basicAclDao;
|
||||
}
|
||||
|
||||
public BasicAclDao getBasicAclDao() {
|
||||
return basicAclDao;
|
||||
}
|
||||
|
||||
public void setBasicAclEntryCache(BasicAclEntryCache basicAclEntryCache) {
|
||||
this.basicAclEntryCache = basicAclEntryCache;
|
||||
}
|
||||
|
||||
public BasicAclEntryCache getBasicAclEntryCache() {
|
||||
return basicAclEntryCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows selection of the <code>AclObjectIdentity</code> class that an
|
||||
* attempt should be made to construct if the passed object does not
|
||||
* implement <code>AclObjectIdentityAware</code>.
|
||||
*
|
||||
* <P>
|
||||
* NB: Any <code>defaultAclObjectIdentityClass</code><b>must</b> provide a
|
||||
* public constructor that accepts an <code>Object</code>. Otherwise it is
|
||||
* not possible for the <code>BasicAclProvider</code> to try to create the
|
||||
* <code>AclObjectIdentity</code> instance at runtime.
|
||||
* </p>
|
||||
*
|
||||
* @param defaultAclObjectIdentityClass
|
||||
*/
|
||||
public void setDefaultAclObjectIdentityClass(
|
||||
Class defaultAclObjectIdentityClass) {
|
||||
this.defaultAclObjectIdentityClass = defaultAclObjectIdentityClass;
|
||||
}
|
||||
|
||||
public Class getDefaultAclObjectIdentityClass() {
|
||||
return defaultAclObjectIdentityClass;
|
||||
}
|
||||
|
||||
public void setEffectiveAclsResolver(
|
||||
EffectiveAclsResolver effectiveAclsResolver) {
|
||||
this.effectiveAclsResolver = effectiveAclsResolver;
|
||||
}
|
||||
|
||||
public EffectiveAclsResolver getEffectiveAclsResolver() {
|
||||
return effectiveAclsResolver;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
if (basicAclDao == null) {
|
||||
throw new IllegalArgumentException("basicAclDao required");
|
||||
}
|
||||
|
||||
if (basicAclEntryCache == null) {
|
||||
throw new IllegalArgumentException("basicAclEntryCache required");
|
||||
}
|
||||
|
||||
if (effectiveAclsResolver == null) {
|
||||
throw new IllegalArgumentException("effectiveAclsResolver required");
|
||||
}
|
||||
|
||||
if ((defaultAclObjectIdentityClass == null)
|
||||
|| (!AclObjectIdentity.class.isAssignableFrom(
|
||||
this.defaultAclObjectIdentityClass))) {
|
||||
throw new IllegalArgumentException(
|
||||
"defaultAclObjectIdentityClass that implements AclObjectIdentity is required");
|
||||
}
|
||||
|
||||
try {
|
||||
Constructor constructor = defaultAclObjectIdentityClass
|
||||
.getConstructor(new Class[] {Object.class});
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
throw new IllegalArgumentException(
|
||||
"defaultAclObjectIdentityClass must provide a constructor that accepts the domain object instance!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates support for the passed object if it an
|
||||
* <code>AclObjectIdentity</code> is returned by {@link
|
||||
* #obtainIdentity(Object)}.
|
||||
*
|
||||
* @param domainInstance the instance to check
|
||||
*
|
||||
* @return <code>true</code> if this provider supports the passed object,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
public boolean supports(Object domainInstance) {
|
||||
if (obtainIdentity(domainInstance) == null) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method looks up the <code>AclObjectIdentity</code> of a passed
|
||||
* domain object instance.
|
||||
*
|
||||
* <P>
|
||||
* This implementation attempts to obtain the
|
||||
* <code>AclObjectIdentity</code> via reflection inspection of the class
|
||||
* for the {@link AclObjectIdentityAware} interface. If this fails, an
|
||||
* attempt is made to construct a {@link
|
||||
* #getDefaultAclObjectIdentityClass()} object by passing the domain
|
||||
* instance object into its constructor.
|
||||
* </p>
|
||||
*
|
||||
* @param domainInstance the domain object instance (never
|
||||
* <code>null</code>)
|
||||
*
|
||||
* @return an ACL object identity, or <code>null</code> if one could not be
|
||||
* obtained
|
||||
*/
|
||||
protected AclObjectIdentity obtainIdentity(Object domainInstance) {
|
||||
if (domainInstance instanceof AclObjectIdentityAware) {
|
||||
AclObjectIdentityAware aclObjectIdentityAware = (AclObjectIdentityAware) domainInstance;
|
||||
|
||||
return aclObjectIdentityAware.getAclObjectIdentity();
|
||||
}
|
||||
|
||||
try {
|
||||
Constructor constructor = defaultAclObjectIdentityClass
|
||||
.getConstructor(new Class[] {Object.class});
|
||||
|
||||
return (AclObjectIdentity) constructor.newInstance(new Object[] {domainInstance});
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private BasicAclEntry[] lookup(AclObjectIdentity aclObjectIdentity) {
|
||||
BasicAclEntry[] result = basicAclEntryCache.getEntriesFromCache(aclObjectIdentity);
|
||||
|
||||
if (result != null) {
|
||||
if (result[0].getRecipient().equals(RECIPIENT_FOR_CACHE_EMPTY)) {
|
||||
return null;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result = basicAclDao.getAcls(aclObjectIdentity);
|
||||
|
||||
if (result == null) {
|
||||
SimpleAclEntry[] emptyAclEntries = {new SimpleAclEntry(RECIPIENT_FOR_CACHE_EMPTY,
|
||||
aclObjectIdentity, null, 0)};
|
||||
basicAclEntryCache.putEntriesInCache(emptyAclEntries);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
basicAclEntryCache.putEntriesInCache(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
import net.sf.acegisecurity.Authentication;
|
||||
import net.sf.acegisecurity.acl.AclEntry;
|
||||
|
||||
|
||||
/**
|
||||
* Determines the ACLs that are effective for a given
|
||||
* <code>Authentication</code> object.
|
||||
*
|
||||
* <P>
|
||||
* Implementations will vary depending on their ability to interpret the
|
||||
* "recipient" object types contained in {@link BasicAclEntry} instances, and
|
||||
* how those recipient object types correspond to
|
||||
* <code>Authentication</code>-presented principals and granted authorities.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* Implementations should not filter the resulting ACL list from lower-order
|
||||
* permissions. So if a resulting ACL list grants a "read" permission, an
|
||||
* "unlimited" permission and a "zero" permission (due to the effective ACLs
|
||||
* for different granted authorities held by the <code>Authentication</code>
|
||||
* object), all three permissions would be returned as distinct
|
||||
* <code>BasicAclEntry</code> instances. It is the responsibility of the
|
||||
* relying classes (voters and business methods) to ignore or handle
|
||||
* lower-order permissions in a business logic dependent manner.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface EffectiveAclsResolver {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Determines the ACLs that apply to the presented
|
||||
* <code>Authentication</code> object.
|
||||
*
|
||||
* @param allAcls every ACL assigned to a domain object instance
|
||||
* @param filteredBy the principal (populated with
|
||||
* <code>GrantedAuthority</code>s along with any other members that
|
||||
* relate to role or group membership) that effective ACLs should
|
||||
* be returned for
|
||||
*
|
||||
* @return the ACLs that apply to the presented principal, or
|
||||
* <code>null</code> if there are none after filtering
|
||||
*/
|
||||
public AclEntry[] resolveEffectiveAcls(AclEntry[] allAcls,
|
||||
Authentication filteredBy);
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
import net.sf.acegisecurity.Authentication;
|
||||
import net.sf.acegisecurity.GrantedAuthority;
|
||||
import net.sf.acegisecurity.acl.AclEntry;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link EffectiveAclsResolver}.
|
||||
*
|
||||
* <P>
|
||||
* This implementation does not need to understand the "recipient" types
|
||||
* presented in a <code>BasicAclEntry</code> because it merely delegates to
|
||||
* the detected {@link Authentication#getPrincipal()} or {@link
|
||||
* Authentication#getAuthorities()}. The principal object or granted
|
||||
* authorities object has its <code>Object.equals(recipient)</code> method
|
||||
* called to make the decision as to whether the recipient in the
|
||||
* <code>BasicAclEntry</code> is the same as the principal or granted
|
||||
* authority.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* This class should prove an adequate ACLs resolver if you're using standard
|
||||
* Acegi Security classes. This is because the typical
|
||||
* <code>Authentication</code> token is
|
||||
* <code>UsernamePasswordAuthenticationToken</code>, which for its
|
||||
* <code>principal</code> is usually a <code>String</code>. The
|
||||
* <code>GrantedAuthorityImpl</code> is typically used for granted
|
||||
* authorities, which tests for equality based on a <code>String</code>. This
|
||||
* means <code>BasicAclDao</code>s simply need to return a <code>String</code>
|
||||
* to represent the recipient. If you use non-<code>String</code> objects, you
|
||||
* will probably require an alternative <code>EffectiveAclsResolver</code>.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class GrantedAuthorityEffectiveAclsResolver
|
||||
implements EffectiveAclsResolver {
|
||||
//~ Methods ================================================================
|
||||
|
||||
public AclEntry[] resolveEffectiveAcls(AclEntry[] allAcls,
|
||||
Authentication filteredBy) {
|
||||
List list = new Vector();
|
||||
|
||||
for (int i = 0; i < allAcls.length; i++) {
|
||||
if (!(allAcls[i] instanceof BasicAclEntry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object recipient = ((BasicAclEntry) allAcls[i]).getRecipient();
|
||||
|
||||
// Allow the Authentication's getPrincipal to decide whether
|
||||
// the presented recipient is "equal" (allows BasicAclDaos to
|
||||
// return Strings rather than proper objects in simple cases)
|
||||
if (filteredBy.getPrincipal().equals(recipient)) {
|
||||
list.add(allAcls[i]);
|
||||
} else {
|
||||
// No direct match against principal; try each authority.
|
||||
// As with the principal, allow each of the Authentication's
|
||||
// granted authorities to decide whether the presented
|
||||
// recipient is "equal"
|
||||
GrantedAuthority[] authorities = filteredBy.getAuthorities();
|
||||
|
||||
if ((authorities == null) || (authorities.length == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int k = 0; k < authorities.length; k++) {
|
||||
if (authorities[k].equals(recipient)) {
|
||||
list.add(allAcls[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return null if appropriate (as per interface contract)
|
||||
if (list.size() > 0) {
|
||||
return (BasicAclEntry[]) list.toArray(new BasicAclEntry[] {});
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link AclObjectIdentity}.
|
||||
*
|
||||
* <P>
|
||||
* Uses <code>String</code>s to store the identity of the domain object
|
||||
* instance. Also offers a constructor that uses reflection to build the
|
||||
* identity information.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class NamedEntityObjectIdentity implements AclObjectIdentity {
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private String classname;
|
||||
private String id;
|
||||
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public NamedEntityObjectIdentity(String classname, String id) {
|
||||
if ((classname == null) || "".equals(classname)) {
|
||||
throw new IllegalArgumentException("classname required");
|
||||
}
|
||||
|
||||
if ((id == null) || "".equals(id)) {
|
||||
throw new IllegalArgumentException("id required");
|
||||
}
|
||||
|
||||
this.classname = classname;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>NamedEntityObjectIdentity</code> based on the passed
|
||||
* object instance. The passed object must provide a <code>getId()</code>
|
||||
* method, otherwise an exception will be thrown.
|
||||
*
|
||||
* @param object the domain object instance to create an identity for
|
||||
*
|
||||
* @throws IllegalAccessException
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
public NamedEntityObjectIdentity(Object object)
|
||||
throws IllegalAccessException, InvocationTargetException {
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException("object cannot be null");
|
||||
}
|
||||
|
||||
this.classname = object.getClass().getName();
|
||||
|
||||
Class clazz = object.getClass();
|
||||
|
||||
try {
|
||||
Method id = clazz.getMethod("getId", null);
|
||||
Object result = id.invoke(object, null);
|
||||
this.id = result.toString();
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
throw new IllegalArgumentException(
|
||||
"object does not provide a getId() method");
|
||||
}
|
||||
}
|
||||
|
||||
protected NamedEntityObjectIdentity() {
|
||||
throw new IllegalArgumentException("Cannot use default constructor");
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Indicates the classname portion of the object identity.
|
||||
*
|
||||
* @return the classname (never <code>null</code>)
|
||||
*/
|
||||
public String getClassname() {
|
||||
return classname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the instance identity portion of the object identity.
|
||||
*
|
||||
* @return the instance identity (never <code>null</code>)
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Important so caching operates properly.
|
||||
*
|
||||
* <P>
|
||||
* Considers an object of the same class equal if it has the same
|
||||
* <code>classname</code> and <code>id</code> properties.
|
||||
* </p>
|
||||
*
|
||||
* @param arg0 object to compare
|
||||
*
|
||||
* @return <code>true</code> if the presented object matches this object
|
||||
*/
|
||||
public boolean equals(Object arg0) {
|
||||
if (arg0 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(arg0 instanceof NamedEntityObjectIdentity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NamedEntityObjectIdentity other = (NamedEntityObjectIdentity) arg0;
|
||||
|
||||
if (this.getId().equals(other.getId())
|
||||
&& this.getClassname().equals(other.getClassname())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Important so caching operates properly.
|
||||
*
|
||||
* @return the hash of the classname and id
|
||||
*/
|
||||
public int hashCode() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(this.classname).append(this.id);
|
||||
|
||||
return sb.toString().hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(this.getClass().getName()).append("[");
|
||||
sb.append("Classname: ").append(this.classname);
|
||||
sb.append("; Identity: ").append(this.id).append("]");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Stores some privileges typical of a domain object.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class SimpleAclEntry extends AbstractBasicAclEntry {
|
||||
//~ Static fields/initializers =============================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SimpleAclEntry.class);
|
||||
|
||||
// Base permissions we permit
|
||||
public static final int NOTHING = 0;
|
||||
public static final int ADMINISTRATION = (int) Math.pow(2, 0);
|
||||
public static final int READ = (int) Math.pow(2, 1);
|
||||
public static final int WRITE = (int) Math.pow(2, 2);
|
||||
public static final int CREATE = (int) Math.pow(2, 3);
|
||||
public static final int DELETE = (int) Math.pow(2, 4);
|
||||
|
||||
// Combinations of base permissions we permit
|
||||
public static final int READ_WRITE_CREATE_DELETE = READ | WRITE | CREATE
|
||||
| DELETE;
|
||||
public static final int READ_WRITE_CREATE = READ | WRITE | CREATE;
|
||||
public static final int READ_WRITE = READ | WRITE;
|
||||
public static final int READ_WRITE_DELETE = READ | WRITE | DELETE;
|
||||
|
||||
// Array required by the abstract superclass via getValidPermissions()
|
||||
private static final int[] validPermissions = {NOTHING, ADMINISTRATION, READ, WRITE, CREATE, DELETE, READ_WRITE_CREATE_DELETE, READ_WRITE_CREATE, READ_WRITE, READ_WRITE_DELETE};
|
||||
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
/**
|
||||
* Allows {@link BasicAclDao} implementations to construct this object
|
||||
* using <code>newInstance()</code>.
|
||||
*
|
||||
* <P>
|
||||
* Normal classes should <B>not</B> use this default constructor.
|
||||
* </p>
|
||||
*/
|
||||
public SimpleAclEntry() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SimpleAclEntry(Object recipient,
|
||||
AclObjectIdentity aclObjectIdentity,
|
||||
AclObjectIdentity aclObjectParentIdentity, int mask) {
|
||||
super(recipient, aclObjectIdentity, aclObjectParentIdentity, mask);
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public int[] getValidPermissions() {
|
||||
return validPermissions;
|
||||
}
|
||||
|
||||
public String printPermissionsBlock(int i) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
if (isPermitted(i, ADMINISTRATION)) {
|
||||
sb.append('A');
|
||||
} else {
|
||||
sb.append('-');
|
||||
}
|
||||
|
||||
if (isPermitted(i, READ)) {
|
||||
sb.append('R');
|
||||
} else {
|
||||
sb.append('-');
|
||||
}
|
||||
|
||||
if (isPermitted(i, WRITE)) {
|
||||
sb.append('W');
|
||||
} else {
|
||||
sb.append('-');
|
||||
}
|
||||
|
||||
if (isPermitted(i, CREATE)) {
|
||||
sb.append('C');
|
||||
} else {
|
||||
sb.append('-');
|
||||
}
|
||||
|
||||
if (isPermitted(i, DELETE)) {
|
||||
sb.append('D');
|
||||
} else {
|
||||
sb.append('-');
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
78
core/src/main/java/org/acegisecurity/acl/basic/cache/BasicAclEntryHolder.java
vendored
Normal file
78
core/src/main/java/org/acegisecurity/acl/basic/cache/BasicAclEntryHolder.java
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic.cache;
|
||||
|
||||
import net.sf.acegisecurity.acl.basic.BasicAclEntry;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* Used by {@link EhCacheBasedAclEntryCache} to store the array of
|
||||
* <code>BasicAclEntry</code>s in the cache.
|
||||
*
|
||||
* <P>
|
||||
* This is necessary because caches store a single object per key, not an
|
||||
* array.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* This class uses value object semantics. ie: construction-based
|
||||
* initialisation without any setters for the properties.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class BasicAclEntryHolder implements Serializable {
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private BasicAclEntry[] basicAclEntries;
|
||||
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
/**
|
||||
* Constructs the <code>BasicAclEntryHolder</code>.
|
||||
*
|
||||
* @param aclEntries to cache (any <code>null</code>s will cause an
|
||||
* exception, which should not be a problem as the contract for
|
||||
* <code>BasicAclEntryCache</code> allows exceptions if
|
||||
* <code>null</code>s are presented)
|
||||
*
|
||||
* @throws IllegalArgumentException if a <code>null</code> exists anywhere
|
||||
* in the <code>aclEntries</code> or if a <code>null</code> is
|
||||
* passed to the constructor
|
||||
*/
|
||||
public BasicAclEntryHolder(BasicAclEntry[] aclEntries) {
|
||||
if (aclEntries == null) {
|
||||
throw new IllegalArgumentException("aclEntries cannot be null");
|
||||
}
|
||||
|
||||
for (int i = 0; i < aclEntries.length; i++) {
|
||||
if (aclEntries[i] == null) {
|
||||
throw new IllegalArgumentException("aclEntries cannot be null");
|
||||
}
|
||||
}
|
||||
|
||||
this.basicAclEntries = aclEntries;
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public BasicAclEntry[] getBasicAclEntries() {
|
||||
return basicAclEntries;
|
||||
}
|
||||
}
|
135
core/src/main/java/org/acegisecurity/acl/basic/cache/EhCacheBasedAclEntryCache.java
vendored
Normal file
135
core/src/main/java/org/acegisecurity/acl/basic/cache/EhCacheBasedAclEntryCache.java
vendored
Normal file
|
@ -0,0 +1,135 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic.cache;
|
||||
|
||||
import net.sf.acegisecurity.acl.basic.AclObjectIdentity;
|
||||
import net.sf.acegisecurity.acl.basic.BasicAclEntry;
|
||||
import net.sf.acegisecurity.acl.basic.BasicAclEntryCache;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheException;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sf.ehcache.Element;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
|
||||
|
||||
/**
|
||||
* Caches <code>BasicAclEntry</code>s using <A
|
||||
* HREF="http://ehcache.sourceforge.net">EHCACHE</a>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class EhCacheBasedAclEntryCache implements BasicAclEntryCache,
|
||||
InitializingBean, DisposableBean {
|
||||
//~ Static fields/initializers =============================================
|
||||
|
||||
private static final Log logger = LogFactory.getLog(EhCacheBasedAclEntryCache.class);
|
||||
private static final String CACHE_NAME = "ehCacheBasedAclEntryCache";
|
||||
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private Cache cache;
|
||||
private CacheManager manager;
|
||||
private int minutesToIdle = 5;
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public BasicAclEntry[] getEntriesFromCache(
|
||||
AclObjectIdentity aclObjectIdentity) {
|
||||
Element element = null;
|
||||
|
||||
try {
|
||||
element = cache.get(aclObjectIdentity);
|
||||
} catch (CacheException cacheException) {
|
||||
throw new DataRetrievalFailureException("Cache failure: "
|
||||
+ cacheException.getMessage());
|
||||
}
|
||||
|
||||
// Return null if cache element has expired or not found
|
||||
if (element == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cache miss: " + aclObjectIdentity);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cache hit: " + (element != null) + "; object: "
|
||||
+ aclObjectIdentity);
|
||||
}
|
||||
|
||||
BasicAclEntryHolder holder = (BasicAclEntryHolder) element.getValue();
|
||||
|
||||
return holder.getBasicAclEntries();
|
||||
}
|
||||
|
||||
public void setMinutesToIdle(int minutesToIdle) {
|
||||
this.minutesToIdle = minutesToIdle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies how many minutes an entry will remain in the cache from when
|
||||
* it was last accessed.
|
||||
*
|
||||
* <P>
|
||||
* Defaults to 5 minutes.
|
||||
* </p>
|
||||
*
|
||||
* @return Returns the minutes an element remains in the cache
|
||||
*/
|
||||
public int getMinutesToIdle() {
|
||||
return minutesToIdle;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (CacheManager.getInstance().cacheExists(CACHE_NAME)) {
|
||||
// don’t remove the cache
|
||||
} else {
|
||||
manager = CacheManager.create();
|
||||
|
||||
// Cache name, max memory, overflowToDisk, eternal, timeToLive, timeToIdle
|
||||
cache = new Cache(CACHE_NAME, Integer.MAX_VALUE, false, false,
|
||||
minutesToIdle * 60, minutesToIdle * 60);
|
||||
|
||||
manager.addCache(cache);
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() throws Exception {
|
||||
manager.removeCache(CACHE_NAME);
|
||||
}
|
||||
|
||||
public void putEntriesInCache(BasicAclEntry[] basicAclEntry) {
|
||||
BasicAclEntryHolder holder = new BasicAclEntryHolder(basicAclEntry);
|
||||
Element element = new Element(basicAclEntry[0].getAclObjectIdentity(),
|
||||
holder);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cache put: " + element.getKey());
|
||||
}
|
||||
|
||||
cache.put(element);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic.cache;
|
||||
|
||||
import net.sf.acegisecurity.acl.basic.AclObjectIdentity;
|
||||
import net.sf.acegisecurity.acl.basic.BasicAclEntry;
|
||||
import net.sf.acegisecurity.acl.basic.BasicAclEntryCache;
|
||||
|
||||
|
||||
/**
|
||||
* Does not perform any caching.
|
||||
*
|
||||
* <P>
|
||||
* <B>Do not use in production settings</B>, as ACL queries are likely to be
|
||||
* extensive.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class NullAclEntryCache implements BasicAclEntryCache {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* As nothing ever stored in the cache, will always return
|
||||
* <code>null</code>.
|
||||
*
|
||||
* @param aclObjectIdentity ignored
|
||||
*
|
||||
* @return always <code>null</code>
|
||||
*/
|
||||
public BasicAclEntry[] getEntriesFromCache(
|
||||
AclObjectIdentity aclObjectIdentity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Meets method signature but doesn't store in any cache.
|
||||
*
|
||||
* @param basicAclEntry ignored
|
||||
*/
|
||||
public void putEntriesInCache(BasicAclEntry[] basicAclEntry) {}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
Caches ACL information for the <code>BasicAclProvider</code>.
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,256 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic.jdbc;
|
||||
|
||||
import net.sf.acegisecurity.acl.basic.AclObjectIdentity;
|
||||
import net.sf.acegisecurity.acl.basic.BasicAclDao;
|
||||
import net.sf.acegisecurity.acl.basic.BasicAclEntry;
|
||||
import net.sf.acegisecurity.acl.basic.NamedEntityObjectIdentity;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
|
||||
import org.springframework.jdbc.core.SqlParameter;
|
||||
import org.springframework.jdbc.core.support.JdbcDaoSupport;
|
||||
import org.springframework.jdbc.object.MappingSqlQuery;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Retrieves ACL details from a JDBC location.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* A default database structure is assumed (see {@link
|
||||
* #DEF_ACLS_BY_OBJECT_IDENTITY_QUERY}). This may be overridden by setting the
|
||||
* default query strings to use. If this does not provide enough flexibility,
|
||||
* another strategy would be to subclass this class and override the {@link
|
||||
* MappingSqlQuery} instance used, via the {@link #initMappingSqlQueries()}
|
||||
* extension point.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class JdbcDaoImpl extends JdbcDaoSupport implements BasicAclDao {
|
||||
//~ Static fields/initializers =============================================
|
||||
|
||||
public static final String DEF_ACLS_BY_OBJECT_IDENTITY_QUERY = "SELECT OBJECT_IDENTITY, RECIPIENT, PARENT_OBJECT_IDENTITY, MASK, ACL_CLASS FROM acls WHERE object_identity = ?";
|
||||
private static final Log logger = LogFactory.getLog(JdbcDaoSupport.class);
|
||||
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private MappingSqlQuery aclsByObjectIdentity;
|
||||
private String aclsByObjectIdentityQuery;
|
||||
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public JdbcDaoImpl() {
|
||||
aclsByObjectIdentityQuery = DEF_ACLS_BY_OBJECT_IDENTITY_QUERY;
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Returns the ACLs associated with the requested
|
||||
* <code>AclObjectIdentity</code>.
|
||||
*
|
||||
* <P>
|
||||
* The {@link BasicAclEntry}s returned by this method will have
|
||||
* <code>String</code>-based recipients. This will not be a problem if you
|
||||
* are using the <code>GrantedAuthorityEffectiveAclsResolver</code>, which
|
||||
* is the default configured against <code>BasicAclProvider</code>.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* This method will only return ACLs for requests where the
|
||||
* <code>AclObjectIdentity</code> is of type {@link
|
||||
* NamedEntityObjectIdentity}. Of course, you can subclass or replace this
|
||||
* class and support your own custom <code>AclObjectIdentity</code> types.
|
||||
* </p>
|
||||
*
|
||||
* @param aclObjectIdentity for which ACL information is required (cannot
|
||||
* be <code>null</code> and must be an instance of
|
||||
* <code>NamedEntityObjectIdentity</code>)
|
||||
*
|
||||
* @return the ACLs that apply (without any <code>null</code>s inside the
|
||||
* array), or <code>null</code> if not found or if an incompatible
|
||||
* <code>AclObjectIdentity</code> was requested
|
||||
*/
|
||||
public BasicAclEntry[] getAcls(AclObjectIdentity aclObjectIdentity) {
|
||||
// Ensure we can process this type of AclObjectIdentity
|
||||
if (!(aclObjectIdentity instanceof NamedEntityObjectIdentity)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
NamedEntityObjectIdentity neoi = (NamedEntityObjectIdentity) aclObjectIdentity;
|
||||
|
||||
// Compose the String we expect to find in the RDBMS
|
||||
String aclObjectIdentityString = neoi.getClassname() + ":"
|
||||
+ neoi.getId();
|
||||
|
||||
// Lookup the BasicAclEntrys from RDBMS (may include null responses)
|
||||
List acls = aclsByObjectIdentity.execute(aclObjectIdentityString);
|
||||
|
||||
// Now prune list of null responses (to meet interface contract)
|
||||
List toReturnAcls = new Vector();
|
||||
Iterator iter = acls.iterator();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
Object object = iter.next();
|
||||
|
||||
if (object != null) {
|
||||
toReturnAcls.add(object);
|
||||
}
|
||||
}
|
||||
|
||||
// Return null if nothing of use found (to meet interface contract)
|
||||
if (toReturnAcls.size() > 0) {
|
||||
return (BasicAclEntry[]) toReturnAcls.toArray(new BasicAclEntry[] {});
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setAclsByObjectIdentity(
|
||||
MappingSqlQuery aclsByObjectIdentityQuery) {
|
||||
this.aclsByObjectIdentity = aclsByObjectIdentityQuery;
|
||||
}
|
||||
|
||||
public MappingSqlQuery getAclsByObjectIdentity() {
|
||||
return aclsByObjectIdentity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the default query string used to retrieve ACLs based on object
|
||||
* identity to be overriden, if default table or column names need to be
|
||||
* changed. The default query is {@link
|
||||
* #DEF_ACLS_BY_OBJECT_IDENTITY_QUERY}; when modifying this query, ensure
|
||||
* that all returned columns are mapped back to the same column names as
|
||||
* in the default query.
|
||||
*
|
||||
* @param queryString The query string to set
|
||||
*/
|
||||
public void setAclsByObjectIdentityQuery(String queryString) {
|
||||
aclsByObjectIdentityQuery = queryString;
|
||||
}
|
||||
|
||||
public String getAclsByObjectIdentityQuery() {
|
||||
return aclsByObjectIdentityQuery;
|
||||
}
|
||||
|
||||
protected void initDao() throws ApplicationContextException {
|
||||
initMappingSqlQueries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension point to allow other MappingSqlQuery objects to be substituted
|
||||
* in a subclass
|
||||
*/
|
||||
protected void initMappingSqlQueries() {
|
||||
setAclsByObjectIdentity(new AclsByObjectIdentityMapping(getDataSource()));
|
||||
}
|
||||
|
||||
//~ Inner Classes ==========================================================
|
||||
|
||||
/**
|
||||
* Query object to look up ACL entries.
|
||||
*
|
||||
* <P>
|
||||
* The executed SQL requires the following information be made available
|
||||
* from the indicated placeholders: 1. OBJECT_IDENTITY, 2. RECIPIENT, 3.
|
||||
* PARENT_OBJECT_IDENTITY, 4. MASK, and 5. ACL_CLASS
|
||||
* </p>
|
||||
*/
|
||||
protected class AclsByObjectIdentityMapping extends MappingSqlQuery {
|
||||
protected AclsByObjectIdentityMapping(DataSource ds) {
|
||||
super(ds, aclsByObjectIdentityQuery);
|
||||
declareParameter(new SqlParameter(Types.VARCHAR));
|
||||
compile();
|
||||
}
|
||||
|
||||
protected Object mapRow(ResultSet rs, int rownum)
|
||||
throws SQLException {
|
||||
String objectIdentity = rs.getString(1);
|
||||
String recipient = rs.getString(2);
|
||||
String parentObjectIdentity = rs.getString(3);
|
||||
int mask = rs.getInt(4);
|
||||
String aclClass = rs.getString(5);
|
||||
|
||||
// Try to create the indicated BasicAclEntry class
|
||||
BasicAclEntry entry;
|
||||
|
||||
try {
|
||||
Class aclClazz = this.getClass().getClassLoader().loadClass(aclClass);
|
||||
entry = (BasicAclEntry) aclClazz.newInstance();
|
||||
} catch (ClassNotFoundException cnf) {
|
||||
logger.error(cnf);
|
||||
|
||||
return null;
|
||||
} catch (InstantiationException ie) {
|
||||
logger.error(ie);
|
||||
|
||||
return null;
|
||||
} catch (IllegalAccessException iae) {
|
||||
logger.error(iae);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Now set each of the ACL's properties
|
||||
entry.setAclObjectIdentity(buildIdentity(objectIdentity));
|
||||
entry.setAclObjectParentIdentity(buildIdentity(parentObjectIdentity));
|
||||
entry.setRecipient(recipient);
|
||||
entry.setMask(mask);
|
||||
|
||||
if ((entry.getRecipient() == null)
|
||||
|| (entry.getAclObjectIdentity() == null)) {
|
||||
// Problem with retrieval of ACL
|
||||
// (shouldn't happen if DB schema defined NOT NULL columns)
|
||||
logger.error("recipient or aclObjectIdentity is null");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
private AclObjectIdentity buildIdentity(String identity) {
|
||||
if (identity == null) {
|
||||
// Must be an empty parent, so return null
|
||||
return null;
|
||||
}
|
||||
|
||||
int delim = identity.lastIndexOf(":");
|
||||
String classname = identity.substring(0, delim);
|
||||
String id = identity.substring(delim + 1);
|
||||
|
||||
return new NamedEntityObjectIdentity(classname, id);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
JDBC-based data access object for ACL information.
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
Access control list implementation based on integer bit masks.
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
<html>
|
||||
<body>
|
||||
Enables retrieval of access control lists (ACLs) for domain object instances.
|
||||
|
||||
<P>The goal of this package is to locate the <code>AclEntry</code>s
|
||||
that apply to a given domain object instance.
|
||||
</P>
|
||||
|
||||
<P>
|
||||
An <code>AclManager</code> has ultimate resposibility for obtaining the
|
||||
<code>AclEntry</code>s instances, with a provider-based implementation
|
||||
available via the <code>AclProviderManager</code> class (and
|
||||
its <code>AclProvider</code> interface.</P>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,207 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import net.sf.acegisecurity.Authentication;
|
||||
import net.sf.acegisecurity.GrantedAuthority;
|
||||
import net.sf.acegisecurity.GrantedAuthorityImpl;
|
||||
import net.sf.acegisecurity.acl.basic.NamedEntityObjectIdentity;
|
||||
import net.sf.acegisecurity.acl.basic.SimpleAclEntry;
|
||||
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link AclProviderManager}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AclProviderManagerTests extends TestCase {
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public AclProviderManagerTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public AclProviderManagerTests(String arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public final void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(AclProviderManagerTests.class);
|
||||
}
|
||||
|
||||
public void testAclLookupFails() {
|
||||
AclProviderManager mgr = makeProviderManager();
|
||||
assertNull(mgr.getAcls(new Integer(5)));
|
||||
}
|
||||
|
||||
public void testAclLookupForGivenAuthenticationSuccess() {
|
||||
AclProviderManager mgr = makeProviderManager();
|
||||
assertNotNull(mgr.getAcls("STRING",
|
||||
new UsernamePasswordAuthenticationToken("marissa", "not used")));
|
||||
}
|
||||
|
||||
public void testAclLookupSuccess() {
|
||||
AclProviderManager mgr = makeProviderManager();
|
||||
assertNotNull(mgr.getAcls("STRING"));
|
||||
}
|
||||
|
||||
public void testRejectsNulls() {
|
||||
AclProviderManager mgr = new AclProviderManager();
|
||||
|
||||
try {
|
||||
mgr.getAcls(null);
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
try {
|
||||
mgr.getAcls(null,
|
||||
new UsernamePasswordAuthenticationToken("marissa", "not used"));
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
try {
|
||||
mgr.getAcls("SOME_DOMAIN_INSTANCE", null);
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testReturnsNullIfNoSupportingProvider() {
|
||||
AclProviderManager mgr = makeProviderManager();
|
||||
assertNull(mgr.getAcls(new Integer(4),
|
||||
new UsernamePasswordAuthenticationToken("marissa", "not used")));
|
||||
assertNull(mgr.getAcls(new Integer(4)));
|
||||
}
|
||||
|
||||
public void testStartupFailsIfProviderListNotContainingProviders()
|
||||
throws Exception {
|
||||
List providers = new Vector();
|
||||
providers.add("THIS_IS_NOT_A_PROVIDER");
|
||||
|
||||
AclProviderManager mgr = new AclProviderManager();
|
||||
|
||||
try {
|
||||
mgr.setProviders(providers);
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testStartupFailsIfProviderListNotSet()
|
||||
throws Exception {
|
||||
AclProviderManager mgr = new AclProviderManager();
|
||||
|
||||
try {
|
||||
mgr.afterPropertiesSet();
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testStartupFailsIfProviderListNull() throws Exception {
|
||||
AclProviderManager mgr = new AclProviderManager();
|
||||
|
||||
try {
|
||||
mgr.setProviders(null);
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testSuccessfulStartup() throws Exception {
|
||||
AclProviderManager mgr = makeProviderManager();
|
||||
mgr.afterPropertiesSet();
|
||||
assertTrue(true);
|
||||
assertEquals(1, mgr.getProviders().size());
|
||||
}
|
||||
|
||||
private AclProviderManager makeProviderManager() {
|
||||
MockProvider provider1 = new MockProvider();
|
||||
List providers = new Vector();
|
||||
providers.add(provider1);
|
||||
|
||||
AclProviderManager mgr = new AclProviderManager();
|
||||
mgr.setProviders(providers);
|
||||
|
||||
return mgr;
|
||||
}
|
||||
|
||||
//~ Inner Classes ==========================================================
|
||||
|
||||
private class MockProvider implements AclProvider {
|
||||
private UsernamePasswordAuthenticationToken marissa = new UsernamePasswordAuthenticationToken("marissa",
|
||||
"not used",
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_FOO"), new GrantedAuthorityImpl("ROLE_BAR")});
|
||||
private SimpleAclEntry entry100Marissa = new SimpleAclEntry(marissa
|
||||
.getPrincipal(),
|
||||
new NamedEntityObjectIdentity("OBJECT", "100"), null, 2);
|
||||
private UsernamePasswordAuthenticationToken scott = new UsernamePasswordAuthenticationToken("scott",
|
||||
"not used",
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_FOO"), new GrantedAuthorityImpl("ROLE_MANAGER")});
|
||||
private SimpleAclEntry entry100Scott = new SimpleAclEntry(scott
|
||||
.getPrincipal(),
|
||||
new NamedEntityObjectIdentity("OBJECT", "100"), null, 4);
|
||||
|
||||
public AclEntry[] getAcls(Object domainInstance,
|
||||
Authentication authentication) {
|
||||
if (authentication.getPrincipal().equals(scott.getPrincipal())) {
|
||||
return new AclEntry[] {entry100Scott};
|
||||
}
|
||||
|
||||
if (authentication.getPrincipal().equals(marissa.getPrincipal())) {
|
||||
return new AclEntry[] {entry100Marissa};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public AclEntry[] getAcls(Object domainInstance) {
|
||||
return new AclEntry[] {entry100Marissa, entry100Scott};
|
||||
}
|
||||
|
||||
/**
|
||||
* Only supports <code>Object</code>s of type <code>String</code>
|
||||
*
|
||||
* @param domainInstance DOCUMENT ME!
|
||||
*
|
||||
* @return DOCUMENT ME!
|
||||
*/
|
||||
public boolean supports(Object domainInstance) {
|
||||
return (domainInstance instanceof String);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,365 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import net.sf.acegisecurity.Authentication;
|
||||
import net.sf.acegisecurity.PopulatedDatabase;
|
||||
import net.sf.acegisecurity.acl.AclEntry;
|
||||
import net.sf.acegisecurity.acl.basic.cache.BasicAclEntryHolder;
|
||||
import net.sf.acegisecurity.acl.basic.cache.NullAclEntryCache;
|
||||
import net.sf.acegisecurity.acl.basic.jdbc.JdbcDaoImpl;
|
||||
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link BasicAclProvider}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class BasicAclProviderTests extends TestCase {
|
||||
//~ Static fields/initializers =============================================
|
||||
|
||||
public static final String OBJECT_IDENTITY = "net.sf.acegisecurity.acl.DomainObject";
|
||||
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public BasicAclProviderTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BasicAclProviderTests(String arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public final void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(BasicAclProviderTests.class);
|
||||
}
|
||||
|
||||
public void testCachingUsedProperly() throws Exception {
|
||||
BasicAclProvider provider = new BasicAclProvider();
|
||||
provider.setBasicAclDao(makePopulatedJdbcDao());
|
||||
|
||||
MockCache cache = new MockCache();
|
||||
provider.setBasicAclEntryCache(cache);
|
||||
|
||||
assertEquals(0, cache.getGets());
|
||||
assertEquals(0, cache.getGetsHits());
|
||||
assertEquals(0, cache.getPuts());
|
||||
assertEquals(0, cache.getBackingMap().size());
|
||||
|
||||
Object object = new MockDomain(1); // has no parents
|
||||
provider.getAcls(object);
|
||||
|
||||
assertEquals(1, cache.getGets());
|
||||
assertEquals(0, cache.getGetsHits());
|
||||
assertEquals(1, cache.getPuts());
|
||||
assertEquals(1, cache.getBackingMap().size());
|
||||
|
||||
provider.getAcls(object);
|
||||
|
||||
assertEquals(2, cache.getGets());
|
||||
assertEquals(1, cache.getGetsHits());
|
||||
assertEquals(1, cache.getPuts());
|
||||
assertEquals(1, cache.getBackingMap().size());
|
||||
|
||||
object = new MockDomain(1000); // does not exist
|
||||
|
||||
provider.getAcls(object);
|
||||
|
||||
assertEquals(3, cache.getGets());
|
||||
assertEquals(1, cache.getGetsHits());
|
||||
assertEquals(2, cache.getPuts());
|
||||
assertEquals(2, cache.getBackingMap().size());
|
||||
|
||||
provider.getAcls(object);
|
||||
|
||||
assertEquals(4, cache.getGets());
|
||||
assertEquals(2, cache.getGetsHits());
|
||||
assertEquals(2, cache.getPuts());
|
||||
assertEquals(2, cache.getBackingMap().size());
|
||||
|
||||
provider.getAcls(object);
|
||||
|
||||
assertEquals(5, cache.getGets());
|
||||
assertEquals(3, cache.getGetsHits());
|
||||
assertEquals(2, cache.getPuts());
|
||||
assertEquals(2, cache.getBackingMap().size());
|
||||
}
|
||||
|
||||
public void testExceptionThrownIfUnsupportedObjectIsSubmitted()
|
||||
throws Exception {
|
||||
BasicAclProvider provider = new BasicAclProvider();
|
||||
provider.setBasicAclDao(makePopulatedJdbcDao());
|
||||
|
||||
// this one should NOT be supported, as it has no getId() method
|
||||
assertFalse(provider.supports(new Integer(34)));
|
||||
|
||||
// try anyway
|
||||
try {
|
||||
provider.getAcls(new Integer(34));
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetAclsForInstanceNotFound() throws Exception {
|
||||
BasicAclProvider provider = new BasicAclProvider();
|
||||
provider.setBasicAclDao(makePopulatedJdbcDao());
|
||||
|
||||
Object object = new MockDomain(546464646);
|
||||
AclEntry[] acls = provider.getAcls(object);
|
||||
assertNull(acls);
|
||||
}
|
||||
|
||||
public void testGetAclsForInstanceWithParentLevels()
|
||||
throws Exception {
|
||||
BasicAclProvider provider = new BasicAclProvider();
|
||||
provider.setBasicAclDao(makePopulatedJdbcDao());
|
||||
|
||||
Object object = new MockDomain(6);
|
||||
AclEntry[] acls = provider.getAcls(object);
|
||||
assertEquals(2, acls.length);
|
||||
|
||||
assertEquals("scott", ((BasicAclEntry) acls[0]).getRecipient());
|
||||
assertEquals("ROLE_SUPERVISOR", ((BasicAclEntry) acls[1]).getRecipient());
|
||||
}
|
||||
|
||||
public void testGetAclsForInstanceWithoutParent() throws Exception {
|
||||
BasicAclProvider provider = new BasicAclProvider();
|
||||
provider.setBasicAclDao(makePopulatedJdbcDao());
|
||||
|
||||
Object object = new MockDomain(7);
|
||||
AclEntry[] acls = provider.getAcls(object);
|
||||
assertEquals(1, acls.length);
|
||||
}
|
||||
|
||||
public void testGetAclsWithAuthentication() throws Exception {
|
||||
BasicAclProvider provider = new BasicAclProvider();
|
||||
provider.setBasicAclDao(makePopulatedJdbcDao());
|
||||
|
||||
Authentication scott = new UsernamePasswordAuthenticationToken("scott",
|
||||
"unused");
|
||||
|
||||
Object object = new MockDomain(6);
|
||||
AclEntry[] acls = provider.getAcls(object, scott);
|
||||
|
||||
assertEquals(1, acls.length);
|
||||
assertEquals("scott", ((BasicAclEntry) acls[0]).getRecipient());
|
||||
}
|
||||
|
||||
public void testGettersSetters() {
|
||||
BasicAclProvider provider = new BasicAclProvider();
|
||||
assertEquals(NullAclEntryCache.class,
|
||||
provider.getBasicAclEntryCache().getClass());
|
||||
assertEquals(NamedEntityObjectIdentity.class,
|
||||
provider.getDefaultAclObjectIdentityClass());
|
||||
assertEquals(GrantedAuthorityEffectiveAclsResolver.class,
|
||||
provider.getEffectiveAclsResolver().getClass());
|
||||
|
||||
provider.setBasicAclEntryCache(null);
|
||||
assertNull(provider.getBasicAclEntryCache());
|
||||
|
||||
provider.setDefaultAclObjectIdentityClass(null);
|
||||
assertNull(provider.getDefaultAclObjectIdentityClass());
|
||||
|
||||
provider.setEffectiveAclsResolver(null);
|
||||
assertNull(provider.getEffectiveAclsResolver());
|
||||
|
||||
provider.setBasicAclDao(new MockDao());
|
||||
assertNotNull(provider.getBasicAclDao());
|
||||
}
|
||||
|
||||
public void testStartupFailsIfNullAclDao() throws Exception {
|
||||
BasicAclProvider provider = new BasicAclProvider();
|
||||
|
||||
try {
|
||||
provider.afterPropertiesSet();
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testStartupFailsIfNullEffectiveAclsResolver()
|
||||
throws Exception {
|
||||
BasicAclProvider provider = new BasicAclProvider();
|
||||
provider.setBasicAclDao(makePopulatedJdbcDao());
|
||||
|
||||
provider.setEffectiveAclsResolver(null);
|
||||
|
||||
try {
|
||||
provider.afterPropertiesSet();
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testStartupFailsIfNullEntryCache() throws Exception {
|
||||
BasicAclProvider provider = new BasicAclProvider();
|
||||
provider.setBasicAclDao(makePopulatedJdbcDao());
|
||||
|
||||
provider.setBasicAclEntryCache(null);
|
||||
|
||||
try {
|
||||
provider.afterPropertiesSet();
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testStartupFailsIfProblemWithAclObjectIdentityClass()
|
||||
throws Exception {
|
||||
BasicAclProvider provider = new BasicAclProvider();
|
||||
provider.setBasicAclDao(makePopulatedJdbcDao());
|
||||
|
||||
// check nulls rejected
|
||||
provider.setDefaultAclObjectIdentityClass(null);
|
||||
|
||||
try {
|
||||
provider.afterPropertiesSet();
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
// check non-AclObjectIdentity classes are also rejected
|
||||
provider.setDefaultAclObjectIdentityClass(String.class);
|
||||
|
||||
try {
|
||||
provider.afterPropertiesSet();
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
// check AclObjectIdentity class without constructor accepting a
|
||||
// domain object is also rejected
|
||||
provider.setDefaultAclObjectIdentityClass(MockAclObjectIdentity.class);
|
||||
|
||||
try {
|
||||
provider.afterPropertiesSet();
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertEquals("defaultAclObjectIdentityClass must provide a constructor that accepts the domain object instance!",
|
||||
expected.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testSupports() throws Exception {
|
||||
BasicAclProvider provider = new BasicAclProvider();
|
||||
provider.setBasicAclDao(makePopulatedJdbcDao());
|
||||
|
||||
// this one should NOT be supported, as it has no getId() method
|
||||
assertFalse(provider.supports(new Integer(34)));
|
||||
|
||||
// this one SHOULD be supported, as it has a getId() method
|
||||
assertTrue(provider.supports(new SomeDomain()));
|
||||
|
||||
// this one SHOULD be supported, as it implements AclObjectIdentityAware
|
||||
assertTrue(provider.supports(new MockDomain(4)));
|
||||
}
|
||||
|
||||
private JdbcDaoImpl makePopulatedJdbcDao() throws Exception {
|
||||
JdbcDaoImpl dao = new JdbcDaoImpl();
|
||||
dao.setDataSource(PopulatedDatabase.getDataSource());
|
||||
dao.afterPropertiesSet();
|
||||
|
||||
return dao;
|
||||
}
|
||||
|
||||
//~ Inner Classes ==========================================================
|
||||
|
||||
private class MockCache implements BasicAclEntryCache {
|
||||
private Map map = new HashMap();
|
||||
private int gets = 0;
|
||||
private int getsHits = 0;
|
||||
private int puts = 0;
|
||||
|
||||
public Map getBackingMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public BasicAclEntry[] getEntriesFromCache(
|
||||
AclObjectIdentity aclObjectIdentity) {
|
||||
gets++;
|
||||
|
||||
Object result = map.get(aclObjectIdentity);
|
||||
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
getsHits++;
|
||||
|
||||
BasicAclEntryHolder holder = (BasicAclEntryHolder) result;
|
||||
|
||||
return holder.getBasicAclEntries();
|
||||
}
|
||||
|
||||
public int getGets() {
|
||||
return gets;
|
||||
}
|
||||
|
||||
public int getGetsHits() {
|
||||
return getsHits;
|
||||
}
|
||||
|
||||
public int getPuts() {
|
||||
return puts;
|
||||
}
|
||||
|
||||
public void putEntriesInCache(BasicAclEntry[] basicAclEntry) {
|
||||
puts++;
|
||||
|
||||
BasicAclEntryHolder holder = new BasicAclEntryHolder(basicAclEntry);
|
||||
map.put(basicAclEntry[0].getAclObjectIdentity(), holder);
|
||||
}
|
||||
}
|
||||
|
||||
private class MockDao implements BasicAclDao {
|
||||
public BasicAclEntry[] getAcls(AclObjectIdentity aclObjectIdentity) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class MockDomain implements AclObjectIdentityAware {
|
||||
private int id;
|
||||
|
||||
public MockDomain(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AclObjectIdentity getAclObjectIdentity() {
|
||||
return new NamedEntityObjectIdentity(OBJECT_IDENTITY,
|
||||
new Integer(id).toString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import net.sf.acegisecurity.GrantedAuthority;
|
||||
import net.sf.acegisecurity.GrantedAuthorityImpl;
|
||||
import net.sf.acegisecurity.acl.AclEntry;
|
||||
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link GrantedAuthorityEffectiveAclsResolver}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class GrantedAuthorityEffectiveAclsResolverTests extends TestCase {
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private SimpleAclEntry entry100RoleEverybody = new SimpleAclEntry("ROLE_EVERYBODY",
|
||||
new NamedEntityObjectIdentity("OBJECT", "100"), null, 14);
|
||||
private SimpleAclEntry entry100RoleOne = new SimpleAclEntry("ROLE_ONE",
|
||||
new NamedEntityObjectIdentity("OBJECT", "100"), null, 0);
|
||||
private SimpleAclEntry entry100RoleTwo = new SimpleAclEntry("ROLE_TWO",
|
||||
new NamedEntityObjectIdentity("OBJECT", "100"), null, 2);
|
||||
private UsernamePasswordAuthenticationToken scott = new UsernamePasswordAuthenticationToken("scott",
|
||||
"not used",
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_EVERYBODY"), new GrantedAuthorityImpl(
|
||||
"ROLE_TWO")});
|
||||
private SimpleAclEntry entry100Scott = new SimpleAclEntry(scott
|
||||
.getPrincipal(), new NamedEntityObjectIdentity("OBJECT", "100"),
|
||||
null, 4);
|
||||
private UsernamePasswordAuthenticationToken dianne = new UsernamePasswordAuthenticationToken("dianne",
|
||||
"not used");
|
||||
private UsernamePasswordAuthenticationToken marissa = new UsernamePasswordAuthenticationToken("marissa",
|
||||
"not used",
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_EVERYBODY"), new GrantedAuthorityImpl("ROLE_ONE")});
|
||||
private SimpleAclEntry entry100Marissa = new SimpleAclEntry(marissa
|
||||
.getPrincipal(), new NamedEntityObjectIdentity("OBJECT", "100"),
|
||||
null, 2);
|
||||
|
||||
// convenience group
|
||||
private SimpleAclEntry[] acls = {entry100Marissa, entry100Scott, entry100RoleEverybody, entry100RoleOne, entry100RoleTwo};
|
||||
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public GrantedAuthorityEffectiveAclsResolverTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public GrantedAuthorityEffectiveAclsResolverTests(String arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public final void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(GrantedAuthorityEffectiveAclsResolverTests.class);
|
||||
}
|
||||
|
||||
public void testResolveAclsForDianneWhoHasANullForAuthorities() {
|
||||
GrantedAuthorityEffectiveAclsResolver resolver = new GrantedAuthorityEffectiveAclsResolver();
|
||||
assertNull(resolver.resolveEffectiveAcls(acls, dianne));
|
||||
}
|
||||
|
||||
public void testResolveAclsForMarissa() {
|
||||
GrantedAuthorityEffectiveAclsResolver resolver = new GrantedAuthorityEffectiveAclsResolver();
|
||||
assertEquals(3, resolver.resolveEffectiveAcls(acls, marissa).length);
|
||||
assertEquals(entry100Marissa,
|
||||
resolver.resolveEffectiveAcls(acls, marissa)[0]);
|
||||
assertEquals(entry100RoleEverybody,
|
||||
resolver.resolveEffectiveAcls(acls, marissa)[1]);
|
||||
assertEquals(entry100RoleOne,
|
||||
resolver.resolveEffectiveAcls(acls, marissa)[2]);
|
||||
}
|
||||
|
||||
public void testResolveAclsForScott() {
|
||||
GrantedAuthorityEffectiveAclsResolver resolver = new GrantedAuthorityEffectiveAclsResolver();
|
||||
assertEquals(3, resolver.resolveEffectiveAcls(acls, scott).length);
|
||||
assertEquals(entry100Scott,
|
||||
resolver.resolveEffectiveAcls(acls, scott)[0]);
|
||||
assertEquals(entry100RoleEverybody,
|
||||
resolver.resolveEffectiveAcls(acls, scott)[1]);
|
||||
assertEquals(entry100RoleTwo,
|
||||
resolver.resolveEffectiveAcls(acls, scott)[2]);
|
||||
}
|
||||
|
||||
public void testSkipsNonBasicAclEntryObjects() {
|
||||
GrantedAuthorityEffectiveAclsResolver resolver = new GrantedAuthorityEffectiveAclsResolver();
|
||||
AclEntry[] basicAcls = {entry100Marissa, entry100Scott, entry100RoleEverybody, entry100RoleOne, new MockAcl(), entry100RoleTwo};
|
||||
assertEquals(3, resolver.resolveEffectiveAcls(basicAcls, marissa).length);
|
||||
}
|
||||
|
||||
//~ Inner Classes ==========================================================
|
||||
|
||||
private class MockAcl implements AclEntry {
|
||||
// does nothing
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
/**
|
||||
* Implements <code>AclObjectIdentity</code> but is incompatible with
|
||||
* <code>BasicAclProvider</code> because it cannot be constructed by passing
|
||||
* in a domain object instance.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class MockAclObjectIdentity implements AclObjectIdentity {
|
||||
// has no "public MockAclObjectIdentity(Object object)" constructor!
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link NamedEntityObjectIdentity}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class NamedEntityObjectIdentityTests extends TestCase {
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public NamedEntityObjectIdentityTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NamedEntityObjectIdentityTests(String arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public final void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(NamedEntityObjectIdentityTests.class);
|
||||
}
|
||||
|
||||
public void testConstructionViaReflection() throws Exception {
|
||||
SomeDomain domainObject = new SomeDomain();
|
||||
domainObject.setId(34);
|
||||
|
||||
NamedEntityObjectIdentity name = new NamedEntityObjectIdentity(domainObject);
|
||||
assertEquals("34", name.getId());
|
||||
assertEquals(domainObject.getClass().getName(), name.getClassname());
|
||||
name.toString();
|
||||
}
|
||||
|
||||
public void testConstructionViaReflectionFailsIfNoGetIdMethod()
|
||||
throws Exception {
|
||||
try {
|
||||
new NamedEntityObjectIdentity(new Integer(45));
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testConstructionViaReflectionFailsIfNullPassed()
|
||||
throws Exception {
|
||||
try {
|
||||
new NamedEntityObjectIdentity(null);
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testDefaultConstructorRejected() {
|
||||
try {
|
||||
new NamedEntityObjectIdentity();
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testEquality() {
|
||||
NamedEntityObjectIdentity original = new NamedEntityObjectIdentity("foo",
|
||||
"12");
|
||||
assertFalse(original.equals(null));
|
||||
assertFalse(original.equals(new Integer(354)));
|
||||
assertFalse(original.equals(
|
||||
new NamedEntityObjectIdentity("foo", "23232")));
|
||||
assertTrue(original.equals(new NamedEntityObjectIdentity("foo", "12")));
|
||||
assertTrue(original.equals(original));
|
||||
}
|
||||
|
||||
public void testNormalConstructionRejectedIfInvalidArguments()
|
||||
throws Exception {
|
||||
try {
|
||||
new NamedEntityObjectIdentity(null, "12");
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
try {
|
||||
new NamedEntityObjectIdentity("classname", null);
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
try {
|
||||
new NamedEntityObjectIdentity("", "12");
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
try {
|
||||
new NamedEntityObjectIdentity("classname", "");
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testNormalOperation() {
|
||||
NamedEntityObjectIdentity name = new NamedEntityObjectIdentity("domain",
|
||||
"id");
|
||||
assertEquals("domain", name.getClassname());
|
||||
assertEquals("id", name.getId());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link SimpleAclEntry}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class SimpleAclEntryTests extends TestCase {
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public SimpleAclEntryTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SimpleAclEntryTests(String arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public final void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(SimpleAclEntryTests.class);
|
||||
}
|
||||
|
||||
public void testCorrectOperation() {
|
||||
String recipient = "marissa";
|
||||
AclObjectIdentity objectIdentity = new NamedEntityObjectIdentity("domain",
|
||||
"12");
|
||||
SimpleAclEntry acl = new SimpleAclEntry(recipient, objectIdentity,
|
||||
null, 0);
|
||||
|
||||
assertFalse(acl.isPermitted(SimpleAclEntry.ADMINISTRATION));
|
||||
acl.addPermission(SimpleAclEntry.ADMINISTRATION);
|
||||
assertTrue(acl.isPermitted(SimpleAclEntry.ADMINISTRATION));
|
||||
assertFalse(acl.isPermitted(SimpleAclEntry.CREATE));
|
||||
assertFalse(acl.isPermitted(SimpleAclEntry.DELETE));
|
||||
assertFalse(acl.isPermitted(SimpleAclEntry.READ));
|
||||
assertFalse(acl.isPermitted(SimpleAclEntry.WRITE));
|
||||
assertEquals("A----", acl.printPermissionsBlock());
|
||||
acl.deletePermission(SimpleAclEntry.ADMINISTRATION);
|
||||
assertFalse(acl.isPermitted(SimpleAclEntry.ADMINISTRATION));
|
||||
assertEquals("-----", acl.printPermissionsBlock());
|
||||
|
||||
acl.addPermissions(new int[] {SimpleAclEntry.READ, SimpleAclEntry.WRITE});
|
||||
acl.addPermission(SimpleAclEntry.CREATE);
|
||||
assertFalse(acl.isPermitted(SimpleAclEntry.ADMINISTRATION));
|
||||
assertTrue(acl.isPermitted(SimpleAclEntry.CREATE));
|
||||
assertFalse(acl.isPermitted(SimpleAclEntry.DELETE));
|
||||
assertTrue(acl.isPermitted(SimpleAclEntry.READ));
|
||||
assertTrue(acl.isPermitted(SimpleAclEntry.WRITE));
|
||||
assertEquals("-RWC-", acl.printPermissionsBlock());
|
||||
|
||||
acl.deletePermission(SimpleAclEntry.CREATE);
|
||||
acl.deletePermissions(new int[] {SimpleAclEntry.READ, SimpleAclEntry.WRITE});
|
||||
assertEquals("-----", acl.printPermissionsBlock());
|
||||
|
||||
acl.togglePermission(SimpleAclEntry.CREATE);
|
||||
assertTrue(acl.isPermitted(SimpleAclEntry.CREATE));
|
||||
assertFalse(acl.isPermitted(SimpleAclEntry.ADMINISTRATION));
|
||||
acl.togglePermission(SimpleAclEntry.CREATE);
|
||||
assertFalse(acl.isPermitted(SimpleAclEntry.CREATE));
|
||||
}
|
||||
|
||||
public void testDetectsNullOnMainConstructor() {
|
||||
String recipient = "marissa";
|
||||
AclObjectIdentity objectIdentity = new NamedEntityObjectIdentity("domain",
|
||||
"12");
|
||||
|
||||
try {
|
||||
new SimpleAclEntry(recipient, null, null, 2);
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
try {
|
||||
new SimpleAclEntry(null, objectIdentity, null, 2);
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGettersSetters() {
|
||||
SimpleAclEntry acl = new SimpleAclEntry();
|
||||
|
||||
AclObjectIdentity objectIdentity = new NamedEntityObjectIdentity("domain",
|
||||
"693");
|
||||
acl.setAclObjectIdentity(objectIdentity);
|
||||
assertEquals(objectIdentity, acl.getAclObjectIdentity());
|
||||
|
||||
AclObjectIdentity parentObjectIdentity = new NamedEntityObjectIdentity("domain",
|
||||
"13");
|
||||
acl.setAclObjectParentIdentity(parentObjectIdentity);
|
||||
assertEquals(parentObjectIdentity, acl.getAclObjectParentIdentity());
|
||||
|
||||
acl.setMask(2);
|
||||
assertEquals(2, acl.getMask());
|
||||
|
||||
acl.setRecipient("scott");
|
||||
assertEquals("scott", acl.getRecipient());
|
||||
}
|
||||
|
||||
public void testRejectsInvalidMasksInAddMethod() {
|
||||
String recipient = "marissa";
|
||||
AclObjectIdentity objectIdentity = new NamedEntityObjectIdentity("domain",
|
||||
"12");
|
||||
SimpleAclEntry acl = new SimpleAclEntry(recipient, objectIdentity,
|
||||
null, 4);
|
||||
|
||||
try {
|
||||
acl.addPermission(Integer.MAX_VALUE);
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testRejectsInvalidMasksInDeleteMethod() {
|
||||
String recipient = "marissa";
|
||||
AclObjectIdentity objectIdentity = new NamedEntityObjectIdentity("domain",
|
||||
"12");
|
||||
SimpleAclEntry acl = new SimpleAclEntry(recipient, objectIdentity,
|
||||
null, 0);
|
||||
acl.addPermissions(new int[] {SimpleAclEntry.READ, SimpleAclEntry.WRITE, SimpleAclEntry.CREATE});
|
||||
|
||||
try {
|
||||
acl.deletePermission(SimpleAclEntry.READ); // can't write if we can't read
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testRejectsInvalidMasksInTogglePermissionMethod() {
|
||||
String recipient = "marissa";
|
||||
AclObjectIdentity objectIdentity = new NamedEntityObjectIdentity("domain",
|
||||
"12");
|
||||
SimpleAclEntry acl = new SimpleAclEntry(recipient, objectIdentity,
|
||||
null, 0);
|
||||
acl.addPermissions(new int[] {SimpleAclEntry.READ, SimpleAclEntry.WRITE, SimpleAclEntry.CREATE});
|
||||
|
||||
try {
|
||||
acl.togglePermission(SimpleAclEntry.READ); // can't write if we can't read
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testToString() {
|
||||
String recipient = "marissa";
|
||||
AclObjectIdentity objectIdentity = new NamedEntityObjectIdentity("domain",
|
||||
"12");
|
||||
SimpleAclEntry acl = new SimpleAclEntry(recipient, objectIdentity,
|
||||
null, 0);
|
||||
acl.addPermissions(new int[] {SimpleAclEntry.READ, SimpleAclEntry.WRITE, SimpleAclEntry.CREATE});
|
||||
assertTrue(acl.toString().endsWith("marissa=-RWC- ............................111. (14)]"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic;
|
||||
|
||||
/**
|
||||
* Simple object to use when testing <code>NamedEntityObjectIdentity</code>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class SomeDomain {
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private int id;
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
66
core/src/test/java/org/acegisecurity/acl/basic/cache/BasicAclEntryHolderTests.java
vendored
Normal file
66
core/src/test/java/org/acegisecurity/acl/basic/cache/BasicAclEntryHolderTests.java
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic.cache;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import net.sf.acegisecurity.acl.basic.BasicAclEntry;
|
||||
import net.sf.acegisecurity.acl.basic.SimpleAclEntry;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link BasicAclEntryHolder}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class BasicAclEntryHolderTests extends TestCase {
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public BasicAclEntryHolderTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BasicAclEntryHolderTests(String arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public final void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(BasicAclEntryHolderTests.class);
|
||||
}
|
||||
|
||||
public void testRejectsNull() throws Exception {
|
||||
try {
|
||||
new BasicAclEntryHolder(null);
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
try {
|
||||
new BasicAclEntryHolder(new BasicAclEntry[] {new SimpleAclEntry(), null, new SimpleAclEntry()});
|
||||
fail("Should have thrown IllegalArgumentException");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
}
|
95
core/src/test/java/org/acegisecurity/acl/basic/cache/EhCacheBasedAclEntryCacheTests.java
vendored
Normal file
95
core/src/test/java/org/acegisecurity/acl/basic/cache/EhCacheBasedAclEntryCacheTests.java
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic.cache;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import net.sf.acegisecurity.acl.basic.AclObjectIdentity;
|
||||
import net.sf.acegisecurity.acl.basic.BasicAclEntry;
|
||||
import net.sf.acegisecurity.acl.basic.NamedEntityObjectIdentity;
|
||||
import net.sf.acegisecurity.acl.basic.SimpleAclEntry;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link EhCacheBasedAclEntryCache}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class EhCacheBasedAclEntryCacheTests extends TestCase {
|
||||
//~ Static fields/initializers =============================================
|
||||
|
||||
private static final AclObjectIdentity OBJECT_100 = new NamedEntityObjectIdentity("OBJECT",
|
||||
"100");
|
||||
private static final AclObjectIdentity OBJECT_200 = new NamedEntityObjectIdentity("OBJECT",
|
||||
"200");
|
||||
private static final BasicAclEntry OBJECT_100_MARISSA = new SimpleAclEntry("marissa",
|
||||
OBJECT_100, null, 2);
|
||||
private static final BasicAclEntry OBJECT_100_SCOTT = new SimpleAclEntry("scott",
|
||||
OBJECT_100, null, 4);
|
||||
private static final BasicAclEntry OBJECT_200_PETER = new SimpleAclEntry("peter",
|
||||
OBJECT_200, null, 4);
|
||||
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public EhCacheBasedAclEntryCacheTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public EhCacheBasedAclEntryCacheTests(String arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public final void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(EhCacheBasedAclEntryCacheTests.class);
|
||||
}
|
||||
|
||||
public void testCacheOperation() throws Exception {
|
||||
EhCacheBasedAclEntryCache cache = new EhCacheBasedAclEntryCache();
|
||||
cache.afterPropertiesSet();
|
||||
|
||||
// execute a second time to test detection of existing instance
|
||||
cache.afterPropertiesSet();
|
||||
|
||||
cache.putEntriesInCache(new BasicAclEntry[] {OBJECT_100_SCOTT, OBJECT_100_MARISSA});
|
||||
cache.putEntriesInCache(new BasicAclEntry[] {OBJECT_200_PETER});
|
||||
|
||||
// Check we can get them from cache again
|
||||
assertEquals(OBJECT_100_SCOTT,
|
||||
cache.getEntriesFromCache(
|
||||
new NamedEntityObjectIdentity("OBJECT", "100"))[0]);
|
||||
assertEquals(OBJECT_100_MARISSA,
|
||||
cache.getEntriesFromCache(
|
||||
new NamedEntityObjectIdentity("OBJECT", "100"))[1]);
|
||||
assertEquals(OBJECT_200_PETER,
|
||||
cache.getEntriesFromCache(
|
||||
new NamedEntityObjectIdentity("OBJECT", "200"))[0]);
|
||||
|
||||
cache.destroy();
|
||||
}
|
||||
|
||||
public void testGettersSetters() {
|
||||
EhCacheBasedAclEntryCache cache = new EhCacheBasedAclEntryCache();
|
||||
cache.setMinutesToIdle(15);
|
||||
assertEquals(15, cache.getMinutesToIdle());
|
||||
}
|
||||
}
|
58
core/src/test/java/org/acegisecurity/acl/basic/cache/NullAclEntryCacheTests.java
vendored
Normal file
58
core/src/test/java/org/acegisecurity/acl/basic/cache/NullAclEntryCacheTests.java
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic.cache;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import net.sf.acegisecurity.acl.basic.BasicAclEntry;
|
||||
import net.sf.acegisecurity.acl.basic.NamedEntityObjectIdentity;
|
||||
import net.sf.acegisecurity.acl.basic.SimpleAclEntry;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link NullAclEntryCache}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class NullAclEntryCacheTests extends TestCase {
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public NullAclEntryCacheTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NullAclEntryCacheTests(String arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public final void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(NullAclEntryCacheTests.class);
|
||||
}
|
||||
|
||||
public void testCacheOperation() throws Exception {
|
||||
NullAclEntryCache cache = new NullAclEntryCache();
|
||||
cache.putEntriesInCache(new BasicAclEntry[] {new SimpleAclEntry()});
|
||||
cache.getEntriesFromCache(new NamedEntityObjectIdentity("not_used",
|
||||
"not_used"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/* Copyright 2004 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sf.acegisecurity.acl.basic.jdbc;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import net.sf.acegisecurity.PopulatedDatabase;
|
||||
import net.sf.acegisecurity.acl.basic.AclObjectIdentity;
|
||||
import net.sf.acegisecurity.acl.basic.BasicAclEntry;
|
||||
import net.sf.acegisecurity.acl.basic.NamedEntityObjectIdentity;
|
||||
|
||||
import org.springframework.jdbc.object.MappingSqlQuery;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link JdbcDaoImpl}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class JdbcDaoImplTests extends TestCase {
|
||||
//~ Static fields/initializers =============================================
|
||||
|
||||
public static final String OBJECT_IDENTITY = "net.sf.acegisecurity.acl.DomainObject";
|
||||
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public JdbcDaoImplTests() {
|
||||
super();
|
||||
}
|
||||
|
||||
public JdbcDaoImplTests(String arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public final void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
junit.textui.TestRunner.run(JdbcDaoImplTests.class);
|
||||
}
|
||||
|
||||
public void testGetsAclsWhichExistInDatabase() throws Exception {
|
||||
JdbcDaoImpl dao = makePopulatedJdbcDao();
|
||||
AclObjectIdentity identity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
|
||||
"2");
|
||||
BasicAclEntry[] acls = dao.getAcls(identity);
|
||||
assertEquals(2, acls.length);
|
||||
}
|
||||
|
||||
public void testGettersSetters() throws Exception {
|
||||
JdbcDaoImpl dao = makePopulatedJdbcDao();
|
||||
dao.setAclsByObjectIdentity(new MockMappingSqlQuery());
|
||||
assertNotNull(dao.getAclsByObjectIdentity());
|
||||
|
||||
dao.setAclsByObjectIdentityQuery("foo");
|
||||
assertEquals("foo", dao.getAclsByObjectIdentityQuery());
|
||||
}
|
||||
|
||||
public void testNullReturnedIfBasicAclEntryClassNotFound()
|
||||
throws Exception {
|
||||
JdbcDaoImpl dao = makePopulatedJdbcDao();
|
||||
AclObjectIdentity identity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
|
||||
"8");
|
||||
BasicAclEntry[] result = dao.getAcls(identity);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
public void testNullReturnedIfEntityNotFound() throws Exception {
|
||||
JdbcDaoImpl dao = makePopulatedJdbcDao();
|
||||
AclObjectIdentity identity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
|
||||
"NOT_VALID_ID");
|
||||
BasicAclEntry[] result = dao.getAcls(identity);
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
public void testRejectsNonNamedEntityObjectIdentity()
|
||||
throws Exception {
|
||||
JdbcDaoImpl dao = new JdbcDaoImpl();
|
||||
AclObjectIdentity identity = new AclObjectIdentity() {}
|
||||
;
|
||||
|
||||
assertNull(dao.getAcls(identity));
|
||||
}
|
||||
|
||||
private JdbcDaoImpl makePopulatedJdbcDao() throws Exception {
|
||||
JdbcDaoImpl dao = new JdbcDaoImpl();
|
||||
dao.setDataSource(PopulatedDatabase.getDataSource());
|
||||
dao.afterPropertiesSet();
|
||||
|
||||
return dao;
|
||||
}
|
||||
|
||||
//~ Inner Classes ==========================================================
|
||||
|
||||
private class MockMappingSqlQuery extends MappingSqlQuery {
|
||||
protected Object mapRow(ResultSet arg0, int arg1)
|
||||
throws SQLException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -69,7 +69,7 @@
|
|||
<sect2 id="security-high-level-design-key-components">
|
||||
<title>Key Components</title>
|
||||
|
||||
<para>The Acegi Security System for Spring essentially comprises six
|
||||
<para>The Acegi Security System for Spring essentially comprises seven
|
||||
key functional parts:</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
|
@ -109,6 +109,11 @@
|
|||
authentication, authorization, run-as replacement and execution of
|
||||
a given operation.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>An acess control list (ACL) management package, which can be
|
||||
used to obtain ACLs for domain object instances.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Secure objects refer to any type of object that can have
|
||||
|
@ -134,8 +139,8 @@
|
|||
<literal>FilterInterceptor</literal>) with complete
|
||||
transparency.</para>
|
||||
|
||||
<para>Each of the six key parts is discussed in detail throughout this
|
||||
document.</para>
|
||||
<para>Each of the seven key parts is discussed in detail throughout
|
||||
this document.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="security-high-level-design-supported-secure-objects">
|
||||
|
@ -2985,6 +2990,370 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
|
|||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="acls">
|
||||
<title>Instance-Based Access Control List (ACL) Security</title>
|
||||
|
||||
<sect2 id="acls-overview">
|
||||
<title>Overview</title>
|
||||
|
||||
<para>THIS FEATURE WAS ADDED IN VERSION 0.6. WE WELCOME YOUR COMMENTS
|
||||
AND IMPROVEMENTS.</para>
|
||||
|
||||
<para>Complex applications often will find the need to define access
|
||||
permissions not simply at a web request or method invocation level.
|
||||
Instead, security decisions need to comprise both who
|
||||
(<literal>Authentication</literal>), where
|
||||
(<literal>MethodInvocation</literal>) and what
|
||||
(<literal>SomeDomainObject</literal>). In other words, authorization
|
||||
decisions also need to consider the actual domain object instance
|
||||
subject of a method invocation.</para>
|
||||
|
||||
<para>Imagine you're designing an application for a pet clinic. There
|
||||
will be two main groups of users of your Spring-based application:
|
||||
staff of the pet clinic, as well as the pet clinic's customers. The
|
||||
staff will have access to all of the data, whilst your customers will
|
||||
only be able to see their own customer records. To make it a little
|
||||
more interesting, your customers can allow other users to see their
|
||||
customer records, such as their "puppy preschool "mentor or president
|
||||
of their local "Pony Club". Using Acegi Security System for Spring as
|
||||
the foundation, you have several approaches that can be
|
||||
used:<orderedlist>
|
||||
<listitem>
|
||||
<para>Write your business methods to enforce the security. You
|
||||
could consult a collection within the
|
||||
<literal>Customer</literal> domain object instance to determine
|
||||
which users have access. By using the
|
||||
<literal>ContextHolder.getContext()</literal> and casting it to
|
||||
<literal>SecureContext</literal>, you'll be able to access the
|
||||
<literal>Authentication</literal> object.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Write an <literal>AccessDecisionVoter</literal> to enforce
|
||||
the security from the <literal>GrantedAuthority[]</literal>s
|
||||
stored in the <literal>Authentication</literal> object. This
|
||||
would mean your <literal>AuthenticationManager</literal> would
|
||||
need to populate the <literal>Authentication</literal> with
|
||||
custom <literal>GrantedAuthority</literal>[]s representing each
|
||||
of the <literal>Customer</literal> domain object instances the
|
||||
principal has access to.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Write an <literal>AccessDecisionVoter</literal> to enforce
|
||||
the security and open the target <literal>Customer</literal>
|
||||
domain object directly. This would mean your voter needs access
|
||||
to a DAO that allows it to retrieve the
|
||||
<literal>Customer</literal> object. It would then access the
|
||||
<literal>Customer</literal> object's collection of approved
|
||||
users and make the appropriate decision.</para>
|
||||
</listitem>
|
||||
</orderedlist></para>
|
||||
|
||||
<para>Each one of these approaches is perfectly legitimate. However,
|
||||
the first couples your authorization checking to your business code.
|
||||
The main problems with this include the enhanced difficulty of unit
|
||||
testing and the fact it would be more difficult to reuse the
|
||||
<literal>Customer</literal> authorization logic elsewhere. Obtaining
|
||||
the <literal>GrantedAuthority[]</literal>s from the
|
||||
<literal>Authentication</literal> object is also fine, but will not
|
||||
scale to large numbers of <literal>Customer</literal>s. If a user
|
||||
might be able to access 5,000 <literal>Customer</literal>s (unlikely
|
||||
in this case, but imagine if it were a popular vet for a large Pony
|
||||
Club!) the amount of memory consumed and time required to construct
|
||||
the <literal>Authentication</literal> object would be undesirable. The
|
||||
final method, opening the <literal>Customer</literal> directly from
|
||||
external code, is probably the best of the three. It achieves
|
||||
separation of concerns, and doesn't misuse memory or CPU cycles, but
|
||||
it is still inefficient in that both the
|
||||
<literal>AccessDecisionVoter</literal> and the eventual business
|
||||
method itself will perform a call to the DAO responsible for
|
||||
retrieving the <literal>Customer</literal> object. Two accesses per
|
||||
method invocation is clearly undesirable. In addition, with every
|
||||
approach listed you'll need to write your own access control list
|
||||
(ACL) persistence and business logic from scratch.</para>
|
||||
|
||||
<para>Fortunately, there is another alternative, which we'll talk
|
||||
about below.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="acls-acl-package">
|
||||
<title>The net.sf.acegisecurity.acl Package</title>
|
||||
|
||||
<para>The <literal>net.sf.acegisecurity.acl</literal> package is very
|
||||
simple, comprising only a handful of interfaces and a single class. It
|
||||
provides the basic foundation for access control list (ACL) lookups.
|
||||
The central interface is <literal>AclManager</literal>, which is
|
||||
defined by two methods:</para>
|
||||
|
||||
<para><programlisting>public AclEntry[] getAcls(java.lang.Object domainInstance);
|
||||
public AclEntry[] getAcls(java.lang.Object domainInstance, Authentication authentication);</programlisting></para>
|
||||
|
||||
<para><literal>AclManager</literal> is intended to be used as a
|
||||
collaborator against your business objects, or, more desirably,
|
||||
<literal>AccessDecisionVoter</literal>s. This means you use Spring's
|
||||
normal <literal>ApplicationContext</literal> features to wire up your
|
||||
<literal>AccessDecisionVoter</literal> (or business method) with an
|
||||
<literal>AclManager</literal>. Consideration was given to placing the
|
||||
ACL information in the <literal>ContextHolder</literal>, but it was
|
||||
felt this would be inefficient both in terms of memory usage as well
|
||||
as the time spent loading potentially unused ACL information. The
|
||||
trade-off of needing to wire up a collaborator for those objects
|
||||
requiring ACL information is rather minor, particularly in a
|
||||
Spring-managed application.</para>
|
||||
|
||||
<para>The first method of the <literal>AclManager</literal> will
|
||||
return all ACLs applying to the domain object instance passed to it.
|
||||
The second method does the same, but only returns those ACLs which
|
||||
apply to the passed <literal>Authentication</literal> object.</para>
|
||||
|
||||
<para>The <literal>AclEntry</literal> interface returned by
|
||||
<literal>AclManager</literal> is merely a marker interface. You will
|
||||
need to provide an implementation that reflects that ACL permissions
|
||||
for your application.</para>
|
||||
|
||||
<para>Rounding out the <literal>net.sf.acegisecurity.acl</literal>
|
||||
package is an <literal>AclProviderManager</literal> class, with a
|
||||
corresponding <literal>AclProvider</literal> interface.
|
||||
<literal>AclProviderManager</literal> is a concrete implementation of
|
||||
<literal>AclManager</literal>, which iterates through registered
|
||||
<literal>AclProvider</literal>s. The first
|
||||
<literal>AclProvider</literal> that indicates it can authoritatively
|
||||
provide ACL information for the presented domain object instance will
|
||||
be used. This is very similar to the
|
||||
<literal>AuthenticationProvider</literal> interface used for
|
||||
authentication.</para>
|
||||
|
||||
<para>With this background, let's now look at a usable ACL
|
||||
implementation.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="acls-masking">
|
||||
<title>Integer Masked ACLs</title>
|
||||
|
||||
<para>Acegi Security System for Spring includes a production-quality
|
||||
ACL provider implementation. The implementation is based on integer
|
||||
masking, which is commonly used for ACL permissions given its
|
||||
flexibility and speed. Anyone who has used Unix's
|
||||
<literal>chmod</literal> command will know all about this type of
|
||||
permission masking (eg <literal>chmod 777</literal>). You'll find the
|
||||
classes and interfaces for the integer masking ACL package under
|
||||
<literal>net.sf.acegisecurity.acl.basic</literal>.</para>
|
||||
|
||||
<para>Extending the <literal>AclEntry</literal> interface is a
|
||||
<literal>BasicAclEntry</literal> interface, with the main methods
|
||||
shown below:</para>
|
||||
|
||||
<para><programlisting>public AclObjectIdentity getAclObjectIdentity();
|
||||
public AclObjectIdentity getAclObjectParentIdentity();
|
||||
public int getMask();
|
||||
public java.lang.Object getRecipient();</programlisting></para>
|
||||
|
||||
<para>As shown, each <literal>BasicAclEntry</literal> has four main
|
||||
properties. The <literal>mask</literal> is the integer that represents
|
||||
the permissions granted to the <literal>recipient</literal>. The
|
||||
<literal>aclObjectIdentity</literal> is able to identify the domain
|
||||
object instance for which the ACL applies, and the
|
||||
<literal>aclObjectParentIdentity</literal> optionally specifies the
|
||||
parent of the domain object instance. Multiple
|
||||
<literal>BasicAclEntry</literal>s usually exist against a single
|
||||
domain object instance, and as suggested by the parent identity
|
||||
property, permissions granted higher in the object hierarchy will
|
||||
trickle down and be inherited (unless blocked by integer zero).</para>
|
||||
|
||||
<para><literal>BasicAclEntry</literal> implementations typically
|
||||
provide convenience methods, such as
|
||||
<literal>isReadAllowed()</literal>, to avoid application classes
|
||||
needing to perform bit masking themselves. The
|
||||
<literal>SimpleAclEntry</literal> and
|
||||
<literal>AbstractBasicAclEntry</literal> demonstrate and provide much
|
||||
of this bit masking logic.</para>
|
||||
|
||||
<para>The <literal>AclObjectIdentity</literal> itself is merely a
|
||||
marker interface, so you need to provide implementations for your
|
||||
domain objects. However, the package does include a
|
||||
<literal>NamedEntityObjectIdentity</literal> implementation which will
|
||||
suit many needs. The <literal>NamedEntityObjectIdentity</literal>
|
||||
identifies a given domain object instance by the classname of the
|
||||
instance and the identity of the instance. A
|
||||
<literal>NamedEntityObjectIdentity</literal> can be constructed
|
||||
manually (by calling the constructor and providing the classname and
|
||||
identity <literal>String</literal>s), or by passing in any domain
|
||||
object that contains a <literal>getId()</literal> method.</para>
|
||||
|
||||
<para>The actual <literal>AclProvider</literal> implementation is
|
||||
named <literal>BasicAclProvider</literal>. It has adopted a similar
|
||||
design to that used by the authentication-related
|
||||
<literal>DaoAuthenticationProvder</literal>. Specifically, you define
|
||||
a <literal>BasicAclDao</literal> against the provider, so different
|
||||
ACL repository types can be accessed in a pluggable manner. The
|
||||
<literal>BasicAclProvider</literal> also supports pluggable cache
|
||||
providers (with Acegi Security System for Spring including an
|
||||
implementation that fronts EH-CACHE).</para>
|
||||
|
||||
<para>The <literal>BasicAclDao</literal> interface is very simple to
|
||||
implement:</para>
|
||||
|
||||
<para><programlisting>public BasicAclEntry[] getAcls(AclObjectIdentity aclObjectIdentity);</programlisting></para>
|
||||
|
||||
<para>A <literal>BasicAclDao</literal> implementation needs to
|
||||
understand the presented <literal>AclObjectIdentity</literal> and how
|
||||
it maps to a storage repository, find the relevant records, and create
|
||||
appropriate <literal>BasicAclEntry</literal> objects and return
|
||||
them.</para>
|
||||
|
||||
<para>Acegi Security includes a single <literal>BasicAclDao</literal>
|
||||
implementation called <literal>JdbcDaoImpl</literal>. As implied by
|
||||
the name, it accesses ACL information from a JDBC database. The
|
||||
default database schema and some sample data will aid in understanding
|
||||
its function:</para>
|
||||
|
||||
<para><programlisting>CREATE TABLE acls (
|
||||
object_identity VARCHAR_IGNORECASE(250) NOT NULL,
|
||||
recipient VARCHAR_IGNORECASE(100) NOT NULL,
|
||||
parent_object_identity VARCHAR_IGNORECASE(250),
|
||||
mask INTEGER NOT NULL,
|
||||
acl_class VARCHAR_IGNORECASE(250) NOT NULL,
|
||||
CONSTRAINT pk_acls PRIMARY KEY(object_identity, recipient)
|
||||
);
|
||||
|
||||
INSERT INTO acls VALUES ('corp.DomainObject:1', 'ROLE_SUPERVISOR', null, 1, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
||||
INSERT INTO acls VALUES ('corp.DomainObject:2', 'ROLE_SUPERVISOR', 'corp.DomainObject:1', 0, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
||||
INSERT INTO acls VALUES ('corp.DomainObject:2', 'marissa', 'corp.DomainObject:1', 2, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
||||
INSERT INTO acls VALUES ('corp.DomainObject:3', 'scott', 'corp.DomainObject:1', 14, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
||||
INSERT INTO acls VALUES ('corp.DomainObject:4', 'inheritance_marker_only', 'corp.DomainObject:1', 0, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
||||
INSERT INTO acls VALUES ('corp.DomainObject:5', 'inheritance_marker_only', 'corp.DomainObject:3', 0, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
||||
INSERT INTO acls VALUES ('corp.DomainObject:6', 'scott', 'corp.DomainObject:3', 1, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
||||
INSERT INTO acls VALUES ('corp.DomainObject:7', 'scott', 'some.invalid.parent:1', 2, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');</programlisting></para>
|
||||
|
||||
<para>The <literal>JdbcDaoImpl</literal> will only respond to requests
|
||||
for <literal>NamedEntityObjectIdentity</literal>s. It converts such
|
||||
identities into a single <literal>String</literal>, comprising
|
||||
the<literal> NamedEntityObjectIdentity.getClassname()</literal> +
|
||||
<literal>":"</literal> +
|
||||
<literal>NamedEntityObjectIdentity.getId()</literal>. This yields the
|
||||
type of <literal>object_identity</literal> values shown above. As
|
||||
indicated by the sample data, each database row corresponds to a
|
||||
single <literal>BasicAclEntry</literal>. As stated earlier and
|
||||
demonstrated by <literal>corp.DomainObject:2</literal> in the above
|
||||
sample data, each domain object instance will often have multiple
|
||||
<literal>BasicAclEntry</literal>[]s.</para>
|
||||
|
||||
<para>As <literal>JdbcDaoImpl</literal> is required to return concrete
|
||||
<literal>BasicAclEntry</literal> classes, it needs to know which
|
||||
<literal>BasicAclEntry</literal> implementation it is to create and
|
||||
populate. This is the role of the <literal>acl_class</literal> column.
|
||||
<literal>JdbcDaoImpl</literal> will create the indicated class and set
|
||||
its <literal>mask</literal>, <literal>recipient</literal>,
|
||||
<literal>aclObjectIdentity</literal> and
|
||||
<literal>aclObjectParentIdentity</literal> properties.</para>
|
||||
|
||||
<para>As you can probably tell from the sample data, the
|
||||
<literal>parent_object_identity</literal> value can either be null or
|
||||
in the same format as the <literal>object_identity</literal>. If
|
||||
non-null, <literal>JdbcDaoImpl</literal> will create a
|
||||
<literal>NamedEntityObjectIdentity</literal> to place inside the
|
||||
returned <literal>BasicAclEntry</literal> class.</para>
|
||||
|
||||
<para>Returning to the <literal>BasicAclProvider</literal>, before it
|
||||
can poll the <literal>BasicAclDao</literal> implementation it needs to
|
||||
convert the domain object instance it was passed into an
|
||||
<literal>AclObjectIdentity</literal>.
|
||||
<literal>BasicAclProvider</literal> has a <literal>protected
|
||||
AclObjectIdentity obtainIdentity(Object domainInstance)</literal>
|
||||
method that is responsible for this. As a protected method, it enables
|
||||
subclasses to easily override. The normal implementation checks
|
||||
whether the passed domain object instance implements the
|
||||
<literal>AclObjectIdentityAware</literal> interface, which is merely a
|
||||
getter for an <literal>AclObjectIdentity</literal>. If the domain
|
||||
object does implement this interface, that is the identity returned.
|
||||
If the domain object does not implement this interface, the method
|
||||
will attempt to create an <literal>AclObjectIdentity</literal> by
|
||||
passing the domain object instance to the constructor of a class
|
||||
defined by the
|
||||
<literal>BasicAclProvider.getDefaultAclObjectIdentity()</literal>
|
||||
method. By default the defined class is
|
||||
<literal>NamedEntityObjectIdentity</literal>, which was described in
|
||||
more detail above. Therefore, you will need to either (i) provide a
|
||||
<literal>getId()</literal> method on your domain objects, (ii)
|
||||
implement <literal>AclObjectIdentityAware</literal> on your domain
|
||||
objects, (iii) provide an alternative
|
||||
<literal>AclObjectIdentity</literal> implementation that will accept
|
||||
your domain object in its constructor, or (iv) override the
|
||||
<literal>obtainIdentity(Object)</literal> method.</para>
|
||||
|
||||
<para>Once the <literal>AclObjectIdentity</literal> of the domain
|
||||
object instance is determined, the <literal>BasicAclProvider</literal>
|
||||
will poll the DAO to obtain its <literal>BasicAclEntry</literal>[]s.
|
||||
If any of the entries returned by the DAO indicate there is a parent,
|
||||
that parent will be polled, and the process will repeat until there is
|
||||
no further parent. The permissions assigned to a
|
||||
<literal>recipient</literal> closest to the domain object instance
|
||||
will always take priority and override any inherited permissions. From
|
||||
the sample data above, the following inherited permissions would
|
||||
apply:</para>
|
||||
|
||||
<para><programlisting>--- Mask integer 0 = no permissions
|
||||
--- Mask integer 1 = administer
|
||||
--- Mask integer 2 = read
|
||||
--- Mask integer 6 = read and write permissions
|
||||
--- Mask integer 14 = read and write and create permissions
|
||||
|
||||
---------------------------------------------------------------------
|
||||
--- *** INHERITED RIGHTS FOR DIFFERENT INSTANCES AND RECIPIENTS ***
|
||||
--- INSTANCE RECIPIENT PERMISSION(S) (COMMENT #INSTANCE)
|
||||
---------------------------------------------------------------------
|
||||
--- 1 ROLE_SUPERVISOR Administer
|
||||
--- 2 ROLE_SUPERVISOR None (overrides parent #1)
|
||||
--- marissa Read
|
||||
--- 3 ROLE_SUPERVISOR Administer (from parent #1)
|
||||
--- scott Read, Write, Create
|
||||
--- 4 ROLE_SUPERVISOR Administer (from parent #1)
|
||||
--- 5 ROLE_SUPERVISOR Administer (from parent #3)
|
||||
--- scott Read, Write, Create (from parent #3)
|
||||
--- 6 ROLE_SUPERVISOR Administer (from parent #3)
|
||||
--- scott Administer (overrides parent #3)
|
||||
--- 7 scott Read (invalid parent ignored)</programlisting></para>
|
||||
|
||||
<para>So the above explains how a domain object instance has its
|
||||
<literal>AclObjectIdentity</literal> discovered, and the
|
||||
<literal>BasicAclDao</literal> will be polled successively until an
|
||||
array of inherited permissions is constructed for the domain object
|
||||
instance. The final step is to determine the
|
||||
<literal>BasicAclEntry</literal>[]s that are actually applicable to a
|
||||
given <literal>Authentication</literal> object.</para>
|
||||
|
||||
<para>As you would recall, the <literal>AclManager</literal> (and all
|
||||
delegates, up to and including <literal>BasicAclProvider</literal>)
|
||||
provides a method which returns only those
|
||||
<literal>BasicAclEntry</literal>[]s applying to a passed
|
||||
<literal>Authentication</literal> object.
|
||||
<literal>BasicAclProvider</literal> delivers this functionality by
|
||||
delegating the filtering operation to an
|
||||
<literal>EffectiveAclsResolver</literal> implementation. The default
|
||||
implementation,
|
||||
<literal>GrantedAuthorityEffectiveAclsResolver</literal>, will iterate
|
||||
through the <literal>BasicAclEntry</literal>[]s and include only those
|
||||
where the <literal>recipient</literal> is equal to either the
|
||||
<literal>Authentication</literal>'s <literal>principal</literal> or
|
||||
any of the <literal>Authentication</literal>'s
|
||||
<literal>GrantedAuthority</literal>[]s. Please refer to the JavaDocs
|
||||
for more information.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="acls-conclusion">
|
||||
<title>Conclusion</title>
|
||||
|
||||
<para>Acegi Security's instance-specific ACL packages shield you from
|
||||
much of the complexity of developing your own ACL approach. The
|
||||
interfaces and classes detailed above provide a scalable, customisable
|
||||
ACL solution that is decoupled from your application code. Whilst the
|
||||
reference documentation may suggest complexity, the basic
|
||||
implementation is able to support most typical applications
|
||||
out-of-the-box.</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="security-filters">
|
||||
<title>Filters</title>
|
||||
|
||||
|
@ -3081,6 +3450,11 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
|
|||
<para>All of the above filters use
|
||||
<literal>FilterToBeanProxy</literal>, which is discussed in the
|
||||
previous section.</para>
|
||||
|
||||
<para>If you're using SiteMesh, ensure the Acegi Security filters
|
||||
execute before the SiteMesh filters are called. This enables the
|
||||
<literal>ContextHolder</literal> to be populated in time for use by
|
||||
SiteMesh decorators.</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
|
|
Loading…
Reference in New Issue