diff --git a/sandbox/src/main/java/org/acegisecurity/acls/domain/AclImpl.java b/sandbox/src/main/java/org/acegisecurity/acls/domain/AclImpl.java
index b99fb33b69..43432aee74 100644
--- a/sandbox/src/main/java/org/acegisecurity/acls/domain/AclImpl.java
+++ b/sandbox/src/main/java/org/acegisecurity/acls/domain/AclImpl.java
@@ -1,500 +1,500 @@
-/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.acegisecurity.acls.domain;
-
-import org.acegisecurity.AccessDeniedException;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.GrantedAuthority;
-
-import org.acegisecurity.acls.AccessControlEntry;
-import org.acegisecurity.acls.Acl;
-import org.acegisecurity.acls.AuditableAcl;
-import org.acegisecurity.acls.MutableAcl;
-import org.acegisecurity.acls.NotFoundException;
-import org.acegisecurity.acls.OwnershipAcl;
-import org.acegisecurity.acls.Permission;
-import org.acegisecurity.acls.UnloadedSidException;
-import org.acegisecurity.acls.objectidentity.ObjectIdentity;
-import org.acegisecurity.acls.sid.PrincipalSid;
-import org.acegisecurity.acls.sid.Sid;
-
-import org.acegisecurity.context.SecurityContextHolder;
-
-import org.springframework.util.Assert;
-
-import java.io.Serializable;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.Vector;
-
-
-/**
- * Base implementation of Acl
.
- *
- * @author Ben Alex
- * @version $Id
- */
-public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
- //~ Static fields/initializers =====================================================================================
-
- private static final int CHANGE_OWNERSHIP = 0;
- private static final int CHANGE_AUDITING = 1;
- private static final int CHANGE_GENERAL = 2;
-
- //~ Instance fields ================================================================================================
-
- private Acl parentAcl;
- private AuditLogger auditLogger = new ConsoleAuditLogger(); // AuditableAcl
- private GrantedAuthority gaGeneralChanges;
- private GrantedAuthority gaModifyAuditing;
- private GrantedAuthority gaTakeOwnership;
- private List aces = new Vector();
- private List deletedAces = new Vector();
- private Long id;
- private ObjectIdentity objectIdentity;
- private Sid owner; // OwnershipAcl
- private Sid[] loadedSids = null; // includes all SIDs the WHERE clause covered, even if there was no ACE for a SID
- private boolean aclDirty = false; // for snapshot detection
- private boolean addedAces = false; // for snapshot detection
- private boolean entriesInheriting = false;
- private boolean updatedAces = false; // for snapshot detection
-
- //~ Constructors ===================================================================================================
-
-/**
- * Minimal constructor, which should be used {@link
- * org.acegisecurity.acls.MutableAclService#createAcl(ObjectIdentity)}.
- *
- * @param objectIdentity the object identity this ACL relates to (required)
- * @param id the primary key assigned to this ACL (required)
- * @param auths an array of GrantedAuthority
s that have
- * special permissions (index 0 is the authority needed to change
- * ownership, index 1 is the authority needed to modify auditing details,
- * index 2 is the authority needed to change other ACL and ACE details) (required)
- */
- public AclImpl(ObjectIdentity objectIdentity, Long id, GrantedAuthority[] auths) {
- Assert.notNull(objectIdentity, "Object Identity required");
- Assert.notNull(id, "Id required");
- this.objectIdentity = objectIdentity;
- this.id = id;
- this.setAuthorities(auths);
- }
-
-/**
- * Full constructor, which should be used by persistence tools that do not
- * provide field-level access features.
- *
- * @param objectIdentity the object identity this ACL relates to (required)
- * @param id the primary key assigned to this ACL (required)
- * @param auths an array of GrantedAuthority
s that have
- * special permissions (index 0 is the authority needed to change
- * ownership, index 1 is the authority needed to modify auditing details,
- * index 2 is the authority needed to change other ACL and ACE details) (required)
- * @param parentAcl the parent (may be null
)
- * @param loadedSids the loaded SIDs if only a subset were loaded (may be
- * null
)
- * @param entriesInheriting if ACEs from the parent should inherit into
- * this ACL
- * @param owner the owner (required)
- */
- public AclImpl(ObjectIdentity objectIdentity, Long id, Acl parentAcl, GrantedAuthority[] auths, Sid[] loadedSids,
- boolean entriesInheriting, Sid owner) {
- Assert.notNull(objectIdentity, "Object Identity required");
- Assert.notNull(id, "Id required");
- Assert.notNull(owner, "Owner required");
- this.objectIdentity = objectIdentity;
- this.id = id;
- setAuthorities(auths);
- this.parentAcl = parentAcl; // may be null
- this.loadedSids = loadedSids; // may be null
- this.entriesInheriting = entriesInheriting;
- this.owner = owner;
- }
-
-/**
- * Private no-argument constructor for use by reflection-based persistence
- * tools along with field-level access.
- */
- private AclImpl() {}
-
- //~ Methods ========================================================================================================
-
- /**
- * Clears the dirty flags on the Acl
, but not any associated ACEs.
- */
- public void clearDirtyFlags() {
- this.aclDirty = false;
- this.addedAces = false;
- this.updatedAces = false;
- }
-
- public void deleteAce(Long aceId) throws NotFoundException {
- securityCheck(CHANGE_GENERAL);
-
- synchronized (aces) {
- int offset = findAceOffset(aceId);
-
- if (offset == 1) {
- throw new NotFoundException("Requested ACE ID not found");
- }
-
- aces.remove(offset);
- deletedAces.add(aceId);
- }
- }
-
- private int findAceOffset(Long aceId) {
- Assert.notNull(aceId, "ACE ID is required");
-
- synchronized (aces) {
- for (int i = 0; i < aces.size(); i++) {
- AccessControlEntry ace = (AccessControlEntry) aces.get(i);
-
- if (ace.getId().equals(aceId)) {
- return i;
- }
- }
- }
-
- return -1;
- }
-
- public AccessControlEntry[] getEntries() {
- // Can safely return AccessControlEntry directly, as they're immutable
- return (AccessControlEntry[]) aces.toArray(new AccessControlEntry[] {});
- }
-
- public Serializable getId() {
- return this.id;
- }
-
- public ObjectIdentity getObjectIdentity() {
- return objectIdentity;
- }
-
- public Sid getOwner() {
- return this.owner;
- }
-
- public Acl getParentAcl() {
- return parentAcl;
- }
-
- public void insertAce(Long afterAceId, Permission permission, Sid sid, boolean granting)
- throws NotFoundException {
- securityCheck(CHANGE_GENERAL);
- Assert.notNull(permission, "Permission required");
- Assert.notNull(sid, "Sid required");
-
- AccessControlEntryImpl ace = new AccessControlEntryImpl(null, this, sid, permission, granting, false, false);
-
- synchronized (aces) {
- if (afterAceId != null) {
- int offset = findAceOffset(afterAceId);
-
- if (offset == -1) {
- throw new NotFoundException("Requested ACE ID not found");
- }
-
- aces.add(offset + 1, ace);
- } else {
- aces.add(ace);
- }
- }
-
- this.addedAces = true;
- }
-
- public boolean isAclDirty() {
- return aclDirty;
- }
-
- public boolean isEntriesInheriting() {
- return entriesInheriting;
- }
-
- /**
- * Determines authorization. The order of the permission
and sid
arguments is
- * extremely important! The method will iterate through each of the permission
s in the order
- * specified. For each iteration, all of the sid
s will be considered, again in the order they are
- * presented. The iteration of each permission:sid
combination will then inspect the ACEs in the
- * order they appear in the ACL. When the first full match is found (ie an ACE that has the SID currently
- * being searched for and the exact permission bit mask being search for), the grant or deny flag for that ACE
- * will prevail. If the ACE specifies to grant access, the method will return true
. If the ACE
- * specifies to deny access, the loop will stop and the next permission
iteration will be performed.
- * If each permission indicates to deny access, the first deny ACE found will be considered the reason for the
- * failure (as it was the first match found, and is therefore the one most logically requiring changes - although
- * not always). If absolutely no matching ACE was found at all for any permission, the parent ACL will be tried
- * (provided that there is a parent and {@link #isEntriesInheriting()} is true
. The parent ACL will
- * also scan its parent and so on. If ultimately no matching ACE is found, a NotFoundException
will
- * be thrown and the caller will need to decide how to handle the permission check. Similarly, if any of the
- * passed SIDs were not loaded by the ACL, the UnloadedSidException
will be thrown.
- *
- * @param permission the exact permissions to scan for (order is important)
- * @param sids the exact SIDs to scan for (order is important)
- * @param administrativeMode if true
denotes the query is for administrative purposes and no auditing
- * will be undertaken
- *
- * @return true
if one of the permissions has been granted, false
if one of the
- * permissions has been specifically revoked
- *
- * @throws NotFoundException if an exact ACE for one of the permission bit masks and SID combination could not be
- * found
- * @throws UnloadedSidException if the passed SIDs are unknown to this ACL because the ACL was only loaded for a
- * subset of SIDs
- */
- public boolean isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode)
- throws NotFoundException, UnloadedSidException {
- Assert.notEmpty(permission, "Permissions required");
- Assert.notEmpty(sids, "SIDs required");
-
- if (!this.isSidLoaded(sids)) {
- throw new UnloadedSidException("ACL was not loaded for one or more SID");
- }
-
- AccessControlEntry firstRejection = null;
-
- for (int i = 0; i < permission.length; i++) {
- for (int x = 0; x < sids.length; x++) {
- // Attempt to find exact match for this permission mask and SID
- Iterator acesIterator = aces.iterator();
- boolean scanNextSid = true;
-
- while (acesIterator.hasNext()) {
- AccessControlEntry ace = (AccessControlEntry) acesIterator.next();
-
- if ((ace.getPermission().getMask() == permission[i].getMask()) && ace.getSid().equals(sids[x])) {
- // Found a matching ACE, so its authorization decision will prevail
- if (ace.isGranting()) {
- // Success
- if (!administrativeMode) {
- auditLogger.logIfNeeded(true, ace);
- }
-
- return true;
- } else {
- // Failure for this permission, so stop search
- // We will see if they have a different permission
- // (this permission is 100% rejected for this SID)
- if (firstRejection == null) {
- // Store first rejection for auditing reasons
- firstRejection = ace;
- }
-
- scanNextSid = false; // helps break the loop
-
- break; // exit "aceIterator" while loop
- }
- }
- }
-
- if (!scanNextSid) {
- break; // exit SID for loop (now try next permission)
- }
- }
- }
-
- if (firstRejection != null) {
- // We found an ACE to reject the request at this point, as no
- // other ACEs were found that granted a different permission
- if (!administrativeMode) {
- auditLogger.logIfNeeded(false, firstRejection);
- }
-
- return false;
- }
-
- // No matches have been found so far
- if (isEntriesInheriting() && (parentAcl != null)) {
- // We have a parent, so let them try to find a matching ACE
- return parentAcl.isGranted(permission, sids, false);
- } else {
- // We either have no parent, or we're the uppermost parent
- throw new NotFoundException("Unable to locate a matching ACE for passed permissions and SIDs");
- }
- }
-
- public boolean isSidLoaded(Sid[] sids) {
- // If loadedSides is null, this indicates all SIDs were loaded
- // Also return true if the caller didn't specify a SID to find
- if ((this.loadedSids == null) || (sids == null) || (sids.length == 0)) {
- return true;
- }
-
- // This ACL applies to a SID subset. Iterate to check it applies
- for (int i = 0; i < sids.length; i++) {
- boolean found = false;
-
- for (int y = 0; y < this.loadedSids.length; y++) {
- if (sids[i].equals(this.loadedSids[y])) {
- // this SID is OK
- found = true;
-
- break; // out of loadedSids for loop
- }
- }
-
- if (!found) {
- return false;
- }
- }
-
- return true;
- }
-
- protected void securityCheck(int changeType) {
- if ((SecurityContextHolder.getContext() == null)
- || (SecurityContextHolder.getContext().getAuthentication() == null)
- || !SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) {
- throw new AccessDeniedException("Authenticated principal required to operate with ACLs");
- }
-
- Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-
- // Check if authorized by virtue of ACL ownership
- Sid currentUser = new PrincipalSid(authentication);
-
- if (currentUser.equals(this.owner) && ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) {
- return;
- }
-
- // Not authorized by ACL ownership; try via adminstrative permissions
- GrantedAuthority requiredAuthority = null;
-
- if (changeType == CHANGE_AUDITING) {
- requiredAuthority = this.gaModifyAuditing;
- } else if (changeType == CHANGE_GENERAL) {
- requiredAuthority = this.gaGeneralChanges;
- } else if (changeType == CHANGE_OWNERSHIP) {
- requiredAuthority = this.gaTakeOwnership;
- } else {
- throw new IllegalArgumentException("Unknown change type");
- }
-
- // Iterate this principal's authorities to determine right
- GrantedAuthority[] auths = authentication.getAuthorities();
-
- for (int i = 0; i < auths.length; i++) {
- if (requiredAuthority.equals(auths[i])) {
- return;
- }
- }
-
- throw new AccessDeniedException(
- "Principal does not have required ACL permissions to perform requested operation");
- }
-
- /**
- * Change the special adminstrative permissions honoured by this object.
Normally a principal must be the - * owner of the ACL in order to make most changes. The authorities passed to this method provide a way for - * non-owners to modify the ACL (and indeed modify audit parameters, which are not available to ACL owners).
- * - * @param auths an array ofGrantedAuthority
s that have administrative permissions (index 0 is the
- * authority needed to change ownership, index 1 is the authority needed to modify auditing details, index
- * 2 is the authority needed to change other ACL and ACE details)
- */
- private void setAuthorities(GrantedAuthority[] auths) {
- Assert.notEmpty(auths, "GrantedAuthority[] with three elements required");
- Assert.isTrue(auths.length == 3, "GrantedAuthority[] with three elements required");
- this.gaTakeOwnership = auths[0];
- this.gaModifyAuditing = auths[1];
- this.gaGeneralChanges = auths[2];
- }
-
- public void setEntriesInheriting(boolean entriesInheriting) {
- securityCheck(CHANGE_GENERAL);
- this.entriesInheriting = entriesInheriting;
- this.aclDirty = true;
- }
-
- public void setOwner(Sid newOwner) {
- securityCheck(CHANGE_OWNERSHIP);
- Assert.notNull(newOwner, "Owner required");
- this.owner = newOwner;
- this.aclDirty = true;
- }
-
- public void setParent(MutableAcl newParent) {
- securityCheck(CHANGE_GENERAL);
- Assert.notNull(newParent, "New Parent required");
- this.parentAcl = newParent;
- this.aclDirty = true;
- }
-
- public String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append("AclImpl[");
- sb.append("id: ").append(this.id).append("; ");
- sb.append("objectIdentity: ").append(this.objectIdentity).append("; ");
- sb.append("owner: ").append(this.owner).append("; ");
-
- Iterator iterator = this.aces.iterator();
- int count = 0;
-
- while (iterator.hasNext()) {
- count++;
-
- if (count == 1) {
- sb.append("\r\n");
- }
-
- sb.append(iterator.next().toString()).append("\r\n");
- }
-
- sb.append("inheriting: ").append(this.entriesInheriting).append("; ");
- sb.append("parent: ").append((this.parentAcl == null) ? "Null" : this.parentAcl.getObjectIdentity());
- sb.append("]");
-
- return sb.toString();
- }
-
- public void updateAce(Long aceId, Permission permission)
- throws NotFoundException {
- securityCheck(CHANGE_GENERAL);
-
- synchronized (aces) {
- int offset = findAceOffset(aceId);
-
- if (offset == 1) {
- throw new NotFoundException("Requested ACE ID not found");
- }
-
- AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(offset);
- ace.setPermission(permission);
- }
-
- this.updatedAces = true;
- }
-
- public void updateAuditing(Long aceId, boolean auditSuccess, boolean auditFailure) {
- securityCheck(CHANGE_AUDITING);
-
- synchronized (aces) {
- int offset = findAceOffset(aceId);
-
- if (offset == 1) {
- throw new NotFoundException("Requested ACE ID not found");
- }
-
- AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(offset);
- ace.setAuditSuccess(auditSuccess);
- ace.setAuditFailure(auditFailure);
- }
-
- this.updatedAces = true;
- }
-}
+/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.acegisecurity.acls.domain;
+
+import org.acegisecurity.AccessDeniedException;
+import org.acegisecurity.Authentication;
+import org.acegisecurity.GrantedAuthority;
+
+import org.acegisecurity.acls.AccessControlEntry;
+import org.acegisecurity.acls.Acl;
+import org.acegisecurity.acls.AuditableAcl;
+import org.acegisecurity.acls.MutableAcl;
+import org.acegisecurity.acls.NotFoundException;
+import org.acegisecurity.acls.OwnershipAcl;
+import org.acegisecurity.acls.Permission;
+import org.acegisecurity.acls.UnloadedSidException;
+import org.acegisecurity.acls.objectidentity.ObjectIdentity;
+import org.acegisecurity.acls.sid.PrincipalSid;
+import org.acegisecurity.acls.sid.Sid;
+
+import org.acegisecurity.context.SecurityContextHolder;
+
+import org.springframework.util.Assert;
+
+import java.io.Serializable;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+
+/**
+ * Base implementation of Acl
.
+ *
+ * @author Ben Alex
+ * @version $Id
+ */
+public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
+ //~ Static fields/initializers =====================================================================================
+
+ private static final int CHANGE_OWNERSHIP = 0;
+ private static final int CHANGE_AUDITING = 1;
+ private static final int CHANGE_GENERAL = 2;
+
+ //~ Instance fields ================================================================================================
+
+ private Acl parentAcl;
+ private AuditLogger auditLogger = new ConsoleAuditLogger(); // AuditableAcl
+ private GrantedAuthority gaGeneralChanges;
+ private GrantedAuthority gaModifyAuditing;
+ private GrantedAuthority gaTakeOwnership;
+ private List aces = new Vector();
+ private List deletedAces = new Vector();
+ private Long id;
+ private ObjectIdentity objectIdentity;
+ private Sid owner; // OwnershipAcl
+ private Sid[] loadedSids = null; // includes all SIDs the WHERE clause covered, even if there was no ACE for a SID
+ private boolean aclDirty = false; // for snapshot detection
+ private boolean addedAces = false; // for snapshot detection
+ private boolean entriesInheriting = false;
+ private boolean updatedAces = false; // for snapshot detection
+
+ //~ Constructors ===================================================================================================
+
+/**
+ * Minimal constructor, which should be used {@link
+ * org.acegisecurity.acls.MutableAclService#createAcl(ObjectIdentity)}.
+ *
+ * @param objectIdentity the object identity this ACL relates to (required)
+ * @param id the primary key assigned to this ACL (required)
+ * @param auths an array of GrantedAuthority
s that have
+ * special permissions (index 0 is the authority needed to change
+ * ownership, index 1 is the authority needed to modify auditing details,
+ * index 2 is the authority needed to change other ACL and ACE details) (required)
+ */
+ public AclImpl(ObjectIdentity objectIdentity, Long id, GrantedAuthority[] auths) {
+ Assert.notNull(objectIdentity, "Object Identity required");
+ Assert.notNull(id, "Id required");
+ this.objectIdentity = objectIdentity;
+ this.id = id;
+ this.setAuthorities(auths);
+ }
+
+/**
+ * Full constructor, which should be used by persistence tools that do not
+ * provide field-level access features.
+ *
+ * @param objectIdentity the object identity this ACL relates to (required)
+ * @param id the primary key assigned to this ACL (required)
+ * @param auths an array of GrantedAuthority
s that have
+ * special permissions (index 0 is the authority needed to change
+ * ownership, index 1 is the authority needed to modify auditing details,
+ * index 2 is the authority needed to change other ACL and ACE details) (required)
+ * @param parentAcl the parent (may be null
)
+ * @param loadedSids the loaded SIDs if only a subset were loaded (may be
+ * null
)
+ * @param entriesInheriting if ACEs from the parent should inherit into
+ * this ACL
+ * @param owner the owner (required)
+ */
+ public AclImpl(ObjectIdentity objectIdentity, Long id, Acl parentAcl, GrantedAuthority[] auths, Sid[] loadedSids,
+ boolean entriesInheriting, Sid owner) {
+ Assert.notNull(objectIdentity, "Object Identity required");
+ Assert.notNull(id, "Id required");
+ Assert.notNull(owner, "Owner required");
+ this.objectIdentity = objectIdentity;
+ this.id = id;
+ setAuthorities(auths);
+ this.parentAcl = parentAcl; // may be null
+ this.loadedSids = loadedSids; // may be null
+ this.entriesInheriting = entriesInheriting;
+ this.owner = owner;
+ }
+
+/**
+ * Private no-argument constructor for use by reflection-based persistence
+ * tools along with field-level access.
+ */
+ private AclImpl() {}
+
+ //~ Methods ========================================================================================================
+
+ /**
+ * Clears the dirty flags on the Acl
, but not any associated ACEs.
+ */
+ public void clearDirtyFlags() {
+ this.aclDirty = false;
+ this.addedAces = false;
+ this.updatedAces = false;
+ }
+
+ public void deleteAce(Long aceId) throws NotFoundException {
+ securityCheck(CHANGE_GENERAL);
+
+ synchronized (aces) {
+ int offset = findAceOffset(aceId);
+
+ if (offset == -1) {
+ throw new NotFoundException("Requested ACE ID not found");
+ }
+
+ aces.remove(offset);
+ deletedAces.add(aceId);
+ }
+ }
+
+ private int findAceOffset(Long aceId) {
+ Assert.notNull(aceId, "ACE ID is required");
+
+ synchronized (aces) {
+ for (int i = 0; i < aces.size(); i++) {
+ AccessControlEntry ace = (AccessControlEntry) aces.get(i);
+
+ if (ace.getId().equals(aceId)) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ public AccessControlEntry[] getEntries() {
+ // Can safely return AccessControlEntry directly, as they're immutable
+ return (AccessControlEntry[]) aces.toArray(new AccessControlEntry[] {});
+ }
+
+ public Serializable getId() {
+ return this.id;
+ }
+
+ public ObjectIdentity getObjectIdentity() {
+ return objectIdentity;
+ }
+
+ public Sid getOwner() {
+ return this.owner;
+ }
+
+ public Acl getParentAcl() {
+ return parentAcl;
+ }
+
+ public void insertAce(Long afterAceId, Permission permission, Sid sid, boolean granting)
+ throws NotFoundException {
+ securityCheck(CHANGE_GENERAL);
+ Assert.notNull(permission, "Permission required");
+ Assert.notNull(sid, "Sid required");
+
+ AccessControlEntryImpl ace = new AccessControlEntryImpl(null, this, sid, permission, granting, false, false);
+
+ synchronized (aces) {
+ if (afterAceId != null) {
+ int offset = findAceOffset(afterAceId);
+
+ if (offset == -1) {
+ throw new NotFoundException("Requested ACE ID not found");
+ }
+
+ aces.add(offset + 1, ace);
+ } else {
+ aces.add(ace);
+ }
+ }
+
+ this.addedAces = true;
+ }
+
+ public boolean isAclDirty() {
+ return aclDirty;
+ }
+
+ public boolean isEntriesInheriting() {
+ return entriesInheriting;
+ }
+
+ /**
+ * Determines authorization. The order of the permission
and sid
arguments is
+ * extremely important! The method will iterate through each of the permission
s in the order
+ * specified. For each iteration, all of the sid
s will be considered, again in the order they are
+ * presented. The iteration of each permission:sid
combination will then inspect the ACEs in the
+ * order they appear in the ACL. When the first full match is found (ie an ACE that has the SID currently
+ * being searched for and the exact permission bit mask being search for), the grant or deny flag for that ACE
+ * will prevail. If the ACE specifies to grant access, the method will return true
. If the ACE
+ * specifies to deny access, the loop will stop and the next permission
iteration will be performed.
+ * If each permission indicates to deny access, the first deny ACE found will be considered the reason for the
+ * failure (as it was the first match found, and is therefore the one most logically requiring changes - although
+ * not always). If absolutely no matching ACE was found at all for any permission, the parent ACL will be tried
+ * (provided that there is a parent and {@link #isEntriesInheriting()} is true
. The parent ACL will
+ * also scan its parent and so on. If ultimately no matching ACE is found, a NotFoundException
will
+ * be thrown and the caller will need to decide how to handle the permission check. Similarly, if any of the
+ * passed SIDs were not loaded by the ACL, the UnloadedSidException
will be thrown.
+ *
+ * @param permission the exact permissions to scan for (order is important)
+ * @param sids the exact SIDs to scan for (order is important)
+ * @param administrativeMode if true
denotes the query is for administrative purposes and no auditing
+ * will be undertaken
+ *
+ * @return true
if one of the permissions has been granted, false
if one of the
+ * permissions has been specifically revoked
+ *
+ * @throws NotFoundException if an exact ACE for one of the permission bit masks and SID combination could not be
+ * found
+ * @throws UnloadedSidException if the passed SIDs are unknown to this ACL because the ACL was only loaded for a
+ * subset of SIDs
+ */
+ public boolean isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode)
+ throws NotFoundException, UnloadedSidException {
+ Assert.notEmpty(permission, "Permissions required");
+ Assert.notEmpty(sids, "SIDs required");
+
+ if (!this.isSidLoaded(sids)) {
+ throw new UnloadedSidException("ACL was not loaded for one or more SID");
+ }
+
+ AccessControlEntry firstRejection = null;
+
+ for (int i = 0; i < permission.length; i++) {
+ for (int x = 0; x < sids.length; x++) {
+ // Attempt to find exact match for this permission mask and SID
+ Iterator acesIterator = aces.iterator();
+ boolean scanNextSid = true;
+
+ while (acesIterator.hasNext()) {
+ AccessControlEntry ace = (AccessControlEntry) acesIterator.next();
+
+ if ((ace.getPermission().getMask() == permission[i].getMask()) && ace.getSid().equals(sids[x])) {
+ // Found a matching ACE, so its authorization decision will prevail
+ if (ace.isGranting()) {
+ // Success
+ if (!administrativeMode) {
+ auditLogger.logIfNeeded(true, ace);
+ }
+
+ return true;
+ } else {
+ // Failure for this permission, so stop search
+ // We will see if they have a different permission
+ // (this permission is 100% rejected for this SID)
+ if (firstRejection == null) {
+ // Store first rejection for auditing reasons
+ firstRejection = ace;
+ }
+
+ scanNextSid = false; // helps break the loop
+
+ break; // exit "aceIterator" while loop
+ }
+ }
+ }
+
+ if (!scanNextSid) {
+ break; // exit SID for loop (now try next permission)
+ }
+ }
+ }
+
+ if (firstRejection != null) {
+ // We found an ACE to reject the request at this point, as no
+ // other ACEs were found that granted a different permission
+ if (!administrativeMode) {
+ auditLogger.logIfNeeded(false, firstRejection);
+ }
+
+ return false;
+ }
+
+ // No matches have been found so far
+ if (isEntriesInheriting() && (parentAcl != null)) {
+ // We have a parent, so let them try to find a matching ACE
+ return parentAcl.isGranted(permission, sids, false);
+ } else {
+ // We either have no parent, or we're the uppermost parent
+ throw new NotFoundException("Unable to locate a matching ACE for passed permissions and SIDs");
+ }
+ }
+
+ public boolean isSidLoaded(Sid[] sids) {
+ // If loadedSides is null, this indicates all SIDs were loaded
+ // Also return true if the caller didn't specify a SID to find
+ if ((this.loadedSids == null) || (sids == null) || (sids.length == 0)) {
+ return true;
+ }
+
+ // This ACL applies to a SID subset. Iterate to check it applies
+ for (int i = 0; i < sids.length; i++) {
+ boolean found = false;
+
+ for (int y = 0; y < this.loadedSids.length; y++) {
+ if (sids[i].equals(this.loadedSids[y])) {
+ // this SID is OK
+ found = true;
+
+ break; // out of loadedSids for loop
+ }
+ }
+
+ if (!found) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected void securityCheck(int changeType) {
+ if ((SecurityContextHolder.getContext() == null)
+ || (SecurityContextHolder.getContext().getAuthentication() == null)
+ || !SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) {
+ throw new AccessDeniedException("Authenticated principal required to operate with ACLs");
+ }
+
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+
+ // Check if authorized by virtue of ACL ownership
+ Sid currentUser = new PrincipalSid(authentication);
+
+ if (currentUser.equals(this.owner) && ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) {
+ return;
+ }
+
+ // Not authorized by ACL ownership; try via adminstrative permissions
+ GrantedAuthority requiredAuthority = null;
+
+ if (changeType == CHANGE_AUDITING) {
+ requiredAuthority = this.gaModifyAuditing;
+ } else if (changeType == CHANGE_GENERAL) {
+ requiredAuthority = this.gaGeneralChanges;
+ } else if (changeType == CHANGE_OWNERSHIP) {
+ requiredAuthority = this.gaTakeOwnership;
+ } else {
+ throw new IllegalArgumentException("Unknown change type");
+ }
+
+ // Iterate this principal's authorities to determine right
+ GrantedAuthority[] auths = authentication.getAuthorities();
+
+ for (int i = 0; i < auths.length; i++) {
+ if (requiredAuthority.equals(auths[i])) {
+ return;
+ }
+ }
+
+ throw new AccessDeniedException(
+ "Principal does not have required ACL permissions to perform requested operation");
+ }
+
+ /**
+ * Change the special adminstrative permissions honoured by this object.Normally a principal must be the + * owner of the ACL in order to make most changes. The authorities passed to this method provide a way for + * non-owners to modify the ACL (and indeed modify audit parameters, which are not available to ACL owners).
+ * + * @param auths an array ofGrantedAuthority
s that have administrative permissions (index 0 is the
+ * authority needed to change ownership, index 1 is the authority needed to modify auditing details, index
+ * 2 is the authority needed to change other ACL and ACE details)
+ */
+ private void setAuthorities(GrantedAuthority[] auths) {
+ Assert.notEmpty(auths, "GrantedAuthority[] with three elements required");
+ Assert.isTrue(auths.length == 3, "GrantedAuthority[] with three elements required");
+ this.gaTakeOwnership = auths[0];
+ this.gaModifyAuditing = auths[1];
+ this.gaGeneralChanges = auths[2];
+ }
+
+ public void setEntriesInheriting(boolean entriesInheriting) {
+ securityCheck(CHANGE_GENERAL);
+ this.entriesInheriting = entriesInheriting;
+ this.aclDirty = true;
+ }
+
+ public void setOwner(Sid newOwner) {
+ securityCheck(CHANGE_OWNERSHIP);
+ Assert.notNull(newOwner, "Owner required");
+ this.owner = newOwner;
+ this.aclDirty = true;
+ }
+
+ public void setParent(MutableAcl newParent) {
+ securityCheck(CHANGE_GENERAL);
+ Assert.notNull(newParent, "New Parent required");
+ this.parentAcl = newParent;
+ this.aclDirty = true;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("AclImpl[");
+ sb.append("id: ").append(this.id).append("; ");
+ sb.append("objectIdentity: ").append(this.objectIdentity).append("; ");
+ sb.append("owner: ").append(this.owner).append("; ");
+
+ Iterator iterator = this.aces.iterator();
+ int count = 0;
+
+ while (iterator.hasNext()) {
+ count++;
+
+ if (count == 1) {
+ sb.append("\r\n");
+ }
+
+ sb.append(iterator.next().toString()).append("\r\n");
+ }
+
+ sb.append("inheriting: ").append(this.entriesInheriting).append("; ");
+ sb.append("parent: ").append((this.parentAcl == null) ? "Null" : this.parentAcl.getObjectIdentity());
+ sb.append("]");
+
+ return sb.toString();
+ }
+
+ public void updateAce(Long aceId, Permission permission)
+ throws NotFoundException {
+ securityCheck(CHANGE_GENERAL);
+
+ synchronized (aces) {
+ int offset = findAceOffset(aceId);
+
+ if (offset == 1) {
+ throw new NotFoundException("Requested ACE ID not found");
+ }
+
+ AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(offset);
+ ace.setPermission(permission);
+ }
+
+ this.updatedAces = true;
+ }
+
+ public void updateAuditing(Long aceId, boolean auditSuccess, boolean auditFailure) {
+ securityCheck(CHANGE_AUDITING);
+
+ synchronized (aces) {
+ int offset = findAceOffset(aceId);
+
+ if (offset == 1) {
+ throw new NotFoundException("Requested ACE ID not found");
+ }
+
+ AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(offset);
+ ace.setAuditSuccess(auditSuccess);
+ ace.setAuditFailure(auditFailure);
+ }
+
+ this.updatedAces = true;
+ }
+}
diff --git a/sandbox/src/main/java/org/acegisecurity/acls/domain/BasePermission.java b/sandbox/src/main/java/org/acegisecurity/acls/domain/BasePermission.java
index e840c4a3bf..b8398aec23 100644
--- a/sandbox/src/main/java/org/acegisecurity/acls/domain/BasePermission.java
+++ b/sandbox/src/main/java/org/acegisecurity/acls/domain/BasePermission.java
@@ -1,102 +1,106 @@
-/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.acegisecurity.acls.domain;
-
-import org.acegisecurity.acls.AclFormattingUtils;
-import org.acegisecurity.acls.Permission;
-
-
-/**
- * DOCUMENT ME!
- *
- * @author $author$
- * @version $Revision$
- */
-public class BasePermission implements Permission {
- //~ Static fields/initializers =====================================================================================
-
- public static final Permission READ = new BasePermission(1 << 0, 'R'); // 1
- public static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2
- public static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4
- public static final Permission ADMINISTRATION = new BasePermission(1 << 3, 'A'); // 8
-
- //~ Instance fields ================================================================================================
-
- private char code;
- private int mask;
-
- //~ Constructors ===================================================================================================
-
- private BasePermission(int mask, char code) {
- this.mask = mask;
- this.code = code;
- }
-
- //~ Methods ========================================================================================================
-
- /**
- * Dynamically creates a CumulativePermission
representing the active bits in the passed mask.
- * NB: Only uses BasePermission
!
- *
- * @param mask to review
- *
- * @return DOCUMENT ME!
- */
- public static Permission buildFromMask(int mask) {
- CumulativePermission permission = new CumulativePermission();
-
- // TODO: Write the rest of it to iterate through the 32 bits and instantiate BasePermissions
- if (mask == 1) {
- permission.set(READ);
- }
-
- if (mask == 2) {
- permission.set(WRITE);
- }
-
- if (mask == 4) {
- permission.set(CREATE);
- }
-
- if (mask == 8) {
- permission.set(ADMINISTRATION);
- }
-
- return permission;
- }
-
- public boolean equals(Object arg0) {
- if (!(arg0 instanceof BasePermission)) {
- return false;
- }
-
- BasePermission rhs = (BasePermission) arg0;
-
- return (this.mask == rhs.getMask());
- }
-
- public int getMask() {
- return mask;
- }
-
- public String getPattern() {
- return AclFormattingUtils.printBinary(mask, code);
- }
-
- public String toString() {
- return "BasePermission[" + getPattern() + "=" + mask + "]";
- }
-}
+/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.acegisecurity.acls.domain;
+
+import org.acegisecurity.acls.AclFormattingUtils;
+import org.acegisecurity.acls.Permission;
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author $author$
+ * @version $Revision$
+ */
+public class BasePermission implements Permission {
+ //~ Static fields/initializers =====================================================================================
+
+ public static final Permission READ = new BasePermission(1 << 0, 'R'); // 1
+ public static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2
+ public static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4
+ public static final Permission DELETE = new BasePermission(1 << 3, 'D'); // 8
+ public static final Permission ADMINISTRATION = new BasePermission(1 << 4, 'A'); // 16
+
+ //~ Instance fields ================================================================================================
+
+ private char code;
+ private int mask;
+
+ //~ Constructors ===================================================================================================
+
+ private BasePermission(int mask, char code) {
+ this.mask = mask;
+ this.code = code;
+ }
+
+ //~ Methods ========================================================================================================
+
+ /**
+ * Dynamically creates a CumulativePermission
representing the active bits in the passed mask.
+ * NB: Only uses BasePermission
!
+ *
+ * @param mask to review
+ *
+ * @return DOCUMENT ME!
+ */
+ public static Permission buildFromMask(int mask) {
+ CumulativePermission permission = new CumulativePermission();
+
+ // TODO: Write the rest of it to iterate through the 32 bits and instantiate BasePermissions
+ if (mask == 1) {
+ permission.set(READ);
+ }
+
+ if (mask == 2) {
+ permission.set(WRITE);
+ }
+
+ if (mask == 4) {
+ permission.set(CREATE);
+ }
+
+ if (mask == 8) {
+ permission.set(DELETE);
+ }
+
+ if (mask == 16) {
+ permission.set(ADMINISTRATION);
+ }
+
+ return permission;
+ }
+
+ public boolean equals(Object arg0) {
+ if (!(arg0 instanceof BasePermission)) {
+ return false;
+ }
+
+ BasePermission rhs = (BasePermission) arg0;
+
+ return (this.mask == rhs.getMask());
+ }
+
+ public int getMask() {
+ return mask;
+ }
+
+ public String getPattern() {
+ return AclFormattingUtils.printBinary(mask, code);
+ }
+
+ public String toString() {
+ return "BasePermission[" + getPattern() + "=" + mask + "]";
+ }
+}
diff --git a/sandbox/src/test/java/org/acegisecurity/acls/domain/PermissionTests.java b/sandbox/src/test/java/org/acegisecurity/acls/domain/PermissionTests.java
index e5e19db92f..4b63c6818f 100644
--- a/sandbox/src/test/java/org/acegisecurity/acls/domain/PermissionTests.java
+++ b/sandbox/src/test/java/org/acegisecurity/acls/domain/PermissionTests.java
@@ -1,70 +1,70 @@
-/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.acegisecurity.acls.domain;
-
-import junit.framework.TestCase;
-
-
-/**
- * Tests BasePermission and CumulativePermission.
- *
- * @author Ben Alex
- * @version $Id${date}
- */
-public class PermissionTests extends TestCase {
- //~ Methods ========================================================================================================
-
- public void testExpectedIntegerValues() {
- assertEquals(1, BasePermission.READ.getMask());
- assertEquals(8, BasePermission.ADMINISTRATION.getMask());
- assertEquals(9, new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION).getMask());
- }
-
- public void testStringConversion() {
- System.out.println("R = " + BasePermission.READ.toString());
- assertEquals("BasePermission[...............................R=1]", BasePermission.READ.toString());
-
- System.out.println("A = " + BasePermission.ADMINISTRATION.toString());
- assertEquals("BasePermission[............................A...=8]", BasePermission.ADMINISTRATION.toString());
-
- System.out.println("R = " + new CumulativePermission().set(BasePermission.READ).toString());
- assertEquals("CumulativePermission[...............................R=1]",
- new CumulativePermission().set(BasePermission.READ).toString());
-
- System.out.println("A = " + new CumulativePermission().set(BasePermission.ADMINISTRATION).toString());
- assertEquals("CumulativePermission[............................A...=8]",
- new CumulativePermission().set(BasePermission.ADMINISTRATION).toString());
-
- System.out.println("RA = "
- + new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ).toString());
- assertEquals("CumulativePermission[............................A..R=9]",
- new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ).toString());
-
- System.out.println("R = "
- + new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
- .clear(BasePermission.ADMINISTRATION).toString());
- assertEquals("CumulativePermission[...............................R=1]",
- new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
- .clear(BasePermission.ADMINISTRATION).toString());
-
- System.out.println("0 = "
- + new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
- .clear(BasePermission.ADMINISTRATION).clear(BasePermission.READ).toString());
- assertEquals("CumulativePermission[................................=0]",
- new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
- .clear(BasePermission.ADMINISTRATION).clear(BasePermission.READ).toString());
- }
-}
+/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.acegisecurity.acls.domain;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests BasePermission and CumulativePermission.
+ *
+ * @author Ben Alex
+ * @version $Id${date}
+ */
+public class PermissionTests extends TestCase {
+ //~ Methods ========================================================================================================
+
+ public void testExpectedIntegerValues() {
+ assertEquals(1, BasePermission.READ.getMask());
+ assertEquals(16, BasePermission.ADMINISTRATION.getMask());
+ assertEquals(17, new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION).getMask());
+ }
+
+ public void testStringConversion() {
+ System.out.println("R = " + BasePermission.READ.toString());
+ assertEquals("BasePermission[...............................R=1]", BasePermission.READ.toString());
+
+ System.out.println("A = " + BasePermission.ADMINISTRATION.toString());
+ assertEquals("BasePermission[...........................A....=16]", BasePermission.ADMINISTRATION.toString());
+
+ System.out.println("R = " + new CumulativePermission().set(BasePermission.READ).toString());
+ assertEquals("CumulativePermission[...............................R=1]",
+ new CumulativePermission().set(BasePermission.READ).toString());
+
+ System.out.println("A = " + new CumulativePermission().set(BasePermission.ADMINISTRATION).toString());
+ assertEquals("CumulativePermission[...........................A....=16]",
+ new CumulativePermission().set(BasePermission.ADMINISTRATION).toString());
+
+ System.out.println("RA = "
+ + new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ).toString());
+ assertEquals("CumulativePermission[...........................A...R=17]",
+ new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ).toString());
+
+ System.out.println("R = "
+ + new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
+ .clear(BasePermission.ADMINISTRATION).toString());
+ assertEquals("CumulativePermission[...............................R=1]",
+ new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
+ .clear(BasePermission.ADMINISTRATION).toString());
+
+ System.out.println("0 = "
+ + new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
+ .clear(BasePermission.ADMINISTRATION).clear(BasePermission.READ).toString());
+ assertEquals("CumulativePermission[................................=0]",
+ new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
+ .clear(BasePermission.ADMINISTRATION).clear(BasePermission.READ).toString());
+ }
+}