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