SEC-239: Initial commit. Work-in-progress only.
This commit is contained in:
parent
4d24c88d1e
commit
5ba40705e8
|
@ -0,0 +1,30 @@
|
|||
package org.acegisecurity.acls;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.acegisecurity.acls.sid.Sid;
|
||||
|
||||
/**
|
||||
* Represents an individual permission assignment within an {@link Acl}.
|
||||
*
|
||||
* <p>
|
||||
* Instances MUST be immutable, as they are returned by <code>Acl</code>
|
||||
* and should not allow client modification.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*
|
||||
*/
|
||||
public interface AccessControlEntry {
|
||||
/**
|
||||
* Obtains an identifier that represents this ACE.
|
||||
*
|
||||
* @return the identifier, or <code>null</code> if unsaved
|
||||
*/
|
||||
public Serializable getId();
|
||||
|
||||
public Acl getAcl();
|
||||
public Sid getSid();
|
||||
public Permission getPermission();
|
||||
public boolean isGranting();
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
|
||||
import org.acegisecurity.acls.sid.Sid;
|
||||
|
||||
|
||||
/**
|
||||
* Represents an access control list (ACL) for a domain object.
|
||||
*
|
||||
* <p>
|
||||
* An <code>Acl</code> represents all ACL entries for a given domain object. In
|
||||
* order to avoid needing references to the domain object itself, this
|
||||
* interface handles indirection between a domain object and an ACL object
|
||||
* identity via the {@link
|
||||
* org.acegisecurity.acls.objectidentity.ObjectIdentity} interface.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* An implementation represents the {@link org.acegisecurity.acls.Permission}
|
||||
* list applicable for some or all {@link org.acegisecurity.acls.sid.Sid}
|
||||
* instances.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface Acl extends Serializable {
|
||||
//~ Methods ================================================================
|
||||
|
||||
|
||||
/**
|
||||
* Returns all of the entries represented by the present <code>Acl</code>
|
||||
* (not parents).
|
||||
*
|
||||
* <p>
|
||||
* This method is typically used for administrative purposes.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The order that entries appear in the array is unspecified. However, if
|
||||
* implementations use particular ordering logic in authorization
|
||||
* decisions, the entries returned by this method <em>MUST</em> be ordered
|
||||
* in that manner.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Do <em>NOT</em> use this method for making authorization decisions.
|
||||
* Instead use {@link #isGranted(Permission[], Sid[])}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This method must operate correctly even if the <code>Acl</code> only
|
||||
* represents a subset of <code>Sid</code>s. The caller is responsible for
|
||||
* correctly handling the result if only a subset of <code>Sid</code>s is
|
||||
* represented.
|
||||
* </p>
|
||||
*
|
||||
* @return the list of entries represented by the <code>Acl</code>
|
||||
*/
|
||||
public AccessControlEntry[] getEntries();
|
||||
|
||||
/**
|
||||
* Obtains the domain object this <code>Acl</code> provides entries for.
|
||||
* This is immutable once an <code>Acl</code> is created.
|
||||
*
|
||||
* @return the object identity
|
||||
*/
|
||||
public ObjectIdentity getObjectIdentity();
|
||||
|
||||
/**
|
||||
* A domain object may have a parent for the purpose of ACL inheritance. If
|
||||
* there is a parent, its ACL can be accessed via this method. In turn,
|
||||
* the parent's parent (grandparent) can be accessed and so on.
|
||||
*
|
||||
* <p>
|
||||
* This method solely represents the presence of a navigation hierarchy
|
||||
* between the parent <code>Acl</code> and this <code>Acl</code>. For
|
||||
* actual inheritance to take place, the {@link #isEntriesInheriting()}
|
||||
* must also be <code>true</code>.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This method must operate correctly even if the <code>Acl</code> only
|
||||
* represents a subset of <code>Sid</code>s. The caller is responsible for
|
||||
* correctly handling the result if only a subset of <code>Sid</code>s is
|
||||
* represented.
|
||||
* </p>
|
||||
*
|
||||
* @return the parent <code>Acl</code>
|
||||
*/
|
||||
public Acl getParentAcl();
|
||||
|
||||
/**
|
||||
* Indicates whether the ACL entries from the {@link #getParentAcl()}
|
||||
* should flow down into the current <code>Acl</code>.
|
||||
*
|
||||
* <p>
|
||||
* The mere link between an <code>Acl</code> and a parent <code>Acl</code>
|
||||
* on its own is insufficient to cause ACL entries to inherit down. This
|
||||
* is because a domain object may wish to have entirely independent
|
||||
* entries, but maintain the link with the parent for navigation purposes.
|
||||
* Thus, this method denotes whether or not the navigation relationship
|
||||
* also extends to the actual inheritence of entries.
|
||||
* </p>
|
||||
*
|
||||
* @return <code>true</code> if parent ACL entries inherit into the current
|
||||
* <code>Acl</code>
|
||||
*/
|
||||
public boolean isEntriesInheriting();
|
||||
|
||||
/**
|
||||
* This is the actual authorization logic method, and must be used whenever
|
||||
* ACL authorization decisions are required.
|
||||
*
|
||||
* <p>
|
||||
* An array of <code>Sid</code>s are presented, representing security
|
||||
* identifies of the current principal. In addition, an array of
|
||||
* <code>Permission</code>s is presented which will have one or more bits
|
||||
* set in order to indicate the permissions needed for an affirmative
|
||||
* authorization decision. An array is presented because holding
|
||||
* <em>any</em> of the <code>Permission</code>s inside the array will be
|
||||
* sufficient for an affirmative authorization.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The actual approach used to make authorization decisions is left to the
|
||||
* implementation and is not specified by this interface. For example, an
|
||||
* implementation <em>MAY</em> search the current ACL in the order the ACL
|
||||
* entries have been stored. If a single entry is found that has the same
|
||||
* active bits as are shown in a passed <code>Permission</code>, that
|
||||
* entry's grant or deny state may determine the authorization decision.
|
||||
* If the case of a deny state, the deny decision will only be relevant if
|
||||
* all other <code>Permission</code>s passed in the array have also been
|
||||
* unsuccessfully searched. If no entry is found that match the bits in
|
||||
* the current ACL, provided that {@link #isEntriesInheriting()} is
|
||||
* <code>true</code>, the authorization decision may be passed to the
|
||||
* parent ACL. If there is no matching entry, the implementation MAY throw
|
||||
* an exception, or make a predefined authorization decision.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This method must operate correctly even if the <code>Acl</code> only
|
||||
* represents a subset of <code>Sid</code>s. The caller is responsible for
|
||||
* correctly handling the result if only a subset of <code>Sid</code>s is
|
||||
* represented.
|
||||
* </p>
|
||||
*
|
||||
* @param permission the permission or permissions required
|
||||
* @param sids the security identities held by the principal
|
||||
* @param administrativeMode if <code>true</code> denotes the query is for
|
||||
* administrative purposes and no logger or auditing (if supported
|
||||
* by the implementation) should be undertaken
|
||||
*
|
||||
* @return <code>true</code> is authorization is granted
|
||||
*
|
||||
* @throws NotFoundException MAY be thrown if an implementation cannot make
|
||||
* an authoritative authorization decision
|
||||
* @throws UnloadedSidException thrown if the <code>Acl</code> does not
|
||||
* have details for one or more of the <code>Sid</code>s passed as
|
||||
* arguments
|
||||
*/
|
||||
public boolean isGranted(Permission[] permission, Sid[] sids,
|
||||
boolean administrativeMode)
|
||||
throws NotFoundException, UnloadedSidException;
|
||||
|
||||
/**
|
||||
* For efficiency reasons an <code>Acl</code> may be loaded and
|
||||
* <em>not</em> contain entries for every <code>Sid</code> in the system.
|
||||
* If an <code>Acl</code> has been loaded and does not represent every
|
||||
* <code>Sid</code>, all methods of the <code>Sid</code> can only be used
|
||||
* within the limited scope of the <code>Sid</code> instances it actually
|
||||
* represents.
|
||||
*
|
||||
* <p>
|
||||
* It is normal to load an <code>Acl</code> for only particular
|
||||
* <code>Sid</code>s if read-only authorization decisions are being made.
|
||||
* However, if user interface reporting or modification of
|
||||
* <code>Acl</code>s are desired, an <code>Acl</code> should be loaded
|
||||
* with all <code>Sid</code>s. This method denotes whether or not the
|
||||
* specified <code>Sid</code>s have been loaded or not.
|
||||
* </p>
|
||||
*
|
||||
* @param sids DOCUMENT ME!
|
||||
*
|
||||
* @return <code>true</code> if every passed <code>Sid</code> is
|
||||
* represented by this <code>Acl</code> instance
|
||||
*/
|
||||
public boolean isSidLoaded(Sid[] sids);
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Utility methods for displaying ACL information.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AclFormattingUtils {
|
||||
//~ Methods ================================================================
|
||||
|
||||
public static String demergePatterns(String original, String removeBits) {
|
||||
Assert.notNull(original, "Original string required");
|
||||
Assert.notNull(removeBits, "Bits To Remove string required");
|
||||
Assert.isTrue(original.length() == removeBits.length(),
|
||||
"Original and Bits To Remove strings must be identical length");
|
||||
|
||||
char[] replacement = new char[original.length()];
|
||||
|
||||
for (int i = 0; i < original.length(); i++) {
|
||||
if (removeBits.charAt(i) == Permission.RESERVED_OFF) {
|
||||
replacement[i] = original.charAt(i);
|
||||
} else {
|
||||
replacement[i] = Permission.RESERVED_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
return new String(replacement);
|
||||
}
|
||||
|
||||
public static String mergePatterns(String original, String extraBits) {
|
||||
Assert.notNull(original, "Original string required");
|
||||
Assert.notNull(extraBits, "Extra Bits string required");
|
||||
Assert.isTrue(original.length() == extraBits.length(),
|
||||
"Original and Extra Bits strings must be identical length");
|
||||
|
||||
char[] replacement = new char[extraBits.length()];
|
||||
|
||||
for (int i = 0; i < extraBits.length(); i++) {
|
||||
if (extraBits.charAt(i) == Permission.RESERVED_OFF) {
|
||||
replacement[i] = original.charAt(i);
|
||||
} else {
|
||||
replacement[i] = extraBits.charAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
return new String(replacement);
|
||||
}
|
||||
|
||||
private static String printBinary(int i, char on, char off) {
|
||||
String s = Integer.toString(i, 2);
|
||||
String pattern = Permission.THIRTY_TWO_RESERVED_OFF;
|
||||
String temp2 = pattern.substring(0, pattern.length() - s.length()) + s;
|
||||
|
||||
return temp2.replace('0', off).replace('1', on);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a representation of the active bits in the presented mask, with
|
||||
* each active bit being denoted by character "".
|
||||
*
|
||||
* <p>
|
||||
* Inactive bits will be denoted by character {@link
|
||||
* Permission#RESERVED_OFF}.
|
||||
* </p>
|
||||
*
|
||||
* @param i the integer bit mask to print the active bits for
|
||||
*
|
||||
* @return a 32-character representation of the bit mask
|
||||
*/
|
||||
public static String printBinary(int i) {
|
||||
return AclFormattingUtils.printBinary(i, '*', Permission.RESERVED_OFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a representation of the active bits in the presented mask, with
|
||||
* each active bit being denoted by the passed character.
|
||||
*
|
||||
* <p>
|
||||
* Inactive bits will be denoted by character {@link
|
||||
* Permission#RESERVED_OFF}.
|
||||
* </p>
|
||||
*
|
||||
* @param mask the integer bit mask to print the active bits for
|
||||
* @param code the character to print when an active bit is detected
|
||||
*
|
||||
* @return a 32-character representation of the bit mask
|
||||
*/
|
||||
public static String printBinary(int mask, char code) {
|
||||
Assert.doesNotContain(new Character(code).toString(),
|
||||
new Character(Permission.RESERVED_ON).toString(),
|
||||
Permission.RESERVED_ON + " is a reserved character code");
|
||||
Assert.doesNotContain(new Character(code).toString(),
|
||||
new Character(Permission.RESERVED_OFF).toString(),
|
||||
Permission.RESERVED_OFF + " is a reserved character code");
|
||||
|
||||
return AclFormattingUtils.printBinary(mask, Permission.RESERVED_ON,
|
||||
Permission.RESERVED_OFF).replace(Permission.RESERVED_ON, code);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls;
|
||||
|
||||
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
|
||||
import org.acegisecurity.acls.sid.Sid;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Provides retrieval of {@link Acl} instances.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface AclService {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Obtains all the <code>Acl</code>s that apply for the passed
|
||||
* <code>Object</code>s.
|
||||
*
|
||||
* <p>
|
||||
* The returned map is keyed on the passed objects, with the values being
|
||||
* the <code>Acl</code> instances. Any unknown objects will not have a map
|
||||
* key.
|
||||
* </p>
|
||||
*
|
||||
* @param objects the objects to find ACL information for
|
||||
*
|
||||
* @return a map with zero or more elements (never <code>null</code>)
|
||||
*/
|
||||
public Map readAclsById(ObjectIdentity[] objects) throws NotFoundException;
|
||||
|
||||
/**
|
||||
* Obtains all the <code>Acl</code>s that apply for the passed
|
||||
* <code>Object</code>s, but only for the security identifies passed.
|
||||
*
|
||||
* <p>
|
||||
* Implementations <em>MAY</em> provide a subset of the ACLs via this
|
||||
* method although this is NOT a requirement. This is intended to allow
|
||||
* performance optimisations within implementations. Callers should
|
||||
* therefore use this method in preference to the alternative overloaded
|
||||
* version which does not have performance optimisation opportunities.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The returned map is keyed on the passed objects, with the values being
|
||||
* the <code>Acl</code> instances. Any unknown objects (or objects for
|
||||
* which the interested <code>Sid</code>s do not have entries) will not
|
||||
* have a map key.
|
||||
* </p>
|
||||
*
|
||||
* @param objects the objects to find ACL information for
|
||||
* @param sids the security identities for which ACL information is
|
||||
* required (may be <code>null</code> to denote all entries)
|
||||
*
|
||||
* @return a map with zero or more elements (never <code>null</code>)
|
||||
*/
|
||||
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids) throws NotFoundException;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls;
|
||||
|
||||
import org.acegisecurity.AcegiSecurityException;
|
||||
|
||||
|
||||
/**
|
||||
* Thrown if an <code>Acl</code> entry already exists for the object.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AlreadyExistsException extends AcegiSecurityException {
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
/**
|
||||
* Constructs an <code>AlreadyExistsException</code> with the specified message.
|
||||
*
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public AlreadyExistsException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an <code>AlreadyExistsException</code> with the specified message
|
||||
* and root cause.
|
||||
*
|
||||
* @param msg the detail message
|
||||
* @param t root cause
|
||||
*/
|
||||
public AlreadyExistsException(String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.acegisecurity.acls;
|
||||
|
||||
/**
|
||||
* Represents an ACE that provides auditing information.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*
|
||||
*/
|
||||
public interface AuditableAccessControlEntry extends AccessControlEntry {
|
||||
public boolean isAuditSuccess();
|
||||
public boolean isAuditFailure();
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.acegisecurity.acls;
|
||||
|
||||
/**
|
||||
* A mutable ACL that provides audit capabilities.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*
|
||||
*/
|
||||
public interface AuditableAcl extends MutableAcl {
|
||||
public void updateAuditing(Long aceId, boolean auditSuccess, boolean auditFailure);
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls;
|
||||
|
||||
import org.acegisecurity.AcegiSecurityException;
|
||||
|
||||
|
||||
/**
|
||||
* Thrown if an {@link Acl} cannot be deleted because children
|
||||
* <code>Acl</code>s exist.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class ChildrenExistException extends AcegiSecurityException {
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
/**
|
||||
* Constructs an <code>ChildrenExistException</code> with the specified
|
||||
* message.
|
||||
*
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public ChildrenExistException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an <code>ChildrenExistException</code> with the specified
|
||||
* message and root cause.
|
||||
*
|
||||
* @param msg the detail message
|
||||
* @param t root cause
|
||||
*/
|
||||
public ChildrenExistException(String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls;
|
||||
|
||||
import org.acegisecurity.AcegiSecurityException;
|
||||
|
||||
|
||||
/**
|
||||
* Thrown if an ACL identity could not be extracted from an object.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class IdentityUnavailableException extends AcegiSecurityException {
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
/**
|
||||
* Constructs an <code>IdentityUnavailableException</code> with the specified message.
|
||||
*
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public IdentityUnavailableException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an <code>IdentityUnavailableException</code> with the specified message
|
||||
* and root cause.
|
||||
*
|
||||
* @param msg the detail message
|
||||
* @param t root cause
|
||||
*/
|
||||
public IdentityUnavailableException(String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.acegisecurity.acls.sid.Sid;
|
||||
|
||||
|
||||
/**
|
||||
* A mutable <code>Acl</code>.
|
||||
*
|
||||
* <p>
|
||||
* A mutable ACL must ensure that appropriate security checks are performed
|
||||
* before allowing access to its methods.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface MutableAcl extends Acl {
|
||||
//~ Methods ================================================================
|
||||
|
||||
|
||||
/**
|
||||
* Obtains an identifier that represents this <code>MutableAcl</code>.
|
||||
*
|
||||
* @return the identifier, or <code>null</code> if unsaved
|
||||
*/
|
||||
public Serializable getId();
|
||||
|
||||
|
||||
public void deleteAce(Long aceId) throws NotFoundException ;
|
||||
|
||||
public void insertAce(Long afterAceId, Permission permission, Sid sid,
|
||||
boolean granting) throws NotFoundException;
|
||||
|
||||
/**
|
||||
* Changes the parent of this ACL.
|
||||
*
|
||||
* @param newParent the new parent
|
||||
*/
|
||||
public void setParent(MutableAcl newParent);
|
||||
|
||||
public void updateAce(Long aceId, Permission permission) throws NotFoundException;
|
||||
|
||||
public void setEntriesInheriting(boolean entriesInheriting);
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls;
|
||||
|
||||
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
|
||||
|
||||
/**
|
||||
* Provides support for creating and storing <code>Acl</code> instances.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface MutableAclService extends AclService {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Creates an empty <code>Acl</code> object in the database. It will have
|
||||
* no entries. The returned object will then be used to add entries.
|
||||
*
|
||||
* @param object the object identity to create
|
||||
*
|
||||
* @return an ACL object with its ID set
|
||||
*
|
||||
* @throws AlreadyExistsException if the passed object identity already has
|
||||
* a record
|
||||
*/
|
||||
public MutableAcl createAcl(ObjectIdentity object)
|
||||
throws AlreadyExistsException;
|
||||
|
||||
/**
|
||||
* Removes the specified entry from the database.
|
||||
*
|
||||
* @param object the object identity to remove
|
||||
* @param deleteChildren whether to cascade the delete to children
|
||||
*
|
||||
* @throws ChildrenExistException if the deleteChildren argument was
|
||||
* <code>false</code> but children exist
|
||||
*/
|
||||
public void deleteAcl(ObjectIdentity object, boolean deleteChildren)
|
||||
throws ChildrenExistException;
|
||||
|
||||
/**
|
||||
* Locates all object identities that use the specified parent. This is
|
||||
* useful for administration tools, and before issuing a {@link
|
||||
* #deleteAcl(ObjectIdentity, boolean)}.
|
||||
*
|
||||
* @param parentIdentity to locate children of
|
||||
*
|
||||
* @return the children (or <code>null</code> if none were found)
|
||||
*/
|
||||
public ObjectIdentity[] findChildren(ObjectIdentity parentIdentity);
|
||||
|
||||
/**
|
||||
* Changes an existing <code>Acl</code> in the database.
|
||||
*
|
||||
* @param acl to modify
|
||||
*
|
||||
* @throws NotFoundException if the relevant record could not be found (did
|
||||
* you remember to use {@link #createAcl(ObjectIdentity)} to
|
||||
* create the object, rather than creating it with the
|
||||
* <code>new</code> keyword?)
|
||||
*/
|
||||
public void updateAcl(MutableAcl acl) throws NotFoundException;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls;
|
||||
|
||||
import org.acegisecurity.AcegiSecurityException;
|
||||
|
||||
|
||||
/**
|
||||
* Thrown if an ACL-related object cannot be found.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class NotFoundException extends AcegiSecurityException {
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
/**
|
||||
* Constructs an <code>NotFoundException</code> with the specified message.
|
||||
*
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public NotFoundException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an <code>NotFoundException</code> with the specified message
|
||||
* and root cause.
|
||||
*
|
||||
* @param msg the detail message
|
||||
* @param t root cause
|
||||
*/
|
||||
public NotFoundException(String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls;
|
||||
|
||||
import org.acegisecurity.acls.sid.Sid;
|
||||
|
||||
/**
|
||||
* A mutable ACL that provides ownership capabilities.
|
||||
*
|
||||
* <p>
|
||||
* Generally the owner of an ACL is able to call any ACL mutator method, as
|
||||
* well as assign a new owner.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface OwnershipAcl extends MutableAcl {
|
||||
//~ Methods ================================================================
|
||||
|
||||
public Sid getOwner();
|
||||
|
||||
public void setOwner(Sid newOwner);
|
||||
}
|
|
@ -0,0 +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;
|
||||
|
||||
import org.acegisecurity.acls.sid.Sid;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a permission granted to a {@link Sid} for a given domain object.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface Permission {
|
||||
//~ Static fields/initializers =============================================
|
||||
|
||||
public static final char RESERVED_ON = '~';
|
||||
public static final char RESERVED_OFF = '.';
|
||||
public static final String THIRTY_TWO_RESERVED_OFF = "................................";
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Returns the bits that represents the permission.
|
||||
*
|
||||
* @return the bits that represent the permission
|
||||
*/
|
||||
public int getMask();
|
||||
|
||||
/**
|
||||
* Returns a 32-character long bit pattern <code>String</code> representing
|
||||
* this permission.
|
||||
*
|
||||
* <p>
|
||||
* Implementations are free to format the pattern as they see fit, although
|
||||
* under no circumstances may {@link #RESERVED_OFF} or {@link
|
||||
* #RESERVED_ON} be used within the pattern. An exemption is in the case
|
||||
* of {@link #RESERVED_OFF} which is used to denote a bit that is off
|
||||
* (clear). Implementations may also elect to use {@link #RESERVED_ON}
|
||||
* internally for computation purposes, although this method may not
|
||||
* return any <code>String</code> containing {@link #RESERVED_ON}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The returned String must be 32 characters in length.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This method is only used for user interface and logging purposes. It is
|
||||
* not used in any permission calculations. Therefore, duplication of
|
||||
* characters within the output is permitted.
|
||||
* </p>
|
||||
*
|
||||
* @return a 32-character bit pattern
|
||||
*/
|
||||
public String getPattern();
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls;
|
||||
|
||||
import org.acegisecurity.AcegiSecurityException;
|
||||
|
||||
|
||||
/**
|
||||
* Thrown if an {@link Acl} cannot perform an operation because it only
|
||||
* loaded a subset of <code>Sid</code>s and the caller has requested details
|
||||
* for an unloaded <code>Sid</code>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class UnloadedSidException extends AcegiSecurityException {
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
/**
|
||||
* Constructs an <code>NotFoundException</code> with the specified message.
|
||||
*
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public UnloadedSidException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an <code>NotFoundException</code> with the specified message
|
||||
* and root cause.
|
||||
*
|
||||
* @param msg the detail message
|
||||
* @param t root cause
|
||||
*/
|
||||
public UnloadedSidException(String msg, Throwable t) {
|
||||
super(msg, t);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package org.acegisecurity.acls.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.acegisecurity.acls.AccessControlEntry;
|
||||
import org.acegisecurity.acls.Acl;
|
||||
import org.acegisecurity.acls.AuditableAccessControlEntry;
|
||||
import org.acegisecurity.acls.Permission;
|
||||
import org.acegisecurity.acls.sid.Sid;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An immutable default implementation of <code>AccessControlEntry</code>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AccessControlEntryImpl implements AccessControlEntry, AuditableAccessControlEntry {
|
||||
private Serializable id;
|
||||
private Acl acl;
|
||||
private Sid sid;
|
||||
private Permission permission;
|
||||
private boolean granting;
|
||||
private boolean auditSuccess = false;
|
||||
private boolean auditFailure = false;
|
||||
private boolean aceDirty = false;
|
||||
|
||||
public void clearDirtyFlags() {
|
||||
this.aceDirty = false;
|
||||
}
|
||||
|
||||
public boolean equals(Object arg0) {
|
||||
if (!(arg0 instanceof AccessControlEntryImpl)) {
|
||||
return false;
|
||||
}
|
||||
AccessControlEntryImpl rhs = (AccessControlEntryImpl) arg0;
|
||||
if (this.aceDirty != rhs.isAceDirty() ||
|
||||
this.auditFailure != rhs.isAuditFailure() ||
|
||||
this.auditSuccess != rhs.isAuditSuccess() ||
|
||||
this.granting != rhs.isGranting() ||
|
||||
!this.acl.equals(rhs.getAcl()) ||
|
||||
!this.id.equals(rhs.getId()) ||
|
||||
!this.permission.equals(rhs.getPermission()) ||
|
||||
!this.sid.equals(rhs.getSid()) ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public AccessControlEntryImpl(Serializable id, Acl acl, Sid sid, Permission permission, boolean granting, boolean auditSuccess, boolean auditFailure) {
|
||||
Assert.notNull(acl, "Acl required");
|
||||
Assert.notNull(sid, "Sid required");
|
||||
Assert.notNull(permission, "Permission required");
|
||||
this.id = id;
|
||||
this.acl = acl; // can be null
|
||||
this.sid = sid;
|
||||
this.permission = permission;
|
||||
this.granting = granting;
|
||||
this.auditSuccess = auditSuccess;
|
||||
this.auditFailure = auditFailure;
|
||||
}
|
||||
|
||||
public Acl getAcl() {
|
||||
return acl;
|
||||
}
|
||||
public boolean isGranting() {
|
||||
return granting;
|
||||
}
|
||||
public Serializable getId() {
|
||||
return id;
|
||||
}
|
||||
public Permission getPermission() {
|
||||
return permission;
|
||||
}
|
||||
public Sid getSid() {
|
||||
return sid;
|
||||
}
|
||||
|
||||
void setPermission(Permission permission) {
|
||||
Assert.notNull(permission, "Permission required");
|
||||
this.permission = permission;
|
||||
this.aceDirty = true;
|
||||
}
|
||||
|
||||
void setId(Serializable id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean isAuditFailure() {
|
||||
return auditFailure;
|
||||
}
|
||||
|
||||
void setAuditFailure(boolean auditFailure) {
|
||||
this.auditFailure = auditFailure;
|
||||
this.aceDirty = true;
|
||||
}
|
||||
|
||||
public boolean isAuditSuccess() {
|
||||
return auditSuccess;
|
||||
}
|
||||
|
||||
void setAuditSuccess(boolean auditSuccess) {
|
||||
this.auditSuccess = auditSuccess;
|
||||
this.aceDirty = true;
|
||||
}
|
||||
|
||||
public boolean isAceDirty() {
|
||||
return aceDirty;
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("AccessControlEntryImpl[");
|
||||
sb.append("id: ").append(this.id).append("; ");
|
||||
sb.append("granting: ").append(this.granting).append("; ");
|
||||
sb.append("sid: ").append(this.sid).append("; ");
|
||||
sb.append("permission: ").append(this.permission);
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +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 java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
* Base implementation of <code>Acl</code>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id
|
||||
*/
|
||||
public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
//~ Instance fields ========================================================
|
||||
private static final int CHANGE_OWNERSHIP = 0;
|
||||
private static final int CHANGE_AUDITING = 1;
|
||||
private static final int CHANGE_GENERAL = 2;
|
||||
|
||||
private GrantedAuthority gaTakeOwnership;
|
||||
private GrantedAuthority gaModifyAuditing;
|
||||
private GrantedAuthority gaGeneralChanges;
|
||||
|
||||
private Acl parentAcl;
|
||||
private AuditLogger auditLogger = new ConsoleAuditLogger(); // AuditableAcl
|
||||
private List aces = new Vector();
|
||||
private List deletedAces = new Vector();
|
||||
private Long id;
|
||||
private ObjectIdentity objectIdentity;
|
||||
private Sid owner; // OwnershipAcl
|
||||
private boolean entriesInheriting = false;
|
||||
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 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 <code>GrantedAuthority</code>s that have
|
||||
* special permissions (index 0 is the authority needed to change
|
||||
* ownership, index 1 is the authority needed to modify auditing details,
|
||||
* index 2 is the authority needed to change other ACL and ACE details) (required)
|
||||
*/
|
||||
public 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the special adminstrative permissions honoured by this
|
||||
* object.
|
||||
*
|
||||
* <p>
|
||||
* Normally a principal must be the owner of the ACL in order to
|
||||
* make most changes. The authorities passed to this method provide
|
||||
* a way for non-owners to modify the ACL (and indeed modify audit
|
||||
* parameters, which are not available to ACL owners).
|
||||
*
|
||||
* @param auths an array of <code>GrantedAuthority</code>s that have
|
||||
* administrative permissions (index 0 is the authority needed to change
|
||||
* ownership, index 1 is the authority needed to modify auditing details,
|
||||
* index 2 is the authority needed to change other ACL and ACE details)
|
||||
*/
|
||||
private void setAuthorities(GrantedAuthority[] auths) {
|
||||
Assert.notEmpty(auths, "GrantedAuthority[] with three elements required");
|
||||
Assert.isTrue(auths.length == 3, "GrantedAuthority[] with three elements required");
|
||||
this.gaTakeOwnership = auths[0];
|
||||
this.gaModifyAuditing = auths[1];
|
||||
this.gaGeneralChanges = auths[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <code>GrantedAuthority</code>s that have
|
||||
* special permissions (index 0 is the authority needed to change
|
||||
* ownership, index 1 is the authority needed to modify auditing details,
|
||||
* index 2 is the authority needed to change other ACL and ACE details) (required)
|
||||
* @param parentAcl the parent (may be <code>null</code>)
|
||||
* @param loadedSids the loaded SIDs if only a subset were loaded (may be
|
||||
* <code>null</code>)
|
||||
* @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 ================================================================
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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 void setEntriesInheriting(boolean entriesInheriting) {
|
||||
securityCheck(CHANGE_GENERAL);
|
||||
this.entriesInheriting = entriesInheriting;
|
||||
this.aclDirty = true;
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
public boolean isEntriesInheriting() {
|
||||
return entriesInheriting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines authorization. The order of the <code>permission</code> and
|
||||
* <code>sid</code> arguments is <em>extremely important</em>! The method
|
||||
* will iterate through each of the <code>permission</code>s in the order
|
||||
* specified. For each iteration, all of the <code>sid</code>s will be
|
||||
* considered, again in the order they are presented. The iteration of
|
||||
* each <code>permission:sid</code> combination will then inspect the ACEs
|
||||
* in the order they appear in the ACL. When the <em>first full match</em>
|
||||
* is found (ie an ACE that has the SID currently being searched for and
|
||||
* the exact permission bit mask being search for), the grant or deny flag
|
||||
* for that ACE will prevail. If the ACE specifies to grant access, the
|
||||
* method will return <code>true</code>. If the ACE specifies to deny
|
||||
* access, the loop will stop and the next <code>permission</code>
|
||||
* iteration will be performed. If each permission indicates to deny
|
||||
* access, the first deny ACE found will be considered the reason for the
|
||||
* failure (as it was the first match found, and is therefore the one most
|
||||
* logically requiring changes - although not always). If absolutely no
|
||||
* matching ACE was found at all for any permission, the parent ACL will
|
||||
* be tried (provided that there is a parent and {@link
|
||||
* #isEntriesInheriting()} is <code>true</code>. The parent ACL will also
|
||||
* scan its parent and so on. If ultimately no matching ACE is found, a
|
||||
* <code>NotFoundException</code> will be thrown and the caller will need
|
||||
* to decide how to handle the permission check. Similarly, if any of the
|
||||
* passed SIDs were not loaded by the ACL, the
|
||||
* <code>UnloadedSidException</code> will be thrown.
|
||||
*
|
||||
* @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 <code>true</code> denotes the query is for
|
||||
* administrative purposes and no auditing will be undertaken
|
||||
*
|
||||
* @return <code>true</code> if one of the permissions has been granted,
|
||||
* <code>false</code> 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 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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the dirty flags on the <code>Acl</code>, but not any
|
||||
* associated ACEs.
|
||||
*/
|
||||
public void clearDirtyFlags() {
|
||||
this.aclDirty = false;
|
||||
this.addedAces = false;
|
||||
this.updatedAces = false;
|
||||
}
|
||||
|
||||
public boolean isAclDirty() {
|
||||
return aclDirty;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.acegisecurity.acls.domain;
|
||||
|
||||
import org.acegisecurity.acls.AccessControlEntry;
|
||||
|
||||
/**
|
||||
* Used by <code>AclImpl</code> to log audit events.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*
|
||||
*/
|
||||
public interface AuditLogger {
|
||||
public void logIfNeeded(boolean granted, AccessControlEntry ace);
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package org.acegisecurity.acls.domain;
|
||||
|
||||
import org.acegisecurity.acls.AclFormattingUtils;
|
||||
import org.acegisecurity.acls.Permission;
|
||||
|
||||
public class BasePermission implements Permission {
|
||||
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
|
||||
|
||||
private int mask;
|
||||
private char code;
|
||||
|
||||
private BasePermission(int mask, char code) {
|
||||
this.mask = mask;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public boolean equals(Object arg0) {
|
||||
if (!(arg0 instanceof BasePermission)) {
|
||||
return false;
|
||||
}
|
||||
BasePermission rhs = (BasePermission) arg0;
|
||||
return (this.mask == rhs.getMask());
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically creates a <code>CumulativePermission</code>
|
||||
* representing the active bits in the passed mask.
|
||||
* NB: Only uses <code>BasePermission</code>!
|
||||
*
|
||||
* @param mask to review
|
||||
*/
|
||||
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 int getMask() {
|
||||
return mask;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "BasePermission[" + getPattern() + "=" + mask + "]";
|
||||
}
|
||||
|
||||
public String getPattern() {
|
||||
return AclFormattingUtils.printBinary(mask, code);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.acegisecurity.acls.domain;
|
||||
|
||||
import org.acegisecurity.acls.AccessControlEntry;
|
||||
import org.acegisecurity.acls.AuditableAccessControlEntry;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class ConsoleAuditLogger implements AuditLogger {
|
||||
public void logIfNeeded(boolean granted, AccessControlEntry ace) {
|
||||
Assert.notNull(ace, "AccessControlEntry required");
|
||||
if (ace instanceof AuditableAccessControlEntry) {
|
||||
AuditableAccessControlEntry auditableAce = (AuditableAccessControlEntry) ace;
|
||||
if (granted && auditableAce.isAuditSuccess()) {
|
||||
System.out.println("GRANTED due to ACE: " + ace);
|
||||
} else if (!granted && auditableAce.isAuditFailure()) {
|
||||
System.out.println("DENIED due to ACE: " + ace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls.domain;
|
||||
|
||||
import org.acegisecurity.acls.AclFormattingUtils;
|
||||
import org.acegisecurity.acls.Permission;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a <code>Permission</code> that is constructed at runtime from
|
||||
* other permissions.
|
||||
*
|
||||
* <p>
|
||||
* Methods return <code>this</code>, in order to facilitate method chaining.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class CumulativePermission implements Permission {
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private String pattern = THIRTY_TWO_RESERVED_OFF;
|
||||
private int mask = 0;
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public CumulativePermission clear(Permission permission) {
|
||||
this.mask &= ~permission.getMask();
|
||||
this.pattern = AclFormattingUtils.demergePatterns(this.pattern,
|
||||
permission.getPattern());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean equals(Object arg0) {
|
||||
if (!(arg0 instanceof CumulativePermission)) {
|
||||
return false;
|
||||
}
|
||||
CumulativePermission rhs = (CumulativePermission) arg0;
|
||||
return (this.mask == rhs.getMask());
|
||||
}
|
||||
|
||||
|
||||
public CumulativePermission clear() {
|
||||
this.mask = 0;
|
||||
this.pattern = THIRTY_TWO_RESERVED_OFF;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMask() {
|
||||
return this.mask;
|
||||
}
|
||||
|
||||
public String getPattern() {
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
public CumulativePermission set(Permission permission) {
|
||||
this.mask |= permission.getMask();
|
||||
this.pattern = AclFormattingUtils.mergePatterns(this.pattern,
|
||||
permission.getPattern());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "CumulativePermission[" + pattern + "=" + this.mask + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package org.acegisecurity.acls.jdbc;
|
||||
|
||||
import org.acegisecurity.acls.domain.AclImpl;
|
||||
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
|
||||
|
||||
/**
|
||||
* A caching layer for {@link JdbcAclService}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*
|
||||
*/
|
||||
public interface AclCache {
|
||||
public AclImpl getFromCache(ObjectIdentity objectIdentity);
|
||||
public AclImpl getFromCache(Long pk);
|
||||
public void putInCache(AclImpl acl); // should walk tree as well!
|
||||
public void evictFromCache(Long pk);
|
||||
}
|
|
@ -0,0 +1,517 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls.jdbc;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.acls.AccessControlEntry;
|
||||
import org.acegisecurity.acls.Acl;
|
||||
import org.acegisecurity.acls.NotFoundException;
|
||||
import org.acegisecurity.acls.Permission;
|
||||
import org.acegisecurity.acls.UnloadedSidException;
|
||||
import org.acegisecurity.acls.domain.AccessControlEntryImpl;
|
||||
import org.acegisecurity.acls.domain.AclImpl;
|
||||
import org.acegisecurity.acls.domain.BasePermission;
|
||||
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
|
||||
import org.acegisecurity.acls.objectidentity.ObjectIdentityImpl;
|
||||
import org.acegisecurity.acls.sid.GrantedAuthoritySid;
|
||||
import org.acegisecurity.acls.sid.PrincipalSid;
|
||||
import org.acegisecurity.acls.sid.Sid;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.PreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Performs lookups in a manner that is compatible with ANSI SQL.
|
||||
*
|
||||
* <p>
|
||||
* NB: This implementation does attempt to provide reasonably optimised lookups
|
||||
* - within the constraints of a normalised database and standard ANSI SQL
|
||||
* features. If you are willing to sacrifice either of these constraints (eg
|
||||
* use a particular database feature such as hierarchical queries or
|
||||
* materalized views, or reduce normalisation) you are likely to achieve better
|
||||
* performance. In such situations you will need to provide your own custom
|
||||
* <code>LookupStrategy</code>. This class does not support subclassing, as
|
||||
* it is likely to change in future releases and therefore subclassing is
|
||||
* unsupported.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public final class BasicLookupStrategy implements LookupStrategy {
|
||||
|
||||
private int batchSize = 50;
|
||||
private AclCache aclCache;
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
private GrantedAuthority[] auths;
|
||||
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
/**
|
||||
* Constructor accepting mandatory arguments
|
||||
*
|
||||
* @param dataSource to access the database
|
||||
* @param aclCache the cache where fully-loaded elements can be stored
|
||||
* @param auths as per the format defined by {@link
|
||||
* AclImpl#setAuthorities(GrantedAuthority[])} for instances
|
||||
* created by this implementation
|
||||
*/
|
||||
public BasicLookupStrategy(DataSource dataSource, AclCache aclCache,
|
||||
GrantedAuthority[] auths) {
|
||||
Assert.notNull(dataSource, "DataSource required");
|
||||
Assert.notNull(aclCache, "AclCache required");
|
||||
Assert.notEmpty(auths, "GrantedAuthority[] with three elements required");
|
||||
Assert.isTrue(auths.length == 3,
|
||||
"GrantedAuthority[] with three elements required");
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.aclCache = aclCache;
|
||||
this.auths = auths;
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public void setBatchSize(int batchSize) {
|
||||
this.batchSize = batchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main method.
|
||||
*
|
||||
* <p>
|
||||
* WARNING: This implementation completely disregards the "sids" argument!
|
||||
* Every item in the cache is expected to contain all SIDs.
|
||||
*
|
||||
* <p>
|
||||
* The implementation works in batch sizes specfied by {@link #batchSize}.
|
||||
*
|
||||
*/
|
||||
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids)
|
||||
throws NotFoundException {
|
||||
Assert.isTrue(batchSize >= 1, "BatchSize must be >= 1");
|
||||
Assert.notEmpty(objects, "Objects to lookup required");
|
||||
|
||||
Map result = new HashMap(); // contains FULLY loaded Acl objects
|
||||
|
||||
Set currentBatchToLoad = new HashSet(); // contains ObjectIdentitys
|
||||
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
// Check we don't already have this ACL in the results
|
||||
if (result.containsKey(objects[i])) {
|
||||
continue; // already in results, so move to next element
|
||||
}
|
||||
|
||||
// Check cache for the present ACL entry
|
||||
Acl acl = aclCache.getFromCache(objects[i]);
|
||||
|
||||
// Ensure any cached element supports all the requested SIDs
|
||||
// (they should always, as our base impl doesn't filter on SID)
|
||||
if (acl != null) {
|
||||
if (acl.isSidLoaded(sids)) {
|
||||
result.put(acl.getObjectIdentity(), acl);
|
||||
continue; // now in results, so move to next element
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Error: SID-filtered element detected when implementation does not perform SID filtering - have you added something to the cache manually?");
|
||||
}
|
||||
}
|
||||
|
||||
// To get this far, we have no choice but to retrieve it via JDBC
|
||||
// (although we don't do it until we get a batch of them to load)
|
||||
currentBatchToLoad.add(objects[i]);
|
||||
|
||||
// Is it time to load from JDBC the currentBatchToLoad?
|
||||
if ((currentBatchToLoad.size() == this.batchSize) || (i+1 == objects.length)) {
|
||||
Map loadedBatch = lookupObjectIdentities((ObjectIdentity[]) currentBatchToLoad
|
||||
.toArray(new ObjectIdentity[] {}));
|
||||
|
||||
// Add loaded batch (all elements 100% initialized) to results
|
||||
result.putAll(loadedBatch);
|
||||
|
||||
// Add the loaded batch to the cache
|
||||
Iterator loadedAclIterator = loadedBatch.values().iterator();
|
||||
|
||||
while (loadedAclIterator.hasNext()) {
|
||||
aclCache.putInCache((AclImpl) loadedAclIterator.next());
|
||||
}
|
||||
|
||||
currentBatchToLoad.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Now we're done, check every requested object identity was found (throw NotFoundException if needed)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a batch of <code>ObjectIdentity</code>s directly from the
|
||||
* database.
|
||||
*
|
||||
* <p>
|
||||
* The caller is responsible for optimization issues, such as selecting the
|
||||
* identities to lookup, ensuring the cache doesn't contain them already,
|
||||
* and adding the returned elements to the cache etc.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This subclass is required to return fully valid <code>Acl</code>s,
|
||||
* including properly-configured parent ACLs.
|
||||
* </p>
|
||||
*/
|
||||
private Map lookupObjectIdentities(final ObjectIdentity[] objectIdentities) {
|
||||
Assert.notEmpty(objectIdentities, "Must provide identities to lookup");
|
||||
|
||||
final Map acls = new HashMap(); // contains Acls with StubAclParents
|
||||
|
||||
// Make the "acls" map contain all requested objectIdentities
|
||||
// (including markers to each parent in the hierarchy)
|
||||
String sql = computeRepeatingSql("(ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY = ? and ACL_CLASS.CLASS = ?)", objectIdentities.length);
|
||||
System.out.println("Executing lookupObjectIdentities; length: " + objectIdentities.length);
|
||||
jdbcTemplate.query(sql,
|
||||
new PreparedStatementSetter() {
|
||||
public void setValues(PreparedStatement ps)
|
||||
throws SQLException {
|
||||
for (int i = 0; i < objectIdentities.length; i++) {
|
||||
// Determine prepared statement values for this iteration
|
||||
String javaType = objectIdentities[i].getJavaType().getName();
|
||||
Assert.isInstanceOf(Long.class, objectIdentities[i].getIdentifier(),"This class requires ObjectIdentity.getIdentifier() to be a Long");
|
||||
long id = ((Long) objectIdentities[i].getIdentifier()).longValue();
|
||||
|
||||
// Inject values
|
||||
ps.setLong((2 * i) + 1, id);
|
||||
ps.setString((2 * i) + 2, javaType);
|
||||
}
|
||||
}
|
||||
}, new ProcessResultSet(acls));
|
||||
|
||||
// Finally, convert our "acls" containing StubAclParents into true Acls
|
||||
Map resultMap = new HashMap();
|
||||
Iterator iter = acls.values().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Acl inputAcl = (Acl) iter.next();
|
||||
Assert.isInstanceOf(AclImpl.class, inputAcl, "Map should have contained an AclImpl");
|
||||
Assert.isInstanceOf(Long.class, ((AclImpl)inputAcl).getId(), "Acl.getId() must be Long");
|
||||
Acl result = convert(acls, (Long)((AclImpl)inputAcl).getId());
|
||||
resultMap.put(result.getObjectIdentity(), result);
|
||||
}
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the primary key IDs specified in "findNow", adding AclImpl
|
||||
* instances with StubAclParents to the "acls" Map.
|
||||
*
|
||||
* @param acls the AclImpls (with StubAclParents)
|
||||
* @param findNow Long-based primary keys to retrieve
|
||||
*/
|
||||
private void lookupPrimaryKeys(final Map acls, final Set findNow) {
|
||||
Assert.notNull(acls, "ACLs are required");
|
||||
Assert.notEmpty(findNow, "Items to find now required");
|
||||
|
||||
String sql = computeRepeatingSql("(ACL_OBJECT_IDENTITY.ID = ?)",findNow.size());
|
||||
System.out.println("Executing lookupPrimaryKeys; length: " + findNow.size());
|
||||
|
||||
jdbcTemplate.query(sql,
|
||||
new PreparedStatementSetter() {
|
||||
public void setValues(PreparedStatement ps) throws SQLException {
|
||||
Iterator iter = findNow.iterator();
|
||||
int i = 0;
|
||||
while (iter.hasNext()) {
|
||||
i++;
|
||||
ps.setLong(i, ((Long)iter.next()).longValue());
|
||||
}
|
||||
}
|
||||
}, new ProcessResultSet(acls));
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts the current <code>ResultSet</code> row, and converts it into
|
||||
* an <code>AclImpl</code> that contains a <code>StubAclParent</code>
|
||||
*
|
||||
* @param acls the Map we should add the converted Acl to
|
||||
* @param rs the ResultSet focused on a current row
|
||||
* @throws SQLException if something goes wrong converting values
|
||||
*/
|
||||
private void convertCurrentResultIntoObject(Map acls, ResultSet rs) throws SQLException {
|
||||
Long id = new Long(rs.getLong("ACL_ID"));
|
||||
|
||||
// If we already have an ACL for this ID, just create the ACE
|
||||
AclImpl acl = (AclImpl) acls.get(id);
|
||||
|
||||
if (acl == null) {
|
||||
// Make an AclImpl and pop it into the Map
|
||||
ObjectIdentity objectIdentity = new ObjectIdentityImpl(rs.getString(
|
||||
"CLASS"), new Long(rs.getLong("OBJECT_ID_IDENTITY")));
|
||||
|
||||
Acl parentAcl = null;
|
||||
long parentAclId = rs.getLong("PARENT_OBJECT");
|
||||
|
||||
if (parentAclId != 0) {
|
||||
parentAcl = new StubAclParent(new Long(parentAclId));
|
||||
}
|
||||
|
||||
boolean entriesInheriting = rs.getBoolean("ENTRIES_INHERITING");
|
||||
Sid owner;
|
||||
|
||||
if (rs.getBoolean("ACL_PRINCIPAL")) {
|
||||
owner = new PrincipalSid(rs.getString("ACL_SID"));
|
||||
} else {
|
||||
owner = new GrantedAuthoritySid(rs.getString("ACL_SID"));
|
||||
}
|
||||
|
||||
acl = new AclImpl(objectIdentity, id, parentAcl, auths, null,
|
||||
entriesInheriting, owner);
|
||||
acls.put(id, acl);
|
||||
}
|
||||
|
||||
// Add an extra ACE to the ACL (ORDER BY maintains the ACE list order)
|
||||
Long aceId = new Long(rs.getLong("ACE_ID"));
|
||||
Sid recipient;
|
||||
|
||||
if (rs.getBoolean("ACE_PRINCIPAL")) {
|
||||
recipient = new PrincipalSid(rs.getString("ACE_SID"));
|
||||
} else {
|
||||
recipient = new GrantedAuthoritySid(rs.getString("ACE_SID"));
|
||||
}
|
||||
|
||||
Permission permission = BasePermission.buildFromMask(rs.getInt("MASK"));
|
||||
boolean granting = rs.getBoolean("GRANTING");
|
||||
boolean auditSuccess = rs.getBoolean("AUDIT_SUCCESS");
|
||||
boolean auditFailure = rs.getBoolean("AUDIT_FAILURE");
|
||||
|
||||
AccessControlEntryImpl ace = new AccessControlEntryImpl(aceId, acl,
|
||||
recipient, permission, granting, auditSuccess, auditFailure);
|
||||
|
||||
|
||||
Field acesField = getAccessibleField(AclImpl.class, "aces");
|
||||
List aces;
|
||||
|
||||
try {
|
||||
aces = (List) acesField.get(acl);
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Could not obtain AclImpl.ace field", ex);
|
||||
}
|
||||
|
||||
// Add the ACE if it doesn't already exist in the ACL.aces field
|
||||
if (!aces.contains(ace)) {
|
||||
aces.add(ace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The final phase of converting the <code>Map</code> of <code>AclImpl</code> instances
|
||||
* which contain <code>StubAclParent</code>s into proper, valid <code>AclImpl</code>s with
|
||||
* correct ACL parents.
|
||||
*
|
||||
* @param inputMap the unconverted <code>AclImpl</code>s
|
||||
* @param inputAcl the current <code>Acl</code> that we wish to convert (this may be
|
||||
* @return
|
||||
*/
|
||||
private AclImpl convert(Map inputMap, Long currentIdentity) {
|
||||
Assert.notEmpty(inputMap, "InputMap required");
|
||||
Assert.notNull(currentIdentity, "CurrentIdentity required");
|
||||
|
||||
// Retrieve this Acl from the InputMap
|
||||
Acl uncastAcl = (Acl) inputMap.get(currentIdentity);
|
||||
Assert.isInstanceOf(AclImpl.class, uncastAcl, "The inputMap contained a non-AclImpl");
|
||||
AclImpl inputAcl = (AclImpl) uncastAcl;
|
||||
|
||||
Acl parent = inputAcl.getParentAcl();
|
||||
if (parent != null && parent instanceof StubAclParent) {
|
||||
// Lookup the parent
|
||||
StubAclParent stubAclParent = (StubAclParent) parent;
|
||||
parent = convert(inputMap, stubAclParent.getId());
|
||||
}
|
||||
|
||||
// Now we have the parent (if there is one), create the true AclImpl
|
||||
AclImpl result = new AclImpl(inputAcl.getObjectIdentity(), (Long)inputAcl.getId(), parent, auths, null, inputAcl.isEntriesInheriting(), inputAcl.getOwner());
|
||||
|
||||
// Copy the "aces" from the input to the destination
|
||||
Field field = getAccessibleField(AclImpl.class, "aces");
|
||||
try {
|
||||
field.set(result, field.get(inputAcl));
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new IllegalStateException("Could not obtain or set AclImpl.ace field");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String computeRepeatingSql(String repeatingSql, int requiredRepetitions) {
|
||||
Assert.isTrue(requiredRepetitions >= 1, "Must be => 1");
|
||||
|
||||
String startSql = "select ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY, ACL_ENTRY.ACE_ORDER, "
|
||||
+ "ACL_OBJECT_IDENTITY.ID as ACL_ID, "
|
||||
+ "ACL_OBJECT_IDENTITY.PARENT_OBJECT, "
|
||||
+ "ACL_OBJECT_IDENTITY,ENTRIES_INHERITING, "
|
||||
+ "ACL_ENTRY.ID as ACE_ID, ACL_ENTRY.MASK, ACL_ENTRY.GRANTING, ACL_ENTRY.AUDIT_SUCCESS, ACL_ENTRY.AUDIT_FAILURE, "
|
||||
+ "ACE_SID.PRINCIPAL as ACE_PRINCIPAL, ACE_SID.SID as ACE_SID, "
|
||||
+ "ACL_SID.PRINCIPAL as ACL_PRINCIPAL, ACL_SID.SID as ACL_SID, "
|
||||
+ "ACL_CLASS.CLASS "
|
||||
+ "from ACL_OBJECT_IDENTITY, ACL_ENTRY, ACL_SID ACE_SID, ACL_SID ACL_SID, ACL_CLASS "
|
||||
+ "where ACL_ENTRY.ACL_OBJECT_IDENTITY = ACL_OBJECT_IDENTITY.ID "
|
||||
+ "and ACE_SID.ID = ACL_ENTRY.SID "
|
||||
+ "and ACL_SID.ID = ACL_OBJECT_IDENTITY.OWNER_SID "
|
||||
+ "and ACL_CLASS.ID = ACL_OBJECT_IDENTITY.OBJECT_ID_CLASS "
|
||||
+ "and ( ";
|
||||
|
||||
String endSql = ") order by ACL_ENTRY.ACL_OBJECT_IDENTITY asc, ACL_ENTRY.ACE_ORDER asc";
|
||||
|
||||
StringBuffer sqlStringBuffer = new StringBuffer();
|
||||
sqlStringBuffer.append(startSql);
|
||||
|
||||
for (int i = 1; i <= requiredRepetitions; i++) {
|
||||
sqlStringBuffer.append(repeatingSql);
|
||||
|
||||
if (i != requiredRepetitions) {
|
||||
sqlStringBuffer.append(" or ");
|
||||
}
|
||||
}
|
||||
|
||||
sqlStringBuffer.append(endSql);
|
||||
|
||||
return sqlStringBuffer.toString();
|
||||
}
|
||||
|
||||
private static Field getAccessibleField(Class clazz, String protectedField) {
|
||||
Field field = null;
|
||||
|
||||
try {
|
||||
field = clazz.getDeclaredField(protectedField);
|
||||
} catch (NoSuchFieldException nsf) {}
|
||||
|
||||
if (field == null) {
|
||||
// Unable to locate, so try the superclass (if there is one)
|
||||
if (clazz.getSuperclass() != null) {
|
||||
getAccessibleField(clazz.getSuperclass(), protectedField);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Couldn't find '"
|
||||
+ protectedField + "' field");
|
||||
}
|
||||
}
|
||||
|
||||
// We have a field, so process
|
||||
field.setAccessible(true);
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
//~ Inner Classes ==========================================================
|
||||
|
||||
private class StubAclParent implements Acl {
|
||||
private Long id;
|
||||
|
||||
public StubAclParent(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AccessControlEntry[] getEntries() {
|
||||
throw new UnsupportedOperationException("Stub only");
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public ObjectIdentity getObjectIdentity() {
|
||||
throw new UnsupportedOperationException("Stub only");
|
||||
}
|
||||
|
||||
public Acl getParentAcl() {
|
||||
throw new UnsupportedOperationException("Stub only");
|
||||
}
|
||||
|
||||
public boolean isEntriesInheriting() {
|
||||
throw new UnsupportedOperationException("Stub only");
|
||||
}
|
||||
|
||||
public boolean isGranted(Permission[] permission, Sid[] sids,
|
||||
boolean administrativeMode)
|
||||
throws NotFoundException, UnloadedSidException {
|
||||
throw new UnsupportedOperationException("Stub only");
|
||||
}
|
||||
|
||||
public boolean isSidLoaded(Sid[] sids) {
|
||||
throw new UnsupportedOperationException("Stub only");
|
||||
}
|
||||
}
|
||||
|
||||
private class ProcessResultSet implements ResultSetExtractor {
|
||||
private Map acls;
|
||||
|
||||
public ProcessResultSet(Map acls) {
|
||||
Assert.notNull(acls, "ACLs cannot be null");
|
||||
this.acls = acls;
|
||||
}
|
||||
|
||||
public Object extractData(ResultSet rs) throws SQLException, DataAccessException {
|
||||
Set parentIdsToLookup = new HashSet(); // Set of parent_id Longs
|
||||
|
||||
while (rs.next()) {
|
||||
// Convert current row into an Acl (albeit with a StubAclParent)
|
||||
convertCurrentResultIntoObject(acls, rs);
|
||||
|
||||
// Figure out if this row means we need to lookup another parent
|
||||
long parentId = rs.getLong("PARENT_OBJECT");
|
||||
|
||||
if (parentId != 0) {
|
||||
// See if its already in the "acls"
|
||||
if (acls.containsKey(new Long(parentId))) {
|
||||
continue; // skip this while element
|
||||
}
|
||||
|
||||
// Now try to find it in the cache
|
||||
Acl cached = aclCache.getFromCache(new Long(parentId));
|
||||
|
||||
if (cached == null) {
|
||||
parentIdsToLookup.add(new Long(parentId));
|
||||
} else {
|
||||
// Pop into the acls map, so our convert method doesn't
|
||||
// need to deal with an unsynchronized AclCache
|
||||
Assert.isInstanceOf(AclImpl.class, cached, "Cached ACL must be an AclImpl");
|
||||
acls.put(((AclImpl)cached).getId(), cached);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup parents, adding Acls (with StubAclParents) to "acl" map
|
||||
if (parentIdsToLookup.size() > 0) {
|
||||
lookupPrimaryKeys(acls, parentIdsToLookup);
|
||||
}
|
||||
|
||||
// Return null to meet ResultSetExtractor method contract
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package org.acegisecurity.acls.jdbc;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheException;
|
||||
import net.sf.ehcache.Element;
|
||||
|
||||
import org.acegisecurity.acls.domain.AclImpl;
|
||||
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class EhCacheBasedAclCache implements AclCache {
|
||||
|
||||
private Cache cache;
|
||||
|
||||
public EhCacheBasedAclCache(Cache cache) {
|
||||
Assert.notNull(cache, "Cache required");
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public AclImpl getFromCache(ObjectIdentity objectIdentity) {
|
||||
Element element = null;
|
||||
try {
|
||||
element = cache.get(objectIdentity);
|
||||
} catch (CacheException ignored) {}
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return (AclImpl) element.getValue();
|
||||
}
|
||||
|
||||
public AclImpl getFromCache(Long pk) {
|
||||
Element element = null;
|
||||
try {
|
||||
element = cache.get(pk);
|
||||
} catch (CacheException ignored) {}
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return (AclImpl) element.getValue();
|
||||
}
|
||||
|
||||
public void putInCache(AclImpl acl) {
|
||||
if (acl.getParentAcl() != null && acl.getParentAcl() instanceof AclImpl) {
|
||||
putInCache((AclImpl)acl.getParentAcl());
|
||||
}
|
||||
cache.put(new Element(acl.getObjectIdentity(), acl));
|
||||
cache.put(new Element(acl.getId(), acl));
|
||||
}
|
||||
|
||||
public void evictFromCache(Long pk) {
|
||||
AclImpl acl = getFromCache(pk);
|
||||
if (acl != null) {
|
||||
cache.remove(pk);
|
||||
cache.remove(acl.getObjectIdentity());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package org.acegisecurity.acls.jdbc;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.acegisecurity.acls.AclService;
|
||||
import org.acegisecurity.acls.NotFoundException;
|
||||
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
|
||||
import org.acegisecurity.acls.sid.Sid;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple JDBC-based implementation of <code>AclService</code>.
|
||||
*
|
||||
* <p>
|
||||
* Requires the "dirty" flags in {@link org.acegisecurity.acls.domain.AclImpl} and {@link org.acegisecurity.acls.domain.AccessControlEntryImpl}
|
||||
* to be set, so that the implementation can detect changed parameters easily.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class JdbcAclService implements AclService/*, MutableAclService */ {
|
||||
|
||||
private AclCache aclCache;
|
||||
private JdbcTemplate template;
|
||||
private LookupStrategy lookupStrategy;
|
||||
|
||||
public JdbcAclService(DataSource dataSource, AclCache aclCache, LookupStrategy lookupStrategy) {
|
||||
Assert.notNull(dataSource, "DataSource required");
|
||||
Assert.notNull(aclCache, "AclCache required");
|
||||
Assert.notNull(lookupStrategy, "LookupStrategy required");
|
||||
this.template = new JdbcTemplate(dataSource);
|
||||
this.aclCache = aclCache;
|
||||
this.lookupStrategy = lookupStrategy;
|
||||
}
|
||||
|
||||
public Map readAclsById(ObjectIdentity[] objects) {
|
||||
return readAclsById(objects, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method required by interface.
|
||||
*/
|
||||
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids) throws NotFoundException {
|
||||
return lookupStrategy.readAclsById(objects, sids);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls.jdbc;
|
||||
|
||||
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
|
||||
import org.acegisecurity.acls.sid.Sid;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* Performs optimised lookups for {@link JdbcAclService}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface LookupStrategy {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Perform database-specific optimized lookup.
|
||||
*
|
||||
* @param objects the identities to lookup (required)
|
||||
* @param sids the SIDs for which identities are required (may be
|
||||
* <code>null</code> - implementations may elect not to provide SID
|
||||
* optimisations)
|
||||
*
|
||||
* @return the <code>Map</code> pursuant to the interface contract for
|
||||
* {@link
|
||||
* org.acegisecurity.acls.AclService#readAclsById(ObjectIdentity[],
|
||||
* Sid[])}
|
||||
*/
|
||||
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids);
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls.objectidentity;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Interface representing the identity of an individual domain object instance.
|
||||
*
|
||||
* <P>
|
||||
* As implementations are used as the key for caching and lookup, 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>ObjectIdentity</code>s equal if
|
||||
* <code>identity1.equals(identity2)</code>, rather than reference-equality of
|
||||
* <code>identity1==identity2</code>.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface ObjectIdentity extends Serializable {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Refer to the <code>java.lang.Object</code> documentation for the
|
||||
* interface contract.
|
||||
*
|
||||
* @param obj to be compared
|
||||
*
|
||||
* @return <code>true</code> if the objects are equal, <code>false</code>
|
||||
* otherwise
|
||||
*/
|
||||
public boolean equals(Object obj);
|
||||
|
||||
/**
|
||||
* Obtains the actual identifier. This identifier must not be reused to
|
||||
* represent other domain objects with the same <code>javaType</code>.
|
||||
*
|
||||
* <p>
|
||||
* Because ACLs are largely immutable, it is strongly recommended to use a
|
||||
* synthetic identifier (such as a database sequence number for the
|
||||
* primary key). Do not use an identifier with business meaning, as that
|
||||
* business meaning may change.
|
||||
* </p>
|
||||
*
|
||||
* @return the identifier (unique within this <code>javaType</code>
|
||||
*/
|
||||
public Serializable getIdentifier();
|
||||
|
||||
/**
|
||||
* Obtains the Java type represented by the domain object.
|
||||
*
|
||||
* @return the Java type of the domain object
|
||||
*/
|
||||
public Class getJavaType();
|
||||
|
||||
/**
|
||||
* Refer to the <code>java.lang.Object</code> documentation for the
|
||||
* interface contract.
|
||||
*
|
||||
* @return a hash code representation of this object
|
||||
*/
|
||||
public int hashCode();
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls.objectidentity;
|
||||
|
||||
import org.acegisecurity.acl.basic.AclObjectIdentity;
|
||||
|
||||
import org.acegisecurity.acls.IdentityUnavailableException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link AclObjectIdentity}.
|
||||
*
|
||||
* <P>
|
||||
* Uses <code>String</code>s to store the identity of the domain object
|
||||
* instance. Also offers a constructor that uses reflection to build the
|
||||
* identity information.
|
||||
* </p>
|
||||
*/
|
||||
public class ObjectIdentityImpl implements ObjectIdentity {
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private Class javaType;
|
||||
private Serializable identifier;
|
||||
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public ObjectIdentityImpl(String javaType, Serializable identifier) {
|
||||
Assert.hasText(javaType, "Java Type required");
|
||||
Assert.notNull(identifier, "identifier required");
|
||||
try {
|
||||
this.javaType = Class.forName(javaType);
|
||||
} catch (Exception ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
}
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public ObjectIdentityImpl(Class javaType, Serializable identifier) {
|
||||
Assert.notNull(javaType, "Java Type required");
|
||||
Assert.notNull(identifier, "identifier required");
|
||||
this.javaType = javaType;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 IdentityUnavailableException if identity could not be extracted
|
||||
*/
|
||||
public ObjectIdentityImpl(Object object)
|
||||
throws IdentityUnavailableException {
|
||||
Assert.notNull(object, "object cannot be null");
|
||||
|
||||
this.javaType = object.getClass();
|
||||
|
||||
Object result;
|
||||
|
||||
try {
|
||||
Method method = this.javaType.getMethod("getId", new Class[] {});
|
||||
result = method.invoke(object, new Object[] {});
|
||||
} catch (Exception e) {
|
||||
throw new IdentityUnavailableException(
|
||||
"Could not extract identity from object " + object, e);
|
||||
}
|
||||
|
||||
Assert.isInstanceOf(Serializable.class, result,
|
||||
"Getter must provide a return value of type Serializable");
|
||||
this.identifier = (Serializable) result;
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Important so caching operates properly.
|
||||
*
|
||||
* <P>
|
||||
* Considers an object of the same class equal if it has the same
|
||||
* <code>classname</code> and <code>id</code> properties.
|
||||
* </p>
|
||||
*
|
||||
* @param arg0 object to compare
|
||||
*
|
||||
* @return <code>true</code> if the presented object matches this object
|
||||
*/
|
||||
public boolean equals(Object arg0) {
|
||||
if (arg0 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(arg0 instanceof ObjectIdentityImpl)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ObjectIdentityImpl other = (ObjectIdentityImpl) arg0;
|
||||
|
||||
if (this.getIdentifier().equals(other.getIdentifier())
|
||||
&& this.getJavaType().equals(other.getJavaType())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Serializable getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public Class getJavaType() {
|
||||
return javaType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Important so caching operates properly.
|
||||
*
|
||||
* @return the hash
|
||||
*/
|
||||
public int hashCode() {
|
||||
int code = 31;
|
||||
code ^= this.javaType.hashCode();
|
||||
code ^= this.identifier.hashCode();
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(this.getClass().getName()).append("[");
|
||||
sb.append("Java Type: ").append(this.javaType);
|
||||
sb.append("; Identifier: ").append(this.identifier).append("]");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
Enables retrieval of access control lists (ACLs) for domain object instances.
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,74 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls.sid;
|
||||
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a <code>GrantedAuthority</code> as a <code>Sid</code>.
|
||||
*
|
||||
* <p>
|
||||
* This is a basic implementation that simply uses the
|
||||
* <code>String</code>-based principal for <code>Sid</code> comparison. More
|
||||
* complex principal objects may wish to provide an alternative
|
||||
* <code>Sid</code> implementation that uses some other identifier.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class GrantedAuthoritySid implements Sid {
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private String grantedAuthority;
|
||||
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public GrantedAuthoritySid(String grantedAuthority) {
|
||||
Assert.hasText(grantedAuthority, "GrantedAuthority required");
|
||||
this.grantedAuthority = grantedAuthority;
|
||||
}
|
||||
|
||||
public GrantedAuthoritySid(GrantedAuthority grantedAuthority) {
|
||||
Assert.notNull(grantedAuthority, "GrantedAuthority required");
|
||||
Assert.notNull(grantedAuthority.getAuthority(),
|
||||
"This Sid is only compatible with GrantedAuthoritys that provide a non-null getAuthority()");
|
||||
this.grantedAuthority = grantedAuthority.getAuthority();
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if ((object == null) || !(object instanceof GrantedAuthoritySid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delegate to getGrantedAuthority() to perform actual comparison (both should be identical)
|
||||
return ((GrantedAuthoritySid) object).getGrantedAuthority()
|
||||
.equals(this.getGrantedAuthority());
|
||||
}
|
||||
|
||||
public String getGrantedAuthority() {
|
||||
return grantedAuthority;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "GrantedAuthoritySid[" + this.grantedAuthority + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls.sid;
|
||||
|
||||
import org.acegisecurity.Authentication;
|
||||
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Represents an <code>Authentication.getPrincipal()</code> as a
|
||||
* <code>Sid</code>.
|
||||
*
|
||||
* <p>
|
||||
* This is a basic implementation that simply uses the
|
||||
* <code>String</code>-based principal for <code>Sid</code> comparison. More
|
||||
* complex principal objects may wish to provide an alternative
|
||||
* <code>Sid</code> implementation that uses some other identifier.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public class PrincipalSid implements Sid {
|
||||
//~ Instance fields ========================================================
|
||||
|
||||
private String principal;
|
||||
|
||||
//~ Constructors ===========================================================
|
||||
|
||||
public PrincipalSid(String principal) {
|
||||
Assert.hasText(principal, "Principal required");
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
public PrincipalSid(Authentication authentication) {
|
||||
Assert.notNull(authentication, "Authentication required");
|
||||
Assert.notNull(authentication.getPrincipal(), "Principal required");
|
||||
this.principal = authentication.getPrincipal().toString();
|
||||
|
||||
if (authentication.getPrincipal() instanceof UserDetails) {
|
||||
this.principal = ((UserDetails) authentication.getPrincipal())
|
||||
.getUsername();
|
||||
}
|
||||
}
|
||||
|
||||
//~ Methods ================================================================
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if ((object == null) || !(object instanceof PrincipalSid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delegate to getPrincipal() to perform actual comparison (both should be identical)
|
||||
return ((PrincipalSid) object).getPrincipal()
|
||||
.equals(this.getPrincipal());
|
||||
}
|
||||
|
||||
public String getPrincipal() {
|
||||
return principal;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "PrincipalSid[" + this.principal + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.acegisecurity.acls.sid;
|
||||
|
||||
/**
|
||||
* A security identity recognised by the ACL system.
|
||||
*
|
||||
* <p>
|
||||
* This interface provides indirection between actual security objects (eg
|
||||
* principals, roles, groups etc) and what is stored inside an
|
||||
* <code>Acl</code>. This is because an <code>Acl</code> will not store an
|
||||
* entire security object, but only an abstraction of it. This interface
|
||||
* therefore provides a simple way to compare these abstracted security
|
||||
* identities with other security identities and actual security objects.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface Sid {
|
||||
//~ Methods ================================================================
|
||||
|
||||
/**
|
||||
* Refer to the <code>java.lang.Object</code> documentation for the
|
||||
* interface contract.
|
||||
*
|
||||
* @param obj to be compared
|
||||
*
|
||||
* @return <code>true</code> if the objects are equal, <code>false</code>
|
||||
* otherwise
|
||||
*/
|
||||
public boolean equals(Object obj);
|
||||
|
||||
/**
|
||||
* Refer to the <code>java.lang.Object</code> documentation for the
|
||||
* interface contract.
|
||||
*
|
||||
* @return a hash code representation of this object
|
||||
*/
|
||||
public int hashCode();
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package org.acegisecurity.acls.domain;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests BasePermission and CumulativePermission.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id${date}
|
||||
*
|
||||
*/
|
||||
public class PermissionTests extends TestCase {
|
||||
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());
|
||||
}
|
||||
|
||||
public void testExpectedIntegerValues() {
|
||||
assertEquals(1, BasePermission.READ.getMask());
|
||||
assertEquals(8, BasePermission.ADMINISTRATION.getMask());
|
||||
assertEquals(9, new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION).getMask());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package org.acegisecurity.acls.jdbc;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
public class DatabaseSeeder {
|
||||
|
||||
public DatabaseSeeder(DataSource dataSource, Resource resource) throws IOException {
|
||||
Assert.notNull(dataSource, "dataSource required");
|
||||
Assert.notNull(resource, "resource required");
|
||||
|
||||
JdbcTemplate template = new JdbcTemplate(dataSource);
|
||||
String sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
|
||||
template.execute(sql);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package org.acegisecurity.acls.jdbc;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.acegisecurity.acls.Acl;
|
||||
import org.acegisecurity.acls.objectidentity.ObjectIdentity;
|
||||
import org.acegisecurity.acls.objectidentity.ObjectIdentityImpl;
|
||||
import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
|
||||
|
||||
public class JdbcAclServiceTests extends AbstractDependencyInjectionSpringContextTests {
|
||||
|
||||
protected String[] getConfigLocations() {
|
||||
return new String[] {"classpath:org/acegisecurity/acls/jdbc/applicationContext-test.xml"};
|
||||
}
|
||||
|
||||
private JdbcAclService jdbcAclService;
|
||||
|
||||
public void testStub() {
|
||||
ObjectIdentity id1 = new ObjectIdentityImpl("sample.contact.Contact", new Long(1));
|
||||
ObjectIdentity id2 = new ObjectIdentityImpl("sample.contact.Contact", new Long(2));
|
||||
ObjectIdentity id3 = new ObjectIdentityImpl("sample.contact.Contact", new Long(3));
|
||||
ObjectIdentity id4 = new ObjectIdentityImpl("sample.contact.Contact", new Long(4));
|
||||
ObjectIdentity id5 = new ObjectIdentityImpl("sample.contact.Contact", new Long(5));
|
||||
ObjectIdentity id6 = new ObjectIdentityImpl("sample.contact.Contact", new Long(6));
|
||||
Map map = jdbcAclService.readAclsById(new ObjectIdentity[] {id1, id2, id3, id4, id5, id6});
|
||||
Iterator iterator = map.keySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
ObjectIdentity identity = (ObjectIdentity) iterator.next();
|
||||
assertEquals(identity, ((Acl)map.get(identity)).getObjectIdentity());
|
||||
System.out.println(map.get(identity));
|
||||
}
|
||||
}
|
||||
|
||||
public void setJdbcAclService(JdbcAclService jdbcAclService) {
|
||||
this.jdbcAclService = jdbcAclService;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
|
||||
|
||||
<!--
|
||||
- Application context containing business beans.
|
||||
-
|
||||
- Used by all artifacts.
|
||||
-
|
||||
- $Id$
|
||||
-->
|
||||
|
||||
<beans>
|
||||
|
||||
<bean id="databaseSeeder" class="org.acegisecurity.acls.jdbc.DatabaseSeeder">
|
||||
<constructor-arg ref="dataSource"/>
|
||||
<constructor-arg value="classpath:org/acegisecurity/acls/jdbc/testData.sql"/>
|
||||
</bean>
|
||||
|
||||
<bean id="aclCache" class="org.acegisecurity.acls.jdbc.EhCacheBasedAclCache">
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
|
||||
<property name="cacheManager">
|
||||
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
|
||||
</property>
|
||||
<property name="cacheName">
|
||||
<value>aclCache</value>
|
||||
</property>
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
|
||||
<bean id="lookupStrategy" class="org.acegisecurity.acls.jdbc.BasicLookupStrategy">
|
||||
<constructor-arg ref="dataSource"/>
|
||||
<constructor-arg ref="aclCache"/>
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<bean class="org.acegisecurity.GrantedAuthorityImpl">
|
||||
<constructor-arg value="ROLE_ADMINISTRATOR"/>
|
||||
</bean>
|
||||
<bean class="org.acegisecurity.GrantedAuthorityImpl">
|
||||
<constructor-arg value="ROLE_ADMINISTRATOR"/>
|
||||
</bean>
|
||||
<bean class="org.acegisecurity.GrantedAuthorityImpl">
|
||||
<constructor-arg value="ROLE_ADMINISTRATOR"/>
|
||||
</bean>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
|
||||
<bean id="aclService" class="org.acegisecurity.acls.jdbc.JdbcAclService">
|
||||
<constructor-arg ref="dataSource"/>
|
||||
<constructor-arg ref="aclCache"/>
|
||||
<constructor-arg ref="lookupStrategy"/>
|
||||
</bean>
|
||||
|
||||
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
|
||||
<property name="driverClassName">
|
||||
<value>org.hsqldb.jdbcDriver</value>
|
||||
</property>
|
||||
<property name="url">
|
||||
<value>jdbc:hsqldb:mem:test</value>
|
||||
</property>
|
||||
<property name="username">
|
||||
<value>sa</value>
|
||||
</property>
|
||||
<property name="password">
|
||||
<value></value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
|
@ -0,0 +1,23 @@
|
|||
-- Not required. Just shows the sort of queries being sent to DB.
|
||||
|
||||
select ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY, ACL_ENTRY.ACE_ORDER,
|
||||
ACL_OBJECT_IDENTITY.ID as ACL_ID,
|
||||
ACL_OBJECT_IDENTITY.PARENT_OBJECT,
|
||||
ACL_OBJECT_IDENTITY,ENTRIES_INHERITING,
|
||||
ACL_ENTRY.ID as ACE_ID, ACL_ENTRY.MASK, ACL_ENTRY.GRANTING, ACL_ENTRY.AUDIT_SUCCESS, ACL_ENTRY.AUDIT_FAILURE,
|
||||
ACE_SID.PRINCIPAL as ACE_PRINCIPAL, ACE_SID.SID as ACE_SID,
|
||||
ACL_SID.PRINCIPAL as ACL_PRINCIPAL, ACL_SID.SID as ACL_SID,
|
||||
ACL_CLASS.CLASS
|
||||
from ACL_OBJECT_IDENTITY, ACL_ENTRY, ACL_SID ACE_SID, ACL_SID ACL_SID, ACL_CLASS
|
||||
where ACL_ENTRY.ACL_OBJECT_IDENTITY = ACL_OBJECT_IDENTITY.ID
|
||||
|
||||
and ACE_SID.ID = ACL_ENTRY.SID
|
||||
and ACL_SID.ID = ACL_OBJECT_IDENTITY.OWNER_SID
|
||||
and ACL_CLASS.ID = ACL_OBJECT_IDENTITY.OBJECT_ID_CLASS
|
||||
and (
|
||||
(ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY = 1
|
||||
and ACL_CLASS.CLASS = 'sample.contact.Contact')
|
||||
or
|
||||
(ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY = 2
|
||||
and ACL_CLASS.CLASS = 'sample.contact.Contact')
|
||||
) order by ACL_ENTRY.ACL_OBJECT_IDENTITY asc, ACL_ENTRY.ACE_ORDER asc
|
|
@ -0,0 +1,70 @@
|
|||
CREATE TABLE ACL_SID(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,
|
||||
PRINCIPAL BOOLEAN NOT NULL,
|
||||
SID VARCHAR_IGNORECASE(100) NOT NULL,
|
||||
CONSTRAINT UNIQUE_UK_1 UNIQUE(SID,PRINCIPAL));
|
||||
|
||||
INSERT INTO ACL_SID VALUES (1, TRUE, 'MARISSA');
|
||||
INSERT INTO ACL_SID VALUES (2, TRUE, 'DIANNE');
|
||||
INSERT INTO ACL_SID VALUES (3, TRUE, 'SCOTT');
|
||||
INSERT INTO ACL_SID VALUES (4, TRUE, 'PETER');
|
||||
|
||||
CREATE TABLE ACL_CLASS(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,
|
||||
CLASS VARCHAR_IGNORECASE(100) NOT NULL,
|
||||
CONSTRAINT UNIQUE_UK_2 UNIQUE(CLASS));
|
||||
|
||||
INSERT INTO ACL_CLASS VALUES (1, 'sample.contact.Contact');
|
||||
|
||||
CREATE TABLE ACL_OBJECT_IDENTITY(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,
|
||||
OBJECT_ID_CLASS BIGINT NOT NULL,
|
||||
OBJECT_ID_IDENTITY BIGINT NOT NULL,
|
||||
PARENT_OBJECT BIGINT,
|
||||
OWNER_SID BIGINT,
|
||||
ENTRIES_INHERITING BOOLEAN NOT NULL,
|
||||
CONSTRAINT UNIQUE_UK_3 UNIQUE(OBJECT_ID_CLASS,OBJECT_ID_IDENTITY),
|
||||
CONSTRAINT FOREIGN_FK_1 FOREIGN KEY(PARENT_OBJECT)REFERENCES ACL_OBJECT_IDENTITY(ID),
|
||||
CONSTRAINT FOREIGN_FK_2 FOREIGN KEY(OBJECT_ID_CLASS)REFERENCES ACL_CLASS(ID),
|
||||
CONSTRAINT FOREIGN_FK_3 FOREIGN KEY(OWNER_SID)REFERENCES ACL_SID(ID));
|
||||
|
||||
INSERT INTO ACL_OBJECT_IDENTITY VALUES (1, 1, 1, NULL, 1, TRUE);
|
||||
INSERT INTO ACL_OBJECT_IDENTITY VALUES (2, 1, 2, 1, 2, TRUE);
|
||||
INSERT INTO ACL_OBJECT_IDENTITY VALUES (3, 1, 3, 1, 1, FALSE);
|
||||
INSERT INTO ACL_OBJECT_IDENTITY VALUES (4, 1, 4, 1, 2, TRUE);
|
||||
INSERT INTO ACL_OBJECT_IDENTITY VALUES (5, 1, 5, 1, 2, FALSE);
|
||||
INSERT INTO ACL_OBJECT_IDENTITY VALUES (6, 1, 6, NULL, 1, TRUE);
|
||||
|
||||
CREATE TABLE ACL_ENTRY(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 100) NOT NULL PRIMARY KEY,
|
||||
ACL_OBJECT_IDENTITY BIGINT NOT NULL,
|
||||
ACE_ORDER INT NOT NULL,
|
||||
SID BIGINT NOT NULL,
|
||||
MASK INTEGER NOT NULL,
|
||||
GRANTING BOOLEAN NOT NULL,
|
||||
AUDIT_SUCCESS BOOLEAN NOT NULL,
|
||||
AUDIT_FAILURE BOOLEAN NOT NULL,
|
||||
CONSTRAINT UNIQUE_UK_4 UNIQUE(ACL_OBJECT_IDENTITY,ACE_ORDER),
|
||||
CONSTRAINT FOREIGN_FK_4 FOREIGN KEY(ACL_OBJECT_IDENTITY) REFERENCES ACL_OBJECT_IDENTITY(ID),
|
||||
CONSTRAINT FOREIGN_FK_5 FOREIGN KEY(SID) REFERENCES ACL_SID(ID));
|
||||
|
||||
INSERT INTO ACL_ENTRY VALUES (1, 1, 1, 2, 8, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (2, 1, 2, 1, 2, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (3, 1, 3, 3, 8, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (4, 2, 1, 3, 4, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (5, 2, 2, 4, 8, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (6, 3, 1, 3, 8, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (7, 3, 2, 4, 8, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (8, 3, 3, 1, 8, FALSE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (9, 4, 1, 4, 8, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (10, 5, 1, 2, 8, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (11, 5, 2, 3, 8, FALSE, TRUE, TRUE);
|
||||
INSERT INTO ACL_ENTRY VALUES (12, 5, 3, 1, 8, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (13, 5, 4, 4, 8, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (14, 6, 1, 2, 1, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (15, 6, 2, 1, 2, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (16, 6, 3, 2, 4, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (17, 6, 4, 3, 2, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (18, 6, 5, 3, 1, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (19, 6, 6, 4, 4, TRUE, FALSE, FALSE);
|
||||
INSERT INTO ACL_ENTRY VALUES (20, 6, 7, 4, 2, TRUE, FALSE, FALSE);
|
Loading…
Reference in New Issue