/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AuditableAcl;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.OwnershipAcl;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.UnloadedSidException;
import org.springframework.util.Assert;
/**
* Base implementation of <code>Acl</code>.
*
* @author Ben Alex
*/
public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
// ~ Instance fields
// ================================================================================================
private Acl parentAcl;
private transient AclAuthorizationStrategy aclAuthorizationStrategy;
private transient PermissionGrantingStrategy permissionGrantingStrategy;
private final List<AccessControlEntry> aces = new ArrayList<AccessControlEntry>();
private ObjectIdentity objectIdentity;
private Serializable id;
private Sid owner; // OwnershipAcl
private List<Sid> loadedSids = null; // includes all SIDs the WHERE clause covered,
// even if there was no ACE for a SID
private boolean entriesInheriting = true;
// ~ Constructors
// ===================================================================================================
/**
* Minimal constructor, which should be used
* {@link org.springframework.security.acls.model.MutableAclService#createAcl(ObjectIdentity)}
* .
*
* @param objectIdentity the object identity this ACL relates to (required)
* @param id the primary key assigned to this ACL (required)
* @param aclAuthorizationStrategy authorization strategy (required)
* @param auditLogger audit logger (required)
*/
public AclImpl(ObjectIdentity objectIdentity, Serializable id,
AclAuthorizationStrategy aclAuthorizationStrategy, AuditLogger auditLogger) {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(id, "Id required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
Assert.notNull(auditLogger, "AuditLogger required");
this.objectIdentity = objectIdentity;
this.id = id;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(
auditLogger);
}
/**
* 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
* @param id the primary key assigned to this ACL
* @param aclAuthorizationStrategy authorization strategy
* @param grantingStrategy the {@code PermissionGrantingStrategy} which will be used
* by the {@code isGranted()} method
* @param parentAcl the parent (may be may be {@code null})
* @param loadedSids the loaded SIDs if only a subset were loaded (may be {@code null}
* )
* @param entriesInheriting if ACEs from the parent should inherit into this ACL
* @param owner the owner (required)
*/
public AclImpl(ObjectIdentity objectIdentity, Serializable id,
AclAuthorizationStrategy aclAuthorizationStrategy,
PermissionGrantingStrategy grantingStrategy, Acl parentAcl,
List<Sid> loadedSids, boolean entriesInheriting, Sid owner) {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(id, "Id required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
Assert.notNull(owner, "Owner required");
this.objectIdentity = objectIdentity;
this.id = id;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.parentAcl = parentAcl; // may be null
this.loadedSids = loadedSids; // may be null
this.entriesInheriting = entriesInheriting;
this.owner = owner;
this.permissionGrantingStrategy = grantingStrategy;
}
/**
* Private no-argument constructor for use by reflection-based persistence tools along
* with field-level access.
*/
@SuppressWarnings("unused")
private AclImpl() {
}
// ~ Methods
// ========================================================================================================
public void deleteAce(int aceIndex) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
this.aces.remove(aceIndex);
}
}
private void verifyAceIndexExists(int aceIndex) {
if (aceIndex < 0) {
throw new NotFoundException("aceIndex must be greater than or equal to zero");
}
if (aceIndex >= this.aces.size()) {
throw new NotFoundException(
"aceIndex must refer to an index of the AccessControlEntry list. "
+ "List size is " + aces.size() + ", index was " + aceIndex);
}
}
public void insertAce(int atIndexLocation, Permission permission, Sid sid,
boolean granting) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.notNull(permission, "Permission required");
Assert.notNull(sid, "Sid required");
if (atIndexLocation < 0) {
throw new NotFoundException(
"atIndexLocation must be greater than or equal to zero");
}
if (atIndexLocation > this.aces.size()) {
throw new NotFoundException(
"atIndexLocation must be less than or equal to the size of the AccessControlEntry collection");
}
AccessControlEntryImpl ace = new AccessControlEntryImpl(null, this, sid,
permission, granting, false, false);
synchronized (aces) {
this.aces.add(atIndexLocation, ace);
}
}
public List<AccessControlEntry> getEntries() {
// Can safely return AccessControlEntry directly, as they're immutable outside the
// ACL package
return new ArrayList<AccessControlEntry>(aces);
}
public Serializable getId() {
return this.id;
}
public ObjectIdentity getObjectIdentity() {
return objectIdentity;
}
public boolean isEntriesInheriting() {
return entriesInheriting;
}
/**
* Delegates to the {@link PermissionGrantingStrategy}.
*
* @throws UnloadedSidException if the passed SIDs are unknown to this ACL because the
* ACL was only loaded for a subset of SIDs
* @see DefaultPermissionGrantingStrategy
*/
public boolean isGranted(List<Permission> permission, List<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");
}
return permissionGrantingStrategy.isGranted(this, permission, sids,
administrativeMode);
}
public boolean isSidLoaded(List<Sid> sids) {
// If loadedSides is null, this indicates all SIDs were loaded
// Also return true if the caller didn't specify a SID to find
if ((this.loadedSids == null) || (sids == null) || (sids.size() == 0)) {
return true;
}
// This ACL applies to a SID subset only. Iterate to check it applies.
for (Sid sid : sids) {
boolean found = false;
for (Sid loadedSid : loadedSids) {
if (sid.equals(loadedSid)) {
// this SID is OK
found = true;
break; // out of loadedSids for loop
}
}
if (!found) {
return false;
}
}
return true;
}
public void setEntriesInheriting(boolean entriesInheriting) {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
this.entriesInheriting = entriesInheriting;
}
public void setOwner(Sid newOwner) {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.notNull(newOwner, "Owner required");
this.owner = newOwner;
}
public Sid getOwner() {
return this.owner;
}
public void setParent(Acl newParent) {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.isTrue(newParent == null || !newParent.equals(this),
"Cannot be the parent of yourself");
this.parentAcl = newParent;
}
public Acl getParentAcl() {
return parentAcl;
}
public void updateAce(int aceIndex, Permission permission) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(aceIndex);
ace.setPermission(permission);
}
}
public void updateAuditing(int aceIndex, boolean auditSuccess, boolean auditFailure) {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_AUDITING);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(aceIndex);
ace.setAuditSuccess(auditSuccess);
ace.setAuditFailure(auditFailure);
}
}
public boolean equals(Object obj) {
if (obj instanceof AclImpl) {
AclImpl rhs = (AclImpl) obj;
if (this.aces.equals(rhs.aces)) {
if ((this.parentAcl == null && rhs.parentAcl == null)
|| (this.parentAcl != null && this.parentAcl
.equals(rhs.parentAcl))) {
if ((this.objectIdentity == null && rhs.objectIdentity == null)
|| (this.objectIdentity != null && this.objectIdentity
.equals(rhs.objectIdentity))) {
if ((this.id == null && rhs.id == null)
|| (this.id != null && this.id.equals(rhs.id))) {
if ((this.owner == null && rhs.owner == null)
|| (this.owner != null && this.owner
.equals(rhs.owner))) {
if (this.entriesInheriting == rhs.entriesInheriting) {
if ((this.loadedSids == null && rhs.loadedSids == null)) {
return true;
}
if (this.loadedSids != null
&& (this.loadedSids.size() == rhs.loadedSids
.size())) {
for (int i = 0; i < this.loadedSids.size(); i++) {
if (!this.loadedSids.get(i).equals(
rhs.loadedSids.get(i))) {
return false;
}
}
return true;
}
}
}
}
}
}
}
}
return false;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("AclImpl[");
sb.append("id: ").append(this.id).append("; ");
sb.append("objectIdentity: ").append(this.objectIdentity).append("; ");
sb.append("owner: ").append(this.owner).append("; ");
int count = 0;
for (AccessControlEntry ace : aces) {
count++;
if (count == 1) {
sb.append("\n");
}
sb.append(ace).append("\n");
}
if (count == 0) {
sb.append("no ACEs; ");
}
sb.append("inheriting: ").append(this.entriesInheriting).append("; ");
sb.append("parent: ").append(
(this.parentAcl == null) ? "Null" : this.parentAcl.getObjectIdentity()
.toString());
sb.append("; ");
sb.append("aclAuthorizationStrategy: ").append(this.aclAuthorizationStrategy)
.append("; ");
sb.append("permissionGrantingStrategy: ").append(this.permissionGrantingStrategy);
sb.append("]");
return sb.toString();
}
}