38 changed files with 3163 additions and 0 deletions
@ -0,0 +1,30 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,5 @@
|
||||
<html> |
||||
<body> |
||||
Enables retrieval of access control lists (ACLs) for domain object instances. |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,74 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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