From 9f512c384e736cb1aa6d777284af902ad5b3131d Mon Sep 17 00:00:00 2001 From: Ben Alex Date: Fri, 17 Nov 2006 02:01:21 +0000 Subject: [PATCH] SEC-239: New ACL module. --- .classpath | 3 +- core/pom.xml | 2 +- .../acls/AccessControlEntry.java | 93 +- .../main/java/org/acegisecurity/acls/Acl.java | 258 ++-- .../acls/AclFormattingUtils.java | 211 ++-- .../org/acegisecurity/acls/AclService.java | 156 ++- .../acls/AlreadyExistsException.java | 77 +- .../acls/AuditableAccessControlEntry.java | 49 +- .../org/acegisecurity/acls/AuditableAcl.java | 48 +- .../acls/ChildrenExistException.java | 77 +- .../acls/IdentityUnavailableException.java | 77 +- .../org/acegisecurity/acls/MutableAcl.java | 115 +- .../acegisecurity/acls/MutableAclService.java | 129 +- .../acegisecurity/acls/NotFoundException.java | 77 +- .../org/acegisecurity/acls/OwnershipAcl.java | 53 +- .../org/acegisecurity/acls/Permission.java | 103 +- .../acls/UnloadedSidException.java | 79 +- .../acls/domain/AccessControlEntryImpl.java | 282 +++-- .../acls/domain/AclAuthorizationStrategy.java | 38 + .../domain/AclAuthorizationStrategyImpl.java | 125 ++ .../acegisecurity/acls/domain/AclImpl.java | 203 +--- .../acls/domain/AuditLogger.java | 51 +- .../acls/domain/BasePermission.java | 164 +++ .../acls/domain/ConsoleAuditLogger.java | 91 +- .../acls/domain/CumulativePermission.java | 157 ++- .../acegisecurity/acls/domain/package.html | 5 + .../org/acegisecurity/acls/jdbc/AclCache.java | 69 +- .../acls/jdbc/BasicLookupStrategy.java | 1034 +++++++++-------- .../acls/jdbc/EhCacheBasedAclCache.java | 208 ++-- .../acls/jdbc/JdbcAclService.java | 114 ++ .../acls/jdbc/JdbcMutableAclService.java | 368 ++++++ .../acls/jdbc/LookupStrategy.java | 77 +- .../org/acegisecurity/acls/jdbc/package.html | 5 + .../acls/objectidentity/ObjectIdentity.java | 113 +- .../objectidentity/ObjectIdentityImpl.java | 281 ++--- .../ObjectIdentityRetrievalStrategy.java | 30 + .../ObjectIdentityRetrievalStrategyImpl.java | 31 + .../acls/objectidentity/package.html | 5 + .../java/org/acegisecurity/acls/package.html | 5 + .../acls/sid/GrantedAuthoritySid.java | 135 ++- .../acegisecurity/acls/sid/PrincipalSid.java | 145 ++- .../java/org/acegisecurity/acls/sid/Sid.java | 73 +- .../acls/sid/SidRetrievalStrategy.java | 32 + .../acls/sid/SidRetrievalStrategyImpl.java | 48 + .../org/acegisecurity/acls/sid/package.html | 5 + .../afterinvocation/AbstractAclProvider.java | 130 +++ ...InvocationCollectionFilteringProvider.java | 133 +++ .../AclEntryAfterInvocationProvider.java | 119 ++ .../afterinvocation/ArrayFilterer.java | 101 ++ ...InvocationCollectionFilteringProvider.java | 228 +--- .../afterinvocation/CollectionFilterer.java | 104 ++ .../afterinvocation/Filterer.java | 51 + .../taglibs/authz/AccessControlListTag.java | 223 ++++ .../acegisecurity/vote/AbstractAclVoter.java | 6 +- .../org/acegisecurity/vote/AclEntryVoter.java | 251 ++++ .../org/acegisecurity/taglibs/authz.tld | 30 + .../acls/domain/PermissionTests.java | 16 +- .../acls/jdbc/DatabaseSeeder.java | 95 +- .../acls/jdbc/JdbcAclServiceTests.java | 222 ++++ .../acls/jdbc/applicationContext-test.xml | 46 +- .../org/acegisecurity/acls/jdbc/select.sql | 20 +- .../org/acegisecurity/acls/jdbc/testData.sql | 45 + doc/docbook/acegi.xml | 306 ++++- project.xml | 2 +- .../annotation/ContactManagerBackend.java | 87 +- ...pplicationContext-common-authorization.xml | 163 --- .../applicationContext-common-business.xml | 72 -- .../applicationContext-acegi-security.xml | 34 +- .../applicationContext-annotations.xml | 185 +-- .../WEB-INF/applicationContext-business.xml | 6 +- .../src/main/webapp/filter/WEB-INF/web.xml | 6 +- .../src/main/webapp/filter/accessDenied.jsp | 16 + .../src/main/webapp/filter/error.html | 5 - .../java/sample/contact/AddPermission.java | 3 +- .../contact/AddPermissionController.java | 30 +- .../contact/AddPermissionValidator.java | 8 +- .../contact/AdminPermissionController.java | 26 +- .../java/sample/contact/ContactManager.java | 8 +- .../sample/contact/ContactManagerBackend.java | 83 +- .../sample/contact/DataSourcePopulator.java | 227 ++-- .../contact/DeletePermissionController.java | 35 +- ...pplicationContext-common-authorization.xml | 171 ++- .../applicationContext-common-business.xml | 9 +- .../common/WEB-INF/contacts-servlet.xml | 4 +- .../common/WEB-INF/jsp/adminPermission.jsp | 17 +- .../common/WEB-INF/jsp/deletePermission.jsp | 6 +- .../main/webapp/common/WEB-INF/jsp/index.jsp | 8 +- .../contact/AbstractContactsSampleTest.java | 115 -- .../sample/contact/GetAllContactsTests.java | 158 ++- .../acls/domain/BasePermission.java | 106 -- .../acls/jdbc/JdbcAclService.java | 78 -- .../java/org/acegisecurity/acls/package.html | 5 - .../acls/jdbc/JdbcAclServiceTests.java | 65 -- .../org/acegisecurity/acls/jdbc/testData.sql | 70 -- 94 files changed, 5759 insertions(+), 3676 deletions(-) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/AccessControlEntry.java (91%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/Acl.java (90%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/AclFormattingUtils.java (97%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/AclService.java (69%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/AlreadyExistsException.java (96%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/AuditableAccessControlEntry.java (97%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/AuditableAcl.java (87%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/ChildrenExistException.java (96%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/IdentityUnavailableException.java (96%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/MutableAcl.java (61%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/MutableAclService.java (72%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/NotFoundException.java (96%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/OwnershipAcl.java (94%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/Permission.java (97%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/UnloadedSidException.java (96%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/domain/AccessControlEntryImpl.java (82%) create mode 100644 core/src/main/java/org/acegisecurity/acls/domain/AclAuthorizationStrategy.java create mode 100644 core/src/main/java/org/acegisecurity/acls/domain/AclAuthorizationStrategyImpl.java rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/domain/AclImpl.java (62%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/domain/AuditLogger.java (96%) create mode 100644 core/src/main/java/org/acegisecurity/acls/domain/BasePermission.java rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/domain/ConsoleAuditLogger.java (87%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/domain/CumulativePermission.java (96%) create mode 100644 core/src/main/java/org/acegisecurity/acls/domain/package.html rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/jdbc/AclCache.java (73%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/jdbc/BasicLookupStrategy.java (78%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/jdbc/EhCacheBasedAclCache.java (59%) create mode 100644 core/src/main/java/org/acegisecurity/acls/jdbc/JdbcAclService.java create mode 100644 core/src/main/java/org/acegisecurity/acls/jdbc/JdbcMutableAclService.java rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/jdbc/LookupStrategy.java (97%) create mode 100644 core/src/main/java/org/acegisecurity/acls/jdbc/package.html rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentity.java (97%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityImpl.java (94%) create mode 100644 core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityRetrievalStrategy.java create mode 100644 core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityRetrievalStrategyImpl.java create mode 100644 core/src/main/java/org/acegisecurity/acls/objectidentity/package.html create mode 100644 core/src/main/java/org/acegisecurity/acls/package.html rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/sid/GrantedAuthoritySid.java (97%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/sid/PrincipalSid.java (97%) rename {sandbox/other => core}/src/main/java/org/acegisecurity/acls/sid/Sid.java (97%) create mode 100644 core/src/main/java/org/acegisecurity/acls/sid/SidRetrievalStrategy.java create mode 100644 core/src/main/java/org/acegisecurity/acls/sid/SidRetrievalStrategyImpl.java create mode 100644 core/src/main/java/org/acegisecurity/acls/sid/package.html create mode 100644 core/src/main/java/org/acegisecurity/afterinvocation/AbstractAclProvider.java create mode 100644 core/src/main/java/org/acegisecurity/afterinvocation/AclEntryAfterInvocationCollectionFilteringProvider.java create mode 100644 core/src/main/java/org/acegisecurity/afterinvocation/AclEntryAfterInvocationProvider.java create mode 100644 core/src/main/java/org/acegisecurity/afterinvocation/ArrayFilterer.java create mode 100644 core/src/main/java/org/acegisecurity/afterinvocation/CollectionFilterer.java create mode 100644 core/src/main/java/org/acegisecurity/afterinvocation/Filterer.java create mode 100644 core/src/main/java/org/acegisecurity/taglibs/authz/AccessControlListTag.java create mode 100644 core/src/main/java/org/acegisecurity/vote/AclEntryVoter.java rename {sandbox/other => core}/src/test/java/org/acegisecurity/acls/domain/PermissionTests.java (84%) rename {sandbox/other => core}/src/test/java/org/acegisecurity/acls/jdbc/DatabaseSeeder.java (92%) create mode 100644 core/src/test/java/org/acegisecurity/acls/jdbc/JdbcAclServiceTests.java rename {sandbox/other => core}/src/test/java/org/acegisecurity/acls/jdbc/applicationContext-test.xml (76%) rename {sandbox/other => core}/src/test/java/org/acegisecurity/acls/jdbc/select.sql (53%) create mode 100644 core/src/test/java/org/acegisecurity/acls/jdbc/testData.sql delete mode 100644 samples/contacts-tiger/src/main/webapp/common/WEB-INF/applicationContext-common-authorization.xml delete mode 100644 samples/contacts-tiger/src/main/webapp/common/WEB-INF/applicationContext-common-business.xml create mode 100644 samples/contacts-tiger/src/main/webapp/filter/accessDenied.jsp delete mode 100644 samples/contacts-tiger/src/main/webapp/filter/error.html delete mode 100644 samples/contacts/src/test/java/sample/contact/AbstractContactsSampleTest.java delete mode 100644 sandbox/other/src/main/java/org/acegisecurity/acls/domain/BasePermission.java delete mode 100644 sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/JdbcAclService.java delete mode 100644 sandbox/other/src/main/java/org/acegisecurity/acls/package.html delete mode 100644 sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/JdbcAclServiceTests.java delete mode 100644 sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/testData.sql diff --git a/.classpath b/.classpath index 781acdcde5..771c31dc30 100644 --- a/.classpath +++ b/.classpath @@ -5,6 +5,7 @@ + @@ -40,7 +41,7 @@ - + diff --git a/core/pom.xml b/core/pom.xml index 57fc9bc260..8ac9c7f24b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -93,7 +93,7 @@ hsqldb hsqldb - 1.7.3.0 + 1.8.0.4 test diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/AccessControlEntry.java b/core/src/main/java/org/acegisecurity/acls/AccessControlEntry.java similarity index 91% rename from sandbox/other/src/main/java/org/acegisecurity/acls/AccessControlEntry.java rename to core/src/main/java/org/acegisecurity/acls/AccessControlEntry.java index 1fbb2aa77a..61ef097fdb 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/AccessControlEntry.java +++ b/core/src/main/java/org/acegisecurity/acls/AccessControlEntry.java @@ -1,25 +1,24 @@ -/* 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; - -import java.io.Serializable; - - +/* 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; + +import java.io.Serializable; + + /** * Represents an individual permission assignment within an {@link Acl}. * @@ -30,28 +29,28 @@ import java.io.Serializable; * @author Ben Alex * @version $Id$ * - */ -public interface AccessControlEntry { - //~ Methods ======================================================================================================== - - public Acl getAcl(); - - /** - * Obtains an identifier that represents this ACE. - * - * @return the identifier, or null if unsaved - */ - public Serializable getId(); - - public Permission getPermission(); - - public Sid getSid(); - - /** - * Indicates the a Permission is being granted to the relevant Sid. If false, - * indicates the permission is being revoked/blocked. - * - * @return true if being granted, false otherwise - */ - public boolean isGranting(); -} + */ +public interface AccessControlEntry { + //~ Methods ======================================================================================================== + + public Acl getAcl(); + + /** + * Obtains an identifier that represents this ACE. + * + * @return the identifier, or null if unsaved + */ + public Serializable getId(); + + public Permission getPermission(); + + public Sid getSid(); + + /** + * Indicates the a Permission is being granted to the relevant Sid. If false, indicates the permission is + * being revoked/blocked. + * + * @return true if being granted, false otherwise + */ + public boolean isGranting(); +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/Acl.java b/core/src/main/java/org/acegisecurity/acls/Acl.java similarity index 90% rename from sandbox/other/src/main/java/org/acegisecurity/acls/Acl.java rename to core/src/main/java/org/acegisecurity/acls/Acl.java index 63783de867..546f9f898e 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/Acl.java +++ b/core/src/main/java/org/acegisecurity/acls/Acl.java @@ -1,26 +1,25 @@ -/* 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.io.Serializable; - - +/* 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.io.Serializable; + + /** * Represents an access control list (ACL) for a domain object. * @@ -40,105 +39,114 @@ import java.io.Serializable; * * @author Ben Alex * @version $Id$ - */ -public interface Acl extends Serializable { - //~ Methods ======================================================================================================== - - /** - * Returns all of the entries represented by the present Acl (not parents).

This method is - * typically used for administrative purposes.

- *

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 MUST be - * ordered in that manner.

- *

Do NOT use this method for making authorization decisions. Instead use {@link - * #isGranted(Permission[], Sid[])}.

- *

This method must operate correctly even if the Acl only represents a subset of - * Sids. The caller is responsible for correctly handling the result if only a subset of - * Sids is represented.

- * - * @return the list of entries represented by the Acl - */ - public AccessControlEntry[] getEntries(); - - /** - * Obtains the domain object this Acl provides entries for. This is immutable once an - * Acl 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.

This - * method solely represents the presence of a navigation hierarchy between the parent Acl and this - * Acl. For actual inheritance to take place, the {@link #isEntriesInheriting()} must also be - * true.

- *

This method must operate correctly even if the Acl only represents a subset of - * Sids. The caller is responsible for correctly handling the result if only a subset of - * Sids is represented.

- * - * @return the parent Acl - */ - public Acl getParentAcl(); - - /** - * Indicates whether the ACL entries from the {@link #getParentAcl()} should flow down into the current - * Acl.

The mere link between an Acl and a parent Acl 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.

- * - * @return true if parent ACL entries inherit into the current Acl - */ - public boolean isEntriesInheriting(); - - /** - * This is the actual authorization logic method, and must be used whenever ACL authorization decisions are - * required.

An array of Sids are presented, representing security identifies of the current - * principal. In addition, an array of Permissions 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 any of the Permissions inside the array will be sufficient for an - * affirmative authorization.

- *

The actual approach used to make authorization decisions is left to the implementation and is not - * specified by this interface. For example, an implementation MAY 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 Permission, 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 Permissions 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 true, 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.

- *

This method must operate correctly even if the Acl only represents a subset of - * Sids. The caller is responsible for correctly handling the result if only a subset of - * Sids is represented.

- * - * @param permission the permission or permissions required - * @param sids the security identities held by the principal - * @param administrativeMode if true denotes the query is for administrative purposes and no logger or - * auditing (if supported by the implementation) should be undertaken - * - * @return true is authorization is granted - * - * @throws NotFoundException MAY be thrown if an implementation cannot make an authoritative authorization decision - * @throws UnloadedSidException thrown if the Acl does not have details for one or more of the - * Sids passed as arguments - */ - public boolean isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode) - throws NotFoundException, UnloadedSidException; - - /** - * For efficiency reasons an Acl may be loaded and not contain entries for every - * Sid in the system. If an Acl has been loaded and does not represent every - * Sid, all methods of the Sid can only be used within the limited scope of the - * Sid instances it actually represents.

It is normal to load an Acl for only - * particular Sids if read-only authorization decisions are being made. However, if user interface - * reporting or modification of Acls are desired, an Acl should be loaded with all - * Sids. This method denotes whether or not the specified Sids have been loaded or not.

- * - * @param sids DOCUMENT ME! - * - * @return true if every passed Sid is represented by this Acl instance - */ - public boolean isSidLoaded(Sid[] sids); -} + */ +public interface Acl extends Serializable { + //~ Methods ======================================================================================================== + + /** + * Returns all of the entries represented by the present Acl (not parents).

This method is + * typically used for administrative purposes.

+ *

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 MUST be + * ordered in that manner.

+ *

Do NOT use this method for making authorization decisions. Instead use {@link + * #isGranted(Permission[], Sid[])}.

+ *

This method must operate correctly even if the Acl only represents a subset of + * Sids. The caller is responsible for correctly handling the result if only a subset of + * Sids is represented.

+ * + * @return the list of entries represented by the Acl + */ + public AccessControlEntry[] getEntries(); + + /** + * Obtains the domain object this Acl provides entries for. This is immutable once an + * Acl is created. + * + * @return the object identity + */ + public ObjectIdentity getObjectIdentity(); + + /** + * Determines the owner of the Acl. The meaning of ownership varies by implementation and is + * unspecified. + * + * @return the owner (may be null if the implementation does not use ownership concepts) + */ + public Sid getOwner(); + + /** + * 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.

This + * method solely represents the presence of a navigation hierarchy between the parent Acl and this + * Acl. For actual inheritance to take place, the {@link #isEntriesInheriting()} must also be + * true.

+ *

This method must operate correctly even if the Acl only represents a subset of + * Sids. The caller is responsible for correctly handling the result if only a subset of + * Sids is represented.

+ * + * @return the parent Acl + */ + public Acl getParentAcl(); + + /** + * Indicates whether the ACL entries from the {@link #getParentAcl()} should flow down into the current + * Acl.

The mere link between an Acl and a parent Acl 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.

+ * + * @return true if parent ACL entries inherit into the current Acl + */ + public boolean isEntriesInheriting(); + + /** + * This is the actual authorization logic method, and must be used whenever ACL authorization decisions are + * required.

An array of Sids are presented, representing security identifies of the current + * principal. In addition, an array of Permissions 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 any of the Permissions inside the array will be sufficient for an + * affirmative authorization.

+ *

The actual approach used to make authorization decisions is left to the implementation and is not + * specified by this interface. For example, an implementation MAY 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 Permission, 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 Permissions 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 true, 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.

+ *

This method must operate correctly even if the Acl only represents a subset of + * Sids.

+ * + * @param permission the permission or permissions required + * @param sids the security identities held by the principal + * @param administrativeMode if true denotes the query is for administrative purposes and no logging + * or auditing (if supported by the implementation) should be undertaken + * + * @return true is authorization is granted + * + * @throws NotFoundException MUST be thrown if an implementation cannot make an authoritative authorization + * decision, usually because there is no ACL information for this particular permission and/or SID + * @throws UnloadedSidException thrown if the Acl does not have details for one or more of the + * Sids passed as arguments + */ + public boolean isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode) + throws NotFoundException, UnloadedSidException; + + /** + * For efficiency reasons an Acl may be loaded and not contain entries for every + * Sid in the system. If an Acl has been loaded and does not represent every + * Sid, all methods of the Sid can only be used within the limited scope of the + * Sid instances it actually represents.

It is normal to load an Acl for only + * particular Sids if read-only authorization decisions are being made. However, if user interface + * reporting or modification of Acls are desired, an Acl should be loaded with all + * Sids. This method denotes whether or not the specified Sids have been loaded or not.

+ * + * @param sids one or more security identities the caller is interest in knowing whether this Sid + * supports + * + * @return true if every passed Sid is represented by this Acl instance + */ + public boolean isSidLoaded(Sid[] sids); +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/AclFormattingUtils.java b/core/src/main/java/org/acegisecurity/acls/AclFormattingUtils.java similarity index 97% rename from sandbox/other/src/main/java/org/acegisecurity/acls/AclFormattingUtils.java rename to core/src/main/java/org/acegisecurity/acls/AclFormattingUtils.java index af9969878d..f846c6619b 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/AclFormattingUtils.java +++ b/core/src/main/java/org/acegisecurity/acls/AclFormattingUtils.java @@ -1,106 +1,105 @@ -/* 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 "".

Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.

- * - * @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.

Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.

- * - * @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); - } -} +/* 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 "".

Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.

+ * + * @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.

Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.

+ * + * @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); + } +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/AclService.java b/core/src/main/java/org/acegisecurity/acls/AclService.java similarity index 69% rename from sandbox/other/src/main/java/org/acegisecurity/acls/AclService.java rename to core/src/main/java/org/acegisecurity/acls/AclService.java index 4f8357dc7d..d3d0c3dd07 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/AclService.java +++ b/core/src/main/java/org/acegisecurity/acls/AclService.java @@ -1,66 +1,100 @@ -/* 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; - - +/* 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 Acls that apply for the passed Objects.

The returned map is - * keyed on the passed objects, with the values being the Acl instances. Any unknown objects will not - * have a map key.

- * - * @param objects the objects to find ACL information for - * - * @return a map with zero or more elements (never null) - * - * @throws NotFoundException DOCUMENT ME! - */ - public Map readAclsById(ObjectIdentity[] objects) throws NotFoundException; - - /** - * Obtains all the Acls that apply for the passed Objects, but only for the - * security identifies passed.

Implementations MAY 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.

- *

The returned map is keyed on the passed objects, with the values being the Acl - * instances. Any unknown objects (or objects for which the interested Sids do not have entries) will - * not have a map key.

- * - * @param objects the objects to find ACL information for - * @param sids the security identities for which ACL information is required (may be null to denote - * all entries) - * - * @return a map with zero or more elements (never null) - * - * @throws NotFoundException DOCUMENT ME! - */ - public Map readAclsById(ObjectIdentity[] objects, Sid[] sids) - throws NotFoundException; -} + */ +public interface AclService { + //~ Methods ======================================================================================================== + + /** + * Locates all object identities that use the specified parent. This is useful for administration tools. + * + * @param parentIdentity to locate children of + * + * @return the children (or null if none were found) + */ + public ObjectIdentity[] findChildren(ObjectIdentity parentIdentity); + + /** + * Same as {@link #readAclsById(ObjectIdentity[])} except it returns only a single Acl.

This method + * should not be called as it does not leverage the underlaying implementation's potential ability to filter + * Acl entries based on a {@link Sid} parameter.

+ * + * @param object DOCUMENT ME! + * + * @return DOCUMENT ME! + * + * @throws NotFoundException DOCUMENT ME! + */ + public Acl readAclById(ObjectIdentity object) throws NotFoundException; + + /** + * Same as {@link #readAclsById(ObjectIdentity[], Sid[])} except it returns only a single Acl. + * + * @param object DOCUMENT ME! + * @param sids DOCUMENT ME! + * + * @return DOCUMENT ME! + * + * @throws NotFoundException DOCUMENT ME! + */ + public Acl readAclById(ObjectIdentity object, Sid[] sids) + throws NotFoundException; + + /** + * Obtains all the Acls that apply for the passed Objects.

The returned map is + * keyed on the passed objects, with the values being the Acl instances. Any unknown objects will not + * have a map key.

+ * + * @param objects the objects to find ACL information for + * + * @return a map with zero or more elements (never null) + * + * @throws NotFoundException DOCUMENT ME! + */ + public Map readAclsById(ObjectIdentity[] objects) throws NotFoundException; + + /** + * Obtains all the Acls that apply for the passed Objects, but only for the + * security identifies passed.

Implementations MAY 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.

+ *

The returned map is keyed on the passed objects, with the values being the Acl + * instances. Any unknown objects (or objects for which the interested Sids do not have entries) will + * not have a map key.

+ * + * @param objects the objects to find ACL information for + * @param sids the security identities for which ACL information is required (may be null to denote + * all entries) + * + * @return a map with zero or more elements (never null) + * + * @throws NotFoundException DOCUMENT ME! + */ + public Map readAclsById(ObjectIdentity[] objects, Sid[] sids) + throws NotFoundException; +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/AlreadyExistsException.java b/core/src/main/java/org/acegisecurity/acls/AlreadyExistsException.java similarity index 96% rename from sandbox/other/src/main/java/org/acegisecurity/acls/AlreadyExistsException.java rename to core/src/main/java/org/acegisecurity/acls/AlreadyExistsException.java index 9ae2d3820e..babb4416e4 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/AlreadyExistsException.java +++ b/core/src/main/java/org/acegisecurity/acls/AlreadyExistsException.java @@ -1,49 +1,48 @@ -/* 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 entry already exists for the object. - * - * @author Ben Alex - * @version $Id$ - */ -public class AlreadyExistsException extends AcegiSecurityException { - //~ Constructors =================================================================================================== - +/* 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 entry already exists for the object. + * + * @author Ben Alex + * @version $Id$ + */ +public class AlreadyExistsException extends AcegiSecurityException { + //~ Constructors =================================================================================================== + /** * Constructs an AlreadyExistsException with the specified message. * * @param msg the detail message - */ - public AlreadyExistsException(String msg) { - super(msg); - } - + */ + public AlreadyExistsException(String msg) { + super(msg); + } + /** * Constructs an AlreadyExistsException 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); - } -} + */ + public AlreadyExistsException(String msg, Throwable t) { + super(msg, t); + } +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/AuditableAccessControlEntry.java b/core/src/main/java/org/acegisecurity/acls/AuditableAccessControlEntry.java similarity index 97% rename from sandbox/other/src/main/java/org/acegisecurity/acls/AuditableAccessControlEntry.java rename to core/src/main/java/org/acegisecurity/acls/AuditableAccessControlEntry.java index 22f7a8fb3a..3fcda04325 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/AuditableAccessControlEntry.java +++ b/core/src/main/java/org/acegisecurity/acls/AuditableAccessControlEntry.java @@ -1,31 +1,30 @@ -/* 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; - +/* 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; + /** * Represents an ACE that provides auditing information. * * @author Ben Alex * @version $Id$ * - */ -public interface AuditableAccessControlEntry extends AccessControlEntry { - //~ Methods ======================================================================================================== - - public boolean isAuditFailure(); - - public boolean isAuditSuccess(); -} + */ +public interface AuditableAccessControlEntry extends AccessControlEntry { + //~ Methods ======================================================================================================== + + public boolean isAuditFailure(); + + public boolean isAuditSuccess(); +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/AuditableAcl.java b/core/src/main/java/org/acegisecurity/acls/AuditableAcl.java similarity index 87% rename from sandbox/other/src/main/java/org/acegisecurity/acls/AuditableAcl.java rename to core/src/main/java/org/acegisecurity/acls/AuditableAcl.java index 51bc15e1a9..cb5c4ecfa9 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/AuditableAcl.java +++ b/core/src/main/java/org/acegisecurity/acls/AuditableAcl.java @@ -1,29 +1,31 @@ -/* 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; - +/* 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; + + /** * A mutable ACL that provides audit capabilities. * * @author Ben Alex * @version $Id$ * - */ -public interface AuditableAcl extends MutableAcl { - //~ Methods ======================================================================================================== - - public void updateAuditing(Long aceId, boolean auditSuccess, boolean auditFailure); -} + */ +public interface AuditableAcl extends MutableAcl { + //~ Methods ======================================================================================================== + + public void updateAuditing(Serializable aceId, boolean auditSuccess, boolean auditFailure); +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/ChildrenExistException.java b/core/src/main/java/org/acegisecurity/acls/ChildrenExistException.java similarity index 96% rename from sandbox/other/src/main/java/org/acegisecurity/acls/ChildrenExistException.java rename to core/src/main/java/org/acegisecurity/acls/ChildrenExistException.java index 0c87da7968..c7c8b07e48 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/ChildrenExistException.java +++ b/core/src/main/java/org/acegisecurity/acls/ChildrenExistException.java @@ -1,50 +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 {@link Acl} cannot be deleted because children Acls exist. - * - * @author Ben Alex - * @version $Id$ - */ -public class ChildrenExistException extends AcegiSecurityException { - //~ Constructors =================================================================================================== - +/* 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 Acls exist. + * + * @author Ben Alex + * @version $Id$ + */ +public class ChildrenExistException extends AcegiSecurityException { + //~ Constructors =================================================================================================== + /** * Constructs an ChildrenExistException with the specified * message. * * @param msg the detail message - */ - public ChildrenExistException(String msg) { - super(msg); - } - + */ + public ChildrenExistException(String msg) { + super(msg); + } + /** * Constructs an ChildrenExistException 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); - } -} + */ + public ChildrenExistException(String msg, Throwable t) { + super(msg, t); + } +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/IdentityUnavailableException.java b/core/src/main/java/org/acegisecurity/acls/IdentityUnavailableException.java similarity index 96% rename from sandbox/other/src/main/java/org/acegisecurity/acls/IdentityUnavailableException.java rename to core/src/main/java/org/acegisecurity/acls/IdentityUnavailableException.java index df97a57aa9..7e346beebb 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/IdentityUnavailableException.java +++ b/core/src/main/java/org/acegisecurity/acls/IdentityUnavailableException.java @@ -1,49 +1,48 @@ -/* 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 =================================================================================================== - +/* 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 IdentityUnavailableException with the specified message. * * @param msg the detail message - */ - public IdentityUnavailableException(String msg) { - super(msg); - } - + */ + public IdentityUnavailableException(String msg) { + super(msg); + } + /** * Constructs an IdentityUnavailableException 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); - } -} + */ + public IdentityUnavailableException(String msg, Throwable t) { + super(msg, t); + } +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/MutableAcl.java b/core/src/main/java/org/acegisecurity/acls/MutableAcl.java similarity index 61% rename from sandbox/other/src/main/java/org/acegisecurity/acls/MutableAcl.java rename to core/src/main/java/org/acegisecurity/acls/MutableAcl.java index 3cc7774df0..8bcd8ab45a 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/MutableAcl.java +++ b/core/src/main/java/org/acegisecurity/acls/MutableAcl.java @@ -1,25 +1,24 @@ -/* 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; - -import java.io.Serializable; - - +/* 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; + +import java.io.Serializable; + + /** * A mutable Acl. * @@ -30,31 +29,47 @@ import java.io.Serializable; * * @author Ben Alex * @version $Id$ - */ -public interface MutableAcl extends Acl { - //~ Methods ======================================================================================================== - - public void deleteAce(Long aceId) throws NotFoundException; - - /** - * Obtains an identifier that represents this MutableAcl. - * - * @return the identifier, or null if unsaved - */ - public Serializable getId(); - - public void insertAce(Long afterAceId, Permission permission, Sid sid, boolean granting) - throws NotFoundException; - - public void setEntriesInheriting(boolean entriesInheriting); - - /** - * 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 interface MutableAcl extends Acl { + //~ Methods ======================================================================================================== + + public void deleteAce(Serializable aceId) throws NotFoundException; + + /** + * Retrieves all of the non-deleted {@link AccessControlEntry} instances currently stored by the + * MutableAcl. The returned objects should be immutable outside the package, and therefore it is safe + * to return them to the caller for informational purposes. The AccessControlEntry information is + * needed so that invocations of update and delete methods on the MutableAcl can refer to a valid + * {@link AccessControlEntry#getId()}. + * + * @return DOCUMENT ME! + */ + public AccessControlEntry[] getEntries(); + + /** + * Obtains an identifier that represents this MutableAcl. + * + * @return the identifier, or null if unsaved + */ + public Serializable getId(); + + public void insertAce(Serializable afterAceId, Permission permission, Sid sid, boolean granting) + throws NotFoundException; + + /** + * Change the value returned by {@link Acl#isEntriesInheriting()}. + * + * @param entriesInheriting the new value + */ + public void setEntriesInheriting(boolean entriesInheriting); + + /** + * Changes the parent of this ACL. + * + * @param newParent the new parent + */ + public void setParent(MutableAcl newParent); + + public void updateAce(Serializable aceId, Permission permission) + throws NotFoundException; +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/MutableAclService.java b/core/src/main/java/org/acegisecurity/acls/MutableAclService.java similarity index 72% rename from sandbox/other/src/main/java/org/acegisecurity/acls/MutableAclService.java rename to core/src/main/java/org/acegisecurity/acls/MutableAclService.java index 66b73f9fe5..9b866e052b 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/MutableAclService.java +++ b/core/src/main/java/org/acegisecurity/acls/MutableAclService.java @@ -1,74 +1,65 @@ -/* 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; - - +/* 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 Acl instances. * * @author Ben Alex * @version $Id$ - */ -public interface MutableAclService extends AclService { - //~ Methods ======================================================================================================== - - /** - * Creates an empty Acl 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 false 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 null if none were found) - */ - public ObjectIdentity[] findChildren(ObjectIdentity parentIdentity); - - /** - * Changes an existing Acl 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 new - * keyword?) - */ - public void updateAcl(MutableAcl acl) throws NotFoundException; -} + */ +public interface MutableAclService extends AclService { + //~ Methods ======================================================================================================== + + /** + * Creates an empty Acl object in the database. It will have no entries. The returned object + * will then be used to add entries. + * + * @param objectIdentity 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 objectIdentity) + throws AlreadyExistsException; + + /** + * Removes the specified entry from the database. + * + * @param objectIdentity the object identity to remove + * @param deleteChildren whether to cascade the delete to children + * + * @throws ChildrenExistException if the deleteChildren argument was false but children exist + */ + public void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren) + throws ChildrenExistException; + + /** + * Changes an existing Acl in the database. + * + * @param acl to modify + * + * @return DOCUMENT ME! + * + * @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 new + * keyword?) + */ + public MutableAcl updateAcl(MutableAcl acl) throws NotFoundException; +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/NotFoundException.java b/core/src/main/java/org/acegisecurity/acls/NotFoundException.java similarity index 96% rename from sandbox/other/src/main/java/org/acegisecurity/acls/NotFoundException.java rename to core/src/main/java/org/acegisecurity/acls/NotFoundException.java index 4129bb18a6..74d0f1004b 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/NotFoundException.java +++ b/core/src/main/java/org/acegisecurity/acls/NotFoundException.java @@ -1,49 +1,48 @@ -/* 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 =================================================================================================== - +/* 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 NotFoundException with the specified message. * * @param msg the detail message - */ - public NotFoundException(String msg) { - super(msg); - } - + */ + public NotFoundException(String msg) { + super(msg); + } + /** * Constructs an NotFoundException 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); - } -} + */ + public NotFoundException(String msg, Throwable t) { + super(msg, t); + } +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/OwnershipAcl.java b/core/src/main/java/org/acegisecurity/acls/OwnershipAcl.java similarity index 94% rename from sandbox/other/src/main/java/org/acegisecurity/acls/OwnershipAcl.java rename to core/src/main/java/org/acegisecurity/acls/OwnershipAcl.java index f4af13deb8..d8c44456a0 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/OwnershipAcl.java +++ b/core/src/main/java/org/acegisecurity/acls/OwnershipAcl.java @@ -1,23 +1,22 @@ -/* 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; - - +/* 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. * @@ -28,11 +27,9 @@ import org.acegisecurity.acls.sid.Sid; * * @author Ben Alex * @version $Id$ - */ -public interface OwnershipAcl extends MutableAcl { - //~ Methods ======================================================================================================== - - public Sid getOwner(); - - public void setOwner(Sid newOwner); -} + */ +public interface OwnershipAcl extends MutableAcl { + //~ Methods ======================================================================================================== + + public void setOwner(Sid newOwner); +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/Permission.java b/core/src/main/java/org/acegisecurity/acls/Permission.java similarity index 97% rename from sandbox/other/src/main/java/org/acegisecurity/acls/Permission.java rename to core/src/main/java/org/acegisecurity/acls/Permission.java index 667b226419..2104090c13 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/Permission.java +++ b/core/src/main/java/org/acegisecurity/acls/Permission.java @@ -1,57 +1,56 @@ -/* 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; - - +/* 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 String representing this permission.

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 String containing {@link - * #RESERVED_ON}.

- *

The returned String must be 32 characters in length.

- *

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.

- * - * @return a 32-character bit pattern - */ - public String getPattern(); -} + */ +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 String representing this permission.

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 String containing {@link + * #RESERVED_ON}.

+ *

The returned String must be 32 characters in length.

+ *

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.

+ * + * @return a 32-character bit pattern + */ + public String getPattern(); +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/UnloadedSidException.java b/core/src/main/java/org/acegisecurity/acls/UnloadedSidException.java similarity index 96% rename from sandbox/other/src/main/java/org/acegisecurity/acls/UnloadedSidException.java rename to core/src/main/java/org/acegisecurity/acls/UnloadedSidException.java index c06130b095..d093d8387e 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/UnloadedSidException.java +++ b/core/src/main/java/org/acegisecurity/acls/UnloadedSidException.java @@ -1,50 +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 {@link Acl} cannot perform an operation because it only loaded a subset of Sids and - * the caller has requested details for an unloaded Sid. - * - * @author Ben Alex - * @version $Id$ - */ -public class UnloadedSidException extends AcegiSecurityException { - //~ Constructors =================================================================================================== - +/* 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 Sids and + * the caller has requested details for an unloaded Sid. + * + * @author Ben Alex + * @version $Id$ + */ +public class UnloadedSidException extends AcegiSecurityException { + //~ Constructors =================================================================================================== + /** * Constructs an NotFoundException with the specified message. * * @param msg the detail message - */ - public UnloadedSidException(String msg) { - super(msg); - } - + */ + public UnloadedSidException(String msg) { + super(msg); + } + /** * Constructs an NotFoundException 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); - } -} + */ + public UnloadedSidException(String msg, Throwable t) { + super(msg, t); + } +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/domain/AccessControlEntryImpl.java b/core/src/main/java/org/acegisecurity/acls/domain/AccessControlEntryImpl.java similarity index 82% rename from sandbox/other/src/main/java/org/acegisecurity/acls/domain/AccessControlEntryImpl.java rename to core/src/main/java/org/acegisecurity/acls/domain/AccessControlEntryImpl.java index b1c25929b4..c810d376f5 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/domain/AccessControlEntryImpl.java +++ b/core/src/main/java/org/acegisecurity/acls/domain/AccessControlEntryImpl.java @@ -1,149 +1,133 @@ -/* 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.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; - -import java.io.Serializable; - - -/** - * An immutable default implementation of AccessControlEntry. - * - * @author Ben Alex - * @version $Id$ - */ -public class AccessControlEntryImpl implements AccessControlEntry, AuditableAccessControlEntry { - //~ Instance fields ================================================================================================ - - private Acl acl; - private Permission permission; - private Serializable id; - private Sid sid; - private boolean aceDirty = false; - private boolean auditFailure = false; - private boolean auditSuccess = false; - private boolean granting; - - //~ Constructors =================================================================================================== - - 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; - } - - //~ Methods ======================================================================================================== - - 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 Acl getAcl() { - return acl; - } - - public Serializable getId() { - return id; - } - - public Permission getPermission() { - return permission; - } - - public Sid getSid() { - return sid; - } - - public boolean isAceDirty() { - return aceDirty; - } - - public boolean isAuditFailure() { - return auditFailure; - } - - public boolean isAuditSuccess() { - return auditSuccess; - } - - public boolean isGranting() { - return granting; - } - - void setAuditFailure(boolean auditFailure) { - this.auditFailure = auditFailure; - this.aceDirty = true; - } - - void setAuditSuccess(boolean auditSuccess) { - this.auditSuccess = auditSuccess; - this.aceDirty = true; - } - - void setId(Serializable id) { - this.id = id; - } - - void setPermission(Permission permission) { - Assert.notNull(permission, "Permission required"); - this.permission = permission; - this.aceDirty = true; - } - - 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(); - } -} +/* 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.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; + +import java.io.Serializable; + + +/** + * An immutable default implementation of AccessControlEntry. + * + * @author Ben Alex + * @version $Id$ + */ +public class AccessControlEntryImpl implements AccessControlEntry, AuditableAccessControlEntry { + //~ Instance fields ================================================================================================ + + private Acl acl; + private Permission permission; + private Serializable id; + private Sid sid; + private boolean auditFailure = false; + private boolean auditSuccess = false; + private boolean granting; + + //~ Constructors =================================================================================================== + + 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; + } + + //~ Methods ======================================================================================================== + + public boolean equals(Object arg0) { + if (!(arg0 instanceof AccessControlEntryImpl)) { + return false; + } + + AccessControlEntryImpl rhs = (AccessControlEntryImpl) arg0; + + if ((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 Acl getAcl() { + return acl; + } + + public Serializable getId() { + return id; + } + + public Permission getPermission() { + return permission; + } + + public Sid getSid() { + return sid; + } + + public boolean isAuditFailure() { + return auditFailure; + } + + public boolean isAuditSuccess() { + return auditSuccess; + } + + public boolean isGranting() { + return granting; + } + + void setAuditFailure(boolean auditFailure) { + this.auditFailure = auditFailure; + } + + void setAuditSuccess(boolean auditSuccess) { + this.auditSuccess = auditSuccess; + } + + void setPermission(Permission permission) { + Assert.notNull(permission, "Permission required"); + this.permission = permission; + } + + 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).append("; "); + sb.append("auditSuccess: ").append(this.auditSuccess).append("; "); + sb.append("auditFailure: ").append(this.auditFailure); + sb.append("]"); + + return sb.toString(); + } +} diff --git a/core/src/main/java/org/acegisecurity/acls/domain/AclAuthorizationStrategy.java b/core/src/main/java/org/acegisecurity/acls/domain/AclAuthorizationStrategy.java new file mode 100644 index 0000000000..7530340600 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/domain/AclAuthorizationStrategy.java @@ -0,0 +1,38 @@ +/* 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.Acl; + + +/** + * Strategy used by {@link AclImpl} to determine whether a principal is permitted to call + * adminstrative methods on the AclImpl. + * + * @author Ben Alex + * @version $Id$ + */ +public interface AclAuthorizationStrategy { + //~ Static fields/initializers ===================================================================================== + + public static final int CHANGE_OWNERSHIP = 0; + public static final int CHANGE_AUDITING = 1; + public static final int CHANGE_GENERAL = 2; + + //~ Methods ======================================================================================================== + + public void securityCheck(Acl acl, int changeType); +} diff --git a/core/src/main/java/org/acegisecurity/acls/domain/AclAuthorizationStrategyImpl.java b/core/src/main/java/org/acegisecurity/acls/domain/AclAuthorizationStrategyImpl.java new file mode 100644 index 0000000000..da35c839a0 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/domain/AclAuthorizationStrategyImpl.java @@ -0,0 +1,125 @@ +/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.acegisecurity.acls.domain; + +import org.acegisecurity.AccessDeniedException; +import org.acegisecurity.Authentication; +import org.acegisecurity.GrantedAuthority; + +import org.acegisecurity.acls.Acl; +import org.acegisecurity.acls.Permission; +import org.acegisecurity.acls.sid.PrincipalSid; +import org.acegisecurity.acls.sid.Sid; +import org.acegisecurity.acls.sid.SidRetrievalStrategy; +import org.acegisecurity.acls.sid.SidRetrievalStrategyImpl; + +import org.acegisecurity.context.SecurityContextHolder; + +import org.springframework.util.Assert; + + +/** + * Default implementation of {@link AclAuthorizationStrategy}.

Permission will be granted provided the current + * principal is either the owner (as defined by the ACL), has {@link BasePermission#ADMINISTRATION} (as defined by the + * ACL and via a {@link Sid} retrieved for the current principal via {@link #sidRetrievalStrategy}), or if the current + * principal holds the relevant system-wide {@link GrantedAuthority} and injected into the constructor.

+ * + * @author Ben Alex + * @version $Id$ + */ +public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy { + //~ Instance fields ================================================================================================ + + private GrantedAuthority gaGeneralChanges; + private GrantedAuthority gaModifyAuditing; + private GrantedAuthority gaTakeOwnership; + private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl(); + + //~ Constructors =================================================================================================== + +/** + * Constructor. The only mandatory parameter relates to the system-wide {@link GrantedAuthority} instances that + * can be held to always permit ACL changes. + * + * @param auths an array of GrantedAuthoritys 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 AclAuthorizationStrategyImpl(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]; + } + + //~ Methods ======================================================================================================== + + public void securityCheck(Acl acl, 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(acl.getOwner()) && ((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; + } + } + + // Try to get permission via ACEs within the ACL + Sid[] sids = sidRetrievalStrategy.getSids(authentication); + + if (acl.isGranted(new Permission[] {BasePermission.ADMINISTRATION}, sids, false)) { + return; + } + + throw new AccessDeniedException( + "Principal does not have required ACL permissions to perform requested operation"); + } + + public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) { + Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required"); + this.sidRetrievalStrategy = sidRetrievalStrategy; + } +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/domain/AclImpl.java b/core/src/main/java/org/acegisecurity/acls/domain/AclImpl.java similarity index 62% rename from sandbox/other/src/main/java/org/acegisecurity/acls/domain/AclImpl.java rename to core/src/main/java/org/acegisecurity/acls/domain/AclImpl.java index ac1996e792..ac290fd194 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/domain/AclImpl.java +++ b/core/src/main/java/org/acegisecurity/acls/domain/AclImpl.java @@ -12,13 +12,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.acegisecurity.acls.domain; -import org.acegisecurity.AccessDeniedException; -import org.acegisecurity.Authentication; -import org.acegisecurity.GrantedAuthority; - import org.acegisecurity.acls.AccessControlEntry; import org.acegisecurity.acls.Acl; import org.acegisecurity.acls.AuditableAcl; @@ -28,11 +23,8 @@ import org.acegisecurity.acls.OwnershipAcl; import org.acegisecurity.acls.Permission; import org.acegisecurity.acls.UnloadedSidException; import org.acegisecurity.acls.objectidentity.ObjectIdentity; -import org.acegisecurity.acls.sid.PrincipalSid; import org.acegisecurity.acls.sid.Sid; -import org.acegisecurity.context.SecurityContextHolder; - import org.springframework.util.Assert; import java.io.Serializable; @@ -49,29 +41,17 @@ import java.util.Vector; * @version $Id */ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { - //~ Static fields/initializers ===================================================================================== - - private static final int CHANGE_OWNERSHIP = 0; - private static final int CHANGE_AUDITING = 1; - private static final int CHANGE_GENERAL = 2; - //~ Instance fields ================================================================================================ private Acl parentAcl; - private AuditLogger auditLogger = new ConsoleAuditLogger(); // AuditableAcl - private GrantedAuthority gaGeneralChanges; - private GrantedAuthority gaModifyAuditing; - private GrantedAuthority gaTakeOwnership; + private AclAuthorizationStrategy aclAuthorizationStrategy; + private AuditLogger auditLogger; private List aces = new Vector(); - private List deletedAces = new Vector(); - private Long id; private ObjectIdentity objectIdentity; + private Serializable id; private Sid owner; // OwnershipAcl private Sid[] loadedSids = null; // includes all SIDs the WHERE clause covered, even if there was no ACE for a SID - private boolean aclDirty = false; // for snapshot detection - private boolean addedAces = false; // for snapshot detection - private boolean entriesInheriting = false; - private boolean updatedAces = false; // for snapshot detection + private boolean entriesInheriting = true; //~ Constructors =================================================================================================== @@ -81,17 +61,19 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { * * @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 GrantedAuthoritys 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 aclAuthorizationStrategy authorization strategy (required) + * @param auditLogger audit logger (required) */ - public AclImpl(ObjectIdentity objectIdentity, Long id, GrantedAuthority[] auths) { + public AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy, + AuditLogger auditLogger) { Assert.notNull(objectIdentity, "Object Identity required"); Assert.notNull(id, "Id required"); + Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required"); + Assert.notNull(auditLogger, "AuditLogger required"); this.objectIdentity = objectIdentity; this.id = id; - this.setAuthorities(auths); + this.aclAuthorizationStrategy = aclAuthorizationStrategy; + this.auditLogger = auditLogger; } /** @@ -100,10 +82,8 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { * * @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 GrantedAuthoritys 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 aclAuthorizationStrategy authorization strategy (required) + * @param auditLogger audit logger (required) * @param parentAcl the parent (may be null) * @param loadedSids the loaded SIDs if only a subset were loaded (may be * null) @@ -111,14 +91,17 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { * this ACL * @param owner the owner (required) */ - public AclImpl(ObjectIdentity objectIdentity, Long id, Acl parentAcl, GrantedAuthority[] auths, Sid[] loadedSids, - boolean entriesInheriting, Sid owner) { + public AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy, + AuditLogger auditLogger, Acl parentAcl, Sid[] loadedSids, boolean entriesInheriting, Sid owner) { Assert.notNull(objectIdentity, "Object Identity required"); Assert.notNull(id, "Id required"); + Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required"); Assert.notNull(owner, "Owner required"); + Assert.notNull(auditLogger, "AuditLogger required"); this.objectIdentity = objectIdentity; this.id = id; - setAuthorities(auths); + this.aclAuthorizationStrategy = aclAuthorizationStrategy; + this.auditLogger = auditLogger; this.parentAcl = parentAcl; // may be null this.loadedSids = loadedSids; // may be null this.entriesInheriting = entriesInheriting; @@ -133,17 +116,8 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { //~ Methods ======================================================================================================== - /** - * Clears the dirty flags on the Acl, but not any associated ACEs. - */ - public void clearDirtyFlags() { - this.aclDirty = false; - this.addedAces = false; - this.updatedAces = false; - } - - public void deleteAce(Long aceId) throws NotFoundException { - securityCheck(CHANGE_GENERAL); + public void deleteAce(Serializable aceId) throws NotFoundException { + aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL); synchronized (aces) { int offset = findAceOffset(aceId); @@ -152,12 +126,11 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { throw new NotFoundException("Requested ACE ID not found"); } - aces.remove(offset); - deletedAces.add(aceId); + this.aces.remove(offset); } } - private int findAceOffset(Long aceId) { + private int findAceOffset(Serializable aceId) { Assert.notNull(aceId, "ACE ID is required"); synchronized (aces) { @@ -174,7 +147,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { } public AccessControlEntry[] getEntries() { - // Can safely return AccessControlEntry directly, as they're immutable + // Can safely return AccessControlEntry directly, as they're immutable outside the ACL package return (AccessControlEntry[]) aces.toArray(new AccessControlEntry[] {}); } @@ -194,9 +167,9 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { return parentAcl; } - public void insertAce(Long afterAceId, Permission permission, Sid sid, boolean granting) + public void insertAce(Serializable afterAceId, Permission permission, Sid sid, boolean granting) throws NotFoundException { - securityCheck(CHANGE_GENERAL); + aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL); Assert.notNull(permission, "Permission required"); Assert.notNull(sid, "Sid required"); @@ -210,17 +183,11 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { throw new NotFoundException("Requested ACE ID not found"); } - aces.add(offset + 1, ace); + this.aces.add(offset + 1, ace); } else { - aces.add(ace); + this.aces.add(ace); } } - - this.addedAces = true; - } - - public boolean isAclDirty() { - return aclDirty; } public boolean isEntriesInheriting() { @@ -231,18 +198,19 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { * Determines authorization. The order of the permission and sid arguments is * extremely important! The method will iterate through each of the permissions in the order * specified. For each iteration, all of the sids will be considered, again in the order they are - * presented. The iteration of each permission:sid combination will then inspect the ACEs in the - * order they appear in the ACL. When the first full match is found (ie an ACE that has the SID currently - * being searched for and the exact permission bit mask being search for), the grant or deny flag for that ACE - * will prevail. If the ACE specifies to grant access, the method will return true. If the ACE - * specifies to deny access, the loop will stop and the next permission iteration will be performed. - * If each permission indicates to deny access, the first deny ACE found will be considered the reason for the - * failure (as it was the first match found, and is therefore the one most logically requiring changes - although - * not always). If absolutely no matching ACE was found at all for any permission, the parent ACL will be tried - * (provided that there is a parent and {@link #isEntriesInheriting()} is true. The parent ACL will - * also scan its parent and so on. If ultimately no matching ACE is found, a NotFoundException will - * be thrown and the caller will need to decide how to handle the permission check. Similarly, if any of the - * passed SIDs were not loaded by the ACL, the UnloadedSidException will be thrown. + * presented. A search will then be performed for the first {@link AccessControlEntry} object that directly + * matches that permission:sid combination. When the first full match is found (ie an ACE + * that has the SID currently being searched for and the exact permission bit mask being search for), the grant or + * deny flag for that ACE will prevail. If the ACE specifies to grant access, the method will return + * true. If the ACE specifies to deny access, the loop will stop and the next permission + * iteration will be performed. If each permission indicates to deny access, the first deny ACE found will be + * considered the reason for the failure (as it was the first match found, and is therefore the one most logically + * requiring changes - although not always). If absolutely no matching ACE was found at all for any permission, + * the parent ACL will be tried (provided that there is a parent and {@link #isEntriesInheriting()} is + * true. The parent ACL will also scan its parent and so on. If ultimately no matching ACE is found, + * a NotFoundException will be thrown and the caller will need to decide how to handle the permission + * check. Similarly, if any of the SID arguments presented to the method were not loaded by the ACL, + * UnloadedSidException will be thrown. * * @param permission the exact permissions to scan for (order is important) * @param sids the exact SIDs to scan for (order is important) @@ -335,7 +303,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { return true; } - // This ACL applies to a SID subset. Iterate to check it applies + // This ACL applies to a SID subset only. Iterate to check it applies. for (int i = 0; i < sids.length; i++) { boolean found = false; @@ -356,83 +324,22 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { return true; } - protected void securityCheck(int changeType) { - if ((SecurityContextHolder.getContext() == null) - || (SecurityContextHolder.getContext().getAuthentication() == null) - || !SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) { - throw new AccessDeniedException("Authenticated principal required to operate with ACLs"); - } - - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - // Check if authorized by virtue of ACL ownership - Sid currentUser = new PrincipalSid(authentication); - - if (currentUser.equals(this.owner) && ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) { - return; - } - - // Not authorized by ACL ownership; try via adminstrative permissions - GrantedAuthority requiredAuthority = null; - - if (changeType == CHANGE_AUDITING) { - requiredAuthority = this.gaModifyAuditing; - } else if (changeType == CHANGE_GENERAL) { - requiredAuthority = this.gaGeneralChanges; - } else if (changeType == CHANGE_OWNERSHIP) { - requiredAuthority = this.gaTakeOwnership; - } else { - throw new IllegalArgumentException("Unknown change type"); - } - - // Iterate this principal's authorities to determine right - GrantedAuthority[] auths = authentication.getAuthorities(); - - for (int i = 0; i < auths.length; i++) { - if (requiredAuthority.equals(auths[i])) { - return; - } - } - - throw new AccessDeniedException( - "Principal does not have required ACL permissions to perform requested operation"); - } - - /** - * Change the special adminstrative permissions honoured by this object.

Normally a principal must be the - * owner of the ACL in order to make most changes. The authorities passed to this method provide a way for - * non-owners to modify the ACL (and indeed modify audit parameters, which are not available to ACL owners).

- * - * @param auths an array of GrantedAuthoritys that have administrative permissions (index 0 is the - * authority needed to change ownership, index 1 is the authority needed to modify auditing details, index - * 2 is the authority needed to change other ACL and ACE details) - */ - private void setAuthorities(GrantedAuthority[] auths) { - Assert.notEmpty(auths, "GrantedAuthority[] with three elements required"); - Assert.isTrue(auths.length == 3, "GrantedAuthority[] with three elements required"); - this.gaTakeOwnership = auths[0]; - this.gaModifyAuditing = auths[1]; - this.gaGeneralChanges = auths[2]; - } - public void setEntriesInheriting(boolean entriesInheriting) { - securityCheck(CHANGE_GENERAL); + aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL); this.entriesInheriting = entriesInheriting; - this.aclDirty = true; } public void setOwner(Sid newOwner) { - securityCheck(CHANGE_OWNERSHIP); + aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_OWNERSHIP); Assert.notNull(newOwner, "Owner required"); this.owner = newOwner; - this.aclDirty = true; } public void setParent(MutableAcl newParent) { - securityCheck(CHANGE_GENERAL); + aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL); Assert.notNull(newParent, "New Parent required"); + Assert.isTrue(!newParent.equals(this), "Cannot be the parent of yourself"); this.parentAcl = newParent; - this.aclDirty = true; } public String toString() { @@ -455,6 +362,10 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { sb.append(iterator.next().toString()).append("\r\n"); } + if (count == 0) { + sb.append("no ACEs; "); + } + sb.append("inheriting: ").append(this.entriesInheriting).append("; "); sb.append("parent: ").append((this.parentAcl == null) ? "Null" : this.parentAcl.getObjectIdentity().toString()); sb.append("]"); @@ -462,9 +373,9 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { return sb.toString(); } - public void updateAce(Long aceId, Permission permission) + public void updateAce(Serializable aceId, Permission permission) throws NotFoundException { - securityCheck(CHANGE_GENERAL); + aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL); synchronized (aces) { int offset = findAceOffset(aceId); @@ -476,12 +387,10 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(offset); ace.setPermission(permission); } - - this.updatedAces = true; } - public void updateAuditing(Long aceId, boolean auditSuccess, boolean auditFailure) { - securityCheck(CHANGE_AUDITING); + public void updateAuditing(Serializable aceId, boolean auditSuccess, boolean auditFailure) { + aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_AUDITING); synchronized (aces) { int offset = findAceOffset(aceId); @@ -494,7 +403,5 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl { ace.setAuditSuccess(auditSuccess); ace.setAuditFailure(auditFailure); } - - this.updatedAces = true; } } diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/domain/AuditLogger.java b/core/src/main/java/org/acegisecurity/acls/domain/AuditLogger.java similarity index 96% rename from sandbox/other/src/main/java/org/acegisecurity/acls/domain/AuditLogger.java rename to core/src/main/java/org/acegisecurity/acls/domain/AuditLogger.java index d618761c7e..9bf11e9276 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/domain/AuditLogger.java +++ b/core/src/main/java/org/acegisecurity/acls/domain/AuditLogger.java @@ -1,32 +1,31 @@ -/* 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.AccessControlEntry; - - +/* 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.AccessControlEntry; + + /** * Used by AclImpl to log audit events. * * @author Ben Alex * @version $Id$ * - */ -public interface AuditLogger { - //~ Methods ======================================================================================================== - - public void logIfNeeded(boolean granted, AccessControlEntry ace); -} + */ +public interface AuditLogger { + //~ Methods ======================================================================================================== + + public void logIfNeeded(boolean granted, AccessControlEntry ace); +} diff --git a/core/src/main/java/org/acegisecurity/acls/domain/BasePermission.java b/core/src/main/java/org/acegisecurity/acls/domain/BasePermission.java new file mode 100644 index 0000000000..58e4d3ba9a --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/domain/BasePermission.java @@ -0,0 +1,164 @@ +/* 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; + +import org.springframework.util.Assert; + +import java.lang.reflect.Field; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Vector; + + +/** + * A set of standard permissions. + * + * @author Ben Alex + * @version $Id$ + */ +public class BasePermission implements Permission { + //~ Static fields/initializers ===================================================================================== + + public static final Permission READ = new BasePermission(1 << 0, 'R'); // 1 + public static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2 + public static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4 + public static final Permission DELETE = new BasePermission(1 << 3, 'D'); // 8 + public static final Permission ADMINISTRATION = new BasePermission(1 << 4, 'A'); // 16 + private static Map locallyDeclaredPermissionsByInteger = new HashMap(); + private static Map locallyDeclaredPermissionsByName = new HashMap(); + + static { + Field[] fields = BasePermission.class.getDeclaredFields(); + + for (int i = 0; i < fields.length; i++) { + try { + Object fieldValue = fields[i].get(null); + + if (BasePermission.class.isAssignableFrom(fieldValue.getClass())) { + // Found a BasePermission static field + BasePermission perm = (BasePermission) fieldValue; + locallyDeclaredPermissionsByInteger.put(new Integer(perm.getMask()), perm); + locallyDeclaredPermissionsByName.put(fields[i].getName(), perm); + } + } catch (Exception ignore) {} + } + } + + //~ Instance fields ================================================================================================ + + private char code; + private int mask; + + //~ Constructors =================================================================================================== + + private BasePermission(int mask, char code) { + this.mask = mask; + this.code = code; + } + + //~ Methods ======================================================================================================== + + /** + * Dynamically creates a CumulativePermission or BasePermission representing the + * active bits in the passed mask. + * + * @param mask to build + * + * @return a Permission representing the requested object + */ + public static Permission buildFromMask(int mask) { + if (locallyDeclaredPermissionsByInteger.containsKey(new Integer(mask))) { + // The requested mask has an exactly match against a statically-defined BasePermission, so return it + return (Permission) locallyDeclaredPermissionsByInteger.get(new Integer(mask)); + } + + // To get this far, we have to use a CumulativePermission + CumulativePermission permission = new CumulativePermission(); + + for (int i = 0; i < 32; i++) { + int permissionToCheck = 1 << i; + + if ((mask & permissionToCheck) == permissionToCheck) { + Permission p = (Permission) locallyDeclaredPermissionsByInteger.get(new Integer(permissionToCheck)); + Assert.state(p != null, + "Mask " + permissionToCheck + " does not have a corresponding static BasePermission"); + permission.set(p); + } + } + + return permission; + } + + public static Permission[] buildFromMask(int[] masks) { + if ((masks == null) || (masks.length == 0)) { + return new Permission[] {}; + } + + List list = new Vector(); + + for (int i = 0; i < masks.length; i++) { + list.add(BasePermission.buildFromMask(masks[i])); + } + + return (Permission[]) list.toArray(new Permission[] {}); + } + + public static Permission buildFromName(String name) { + Assert.isTrue(locallyDeclaredPermissionsByName.containsKey(name), "Unknown permission '" + name + "'"); + + return (Permission) locallyDeclaredPermissionsByName.get(name); + } + + public static Permission[] buildFromName(String[] names) { + if ((names == null) || (names.length == 0)) { + return new Permission[] {}; + } + + List list = new Vector(); + + for (int i = 0; i < names.length; i++) { + list.add(BasePermission.buildFromName(names[i])); + } + + return (Permission[]) list.toArray(new Permission[] {}); + } + + public boolean equals(Object arg0) { + if (!(arg0 instanceof BasePermission)) { + return false; + } + + BasePermission rhs = (BasePermission) arg0; + + return (this.mask == rhs.getMask()); + } + + public int getMask() { + return mask; + } + + public String getPattern() { + return AclFormattingUtils.printBinary(mask, code); + } + + public String toString() { + return "BasePermission[" + getPattern() + "=" + mask + "]"; + } +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/domain/ConsoleAuditLogger.java b/core/src/main/java/org/acegisecurity/acls/domain/ConsoleAuditLogger.java similarity index 87% rename from sandbox/other/src/main/java/org/acegisecurity/acls/domain/ConsoleAuditLogger.java rename to core/src/main/java/org/acegisecurity/acls/domain/ConsoleAuditLogger.java index a0f2ff230f..da867b0bdf 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/domain/ConsoleAuditLogger.java +++ b/core/src/main/java/org/acegisecurity/acls/domain/ConsoleAuditLogger.java @@ -1,46 +1,45 @@ -/* 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.AccessControlEntry; -import org.acegisecurity.acls.AuditableAccessControlEntry; - -import org.springframework.util.Assert; - - -/** - * DOCUMENT ME! - * - * @author $author$ - * @version $Revision$ - */ -public class ConsoleAuditLogger implements AuditLogger { - //~ Methods ======================================================================================================== - - 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); - } - } - } -} +/* 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.AccessControlEntry; +import org.acegisecurity.acls.AuditableAccessControlEntry; + +import org.springframework.util.Assert; + + +/** + * A bsaic implementation of {@link AuditLogger}. + * + * @author Ben Alex + * @version $Id$ + */ +public class ConsoleAuditLogger implements AuditLogger { + //~ Methods ======================================================================================================== + + 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); + } + } + } +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/domain/CumulativePermission.java b/core/src/main/java/org/acegisecurity/acls/domain/CumulativePermission.java similarity index 96% rename from sandbox/other/src/main/java/org/acegisecurity/acls/domain/CumulativePermission.java rename to core/src/main/java/org/acegisecurity/acls/domain/CumulativePermission.java index 6bf953f855..33b2e5e70e 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/domain/CumulativePermission.java +++ b/core/src/main/java/org/acegisecurity/acls/domain/CumulativePermission.java @@ -1,79 +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.domain; - -import org.acegisecurity.acls.AclFormattingUtils; -import org.acegisecurity.acls.Permission; - - -/** - * Represents a Permission that is constructed at runtime from other permissions.

Methods return - * this, in order to facilitate method chaining.

- * - * @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 CumulativePermission clear() { - this.mask = 0; - this.pattern = THIRTY_TWO_RESERVED_OFF; - - return this; - } - - public boolean equals(Object arg0) { - if (!(arg0 instanceof CumulativePermission)) { - return false; - } - - CumulativePermission rhs = (CumulativePermission) arg0; - - return (this.mask == rhs.getMask()); - } - - 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 + "]"; - } -} +/* 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 Permission that is constructed at runtime from other permissions.

Methods return + * this, in order to facilitate method chaining.

+ * + * @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 CumulativePermission clear() { + this.mask = 0; + this.pattern = THIRTY_TWO_RESERVED_OFF; + + return this; + } + + public boolean equals(Object arg0) { + if (!(arg0 instanceof CumulativePermission)) { + return false; + } + + CumulativePermission rhs = (CumulativePermission) arg0; + + return (this.mask == rhs.getMask()); + } + + 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 + "]"; + } +} diff --git a/core/src/main/java/org/acegisecurity/acls/domain/package.html b/core/src/main/java/org/acegisecurity/acls/domain/package.html new file mode 100644 index 0000000000..9172c16367 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/domain/package.html @@ -0,0 +1,5 @@ + + +Basic implementation of access control lists (ACLs) interfaces. + + diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/AclCache.java b/core/src/main/java/org/acegisecurity/acls/jdbc/AclCache.java similarity index 73% rename from sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/AclCache.java rename to core/src/main/java/org/acegisecurity/acls/jdbc/AclCache.java index 672a8c7820..25743d5c33 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/AclCache.java +++ b/core/src/main/java/org/acegisecurity/acls/jdbc/AclCache.java @@ -1,39 +1,42 @@ -/* 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.domain.AclImpl; -import org.acegisecurity.acls.objectidentity.ObjectIdentity; - - +/* 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.MutableAcl; +import org.acegisecurity.acls.objectidentity.ObjectIdentity; + +import java.io.Serializable; + + /** * A caching layer for {@link JdbcAclService}. * * @author Ben Alex * @version $Id$ * - */ -public interface AclCache { - //~ Methods ======================================================================================================== - - public void evictFromCache(Long pk); - - public AclImpl getFromCache(ObjectIdentity objectIdentity); - - public AclImpl getFromCache(Long pk); - - public void putInCache(AclImpl acl); // should walk tree as well! -} + */ +public interface AclCache { + //~ Methods ======================================================================================================== + + public void evictFromCache(Serializable pk); + + public void evictFromCache(ObjectIdentity objectIdentity); + + public MutableAcl getFromCache(ObjectIdentity objectIdentity); + + public MutableAcl getFromCache(Serializable pk); + + public void putInCache(MutableAcl acl); +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/BasicLookupStrategy.java b/core/src/main/java/org/acegisecurity/acls/jdbc/BasicLookupStrategy.java similarity index 78% rename from sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/BasicLookupStrategy.java rename to core/src/main/java/org/acegisecurity/acls/jdbc/BasicLookupStrategy.java index b132dddbee..e62263b0a2 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/BasicLookupStrategy.java +++ b/core/src/main/java/org/acegisecurity/acls/jdbc/BasicLookupStrategy.java @@ -1,521 +1,523 @@ -/* 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.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; - -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; - - -/** - * Performs lookups in a manner that is compatible with ANSI SQL.

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 LookupStrategy. This class does not support - * subclassing, as it is likely to change in future releases and therefore subclassing is unsupported.

- * - * @author Ben Alex - * @version $Id$ - */ -public final class BasicLookupStrategy implements LookupStrategy { - //~ Instance fields ================================================================================================ - - private AclCache aclCache; - private JdbcTemplate jdbcTemplate; - private GrantedAuthority[] auths; - private int batchSize = 50; - - //~ Constructors =================================================================================================== - +/* 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.AccessControlEntry; +import org.acegisecurity.acls.Acl; +import org.acegisecurity.acls.MutableAcl; +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.AclAuthorizationStrategy; +import org.acegisecurity.acls.domain.AclImpl; +import org.acegisecurity.acls.domain.AuditLogger; +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.acegisecurity.util.FieldUtils; + +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; + +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; + + +/** + * Performs lookups in a manner that is compatible with ANSI SQL.

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 LookupStrategy. This class does not support + * subclassing, as it is likely to change in future releases and therefore subclassing is unsupported.

+ * + * @author Ben Alex + * @version $Id$ + */ +public final class BasicLookupStrategy implements LookupStrategy { + //~ Instance fields ================================================================================================ + + private AclAuthorizationStrategy aclAuthorizationStrategy; + private AclCache aclCache; + private AuditLogger auditLogger; + private JdbcTemplate jdbcTemplate; + private int batchSize = 50; + + //~ 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 ======================================================================================================== - - 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(); - } - - /** - * The final phase of converting the Map of AclImpl instances which contain - * StubAclParents into proper, valid AclImpls with correct ACL parents. - * - * @param inputMap the unconverted AclImpls - * @param currentIdentity the currentAcl that we wish to convert (this may be - * - * @return - * - * @throws IllegalStateException DOCUMENT ME! - */ - 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; - } - - /** - * Accepts the current ResultSet row, and converts it into an AclImpl that - * contains a StubAclParent - * - * @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 - * @throws IllegalStateException DOCUMENT ME! - */ - 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: cause[" + ex.getMessage() + "]"); - - } - - // Add the ACE if it doesn't already exist in the ACL.aces field - if (!aces.contains(ace)) { - aces.add(ace); - } - } - - 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) { - field = getAccessibleField(clazz.getSuperclass(), protectedField); - } else { - throw new IllegalArgumentException("Couldn't find '" + protectedField + "' field"); - } - } - - // We have a field, so process - field.setAccessible(true); - - return field; - } - - /** - * Looks up a batch of ObjectIdentitys directly from the database.

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.

- *

This subclass is required to return fully valid Acls, including properly-configured - * parent ACLs.

- * - * @param objectIdentities DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - 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)); - } - - /** - * The main method.

WARNING: This implementation completely disregards the "sids" argument! Every item in - * the cache is expected to contain all SIDs.

- *

The implementation works in batch sizes specfied by {@link #batchSize}.

- * - * @param objects DOCUMENT ME! - * @param sids DOCUMENT ME! - * - * @return DOCUMENT ME! - * - * @throws NotFoundException DOCUMENT ME! - * @throws IllegalStateException DOCUMENT ME! - */ - 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; - } - - public void setBatchSize(int batchSize) { - this.batchSize = batchSize; - } - - //~ Inner Classes ================================================================================================== - - 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; - } - } - - 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"); - } - } -} + * @param aclAuthorizationStrategy authorization strategy (required) + */ + public BasicLookupStrategy(DataSource dataSource, AclCache aclCache, + AclAuthorizationStrategy aclAuthorizationStrategy, AuditLogger auditLogger) { + Assert.notNull(dataSource, "DataSource required"); + Assert.notNull(aclCache, "AclCache required"); + Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required"); + Assert.notNull(auditLogger, "AuditLogger required"); + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.aclCache = aclCache; + this.aclAuthorizationStrategy = aclAuthorizationStrategy; + this.auditLogger = auditLogger; + } + + //~ Methods ======================================================================================================== + + 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, " + + "ACL_SID.PRINCIPAL as ACE_PRINCIPAL, ACL_SID.SID as ACE_SID, " + + "ACLI_SID.PRINCIPAL as ACL_PRINCIPAL, ACLI_SID.SID as ACL_SID, " + "ACL_CLASS.CLASS " + + "from ACL_OBJECT_IDENTITY, ACL_SID ACLI_SID, ACL_CLASS " + + "LEFT JOIN ACL_ENTRY ON ACL_OBJECT_IDENTITY.ID = ACL_ENTRY.ACL_OBJECT_IDENTITY " + + "LEFT JOIN ACL_SID ON ACL_ENTRY.SID = ACL_SID.ID where ACLI_SID.ID = ACL_OBJECT_IDENTITY.OWNER_SID " + + "and ACL_CLASS.ID = ACL_OBJECT_IDENTITY.OBJECT_ID_CLASS " + "and ( "; + + String endSql = ") order by ACL_OBJECT_IDENTITY.OBJECT_ID_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(); + } + + /** + * The final phase of converting the Map of AclImpl instances which contain + * StubAclParents into proper, valid AclImpls with correct ACL parents. + * + * @param inputMap the unconverted AclImpls + * @param currentIdentity the currentAcl that we wish to convert (this may be + * + * @return + * + * @throws IllegalStateException DOCUMENT ME! + */ + 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(), aclAuthorizationStrategy, + auditLogger, parent, null, inputAcl.isEntriesInheriting(), inputAcl.getOwner()); + + // Copy the "aces" from the input to the destination + Field field = FieldUtils.getField(AclImpl.class, "aces"); + + try { + field.setAccessible(true); + field.set(result, field.get(inputAcl)); + } catch (IllegalAccessException ex) { + throw new IllegalStateException("Could not obtain or set AclImpl.ace field"); + } + + return result; + } + + /** + * Accepts the current ResultSet row, and converts it into an AclImpl that + * contains a StubAclParent + * + * @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 + * @throws IllegalStateException DOCUMENT ME! + */ + 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, aclAuthorizationStrategy, auditLogger, parentAcl, null, + entriesInheriting, owner); + acls.put(id, acl); + } + + // Add an extra ACE to the ACL (ORDER BY maintains the ACE list order) + // It is permissable to have no ACEs in an ACL (which is detected by a null ACE_SID) + if (rs.getString("ACE_SID") != null) { + 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 = FieldUtils.getField(AclImpl.class, "aces"); + List aces; + + try { + acesField.setAccessible(true); + aces = (List) acesField.get(acl); + } catch (IllegalAccessException ex) { + throw new IllegalStateException("Could not obtain AclImpl.ace field: cause[" + ex.getMessage() + "]"); + } + + // Add the ACE if it doesn't already exist in the ACL.aces field + if (!aces.contains(ace)) { + aces.add(ace); + } + } + } + + /** + * Looks up a batch of ObjectIdentitys directly from the database.

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.

+ *

This subclass is required to return fully valid Acls, including properly-configured + * parent ACLs.

+ * + * @param objectIdentities DOCUMENT ME! + * @param sids DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + private Map lookupObjectIdentities(final ObjectIdentity[] objectIdentities, Sid[] sids) { + 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); + + 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, sids)); + + // 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 + * @param sids DOCUMENT ME! + */ + private void lookupPrimaryKeys(final Map acls, final Set findNow, final Sid[] sids) { + Assert.notNull(acls, "ACLs are required"); + Assert.notEmpty(findNow, "Items to find now required"); + + String sql = computeRepeatingSql("(ACL_OBJECT_IDENTITY.ID = ?)", 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, sids)); + } + + /** + * The main method.

WARNING: This implementation completely disregards the "sids" parameter! Every item + * in the cache is expected to contain all SIDs. If you have serious performance needs (eg a very large number of + * SIDs per object identity), you'll probably want to develop a custom {@link LookupStrategy} implementation + * instead.

+ *

The implementation works in batch sizes specfied by {@link #batchSize}.

+ * + * @param objects DOCUMENT ME! + * @param sids DOCUMENT ME! + * + * @return DOCUMENT ME! + * + * @throws NotFoundException DOCUMENT ME! + * @throws IllegalStateException DOCUMENT ME! + */ + 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 + 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[] {}), sids); + + // 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(); + } + } + + // Now we're done, check every requested object identity was found (throw NotFoundException if needed) + for (int i = 0; i < objects.length; i++) { + if (!result.containsKey(objects[i])) { + throw new NotFoundException("Unable to find ACL information for object identity '" + + objects[i].toString() + "'"); + } + } + + return result; + } + + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + } + + //~ Inner Classes ================================================================================================== + + private class ProcessResultSet implements ResultSetExtractor { + private Map acls; + private Sid[] sids; + + public ProcessResultSet(Map acls, Sid[] sids) { + Assert.notNull(acls, "ACLs cannot be null"); + this.acls = acls; + this.sids = sids; // can be null + } + + 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 it's already in the "acls" + if (acls.containsKey(new Long(parentId))) { + continue; // skip this while iteration + } + + // Now try to find it in the cache + MutableAcl cached = aclCache.getFromCache(new Long(parentId)); + + if ((cached == null) || !cached.isSidLoaded(sids)) { + parentIdsToLookup.add(new Long(parentId)); + } else { + // Pop into the acls map, so our convert method doesn't + // need to deal with an unsynchronized AclCache + acls.put(cached.getId(), cached); + } + } + } + + // Lookup parents, adding Acls (with StubAclParents) to "acl" map + if (parentIdsToLookup.size() > 0) { + lookupPrimaryKeys(acls, parentIdsToLookup, sids); + } + + // Return null to meet ResultSetExtractor method contract + return null; + } + } + + 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 Sid getOwner() { + 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"); + } + } +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/EhCacheBasedAclCache.java b/core/src/main/java/org/acegisecurity/acls/jdbc/EhCacheBasedAclCache.java similarity index 59% rename from sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/EhCacheBasedAclCache.java rename to core/src/main/java/org/acegisecurity/acls/jdbc/EhCacheBasedAclCache.java index 656668a6ec..ab0bf35e50 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/EhCacheBasedAclCache.java +++ b/core/src/main/java/org/acegisecurity/acls/jdbc/EhCacheBasedAclCache.java @@ -1,93 +1,115 @@ -/* 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 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; - - -/** - * DOCUMENT ME! - * - * @author $author$ - * @version $Revision$ - */ -public class EhCacheBasedAclCache implements AclCache { - //~ Instance fields ================================================================================================ - - private Cache cache; - - //~ Constructors =================================================================================================== - - public EhCacheBasedAclCache(Cache cache) { - Assert.notNull(cache, "Cache required"); - this.cache = cache; - } - - //~ Methods ======================================================================================================== - - public void evictFromCache(Long pk) { - AclImpl acl = getFromCache(pk); - - if (acl != null) { - cache.remove(pk); - cache.remove(acl.getObjectIdentity()); - } - } - - 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)); - } -} +/* 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 net.sf.ehcache.Cache; +import net.sf.ehcache.CacheException; +import net.sf.ehcache.Element; + +import org.acegisecurity.acls.MutableAcl; +import org.acegisecurity.acls.objectidentity.ObjectIdentity; + +import org.springframework.util.Assert; + +import java.io.Serializable; + + +/** + * Simple implementation of {@link AclCache} that delegates to EH-CACHE. + * + * @author Ben Alex + * @version $Id$ + */ +public class EhCacheBasedAclCache implements AclCache { + //~ Instance fields ================================================================================================ + + private Cache cache; + + //~ Constructors =================================================================================================== + + public EhCacheBasedAclCache(Cache cache) { + Assert.notNull(cache, "Cache required"); + this.cache = cache; + } + + //~ Methods ======================================================================================================== + + public void evictFromCache(Serializable pk) { + Assert.notNull(pk, "Primary key (identifier) required"); + + MutableAcl acl = getFromCache(pk); + + if (acl != null) { + cache.remove(acl.getId()); + cache.remove(acl.getObjectIdentity()); + } + } + + public void evictFromCache(ObjectIdentity objectIdentity) { + Assert.notNull(objectIdentity, "ObjectIdentity required"); + + MutableAcl acl = getFromCache(objectIdentity); + + if (acl != null) { + cache.remove(acl.getId()); + cache.remove(acl.getObjectIdentity()); + } + } + + public MutableAcl getFromCache(ObjectIdentity objectIdentity) { + Assert.notNull(objectIdentity, "ObjectIdentity required"); + + Element element = null; + + try { + element = cache.get(objectIdentity); + } catch (CacheException ignored) {} + + if (element == null) { + return null; + } + + return (MutableAcl) element.getValue(); + } + + public MutableAcl getFromCache(Serializable pk) { + Assert.notNull(pk, "Primary key (identifier) required"); + + Element element = null; + + try { + element = cache.get(pk); + } catch (CacheException ignored) {} + + if (element == null) { + return null; + } + + return (MutableAcl) element.getValue(); + } + + public void putInCache(MutableAcl acl) { + Assert.notNull(acl, "Acl required"); + Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required"); + Assert.notNull(acl.getId(), "ID required"); + + if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) { + putInCache((MutableAcl) acl.getParentAcl()); + } + + cache.put(new Element(acl.getObjectIdentity(), acl)); + cache.put(new Element(acl.getId(), acl)); + } +} diff --git a/core/src/main/java/org/acegisecurity/acls/jdbc/JdbcAclService.java b/core/src/main/java/org/acegisecurity/acls/jdbc/JdbcAclService.java new file mode 100644 index 0000000000..cac18a5c13 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/jdbc/JdbcAclService.java @@ -0,0 +1,114 @@ +/* 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.Acl; +import org.acegisecurity.acls.AclService; +import org.acegisecurity.acls.NotFoundException; +import org.acegisecurity.acls.objectidentity.ObjectIdentity; +import org.acegisecurity.acls.objectidentity.ObjectIdentityImpl; +import org.acegisecurity.acls.sid.Sid; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; + +import org.springframework.util.Assert; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import java.util.List; +import java.util.Map; + +import javax.sql.DataSource; + + +/** + * Simple JDBC-based implementation of AclService.

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 { + //~ Static fields/initializers ===================================================================================== + + protected static final Log log = LogFactory.getLog(JdbcAclService.class); + private static final String selectAclObjectWithParent = "SELECT obj.object_id_identity obj_id, class.class class " + + "FROM acl_object_identity obj, acl_object_identity parent, acl_class class " + + "WHERE obj.parent_object = parent.id AND obj.object_id_class = class.id " + + "AND parent.object_id_identity = ? AND parent.object_id_class = (" + + "SELECT id FROM acl_class WHERE acl_class.class = ?)"; + + //~ Instance fields ================================================================================================ + + protected JdbcTemplate jdbcTemplate; + private LookupStrategy lookupStrategy; + + //~ Constructors =================================================================================================== + + public JdbcAclService(DataSource dataSource, LookupStrategy lookupStrategy) { + Assert.notNull(dataSource, "DataSource required"); + Assert.notNull(lookupStrategy, "LookupStrategy required"); + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.lookupStrategy = lookupStrategy; + } + + //~ Methods ======================================================================================================== + + public ObjectIdentity[] findChildren(ObjectIdentity parentIdentity) { + Object[] args = {parentIdentity.getIdentifier(), parentIdentity.getJavaType().getName()}; + List objects = jdbcTemplate.query(selectAclObjectWithParent, args, + new RowMapper() { + public Object mapRow(ResultSet rs, int rowNum) + throws SQLException { + String javaType = rs.getString("class"); + String identifier = rs.getString("obj_id"); + + return new ObjectIdentityImpl(javaType, identifier); + } + }); + + return (ObjectIdentityImpl[]) objects.toArray(new ObjectIdentityImpl[] {}); + } + + public Acl readAclById(ObjectIdentity object, Sid[] sids) + throws NotFoundException { + Map map = readAclsById(new ObjectIdentity[] {object}, sids); + + if (map.size() == 0) { + throw new NotFoundException("Could not find ACL"); + } else { + return (Acl) map.get(object); + } + } + + public Acl readAclById(ObjectIdentity object) throws NotFoundException { + return readAclById(object, null); + } + + public Map readAclsById(ObjectIdentity[] objects) { + return readAclsById(objects, null); + } + + public Map readAclsById(ObjectIdentity[] objects, Sid[] sids) + throws NotFoundException { + return lookupStrategy.readAclsById(objects, sids); + } +} diff --git a/core/src/main/java/org/acegisecurity/acls/jdbc/JdbcMutableAclService.java b/core/src/main/java/org/acegisecurity/acls/jdbc/JdbcMutableAclService.java new file mode 100644 index 0000000000..d4f036d7d5 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/jdbc/JdbcMutableAclService.java @@ -0,0 +1,368 @@ +/* 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.Authentication; + +import org.acegisecurity.acls.AccessControlEntry; +import org.acegisecurity.acls.Acl; +import org.acegisecurity.acls.AlreadyExistsException; +import org.acegisecurity.acls.ChildrenExistException; +import org.acegisecurity.acls.MutableAcl; +import org.acegisecurity.acls.MutableAclService; +import org.acegisecurity.acls.NotFoundException; +import org.acegisecurity.acls.domain.AccessControlEntryImpl; +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.acegisecurity.context.SecurityContextHolder; + +import org.springframework.dao.DataAccessException; + +import org.springframework.jdbc.core.BatchPreparedStatementSetter; + +import org.springframework.transaction.support.TransactionSynchronizationManager; + +import org.springframework.util.Assert; + +import java.lang.reflect.Array; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import java.util.List; + +import javax.sql.DataSource; + + +/** + * Provides a base implementation of {@link MutableAclService}. + * + * @author Ben Alex + * @author Johannes Zlattinger + * @version $Id$ + */ +public class JdbcMutableAclService extends JdbcAclService implements MutableAclService { + //~ Instance fields ================================================================================================ + + private AclCache aclCache; + private String deleteClassByClassNameString = "DELETE FROM acl_class WHERE class=?"; + private String deleteEntryByObjectIdentityForeignKey = "DELETE FROM acl_entry WHERE acl_object_identity=?"; + private String deleteObjectIdentityByPrimaryKey = "DELETE FROM acl_object_identity WHERE id=?"; + private String identityQuery = "call identity()"; + private String insertClass = "INSERT INTO acl_class (id, class) VALUES (null, ?)"; + private String insertEntry = "INSERT INTO acl_entry " + + "(id, acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)" + + "VALUES (null, ?, ?, ?, ?, ?, ?, ?)"; + private String insertObjectIdentity = "INSERT INTO acl_object_identity " + + "(id, object_id_class, object_id_identity, owner_sid, entries_inheriting) " + "VALUES (null, ?, ?, ?, ?)"; + private String insertSid = "INSERT INTO acl_sid (id, principal, sid) VALUES (null, ?, ?)"; + private String selectClassPrimaryKey = "SELECT id FROM acl_class WHERE class=?"; + private String selectCountObjectIdentityRowsForParticularClassNameString = "SELECT COUNT(acl_object_identity.id) " + + "FROM acl_object_identity, acl_class WHERE acl_class.id = acl_object_identity.object_id_class and class=?"; + private String selectObjectIdentityPrimaryKey = "SELECT acl_object_identity.id FROM acl_object_identity, acl_class " + + "WHERE acl_object_identity.object_id_class = acl_class.id and acl_class.class=? " + + "and acl_object_identity.object_id_identity = ?"; + private String selectSidPrimaryKey = "SELECT id FROM acl_sid WHERE principal=? AND sid=?"; + private String updateObjectIdentity = "UPDATE acl_object_identity SET " + + "parent_object = ?, owner_sid = ?, entries_inheriting = ?" + "where id = ?"; + + //~ Constructors =================================================================================================== + + public JdbcMutableAclService(DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) { + super(dataSource, lookupStrategy); + Assert.notNull(aclCache, "AclCache required"); + this.aclCache = aclCache; + } + + //~ Methods ======================================================================================================== + + public MutableAcl createAcl(ObjectIdentity objectIdentity) + throws AlreadyExistsException { + Assert.notNull(objectIdentity, "Object Identity required"); + + // Check this object identity hasn't already been persisted + if (retrieveObjectIdentityPrimaryKey(objectIdentity) != null) { + throw new AlreadyExistsException("Object identity '" + objectIdentity + "' already exists"); + } + + // Need to retrieve the current principal, in order to know who "owns" this ACL (can be changed later on) + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + PrincipalSid sid = new PrincipalSid(auth); + + // Create the acl_object_identity row + createObjectIdentity(objectIdentity, sid); + + // Retrieve the ACL via superclass (ensures cache registration, proper retrieval etc) + Acl acl = readAclById(objectIdentity); + Assert.isInstanceOf(MutableAcl.class, acl, "MutableAcl should be been returned"); + + return (MutableAcl) acl; + } + + /** + * Creates a new row in acl_entry for every ACE defined in the passed MutableAcl object. + * + * @param acl containing the ACEs to insert + */ + protected void createEntries(final MutableAcl acl) { + jdbcTemplate.batchUpdate(insertEntry, + new BatchPreparedStatementSetter() { + public int getBatchSize() { + return acl.getEntries().length; + } + + public void setValues(PreparedStatement stmt, int i) + throws SQLException { + AccessControlEntry entry_ = (AccessControlEntry) Array.get(acl.getEntries(), i); + Assert.isTrue(entry_ instanceof AccessControlEntryImpl, "Unknown ACE class"); + + AccessControlEntryImpl entry = (AccessControlEntryImpl) entry_; + + stmt.setLong(1, ((Long) acl.getId()).longValue()); + stmt.setInt(2, i); + stmt.setLong(3, createOrRetrieveSidPrimaryKey(entry.getSid(), true).longValue()); + stmt.setInt(4, entry.getPermission().getMask()); + stmt.setBoolean(5, entry.isGranting()); + stmt.setBoolean(6, entry.isAuditSuccess()); + stmt.setBoolean(7, entry.isAuditFailure()); + } + }); + } + + /** + * Creates an entry in the acl_object_identity table for the passed ObjectIdentity. The Sid is also + * necessary, as acl_object_identity has defined the sid column as non-null. + * + * @param object to represent an acl_object_identity for + * @param owner for the SID column (will be created if there is no acl_sid entry for this particular Sid already) + */ + protected void createObjectIdentity(ObjectIdentity object, Sid owner) { + Long sidId = createOrRetrieveSidPrimaryKey(owner, true); + Long classId = createOrRetrieveClassPrimaryKey(object.getJavaType(), true); + jdbcTemplate.update(insertObjectIdentity, + new Object[] {classId, object.getIdentifier().toString(), sidId, new Boolean(true)}); + } + + /** + * Retrieves the primary key from acl_class, creating a new row if needed and the allowCreate property is + * true. + * + * @param clazz to find or create an entry for (this implementation uses the fully-qualified class name String) + * @param allowCreate true if creation is permitted if not found + * + * @return the primary key or null if not found + */ + protected Long createOrRetrieveClassPrimaryKey(Class clazz, boolean allowCreate) { + List classIds = jdbcTemplate.queryForList(selectClassPrimaryKey, new Object[] {clazz.getName()}, Long.class); + Long classId = null; + + if (classIds.isEmpty()) { + if (allowCreate) { + classId = null; + jdbcTemplate.update(insertClass, new Object[] {clazz.getName()}); + Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), "Transaction must be running"); + classId = new Long(jdbcTemplate.queryForLong(identityQuery)); + } + } else { + classId = (Long) classIds.iterator().next(); + } + + return classId; + } + + /** + * Retrieves the primary key from acl_sid, creating a new row if needed and the allowCreate property is + * true. + * + * @param sid to find or create + * @param allowCreate true if creation is permitted if not found + * + * @return the primary key or null if not found + * + * @throws IllegalArgumentException DOCUMENT ME! + */ + protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) { + Assert.notNull(sid, "Sid required"); + + String sidName = null; + boolean principal = true; + + if (sid instanceof PrincipalSid) { + sidName = ((PrincipalSid) sid).getPrincipal(); + } else if (sid instanceof GrantedAuthoritySid) { + sidName = ((GrantedAuthoritySid) sid).getGrantedAuthority(); + principal = false; + } else { + throw new IllegalArgumentException("Unsupported implementation of Sid"); + } + + List sidIds = jdbcTemplate.queryForList(selectSidPrimaryKey, new Object[] {new Boolean(principal), sidName}, + Long.class); + Long sidId = null; + + if (sidIds.isEmpty()) { + if (allowCreate) { + sidId = null; + jdbcTemplate.update(insertSid, new Object[] {new Boolean(principal), sidName}); + Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), "Transaction must be running"); + sidId = new Long(jdbcTemplate.queryForLong(identityQuery)); + } + } else { + sidId = (Long) sidIds.iterator().next(); + } + + return sidId; + } + + public void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren) + throws ChildrenExistException { + Assert.notNull(objectIdentity, "Object Identity required"); + Assert.notNull(objectIdentity.getIdentifier(), "Object Identity doesn't provide an identifier"); + + // Recursively call this method for children, or handle children if they don't want automatic recursion + ObjectIdentity[] children = findChildren(objectIdentity); + + if (deleteChildren) { + for (int i = 0; i < children.length; i++) { + deleteAcl(children[i], true); + } + } else if (children.length > 0) { + throw new ChildrenExistException("Cannot delete '" + objectIdentity + "' (has " + children.length + + " children)"); + } + + // Delete this ACL's ACEs in the acl_entry table + deleteEntries(objectIdentity); + + // Delete this ACL's acl_object_identity row + deleteObjectIdentityAndOptionallyClass(objectIdentity); + + // Clear the cache + aclCache.evictFromCache(objectIdentity); + } + + /** + * Deletes all ACEs defined in the acl_entry table belonging to the presented ObjectIdentity + * + * @param oid the rows in acl_entry to delete + */ + protected void deleteEntries(ObjectIdentity oid) { + jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey, new Object[] {retrieveObjectIdentityPrimaryKey(oid)}); + } + + /** + * Deletes a single row from acl_object_identity that is associated with the presented ObjectIdentity. In + * addition, deletes the corresponding row from acl_class if there are no more entries in acl_object_identity that + * use that particular acl_class. This keeps the acl_class table reasonably small. + * + * @param oid to delete the acl_object_identity (and clean up acl_class for that class name if appropriate) + */ + protected void deleteObjectIdentityAndOptionallyClass(ObjectIdentity oid) { + // Delete the acl_object_identity row + jdbcTemplate.update(deleteObjectIdentityByPrimaryKey, new Object[] {retrieveObjectIdentityPrimaryKey(oid)}); + + // Delete the acl_class row, assuming there are no other references to it in acl_object_identity + Object[] className = {oid.getJavaType().getName()}; + long numObjectIdentities = jdbcTemplate.queryForLong(selectCountObjectIdentityRowsForParticularClassNameString, + className); + + if (numObjectIdentities == 0) { + // No more rows + jdbcTemplate.update(deleteClassByClassNameString, className); + } + } + + /** + * Retrieves the primary key from the acl_object_identity table for the passed ObjectIdentity. Unlike some + * other methods in this implementation, this method will NOT create a row (use {@link + * #createObjectIdentity(ObjectIdentity, Sid)} instead). + * + * @param oid to find + * + * @return the object identity or null if not found + */ + protected Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) { + try { + return new Long(jdbcTemplate.queryForLong(selectObjectIdentityPrimaryKey, + new Object[] {oid.getJavaType().getName(), oid.getIdentifier()})); + } catch (DataAccessException notFound) { + return null; + } + } + + /** + * This implementation will simply delete all ACEs in the database and recreate them on each invocation of + * this method. A more comprehensive implementation might use dirty state checking, or more likely use ORM + * capabilities for create, update and delete operations of {@link MutableAcl}. + * + * @param acl DOCUMENT ME! + * + * @return DOCUMENT ME! + * + * @throws NotFoundException DOCUMENT ME! + */ + public MutableAcl updateAcl(MutableAcl acl) throws NotFoundException { + Assert.notNull(acl.getId(), "Object Identity doesn't provide an identifier"); + + // Delete this ACL's ACEs in the acl_entry table + deleteEntries(acl.getObjectIdentity()); + + // Create this ACL's ACEs in the acl_entry table + createEntries(acl); + + // Change the mutable columns in acl_object_identity + updateObjectIdentity(acl); + + // Clear the cache + aclCache.evictFromCache(acl.getObjectIdentity()); + + // Retrieve the ACL via superclass (ensures cache registration, proper retrieval etc) + return (MutableAcl) super.readAclById(acl.getObjectIdentity()); + } + + /** + * Updates an existing acl_object_identity row, with new information presented in the passed MutableAcl + * object. Also will create an acl_sid entry if needed for the Sid that owns the MutableAcl. + * + * @param acl to modify (a row must already exist in acl_object_identity) + * + * @throws NotFoundException DOCUMENT ME! + */ + protected void updateObjectIdentity(MutableAcl acl) { + Long parentId = null; + + if (acl.getParentAcl() != null) { + Assert.isInstanceOf(ObjectIdentityImpl.class, acl.getParentAcl().getObjectIdentity(), + "Implementation only supports ObjectIdentityImpl"); + + ObjectIdentityImpl oii = (ObjectIdentityImpl) acl.getParentAcl().getObjectIdentity(); + parentId = retrieveObjectIdentityPrimaryKey(oii); + } + + Assert.notNull(acl.getOwner(), "Owner is required in this implementation"); + + Long ownerSid = createOrRetrieveSidPrimaryKey(acl.getOwner(), true); + int count = jdbcTemplate.update(updateObjectIdentity, + new Object[] {parentId, ownerSid, new Boolean(acl.isEntriesInheriting()), acl.getId()}); + + if (count != 1) { + throw new NotFoundException("Unable to locate ACL to update"); + } + } +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/LookupStrategy.java b/core/src/main/java/org/acegisecurity/acls/jdbc/LookupStrategy.java similarity index 97% rename from sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/LookupStrategy.java rename to core/src/main/java/org/acegisecurity/acls/jdbc/LookupStrategy.java index 2ace12b6fc..6bb0cef91c 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/LookupStrategy.java +++ b/core/src/main/java/org/acegisecurity/acls/jdbc/LookupStrategy.java @@ -1,44 +1,43 @@ -/* 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; - - +/* 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 null - implementations may elect not - * to provide SID optimisations) - * - * @return the Map pursuant to the interface contract for {@link - * org.acegisecurity.acls.AclService#readAclsById(ObjectIdentity[], Sid[])} - */ - public Map readAclsById(ObjectIdentity[] objects, Sid[] sids); -} + */ +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 null - implementations may elect not + * to provide SID optimisations) + * + * @return the Map pursuant to the interface contract for {@link + * org.acegisecurity.acls.AclService#readAclsById(ObjectIdentity[], Sid[])} + */ + public Map readAclsById(ObjectIdentity[] objects, Sid[] sids); +} diff --git a/core/src/main/java/org/acegisecurity/acls/jdbc/package.html b/core/src/main/java/org/acegisecurity/acls/jdbc/package.html new file mode 100644 index 0000000000..adbee4c539 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/jdbc/package.html @@ -0,0 +1,5 @@ + + +JDBC-based persistence of ACL information. + + diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentity.java b/core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentity.java similarity index 97% rename from sandbox/other/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentity.java rename to core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentity.java index b92cd44565..c8b347693f 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentity.java +++ b/core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentity.java @@ -1,23 +1,22 @@ -/* 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; - - +/* 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. * @@ -32,40 +31,40 @@ import java.io.Serializable; * * @author Ben Alex * @version $Id$ - */ -public interface ObjectIdentity extends Serializable { - //~ Methods ======================================================================================================== - - /** - * Refer to the java.lang.Object documentation for the interface contract. - * - * @param obj to be compared - * - * @return true if the objects are equal, false otherwise - */ - public boolean equals(Object obj); - - /** - * Obtains the actual identifier. This identifier must not be reused to represent other domain objects with - * the same javaType.

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.

- * - * @return the identifier (unique within this javaType - */ - 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 java.lang.Object documentation for the interface contract. - * - * @return a hash code representation of this object - */ - public int hashCode(); -} + */ +public interface ObjectIdentity extends Serializable { + //~ Methods ======================================================================================================== + + /** + * Refer to the java.lang.Object documentation for the interface contract. + * + * @param obj to be compared + * + * @return true if the objects are equal, false otherwise + */ + public boolean equals(Object obj); + + /** + * Obtains the actual identifier. This identifier must not be reused to represent other domain objects with + * the same javaType.

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.

+ * + * @return the identifier (unique within this javaType + */ + 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 java.lang.Object documentation for the interface contract. + * + * @return a hash code representation of this object + */ + public int hashCode(); +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityImpl.java b/core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityImpl.java similarity index 94% rename from sandbox/other/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityImpl.java rename to core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityImpl.java index 816cbbf473..fe3a309a24 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityImpl.java +++ b/core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityImpl.java @@ -1,146 +1,147 @@ -/* 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; - - +/* 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}.

Uses Strings to store the identity of the + * domain object instance. Also offers a constructor that uses reflection to build the identity information.

+ */ +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; + } + /** - * Simple implementation of {@link AclObjectIdentity}.

Uses Strings to store the identity of the - * domain object instance. Also offers a constructor that uses reflection to build the identity information.

- */ -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 NamedEntityObjectIdentity based on the passed + * Creates the ObjectIdentityImpl based on the passed * object instance. The passed object must provide a getId() - * method, otherwise an exception will be thrown. + * method, otherwise an exception will be thrown. The object passed will + * be considered the {@link #javaType}, so if more control is required, + * an alternate constructor should be used instead. * * @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.

Considers an object of the same class equal if it has the same - * classname and id properties.

- * - * @param arg0 object to compare - * - * @return true 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(); - } -} + */ + 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.

Considers an object of the same class equal if it has the same + * classname and id properties.

+ * + * @param arg0 object to compare + * + * @return true 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(); + } +} diff --git a/core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityRetrievalStrategy.java b/core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityRetrievalStrategy.java new file mode 100644 index 0000000000..7687ab21b1 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityRetrievalStrategy.java @@ -0,0 +1,30 @@ +/* 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; + +/** + * Strategy interface that provides the ability to determine which {@link ObjectIdentity} + * will be returned for a particular domain object + * + * @author Ben Alex + * @version $Id$ + * + */ +public interface ObjectIdentityRetrievalStrategy { + //~ Methods ======================================================================================================== + + public ObjectIdentity getObjectIdentity(Object domainObject); +} diff --git a/core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityRetrievalStrategyImpl.java b/core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityRetrievalStrategyImpl.java new file mode 100644 index 0000000000..436e535192 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/objectidentity/ObjectIdentityRetrievalStrategyImpl.java @@ -0,0 +1,31 @@ +/* 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; + +/** + * Basic implementation of {@link ObjectIdentityRetrievalStrategy} that uses the constructor of {@link + * ObjectIdentityImpl} to create the {@link ObjectIdentity}. + * + * @author Ben Alex + * @version $Id$ + */ +public class ObjectIdentityRetrievalStrategyImpl implements ObjectIdentityRetrievalStrategy { + //~ Methods ======================================================================================================== + + public ObjectIdentity getObjectIdentity(Object domainObject) { + return new ObjectIdentityImpl(domainObject); + } +} diff --git a/core/src/main/java/org/acegisecurity/acls/objectidentity/package.html b/core/src/main/java/org/acegisecurity/acls/objectidentity/package.html new file mode 100644 index 0000000000..8e08176b32 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/objectidentity/package.html @@ -0,0 +1,5 @@ + + +Provides indirection between ACL packages and domain objects. + + diff --git a/core/src/main/java/org/acegisecurity/acls/package.html b/core/src/main/java/org/acegisecurity/acls/package.html new file mode 100644 index 0000000000..e738e3cb94 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/package.html @@ -0,0 +1,5 @@ + + +Interfaces and shared classes to manage access control lists (ACLs) for domain object instances. + + diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/sid/GrantedAuthoritySid.java b/core/src/main/java/org/acegisecurity/acls/sid/GrantedAuthoritySid.java similarity index 97% rename from sandbox/other/src/main/java/org/acegisecurity/acls/sid/GrantedAuthoritySid.java rename to core/src/main/java/org/acegisecurity/acls/sid/GrantedAuthoritySid.java index b298f93f2a..4572805d58 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/sid/GrantedAuthoritySid.java +++ b/core/src/main/java/org/acegisecurity/acls/sid/GrantedAuthoritySid.java @@ -1,68 +1,67 @@ -/* 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 GrantedAuthority as a Sid.

This is a basic implementation that simply - * uses the String-based principal for Sid comparison. More complex principal objects may - * wish to provide an alternative Sid implementation that uses some other identifier.

- * - * @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 + "]"; - } -} +/* 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 GrantedAuthority as a Sid.

This is a basic implementation that simply + * uses the String-based principal for Sid comparison. More complex principal objects may + * wish to provide an alternative Sid implementation that uses some other identifier.

+ * + * @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 + "]"; + } +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/sid/PrincipalSid.java b/core/src/main/java/org/acegisecurity/acls/sid/PrincipalSid.java similarity index 97% rename from sandbox/other/src/main/java/org/acegisecurity/acls/sid/PrincipalSid.java rename to core/src/main/java/org/acegisecurity/acls/sid/PrincipalSid.java index c76d68fe7c..59952aac41 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/sid/PrincipalSid.java +++ b/core/src/main/java/org/acegisecurity/acls/sid/PrincipalSid.java @@ -1,73 +1,72 @@ -/* 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 Authentication.getPrincipal() as a Sid.

This is a basic implementation - * that simply uses the String-based principal for Sid comparison. More complex principal - * objects may wish to provide an alternative Sid implementation that uses some other identifier.

- * - * @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 + "]"; - } -} +/* 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 Authentication.getPrincipal() as a Sid.

This is a basic implementation + * that simply uses the String-based principal for Sid comparison. More complex principal + * objects may wish to provide an alternative Sid implementation that uses some other identifier.

+ * + * @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 + "]"; + } +} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/sid/Sid.java b/core/src/main/java/org/acegisecurity/acls/sid/Sid.java similarity index 97% rename from sandbox/other/src/main/java/org/acegisecurity/acls/sid/Sid.java rename to core/src/main/java/org/acegisecurity/acls/sid/Sid.java index a72289046d..96cbdbe9fe 100644 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/sid/Sid.java +++ b/core/src/main/java/org/acegisecurity/acls/sid/Sid.java @@ -1,20 +1,19 @@ -/* 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; - +/* 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. * @@ -29,23 +28,23 @@ package org.acegisecurity.acls.sid; * * @author Ben Alex * @version $Id$ - */ -public interface Sid { - //~ Methods ======================================================================================================== - - /** - * Refer to the java.lang.Object documentation for the interface contract. - * - * @param obj to be compared - * - * @return true if the objects are equal, false otherwise - */ - public boolean equals(Object obj); - - /** - * Refer to the java.lang.Object documentation for the interface contract. - * - * @return a hash code representation of this object - */ - public int hashCode(); -} + */ +public interface Sid { + //~ Methods ======================================================================================================== + + /** + * Refer to the java.lang.Object documentation for the interface contract. + * + * @param obj to be compared + * + * @return true if the objects are equal, false otherwise + */ + public boolean equals(Object obj); + + /** + * Refer to the java.lang.Object documentation for the interface contract. + * + * @return a hash code representation of this object + */ + public int hashCode(); +} diff --git a/core/src/main/java/org/acegisecurity/acls/sid/SidRetrievalStrategy.java b/core/src/main/java/org/acegisecurity/acls/sid/SidRetrievalStrategy.java new file mode 100644 index 0000000000..209bae7209 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/sid/SidRetrievalStrategy.java @@ -0,0 +1,32 @@ +/* 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; + + +/** + * Strategy interface that provides an ability to determine the {@link Sid} instances applicable + * for an {@link Authentication}. + * + * @author Ben Alex + * @version $Id$ + */ +public interface SidRetrievalStrategy { + //~ Methods ======================================================================================================== + + public Sid[] getSids(Authentication authentication); +} diff --git a/core/src/main/java/org/acegisecurity/acls/sid/SidRetrievalStrategyImpl.java b/core/src/main/java/org/acegisecurity/acls/sid/SidRetrievalStrategyImpl.java new file mode 100644 index 0000000000..7fe5a8b9c6 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/sid/SidRetrievalStrategyImpl.java @@ -0,0 +1,48 @@ +/* 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.GrantedAuthority; + +import java.util.List; +import java.util.Vector; + + +/** + * Basic implementation of {@link SidRetrievalStrategy} that creates a {@link Sid} for the principal, as well as + * every granted authority the principal holds.

The returned array will always contain the {@link PrincipalSid} + * before any {@link GrantedAuthoritySid} elements.

+ * + * @author Ben Alex + * @version $Id$ + */ +public class SidRetrievalStrategyImpl implements SidRetrievalStrategy { + //~ Methods ======================================================================================================== + + public Sid[] getSids(Authentication authentication) { + List list = new Vector(); + list.add(new PrincipalSid(authentication)); + + GrantedAuthority[] authorities = authentication.getAuthorities(); + + for (int i = 0; i < authorities.length; i++) { + list.add(new GrantedAuthoritySid(authorities[i])); + } + + return (Sid[]) list.toArray(new Sid[] {}); + } +} diff --git a/core/src/main/java/org/acegisecurity/acls/sid/package.html b/core/src/main/java/org/acegisecurity/acls/sid/package.html new file mode 100644 index 0000000000..ea25c9430b --- /dev/null +++ b/core/src/main/java/org/acegisecurity/acls/sid/package.html @@ -0,0 +1,5 @@ + + +Provides indirection between ACL packages and security identities, such as principals and GrantedAuthority[]s. + + diff --git a/core/src/main/java/org/acegisecurity/afterinvocation/AbstractAclProvider.java b/core/src/main/java/org/acegisecurity/afterinvocation/AbstractAclProvider.java new file mode 100644 index 0000000000..968af86903 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/afterinvocation/AbstractAclProvider.java @@ -0,0 +1,130 @@ +/* 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.afterinvocation; + +import org.acegisecurity.Authentication; +import org.acegisecurity.ConfigAttribute; + +import org.acegisecurity.acls.Acl; +import org.acegisecurity.acls.AclService; +import org.acegisecurity.acls.NotFoundException; +import org.acegisecurity.acls.Permission; +import org.acegisecurity.acls.domain.BasePermission; +import org.acegisecurity.acls.objectidentity.ObjectIdentity; +import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategy; +import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategyImpl; +import org.acegisecurity.acls.sid.Sid; +import org.acegisecurity.acls.sid.SidRetrievalStrategy; +import org.acegisecurity.acls.sid.SidRetrievalStrategyImpl; + +import org.springframework.util.Assert; + + +/** + * DOCUMENT ME! + * + * @author $author$ + * @version $Revision$ + */ +public abstract class AbstractAclProvider implements AfterInvocationProvider { + //~ Instance fields ================================================================================================ + + private AclService aclService; + private Class processDomainObjectClass = Object.class; + private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl(); + private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl(); + private String processConfigAttribute; + private Permission[] requirePermission = {BasePermission.READ}; + + //~ Constructors =================================================================================================== + + public AbstractAclProvider(AclService aclService, String processConfigAttribute, Permission[] requirePermission) { + Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory"); + Assert.notNull(aclService, "An AclService is mandatory"); + + if ((requirePermission == null) || (requirePermission.length == 0)) { + throw new IllegalArgumentException("One or more requirePermission entries is mandatory"); + } + + this.aclService = aclService; + this.processConfigAttribute = processConfigAttribute; + this.requirePermission = requirePermission; + } + + //~ Methods ======================================================================================================== + + protected Class getProcessDomainObjectClass() { + return processDomainObjectClass; + } + + protected boolean hasPermission(Authentication authentication, Object domainObject) { + // Obtain the OID applicable to the domain object + ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy.getObjectIdentity(domainObject); + + // Obtain the SIDs applicable to the principal + Sid[] sids = sidRetrievalStrategy.getSids(authentication); + + Acl acl = null; + + try { + // Lookup only ACLs for SIDs we're interested in + acl = aclService.readAclById(objectIdentity, sids); + + return acl.isGranted(requirePermission, sids, false); + } catch (NotFoundException ignore) { + return false; + } + } + + public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) { + Assert.notNull(objectIdentityRetrievalStrategy, "ObjectIdentityRetrievalStrategy required"); + this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy; + } + + protected void setProcessConfigAttribute(String processConfigAttribute) { + Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory"); + this.processConfigAttribute = processConfigAttribute; + } + + public void setProcessDomainObjectClass(Class processDomainObjectClass) { + Assert.notNull(processDomainObjectClass, "processDomainObjectClass cannot be set to null"); + this.processDomainObjectClass = processDomainObjectClass; + } + + public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) { + Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required"); + this.sidRetrievalStrategy = sidRetrievalStrategy; + } + + public boolean supports(ConfigAttribute attribute) { + if ((attribute.getAttribute() != null) && attribute.getAttribute().equals(this.processConfigAttribute)) { + return true; + } else { + return false; + } + } + + /** + * This implementation supports any type of class, because it does not query the presented secure object. + * + * @param clazz the secure object + * + * @return always true + */ + public boolean supports(Class clazz) { + return true; + } +} diff --git a/core/src/main/java/org/acegisecurity/afterinvocation/AclEntryAfterInvocationCollectionFilteringProvider.java b/core/src/main/java/org/acegisecurity/afterinvocation/AclEntryAfterInvocationCollectionFilteringProvider.java new file mode 100644 index 0000000000..22312fe534 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/afterinvocation/AclEntryAfterInvocationCollectionFilteringProvider.java @@ -0,0 +1,133 @@ +/* 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.afterinvocation; + +import org.acegisecurity.AccessDeniedException; +import org.acegisecurity.Authentication; +import org.acegisecurity.AuthorizationServiceException; +import org.acegisecurity.ConfigAttribute; +import org.acegisecurity.ConfigAttributeDefinition; + +import org.acegisecurity.acls.Acl; +import org.acegisecurity.acls.AclService; +import org.acegisecurity.acls.Permission; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Collection; +import java.util.Iterator; + + +/** + *

Given a Collection of domain object instances returned from a secure object invocation, remove + * any Collection elements the principal does not have appropriate permission to access as defined by the + * {@link AclService}.

+ *

The AclService is used to retrieve the access control list (ACL) permissions associated with + * each Collection domain object instance element for the current Authentication object.

+ *

This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()} matches the {@link + * #processConfigAttribute}. The provider will then lookup the ACLs from the AclService and ensure the + * principal is {@link Acl#isGranted(org.acegisecurity.acls.Permission[], org.acegisecurity.acls.sid.Sid[], boolean)} + * when presenting the {@link #requirePermission} array to that method.

+ *

If the principal does not have permission, that element will not be included in the returned + * Collection.

+ *

Often users will setup a BasicAclEntryAfterInvocationProvider with a {@link + * #processConfigAttribute} of AFTER_ACL_COLLECTION_READ and a {@link #requirePermission} of + * BasePermission.READ. These are also the defaults.

+ *

If the provided returnObject is null, a nullCollection + * will be returned. If the provided returnObject is not a Collection, an {@link + * AuthorizationServiceException} will be thrown.

+ *

All comparisons and prefixes are case sensitive.

+ * + * @author Ben Alex + * @author Paulo Neves + * @version $Id$ + */ +public class AclEntryAfterInvocationCollectionFilteringProvider extends AbstractAclProvider { + //~ Static fields/initializers ===================================================================================== + + protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class); + + //~ Constructors =================================================================================================== + + public AclEntryAfterInvocationCollectionFilteringProvider(AclService aclService, Permission[] requirePermission) { + super(aclService, "AFTER_ACL_COLLECTION_READ", requirePermission); + } + + //~ Methods ======================================================================================================== + + public Object decide(Authentication authentication, Object object, ConfigAttributeDefinition config, + Object returnedObject) throws AccessDeniedException { + Iterator iter = config.getConfigAttributes(); + + while (iter.hasNext()) { + ConfigAttribute attr = (ConfigAttribute) iter.next(); + + if (this.supports(attr)) { + // Need to process the Collection for this invocation + if (returnedObject == null) { + if (logger.isDebugEnabled()) { + logger.debug("Return object is null, skipping"); + } + + return null; + } + + Filterer filterer = null; + + if (returnedObject instanceof Collection) { + Collection collection = (Collection) returnedObject; + filterer = new CollectionFilterer(collection); + } else if (returnedObject.getClass().isArray()) { + Object[] array = (Object[]) returnedObject; + filterer = new ArrayFilterer(array); + } else { + throw new AuthorizationServiceException( + "A Collection or an array (or null) was required as the returnedObject, but the returnedObject was: " + + returnedObject); + } + + // Locate unauthorised Collection elements + Iterator collectionIter = filterer.iterator(); + + while (collectionIter.hasNext()) { + Object domainObject = collectionIter.next(); + + boolean hasPermission = false; + + if (domainObject == null) { + hasPermission = true; + } else if (!getProcessDomainObjectClass().isAssignableFrom(domainObject.getClass())) { + hasPermission = true; + } else { + hasPermission = hasPermission(authentication, domainObject); + + if (!hasPermission) { + filterer.remove(domainObject); + + if (logger.isDebugEnabled()) { + logger.debug("Principal is NOT authorised for element: " + domainObject); + } + } + } + } + + return filterer.getFilteredObject(); + } + } + + return returnedObject; + } +} diff --git a/core/src/main/java/org/acegisecurity/afterinvocation/AclEntryAfterInvocationProvider.java b/core/src/main/java/org/acegisecurity/afterinvocation/AclEntryAfterInvocationProvider.java new file mode 100644 index 0000000000..4dd9b440b5 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/afterinvocation/AclEntryAfterInvocationProvider.java @@ -0,0 +1,119 @@ +/* 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.afterinvocation; + +import org.acegisecurity.AccessDeniedException; +import org.acegisecurity.AcegiMessageSource; +import org.acegisecurity.Authentication; +import org.acegisecurity.ConfigAttribute; +import org.acegisecurity.ConfigAttributeDefinition; + +import org.acegisecurity.acls.Acl; +import org.acegisecurity.acls.AclService; +import org.acegisecurity.acls.Permission; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceAware; +import org.springframework.context.support.MessageSourceAccessor; + +import java.util.Iterator; + + +/** + *

Given a domain object instance returned from a secure object invocation, ensures the principal has + * appropriate permission as defined by the {@link AclService}.

+ *

The AclService is used to retrieve the access control list (ACL) permissions associated with a + * domain object instance for the current Authentication object.

+ *

This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()} matches the {@link + * #processConfigAttribute}. The provider will then lookup the ACLs from the AclService and ensure the + * principal is {@link Acl#isGranted(org.acegisecurity.acls.Permission[], org.acegisecurity.acls.sid.Sid[], boolean)} + * when presenting the {@link #requirePermission} array to that method.

+ *

Often users will setup an AclEntryAfterInvocationProvider with a {@link + * #processConfigAttribute} of AFTER_ACL_READ and a {@link #requirePermission} of + * BasePermission.READ. These are also the defaults.

+ *

If the principal does not have sufficient permissions, an AccessDeniedException will be thrown.

+ *

If the provided returnObject is null, permission will always be granted and + * null will be returned.

+ *

All comparisons and prefixes are case sensitive.

+ */ +public class AclEntryAfterInvocationProvider extends AbstractAclProvider implements MessageSourceAware { + //~ Static fields/initializers ===================================================================================== + + protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationProvider.class); + + //~ Instance fields ================================================================================================ + + protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor(); + + //~ Constructors =================================================================================================== + + public AclEntryAfterInvocationProvider(AclService aclService, Permission[] requirePermission) { + super(aclService, "AFTER_ACL_READ", requirePermission); + } + + //~ Methods ======================================================================================================== + + public Object decide(Authentication authentication, Object object, ConfigAttributeDefinition config, + Object returnedObject) throws AccessDeniedException { + Iterator iter = config.getConfigAttributes(); + + while (iter.hasNext()) { + ConfigAttribute attr = (ConfigAttribute) iter.next(); + + if (this.supports(attr)) { + // Need to make an access decision on this invocation + if (returnedObject == null) { + // AclManager interface contract prohibits nulls + // As they have permission to null/nothing, grant access + if (logger.isDebugEnabled()) { + logger.debug("Return object is null, skipping"); + } + + return null; + } + + if (!getProcessDomainObjectClass().isAssignableFrom(returnedObject.getClass())) { + if (logger.isDebugEnabled()) { + logger.debug("Return object is not applicable for this provider, skipping"); + } + + return null; + } + + if (hasPermission(authentication, returnedObject)) { + return returnedObject; + } else { + if (logger.isDebugEnabled()) { + logger.debug("Denying access"); + } + + throw new AccessDeniedException(messages.getMessage( + "BasicAclEntryAfterInvocationProvider.noPermission", + new Object[] {authentication.getName(), returnedObject}, + "Authentication {0} has NO permissions to the domain object {1}")); + } + } + } + + return returnedObject; + } + + public void setMessageSource(MessageSource messages) { + this.messages = new MessageSourceAccessor(messages); + } +} diff --git a/core/src/main/java/org/acegisecurity/afterinvocation/ArrayFilterer.java b/core/src/main/java/org/acegisecurity/afterinvocation/ArrayFilterer.java new file mode 100644 index 0000000000..4b8def73ca --- /dev/null +++ b/core/src/main/java/org/acegisecurity/afterinvocation/ArrayFilterer.java @@ -0,0 +1,101 @@ +/* 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.afterinvocation; + +import org.apache.commons.collections.iterators.ArrayIterator; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.lang.reflect.Array; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + + +/** + * A filter used to filter arrays. + * + * @author Ben Alex + * @author Paulo Neves + * @version $Id$ + */ +class ArrayFilterer implements Filterer { + //~ Static fields/initializers ===================================================================================== + + protected static final Log logger = LogFactory.getLog(BasicAclEntryAfterInvocationCollectionFilteringProvider.class); + + //~ Instance fields ================================================================================================ + + private Set removeList; + private Object[] list; + + //~ Constructors =================================================================================================== + + ArrayFilterer(Object[] list) { + this.list = list; + + // Collect the removed objects to a HashSet so that + // it is fast to lookup them when a filtered array + // is constructed. + removeList = new HashSet(); + } + + //~ Methods ======================================================================================================== + + /** + * + * @see org.acegisecurity.afterinvocation.Filterer#getFilteredObject() + */ + public Object getFilteredObject() { + // Recreate an array of same type and filter the removed objects. + int originalSize = list.length; + int sizeOfResultingList = originalSize - removeList.size(); + Object[] filtered = (Object[]) Array.newInstance(list.getClass().getComponentType(), sizeOfResultingList); + + for (int i = 0, j = 0; i < list.length; i++) { + Object object = list[i]; + + if (!removeList.contains(object)) { + filtered[j] = object; + j++; + } + } + + if (logger.isDebugEnabled()) { + logger.debug("Original array contained " + originalSize + " elements; now contains " + sizeOfResultingList + + " elements"); + } + + return filtered; + } + + /** + * + * @see org.acegisecurity.afterinvocation.Filterer#iterator() + */ + public Iterator iterator() { + return new ArrayIterator(list); + } + + /** + * + * @see org.acegisecurity.afterinvocation.Filterer#remove(java.lang.Object) + */ + public void remove(Object object) { + removeList.add(object); + } +} diff --git a/core/src/main/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationCollectionFilteringProvider.java b/core/src/main/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationCollectionFilteringProvider.java index c35e751f8c..9d98d7a0d7 100644 --- a/core/src/main/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationCollectionFilteringProvider.java +++ b/core/src/main/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationCollectionFilteringProvider.java @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.acegisecurity.afterinvocation; import org.acegisecurity.AccessDeniedException; @@ -26,7 +25,6 @@ import org.acegisecurity.acl.AclManager; import org.acegisecurity.acl.basic.BasicAclEntry; import org.acegisecurity.acl.basic.SimpleAclEntry; -import org.apache.commons.collections.iterators.ArrayIterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -34,12 +32,8 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; -import java.lang.reflect.Array; - import java.util.Collection; -import java.util.HashSet; import java.util.Iterator; -import java.util.Set; /** @@ -135,42 +129,40 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider implements boolean hasPermission = false; - AclEntry[] acls = null; - if (domainObject == null) { hasPermission = true; } else if (!processDomainObjectClass.isAssignableFrom(domainObject.getClass())) { hasPermission = true; } else { - acls = aclManager.getAcls(domainObject, authentication); - } + AclEntry[] acls = aclManager.getAcls(domainObject, authentication); - if ((acls != null) && (acls.length != 0)) { - for (int i = 0; i < acls.length; i++) { - // Locate processable AclEntrys - if (acls[i] instanceof BasicAclEntry) { - BasicAclEntry processableAcl = (BasicAclEntry) acls[i]; + if ((acls != null) && (acls.length != 0)) { + for (int i = 0; i < acls.length; i++) { + // Locate processable AclEntrys + if (acls[i] instanceof BasicAclEntry) { + BasicAclEntry processableAcl = (BasicAclEntry) acls[i]; - // See if principal has any of the required permissions - for (int y = 0; y < requirePermission.length; y++) { - if (processableAcl.isPermitted(requirePermission[y])) { - hasPermission = true; + // See if principal has any of the required permissions + for (int y = 0; y < requirePermission.length; y++) { + if (processableAcl.isPermitted(requirePermission[y])) { + hasPermission = true; - if (logger.isDebugEnabled()) { - logger.debug("Principal is authorised for element: " + domainObject - + " due to ACL: " + processableAcl.toString()); + if (logger.isDebugEnabled()) { + logger.debug("Principal is authorised for element: " + domainObject + + " due to ACL: " + processableAcl.toString()); + } } } } } } - } - if (!hasPermission) { - filterer.remove(domainObject); + if (!hasPermission) { + filterer.remove(domainObject); - if (logger.isDebugEnabled()) { - logger.debug("Principal is NOT authorised for element: " + domainObject); + if (logger.isDebugEnabled()) { + logger.debug("Principal is NOT authorised for element: " + domainObject); + } } } } @@ -212,9 +204,11 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider implements } /** - * Allow setting permissions with String literals instead of integers as {@link #setRequirePermission(int[])} - * + * Allow setting permissions with String literals instead of integers as {@link + * #setRequirePermission(int[])} + * * @param requirePermission permission literals + * * @see SimpleAclEntry#parsePermissions(String[]) for valid values */ public void setRequirePermissionFromString(String[] requirePermission) { @@ -240,179 +234,3 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider implements return true; } } - - -/** - * Filter strategy interface. - */ -interface Filterer { - //~ Methods ======================================================================================================== - - /** - * Gets the filtered collection or array. - * - * @return the filtered collection or array - */ - public Object getFilteredObject(); - - /** - * Returns an iterator over the filtered collection or array. - * - * @return an Iterator - */ - public Iterator iterator(); - - /** - * Removes the the given object from the resulting list. - * - * @param object the object to be removed - */ - public void remove(Object object); -} - - -/** - * A filter used to filter Collections. - */ -class CollectionFilterer implements Filterer { - //~ Static fields/initializers ===================================================================================== - - protected static final Log logger = LogFactory.getLog(BasicAclEntryAfterInvocationCollectionFilteringProvider.class); - - //~ Instance fields ================================================================================================ - - private Collection collection; - - // collectionIter offers significant performance optimisations (as - // per acegisecurity-developer mailing list conversation 19/5/05) - private Iterator collectionIter; - private Set removeList; - - //~ Constructors =================================================================================================== - - CollectionFilterer(Collection collection) { - this.collection = collection; - - // We create a Set of objects to be removed from the Collection, - // as ConcurrentModificationException prevents removal during - // iteration, and making a new Collection to be returned is - // problematic as the original Collection implementation passed - // to the method may not necessarily be re-constructable (as - // the Collection(collection) constructor is not guaranteed and - // manually adding may lose sort order or other capabilities) - removeList = new HashSet(); - } - - //~ Methods ======================================================================================================== - - /** - * - * @see org.acegisecurity.afterinvocation.Filterer#getFilteredObject() - */ - public Object getFilteredObject() { - // Now the Iterator has ended, remove Objects from Collection - Iterator removeIter = removeList.iterator(); - - int originalSize = collection.size(); - - while (removeIter.hasNext()) { - collection.remove(removeIter.next()); - } - - if (logger.isDebugEnabled()) { - logger.debug("Original collection contained " + originalSize + " elements; now contains " - + collection.size() + " elements"); - } - - return collection; - } - - /** - * - * @see org.acegisecurity.afterinvocation.Filterer#iterator() - */ - public Iterator iterator() { - collectionIter = collection.iterator(); - - return collectionIter; - } - - /** - * - * @see org.acegisecurity.afterinvocation.Filterer#remove(java.lang.Object) - */ - public void remove(Object object) { - removeList.add(object); - } -} - - -/** - * A filter used to filter arrays. - */ -class ArrayFilterer implements Filterer { - //~ Static fields/initializers ===================================================================================== - - protected static final Log logger = LogFactory.getLog(BasicAclEntryAfterInvocationCollectionFilteringProvider.class); - - //~ Instance fields ================================================================================================ - - private Set removeList; - private Object[] list; - - //~ Constructors =================================================================================================== - - ArrayFilterer(Object[] list) { - this.list = list; - - // Collect the removed objects to a HashSet so that - // it is fast to lookup them when a filtered array - // is constructed. - removeList = new HashSet(); - } - - //~ Methods ======================================================================================================== - - /** - * - * @see org.acegisecurity.afterinvocation.Filterer#getFilteredObject() - */ - public Object getFilteredObject() { - // Recreate an array of same type and filter the removed objects. - int originalSize = list.length; - int sizeOfResultingList = originalSize - removeList.size(); - Object[] filtered = (Object[]) Array.newInstance(list.getClass().getComponentType(), sizeOfResultingList); - - for (int i = 0, j = 0; i < list.length; i++) { - Object object = list[i]; - - if (!removeList.contains(object)) { - filtered[j] = object; - j++; - } - } - - if (logger.isDebugEnabled()) { - logger.debug("Original array contained " + originalSize + " elements; now contains " + sizeOfResultingList - + " elements"); - } - - return filtered; - } - - /** - * - * @see org.acegisecurity.afterinvocation.Filterer#iterator() - */ - public Iterator iterator() { - return new ArrayIterator(list); - } - - /** - * - * @see org.acegisecurity.afterinvocation.Filterer#remove(java.lang.Object) - */ - public void remove(Object object) { - removeList.add(object); - } -} diff --git a/core/src/main/java/org/acegisecurity/afterinvocation/CollectionFilterer.java b/core/src/main/java/org/acegisecurity/afterinvocation/CollectionFilterer.java new file mode 100644 index 0000000000..42daab4176 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/afterinvocation/CollectionFilterer.java @@ -0,0 +1,104 @@ +/* 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.afterinvocation; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + + +/** + * A filter used to filter Collections. + * + * @author Ben Alex + * @author Paulo Neves + * @version $Id$ + */ +class CollectionFilterer implements Filterer { + //~ Static fields/initializers ===================================================================================== + + protected static final Log logger = LogFactory.getLog(BasicAclEntryAfterInvocationCollectionFilteringProvider.class); + + //~ Instance fields ================================================================================================ + + private Collection collection; + + // collectionIter offers significant performance optimisations (as + // per acegisecurity-developer mailing list conversation 19/5/05) + private Iterator collectionIter; + private Set removeList; + + //~ Constructors =================================================================================================== + + CollectionFilterer(Collection collection) { + this.collection = collection; + + // We create a Set of objects to be removed from the Collection, + // as ConcurrentModificationException prevents removal during + // iteration, and making a new Collection to be returned is + // problematic as the original Collection implementation passed + // to the method may not necessarily be re-constructable (as + // the Collection(collection) constructor is not guaranteed and + // manually adding may lose sort order or other capabilities) + removeList = new HashSet(); + } + + //~ Methods ======================================================================================================== + + /** + * + * @see org.acegisecurity.afterinvocation.Filterer#getFilteredObject() + */ + public Object getFilteredObject() { + // Now the Iterator has ended, remove Objects from Collection + Iterator removeIter = removeList.iterator(); + + int originalSize = collection.size(); + + while (removeIter.hasNext()) { + collection.remove(removeIter.next()); + } + + if (logger.isDebugEnabled()) { + logger.debug("Original collection contained " + originalSize + " elements; now contains " + + collection.size() + " elements"); + } + + return collection; + } + + /** + * + * @see org.acegisecurity.afterinvocation.Filterer#iterator() + */ + public Iterator iterator() { + collectionIter = collection.iterator(); + + return collectionIter; + } + + /** + * + * @see org.acegisecurity.afterinvocation.Filterer#remove(java.lang.Object) + */ + public void remove(Object object) { + removeList.add(object); + } +} diff --git a/core/src/main/java/org/acegisecurity/afterinvocation/Filterer.java b/core/src/main/java/org/acegisecurity/afterinvocation/Filterer.java new file mode 100644 index 0000000000..fb334b4aae --- /dev/null +++ b/core/src/main/java/org/acegisecurity/afterinvocation/Filterer.java @@ -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.afterinvocation; + +import java.util.Iterator; + + +/** + * Filter strategy interface. + * + * @author Ben Alex + * @author Paulo Neves + * @version $Id$ + */ +interface Filterer { + //~ Methods ======================================================================================================== + + /** + * Gets the filtered collection or array. + * + * @return the filtered collection or array + */ + public Object getFilteredObject(); + + /** + * Returns an iterator over the filtered collection or array. + * + * @return an Iterator + */ + public Iterator iterator(); + + /** + * Removes the the given object from the resulting list. + * + * @param object the object to be removed + */ + public void remove(Object object); +} diff --git a/core/src/main/java/org/acegisecurity/taglibs/authz/AccessControlListTag.java b/core/src/main/java/org/acegisecurity/taglibs/authz/AccessControlListTag.java new file mode 100644 index 0000000000..3c780183af --- /dev/null +++ b/core/src/main/java/org/acegisecurity/taglibs/authz/AccessControlListTag.java @@ -0,0 +1,223 @@ +/* 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.taglibs.authz; + +import org.acegisecurity.acls.Acl; +import org.acegisecurity.acls.AclService; +import org.acegisecurity.acls.NotFoundException; +import org.acegisecurity.acls.Permission; +import org.acegisecurity.acls.domain.BasePermission; +import org.acegisecurity.acls.objectidentity.ObjectIdentity; +import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategy; +import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategyImpl; +import org.acegisecurity.acls.sid.Sid; +import org.acegisecurity.acls.sid.SidRetrievalStrategy; +import org.acegisecurity.acls.sid.SidRetrievalStrategyImpl; + +import org.acegisecurity.context.SecurityContextHolder; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.context.ApplicationContext; + +import org.springframework.web.context.support.WebApplicationContextUtils; +import org.springframework.web.util.ExpressionEvaluationUtils; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +import javax.servlet.ServletContext; +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.PageContext; +import javax.servlet.jsp.tagext.Tag; +import javax.servlet.jsp.tagext.TagSupport; + + +/** + * An implementation of {@link javax.servlet.jsp.tagext.Tag} that allows its body through if some authorizations + * are granted to the request's principal.

One or more comma separate numeric are specified via the + * hasPermission attribute. Those permissions are then converted into {@link Permission} instances. These + * instances are then presented as an array to the {@link Acl#isGranted(Permission[], + * org.acegisecurity.acls.sid.Sid[], boolean)} method. The {@link Sid} presented is determined by the {@link + * SidRetrievalStrategy}.

+ *

For this class to operate it must be able to access the application context via the + * WebApplicationContextUtils and locate an {@link AclService} and {@link SidRetrievalStrategy}. + * Application contexts must provide one and only one of these Java types.

+ * + * @author Ben Alex + * @version $Id$ + */ +public class AccessControlListTag extends TagSupport { + //~ Static fields/initializers ===================================================================================== + + protected static final Log logger = LogFactory.getLog(AccessControlListTag.class); + + //~ Instance fields ================================================================================================ + + private AclService aclService; + private ApplicationContext applicationContext; + private Object domainObject; + private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy; + private SidRetrievalStrategy sidRetrievalStrategy; + private String hasPermission = ""; + + //~ Methods ======================================================================================================== + + public int doStartTag() throws JspException { + initializeIfRequired(); + + if ((null == hasPermission) || "".equals(hasPermission)) { + return Tag.SKIP_BODY; + } + + final String evaledPermissionsString = ExpressionEvaluationUtils.evaluateString("hasPermission", hasPermission, + pageContext); + + Permission[] requiredPermissions = null; + + try { + requiredPermissions = parsePermissionsString(evaledPermissionsString); + } catch (NumberFormatException nfe) { + throw new JspException(nfe); + } + + Object resolvedDomainObject = null; + + if (domainObject instanceof String) { + resolvedDomainObject = ExpressionEvaluationUtils.evaluate("domainObject", (String) domainObject, + Object.class, pageContext); + } else { + resolvedDomainObject = domainObject; + } + + if (resolvedDomainObject == null) { + if (logger.isDebugEnabled()) { + logger.debug("domainObject resolved to null, so including tag body"); + } + + // Of course they have access to a null object! + return Tag.EVAL_BODY_INCLUDE; + } + + if (SecurityContextHolder.getContext().getAuthentication() == null) { + if (logger.isDebugEnabled()) { + logger.debug( + "SecurityContextHolder did not return a non-null Authentication object, so skipping tag body"); + } + + return Tag.SKIP_BODY; + } + + Sid[] sids = sidRetrievalStrategy.getSids(SecurityContextHolder.getContext().getAuthentication()); + ObjectIdentity oid = objectIdentityRetrievalStrategy.getObjectIdentity(resolvedDomainObject); + + // Obtain aclEntrys applying to the current Authentication object + try { + Acl acl = aclService.readAclById(oid, sids); + + if (acl.isGranted(requiredPermissions, sids, false)) { + return Tag.EVAL_BODY_INCLUDE; + } else { + return Tag.SKIP_BODY; + } + } catch (NotFoundException nfe) { + return Tag.SKIP_BODY; + } + } + + /** + * Allows test cases to override where application context obtained from. + * + * @param pageContext so the ServletContext can be accessed as required by Spring's + * WebApplicationContextUtils + * + * @return the Spring application context (never null) + */ + protected ApplicationContext getContext(PageContext pageContext) { + ServletContext servletContext = pageContext.getServletContext(); + + return WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); + } + + public Object getDomainObject() { + return domainObject; + } + + public String getHasPermission() { + return hasPermission; + } + + private void initializeIfRequired() throws JspException { + if (applicationContext == null) { + this.applicationContext = getContext(pageContext); + + Map map = applicationContext.getBeansOfType(AclService.class); + + if (map.size() != 1) { + throw new JspException( + "Found incorrect number of AclService instances in application context - you must have only have one!"); + } + + aclService = (AclService) map.values().iterator().next(); + + map = applicationContext.getBeansOfType(SidRetrievalStrategy.class); + + if (map.size() == 0) { + sidRetrievalStrategy = new SidRetrievalStrategyImpl(); + } else if (map.size() == 1) { + sidRetrievalStrategy = (SidRetrievalStrategy) map.values().iterator().next(); + } else { + throw new JspException( + "Found incorrect number of SidRetrievalStrategy instances in application context - you must have only have one!"); + } + + map = applicationContext.getBeansOfType(ObjectIdentityRetrievalStrategy.class); + + if (map.size() == 0) { + objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl(); + } else if (map.size() == 1) { + objectIdentityRetrievalStrategy = (ObjectIdentityRetrievalStrategy) map.values().iterator().next(); + } else { + throw new JspException( + "Found incorrect number of ObjectIdentityRetrievalStrategy instances in application context - you must have only have one!"); + } + } + } + + private Permission[] parsePermissionsString(String integersString) + throws NumberFormatException { + final Set permissions = new HashSet(); + final StringTokenizer tokenizer; + tokenizer = new StringTokenizer(integersString, ",", false); + + while (tokenizer.hasMoreTokens()) { + String integer = tokenizer.nextToken(); + permissions.add(BasePermission.buildFromMask(new Integer(integer).intValue())); + } + + return (Permission[]) permissions.toArray(new Permission[] {}); + } + + public void setDomainObject(Object domainObject) { + this.domainObject = domainObject; + } + + public void setHasPermission(String hasPermission) { + this.hasPermission = hasPermission; + } +} diff --git a/core/src/main/java/org/acegisecurity/vote/AbstractAclVoter.java b/core/src/main/java/org/acegisecurity/vote/AbstractAclVoter.java index ba0fd58451..3819df738e 100644 --- a/core/src/main/java/org/acegisecurity/vote/AbstractAclVoter.java +++ b/core/src/main/java/org/acegisecurity/vote/AbstractAclVoter.java @@ -12,13 +12,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.acegisecurity.vote; import org.acegisecurity.AuthorizationServiceException; -import org.acegisecurity.acl.AclManager; - import org.aopalliance.intercept.MethodInvocation; import org.aspectj.lang.JoinPoint; @@ -28,8 +25,7 @@ import org.springframework.util.Assert; /** - *

Given a domain object instance passed as a method argument, ensures the principal has appropriate permission - * as defined by the {@link AclManager}.

+ *

Provides helper methods for writing domain object ACL voters. Is not bound to any particular ACL system.

* * @author Ben Alex * @version $Id$ diff --git a/core/src/main/java/org/acegisecurity/vote/AclEntryVoter.java b/core/src/main/java/org/acegisecurity/vote/AclEntryVoter.java new file mode 100644 index 0000000000..4217a91426 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/vote/AclEntryVoter.java @@ -0,0 +1,251 @@ +/* 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.vote; + +import org.acegisecurity.Authentication; +import org.acegisecurity.AuthorizationServiceException; +import org.acegisecurity.ConfigAttribute; +import org.acegisecurity.ConfigAttributeDefinition; + +import org.acegisecurity.acls.Acl; +import org.acegisecurity.acls.AclService; +import org.acegisecurity.acls.NotFoundException; +import org.acegisecurity.acls.Permission; +import org.acegisecurity.acls.objectidentity.ObjectIdentity; +import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategy; +import org.acegisecurity.acls.objectidentity.ObjectIdentityRetrievalStrategyImpl; +import org.acegisecurity.acls.sid.Sid; +import org.acegisecurity.acls.sid.SidRetrievalStrategy; +import org.acegisecurity.acls.sid.SidRetrievalStrategyImpl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.util.Assert; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import java.util.Iterator; + + +/** + *

Given a domain object instance passed as a method argument, ensures the principal has appropriate permission + * as indicated by the {@link AclService}.

+ *

The AclService is used to retrieve the access control list (ACL) permissions associated with a + * domain object instance for the current Authentication object.

+ *

The voter will vote if any {@link ConfigAttribute#getAttribute()} matches the {@link + * #processConfigAttribute}. The provider will then locate the first method argument of type {@link + * #processDomainObjectClass}. Assuming that method argument is non-null, the provider will then lookup the ACLs from + * the AclManager and ensure the principal is {@link Acl#isGranted(org.acegisecurity.acls.Permission[], + * org.acegisecurity.acls.sid.Sid[], boolean)} when presenting the {@link #requirePermission} array to that method.

+ *

If the method argument is null, the voter will abstain from voting. If the method argument + * could not be found, an {@link org.acegisecurity.AuthorizationServiceException} will be thrown.

+ *

In practical terms users will typically setup a number of AclEntryVoters. Each will have a + * different {@link #processDomainObjectClass}, {@link #processConfigAttribute} and {@link #requirePermission} + * combination. For example, a small application might employ the following instances of AclEntryVoter: + *

    + *
  • Process domain object class BankAccount, configuration attribute + * VOTE_ACL_BANK_ACCONT_READ, require permission BasePermission.READ
  • + *
  • Process domain object class BankAccount, configuration attribute + * VOTE_ACL_BANK_ACCOUNT_WRITE, require permission list BasePermission.WRITE and + * BasePermission.CREATE (allowing the principal to have either of these two permissions
  • + *
  • Process domain object class Customer, configuration attribute + * VOTE_ACL_CUSTOMER_READ, require permission BasePermission.READ
  • + *
  • Process domain object class Customer, configuration attribute + * VOTE_ACL_CUSTOMER_WRITE, require permission list BasePermission.WRITE and + * BasePermission.CREATE
  • + *
+ * Alternatively, you could have used a common superclass or interface for the {@link #processDomainObjectClass} + * if both BankAccount and Customer had common parents.

+ *

If the principal does not have sufficient permissions, the voter will vote to deny access.

+ *

All comparisons and prefixes are case sensitive.

+ * + * @author Ben Alex + * @version $Id$ + */ +public class AclEntryVoter extends AbstractAclVoter { + //~ Static fields/initializers ===================================================================================== + + private static final Log logger = LogFactory.getLog(AclEntryVoter.class); + + //~ Instance fields ================================================================================================ + + private AclService aclService; + private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl(); + private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl(); + private String internalMethod; + private String processConfigAttribute; + private Permission[] requirePermission; + + //~ Constructors =================================================================================================== + + public AclEntryVoter(AclService aclService, String processConfigAttribute, Permission[] requirePermission) { + Assert.notNull(processConfigAttribute, "A processConfigAttribute is mandatory"); + Assert.notNull(aclService, "An AclService is mandatory"); + + if ((requirePermission == null) || (requirePermission.length == 0)) { + throw new IllegalArgumentException("One or more requirePermission entries is mandatory"); + } + + this.aclService = aclService; + this.processConfigAttribute = processConfigAttribute; + this.requirePermission = requirePermission; + } + + //~ Methods ======================================================================================================== + + /** + * Optionally specifies a method of the domain object that will be used to obtain a contained domain + * object. That contained domain object will be used for the ACL evaluation. This is useful if a domain object + * contains a parent that an ACL evaluation should be targeted for, instead of the child domain object (which + * perhaps is being created and as such does not yet have any ACL permissions) + * + * @return null to use the domain object, or the name of a method (that requires no arguments) that + * should be invoked to obtain an Object which will be the domain object used for ACL + * evaluation + */ + public String getInternalMethod() { + return internalMethod; + } + + public String getProcessConfigAttribute() { + return processConfigAttribute; + } + + public void setInternalMethod(String internalMethod) { + this.internalMethod = internalMethod; + } + + public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) { + Assert.notNull(objectIdentityRetrievalStrategy, "ObjectIdentityRetrievalStrategy required"); + this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy; + } + + public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) { + Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required"); + this.sidRetrievalStrategy = sidRetrievalStrategy; + } + + public boolean supports(ConfigAttribute attribute) { + if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getProcessConfigAttribute())) { + return true; + } else { + return false; + } + } + + public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) { + Iterator iter = config.getConfigAttributes(); + + while (iter.hasNext()) { + ConfigAttribute attr = (ConfigAttribute) iter.next(); + + if (this.supports(attr)) { + // Need to make an access decision on this invocation + // Attempt to locate the domain object instance to process + Object domainObject = getDomainObjectInstance(object); + + // If domain object is null, vote to abstain + if (domainObject == null) { + if (logger.isDebugEnabled()) { + logger.debug("Voting to abstain - domainObject is null"); + } + + return AccessDecisionVoter.ACCESS_ABSTAIN; + } + + // Evaluate if we are required to use an inner domain object + if ((internalMethod != null) && !"".equals(internalMethod)) { + try { + Class clazz = domainObject.getClass(); + Method method = clazz.getMethod(internalMethod, new Class[] {}); + domainObject = method.invoke(domainObject, new Object[] {}); + } catch (NoSuchMethodException nsme) { + throw new AuthorizationServiceException("Object of class '" + domainObject.getClass() + + "' does not provide the requested internalMethod: " + internalMethod); + } catch (IllegalAccessException iae) { + if (logger.isDebugEnabled()) { + logger.debug("IllegalAccessException", iae); + + if (iae.getCause() != null) { + logger.debug("Cause: " + iae.getCause().getMessage(), iae.getCause()); + } + } + + throw new AuthorizationServiceException("Problem invoking internalMethod: " + internalMethod + + " for object: " + domainObject); + } catch (InvocationTargetException ite) { + if (logger.isDebugEnabled()) { + logger.debug("InvocationTargetException", ite); + + if (ite.getCause() != null) { + logger.debug("Cause: " + ite.getCause().getMessage(), ite.getCause()); + } + } + + throw new AuthorizationServiceException("Problem invoking internalMethod: " + internalMethod + + " for object: " + domainObject); + } + } + + // Obtain the OID applicable to the domain object + ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy.getObjectIdentity(domainObject); + + // Obtain the SIDs applicable to the principal + Sid[] sids = sidRetrievalStrategy.getSids(authentication); + + Acl acl; + + try { + // Lookup only ACLs for SIDs we're interested in + acl = aclService.readAclById(objectIdentity, sids); + } catch (NotFoundException nfe) { + if (logger.isDebugEnabled()) { + logger.debug("Voting to deny access - no ACLs apply for this principal"); + } + + return AccessDecisionVoter.ACCESS_DENIED; + } + + try { + if (acl.isGranted(requirePermission, sids, false)) { + if (logger.isDebugEnabled()) { + logger.debug("Voting to grant access"); + } + + return AccessDecisionVoter.ACCESS_GRANTED; + } else { + if (logger.isDebugEnabled()) { + logger.debug( + "Voting to deny access - ACLs returned, but insufficient permissions for this principal"); + } + + return AccessDecisionVoter.ACCESS_DENIED; + } + } catch (NotFoundException nfe) { + if (logger.isDebugEnabled()) { + logger.debug("Voting to deny access - no ACLs apply for this principal"); + } + + return AccessDecisionVoter.ACCESS_DENIED; + } + } + } + + // No configuration attribute matched, so abstain + return AccessDecisionVoter.ACCESS_ABSTAIN; + } +} diff --git a/core/src/main/resources/org/acegisecurity/taglibs/authz.tld b/core/src/main/resources/org/acegisecurity/taglibs/authz.tld index b87b169650..ca44f27dac 100644 --- a/core/src/main/resources/org/acegisecurity/taglibs/authz.tld +++ b/core/src/main/resources/org/acegisecurity/taglibs/authz.tld @@ -113,4 +113,34 @@ + + accesscontrollist + org.acegisecurity.taglibs.authz.AccessControlListTag + + Allows inclusion of a tag body if the current Authentication + has one of the specified permissions to the presented + domain object instance. + + + + hasPermission + true + true + + A comma separated list of integers, each representing a + required bit mask permission from a subclass of + org.acegisecurity.acl.basic.AbstractBasicAclEntry. + + + + domainObject + true + true + + The actual domain object instance for which permissions + are being evaluated. + + + + diff --git a/sandbox/other/src/test/java/org/acegisecurity/acls/domain/PermissionTests.java b/core/src/test/java/org/acegisecurity/acls/domain/PermissionTests.java similarity index 84% rename from sandbox/other/src/test/java/org/acegisecurity/acls/domain/PermissionTests.java rename to core/src/test/java/org/acegisecurity/acls/domain/PermissionTests.java index 4b63c6818f..ba4639fd1d 100644 --- a/sandbox/other/src/test/java/org/acegisecurity/acls/domain/PermissionTests.java +++ b/core/src/test/java/org/acegisecurity/acls/domain/PermissionTests.java @@ -12,11 +12,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.acegisecurity.acls.domain; import junit.framework.TestCase; +import org.acegisecurity.acls.Permission; + /** * Tests BasePermission and CumulativePermission. @@ -30,7 +31,18 @@ public class PermissionTests extends TestCase { public void testExpectedIntegerValues() { assertEquals(1, BasePermission.READ.getMask()); assertEquals(16, BasePermission.ADMINISTRATION.getMask()); - assertEquals(17, new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION).getMask()); + assertEquals(7, + new CumulativePermission().set(BasePermission.READ).set(BasePermission.WRITE).set(BasePermission.CREATE) + .getMask()); + assertEquals(17, + new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION).getMask()); + } + + public void testFromInteger() { + Permission permission = BasePermission.buildFromMask(7); + System.out.println("7 = " + permission.toString()); + permission = BasePermission.buildFromMask(4); + System.out.println("4 = " + permission.toString()); } public void testStringConversion() { diff --git a/sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/DatabaseSeeder.java b/core/src/test/java/org/acegisecurity/acls/jdbc/DatabaseSeeder.java similarity index 92% rename from sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/DatabaseSeeder.java rename to core/src/test/java/org/acegisecurity/acls/jdbc/DatabaseSeeder.java index 4502ddd538..aebf9ef38f 100644 --- a/sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/DatabaseSeeder.java +++ b/core/src/test/java/org/acegisecurity/acls/jdbc/DatabaseSeeder.java @@ -1,48 +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.springframework.core.io.Resource; - -import org.springframework.jdbc.core.JdbcTemplate; - -import org.springframework.util.Assert; -import org.springframework.util.FileCopyUtils; - -import java.io.IOException; - -import javax.sql.DataSource; - - -/** - * DOCUMENT ME! - * - * @author $author$ - * @version $Revision$ - */ -public class DatabaseSeeder { - //~ Constructors =================================================================================================== - - 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); - } -} +/* 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.springframework.core.io.Resource; + +import org.springframework.jdbc.core.JdbcTemplate; + +import org.springframework.util.Assert; +import org.springframework.util.FileCopyUtils; + +import java.io.IOException; + +import javax.sql.DataSource; + + +/** + * Seeds the database for {@link JdbcAclServiceTests}. + * + * @author Ben Alex + * @version $Id$ + */ +public class DatabaseSeeder { + //~ Constructors =================================================================================================== + + 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); + } +} diff --git a/core/src/test/java/org/acegisecurity/acls/jdbc/JdbcAclServiceTests.java b/core/src/test/java/org/acegisecurity/acls/jdbc/JdbcAclServiceTests.java new file mode 100644 index 0000000000..8d6c8cc651 --- /dev/null +++ b/core/src/test/java/org/acegisecurity/acls/jdbc/JdbcAclServiceTests.java @@ -0,0 +1,222 @@ +/* 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.Authentication; +import org.acegisecurity.GrantedAuthority; +import org.acegisecurity.GrantedAuthorityImpl; + +import org.acegisecurity.acls.AccessControlEntry; +import org.acegisecurity.acls.MutableAcl; +import org.acegisecurity.acls.NotFoundException; +import org.acegisecurity.acls.Permission; +import org.acegisecurity.acls.domain.BasePermission; +import org.acegisecurity.acls.objectidentity.ObjectIdentity; +import org.acegisecurity.acls.objectidentity.ObjectIdentityImpl; +import org.acegisecurity.acls.sid.PrincipalSid; +import org.acegisecurity.acls.sid.Sid; + +import org.acegisecurity.context.SecurityContextHolder; + +import org.acegisecurity.providers.TestingAuthenticationToken; + +import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests; + +import java.util.Map; + + +/** + * Integration tests the ACL system using an in-memory database. + * + * @author Ben Alex + * @version $Id$ + */ +public class JdbcAclServiceTests extends AbstractTransactionalDataSourceSpringContextTests { + //~ Instance fields ================================================================================================ + + private JdbcMutableAclService jdbcMutableAclService; + + //~ Methods ======================================================================================================== + + protected String[] getConfigLocations() { + return new String[] {"classpath:org/acegisecurity/acls/jdbc/applicationContext-test.xml"}; + } + + public void setJdbcMutableAclService(JdbcMutableAclService jdbcAclService) { + this.jdbcMutableAclService = jdbcAclService; + } + + public void testLifecycle() { + setComplete(); + + Authentication auth = new TestingAuthenticationToken("ben", "ignored", + new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ADMINISTRATOR")}); + auth.setAuthenticated(true); + SecurityContextHolder.getContext().setAuthentication(auth); + + ObjectIdentity topParentOid = new ObjectIdentityImpl("sample.contact.Contact", new Long(100)); + ObjectIdentity middleParentOid = new ObjectIdentityImpl("sample.contact.Contact", new Long(101)); + ObjectIdentity childOid = new ObjectIdentityImpl("sample.contact.Contact", new Long(102)); + + MutableAcl topParent = jdbcMutableAclService.createAcl(topParentOid); + MutableAcl middleParent = jdbcMutableAclService.createAcl(middleParentOid); + MutableAcl child = jdbcMutableAclService.createAcl(childOid); + + // Specify the inheritence hierarchy + middleParent.setParent(topParent); + child.setParent(middleParent); + + // Now let's add a couple of permissions + topParent.insertAce(null, BasePermission.READ, new PrincipalSid(auth), true); + topParent.insertAce(null, BasePermission.WRITE, new PrincipalSid(auth), false); + middleParent.insertAce(null, BasePermission.DELETE, new PrincipalSid(auth), true); + child.insertAce(null, BasePermission.DELETE, new PrincipalSid(auth), false); + + // Explictly save the changed ACL + jdbcMutableAclService.updateAcl(topParent); + jdbcMutableAclService.updateAcl(middleParent); + jdbcMutableAclService.updateAcl(child); + + // Let's check if we can read them back correctly + Map map = jdbcMutableAclService.readAclsById(new ObjectIdentity[] {topParentOid, middleParentOid, childOid}); + assertEquals(3, map.size()); + + // Replace our current objects with their retrieved versions + topParent = (MutableAcl) map.get(topParentOid); + middleParent = (MutableAcl) map.get(middleParentOid); + child = (MutableAcl) map.get(childOid); + + // Check the retrieved versions has IDs + assertNotNull(topParent.getId()); + assertNotNull(middleParent.getId()); + assertNotNull(child.getId()); + + // Check their parents were correctly persisted + assertNull(topParent.getParentAcl()); + assertEquals(topParentOid, middleParent.getParentAcl().getObjectIdentity()); + assertEquals(middleParentOid, child.getParentAcl().getObjectIdentity()); + + // Check their ACEs were correctly persisted + assertEquals(2, topParent.getEntries().length); + assertEquals(1, middleParent.getEntries().length); + assertEquals(1, child.getEntries().length); + + // Check the retrieved rights are correct + assertTrue(topParent.isGranted(new Permission[] {BasePermission.READ}, new Sid[] {new PrincipalSid(auth)}, false)); + assertFalse(topParent.isGranted(new Permission[] {BasePermission.WRITE}, new Sid[] {new PrincipalSid(auth)}, + false)); + assertTrue(middleParent.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)}, + false)); + assertFalse(child.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)}, false)); + + try { + child.isGranted(new Permission[] {BasePermission.ADMINISTRATION}, new Sid[] {new PrincipalSid(auth)}, false); + fail("Should have thrown NotFoundException"); + } catch (NotFoundException expected) { + assertTrue(true); + } + + // Now check the inherited rights (when not explicitly overridden) also look OK + assertTrue(child.isGranted(new Permission[] {BasePermission.READ}, new Sid[] {new PrincipalSid(auth)}, false)); + assertFalse(child.isGranted(new Permission[] {BasePermission.WRITE}, new Sid[] {new PrincipalSid(auth)}, false)); + assertFalse(child.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)}, false)); + + // Next change the child so it doesn't inherit permissions from above + child.setEntriesInheriting(false); + jdbcMutableAclService.updateAcl(child); + child = (MutableAcl) jdbcMutableAclService.readAclById(childOid); + assertFalse(child.isEntriesInheriting()); + + // Check the child permissions no longer inherit + assertFalse(child.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)}, true)); + + try { + child.isGranted(new Permission[] {BasePermission.READ}, new Sid[] {new PrincipalSid(auth)}, true); + fail("Should have thrown NotFoundException"); + } catch (NotFoundException expected) { + assertTrue(true); + } + + try { + child.isGranted(new Permission[] {BasePermission.WRITE}, new Sid[] {new PrincipalSid(auth)}, true); + fail("Should have thrown NotFoundException"); + } catch (NotFoundException expected) { + assertTrue(true); + } + + // Let's add an identical permission to the child, but it'll appear AFTER the current permission, so has no impact + child.insertAce(null, BasePermission.DELETE, new PrincipalSid(auth), true); + + // Let's also add another permission to the child + child.insertAce(null, BasePermission.CREATE, new PrincipalSid(auth), true); + + // Save the changed child + jdbcMutableAclService.updateAcl(child); + child = (MutableAcl) jdbcMutableAclService.readAclById(childOid); + assertEquals(3, child.getEntries().length); + + // Output permissions + for (int i = 0; i < child.getEntries().length; i++) { + System.out.println(child.getEntries()[i]); + } + + // Check the permissions are as they should be + assertFalse(child.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)}, true)); // as earlier permission overrode + assertTrue(child.isGranted(new Permission[] {BasePermission.CREATE}, new Sid[] {new PrincipalSid(auth)}, true)); + + // Now check the first ACE (index 0) really is DELETE for our Sid and is non-granting + AccessControlEntry entry = child.getEntries()[0]; + assertEquals(BasePermission.DELETE.getMask(), entry.getPermission().getMask()); + assertEquals(new PrincipalSid(auth), entry.getSid()); + assertFalse(entry.isGranting()); + assertNotNull(entry.getId()); + + // Now delete that first ACE + child.deleteAce(entry.getId()); + + // Save and check it worked + child = jdbcMutableAclService.updateAcl(child); + assertEquals(2, child.getEntries().length); + assertTrue(child.isGranted(new Permission[] {BasePermission.DELETE}, new Sid[] {new PrincipalSid(auth)}, false)); + + SecurityContextHolder.clearContext(); + } + +/* public void testCumulativePermissions() { + setComplete(); + Authentication auth = new TestingAuthenticationToken("ben", "ignored", new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ADMINISTRATOR")}); + auth.setAuthenticated(true); + SecurityContextHolder.getContext().setAuthentication(auth); + + ObjectIdentity topParentOid = new ObjectIdentityImpl("sample.contact.Contact", new Long(110)); + MutableAcl topParent = jdbcMutableAclService.createAcl(topParentOid); + + // Add an ACE permission entry + CumulativePermission cm = new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION); + assertEquals(17, cm.getMask()); + topParent.insertAce(null, cm, new PrincipalSid(auth), true); + assertEquals(1, topParent.getEntries().length); + + // Explictly save the changed ACL + topParent = jdbcMutableAclService.updateAcl(topParent); + + // Check the mask was retrieved correctly + assertEquals(17, topParent.getEntries()[0].getPermission().getMask()); + assertTrue(topParent.isGranted(new Permission[] {cm}, new Sid[] {new PrincipalSid(auth)}, true)); + + SecurityContextHolder.clearContext(); + } + */ +} diff --git a/sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/applicationContext-test.xml b/core/src/test/java/org/acegisecurity/acls/jdbc/applicationContext-test.xml similarity index 76% rename from sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/applicationContext-test.xml rename to core/src/test/java/org/acegisecurity/acls/jdbc/applicationContext-test.xml index 50436f7ae0..e00a793d56 100644 --- a/sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/applicationContext-test.xml +++ b/core/src/test/java/org/acegisecurity/acls/jdbc/applicationContext-test.xml @@ -15,6 +15,10 @@ + + + + @@ -31,26 +35,33 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + - - + + @@ -58,7 +69,8 @@ org.hsqldb.jdbcDriver - jdbc:hsqldb:mem:test + jdbc:hsqldb:mem:test + sa diff --git a/sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/select.sql b/core/src/test/java/org/acegisecurity/acls/jdbc/select.sql similarity index 53% rename from sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/select.sql rename to core/src/test/java/org/acegisecurity/acls/jdbc/select.sql index 06a1b8af16..2a641735ba 100644 --- a/sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/select.sql +++ b/core/src/test/java/org/acegisecurity/acls/jdbc/select.sql @@ -1,23 +1,27 @@ -- 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_SID.PRINCIPAL as ACE_PRINCIPAL, ACL_SID.SID as ACE_SID, +ACLI_SID.PRINCIPAL as ACL_PRINCIPAL, ACLI_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 +from ACL_OBJECT_IDENTITY, ACL_SID ACLI_SID, ACL_CLASS +LEFT JOIN ACL_ENTRY ON ACL_OBJECT_IDENTITY.ID = ACL_ENTRY.ACL_OBJECT_IDENTITY +LEFT JOIN ACL_SID ON ACL_ENTRY.SID = ACL_SID.ID + +where + +ACLI_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 +(ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY = 2000 and ACL_CLASS.CLASS = 'sample.contact.Contact') -) order by ACL_ENTRY.ACL_OBJECT_IDENTITY asc, ACL_ENTRY.ACE_ORDER asc +) order by ACL_OBJECT_IDENTITY.OBJECT_ID_IDENTITY asc, ACL_ENTRY.ACE_ORDER asc diff --git a/core/src/test/java/org/acegisecurity/acls/jdbc/testData.sql b/core/src/test/java/org/acegisecurity/acls/jdbc/testData.sql new file mode 100644 index 0000000000..28d596a3f5 --- /dev/null +++ b/core/src/test/java/org/acegisecurity/acls/jdbc/testData.sql @@ -0,0 +1,45 @@ +-- Injected into DatabaseSeeder via applicationContext-test.xml (see test case JdbcAclServiceTests) + +-- DROP TABLE ACL_ENTRY; +-- DROP TABLE ACL_OBJECT_IDENTITY; +-- DROP TABLE ACL_CLASS; +-- DROP TABLE ACL_SID; + + +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)); + +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)); + +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)); diff --git a/doc/docbook/acegi.xml b/doc/docbook/acegi.xml index 55af2b9376..8653a5c999 100644 --- a/doc/docbook/acegi.xml +++ b/doc/docbook/acegi.xml @@ -4702,9 +4702,97 @@ public boolean supports(Class clazz); ACL-Aware AfterInvocationProviders - TODO: This section will be removed when we deprecate the - existing ACL package. It should be discussed with the context of the - ACL implementation chapter instead. + PLEASE NOTE: Acegi Security 1.0.3 contains a preview of a new + ACL module. The new ACL module is a significant rewrite of the + existing ACL module. The new module can be found under the + org.acegisecurity.acls package, with the old ACL + module under org.acegisecurity.acl. We encourage + users to consider testing with the new ACL module and build + applications with it. The old ACL module should be considered + deprecated and may be removed from a future release. The following + information relates to the new ACL package, and is thus + recommended. + + A common services layer method we've all written at one stage + or another looks like this: + + public Contact getById(Integer id); + + Quite often, only principals with permission to read the + Contact should be allowed to obtain it. In this + situation the AccessDecisionManager approach + provided by the AbstractSecurityInterceptor will + not suffice. This is because the identity of the + Contact is all that is available before the + secure object is invoked. The + AclAfterInvocationProvider delivers a solution, + and is configured as follows: + + <bean id="afterAclRead" class="org.acegisecurity.afterinvocation.AclEntryAfterInvocationProvider"> + <constructor-arg> + <ref bean="aclService"/> + </constructor-arg> + <constructor-arg> + <list> + <ref local="org.acegisecurity.acls.domain.BasePermission.ADMINISTRATION"/> + <ref local="org.acegisecurity.acls.domain.BasePermission.READ"/> + </list> + </constructor-arg> +</bean> + + In the above example, the Contact will be + retrieved and passed to the + AclEntryAfterInvocationProvider. The provider + will thrown an AccessDeniedException if one of + the listed requirePermissions is not held by the + Authentication. The + AclEntryAfterInvocationProvider queries the + AclService to determine the ACL that applies for + this domain object to this Authentication. + + Similar to the + AclEntryAfterInvocationProvider is + AclEntryAfterInvocationCollectionFilteringProvider. + It is designed to remove Collection or array + elements for which a principal does not have access. It never thrown + an AccessDeniedException - simply silently + removes the offending elements. The provider is configured as + follows: + + <bean id="afterAclCollectionRead" class="org.acegisecurity.afterinvocation.AclEntryAfterInvocationCollectionFilteringProvider"> + <constructor-arg> + <ref bean="aclService"/> + </constructor-arg> + <constructor-arg> + <list> + <ref local="org.acegisecurity.acls.domain.BasePermission.ADMINISTRATION"/> + <ref local="org.acegisecurity.acls.domain.BasePermission.READ"/> + </list> + </constructor-arg> +</bean> + + As you can imagine, the returned Object + must be a Collection or array for this provider + to operate. It will remove any element if the + AclManager indicates the + Authentication does not hold one of the listed + requirePermissions. + + The Contacts sample application demonstrates these two + AfterInvocationProviders. + + + + ACL-Aware AfterInvocationProviders (old ACL module) + + PLEASE NOTE: Acegi Security 1.0.3 contains a preview of a new + ACL module. The new ACL module is a significant rewrite of the + existing ACL module. The new module can be found under the + org.acegisecurity.acls package, with the old ACL + module under org.acegisecurity.acl. We encourage + users to consider testing with the new ACL module and build + applications with it. The old ACL module should be considered + deprecated and may be removed from a future release. A common services layer method we've all written at one stage or another looks like this: @@ -4838,25 +4926,28 @@ public boolean supports(Class clazz); ifAllGranted, and finally, if AnyGranted. - AclTag is used to include content if the - current principal has a ACL to the indicated domain object. + AccessControlListTag is used to include + content if the current principal has an ACL to the indicated domain + object. The following JSP fragment illustrates how to use the - AclTag: + AccessControlListTag: - -<authz:acl domainObject="${contact}" hasPermission="16,1"> + <authz:accesscontrollist domainObject="${contact}" hasPermission="8,16"> <td><A HREF="<c:url value="del.htm"><c:param name="contactId" value="${contact.id}"/></c:url>">Del</A></td> -</authz:acl> - - +</authz:accesscontrollist> This tag would cause the tag's body to be output if the principal holds either permission 16 or permission 1 for the "contact" domain object. The numbers are actually integers that are used with - AbstractBasicAclEntry bit masking. Please refer to - the ACL section of this reference guide to understand more about the - ACL capabilities of Acegi Security + BasePermission bit masking. Please refer to the ACL + section of this reference guide to understand more about the ACL + capabilities of Acegi Security. + + AclTag is part of the old ACL module and + should be considered deprecated. For the sake of historical reference, + works exactly the samae as + AccessControlListTag. @@ -5371,6 +5462,15 @@ public aspect DomainObjectInstanceSecurityAspect implements InitializingBean {
Overview + PLEASE NOTE: Acegi Security 1.0.3 contains a preview of a new + ACL module. The new ACL module is a significant rewrite of the + existing ACL module. The new module can be found under the + org.acegisecurity.acls package, with the old ACL + module under org.acegisecurity.acl. We encourage + users to consider testing with the new ACL module and build + applications with it. The old ACL module should be considered + deprecated and may be removed from a future release. + Complex applications often will find the need to define access permissions not simply at a web request or method invocation level. Instead, security decisions need to comprise both who @@ -5448,7 +5548,183 @@ public aspect DomainObjectInstanceSecurityAspect implements InitializingBean { about below.
-
+
+ Key Concepts + + The org.acegisecurity.acls package should be consulted for its + major interfaces. The key interfaces are: + + + + Acl: Every domain object has one and only + one Acl object, which internally holds the + AccessControlEntrys as well as knows the owner + of the Acl. An Acl does not refer directly to + the domain object, but instead to an + ObjectIdentity. + + + + AccessControlEntry: An + Acl holds multiple AccessControlEntrys, which + are often abbreviated as ACEs in the framework. Each ACE refers to + a specific tuple of Permission, + Sid and Acl. An ACE can also + be granting or non-granting and contain audit settings. + + + + Permission: A permission represents an + immutable particular bit mask, and offers convenience functions + for bit masking and outputting information. + + + + Sid: The ACL module needs to refer to + principals and GrantedAuthority[]s. A level of + indirection is provided by the Sid interface. + Common classes include PrincipalSid (to + represent the principal inside an + Authentication object) and + GrantedAuthoritySid. + + + + ObjectIdentity: Each domain object is + represented internally within the ACL module by an + ObjectIdentity. + + + + AclService: Retrieves the + Acl applicable for a given + ObjectIdentity. + + + + MutableAclService: Allows a modified + Acl to be presented for persistence. It is not + essential to use this interface if you do not wish. + + + + The ACL module was based on extensive feedback from the user + community following real-world use of the original ACL module. This + feedback resulted in a rearchitecture of the ACL module to offer + significantly enhanced performance (particularly in the area of + database retrieval), significantly better encapsulation, higher + cohesion, and enhanced customisation points. + + The Contacts Sample that ships with Acegi Security 1.0.3 offers + a demonstration of the new ACL module. Converting Contacts from using + the old module to the new module was relatively simple, and users of + the old ACL module will likely find their applications can be modified + with relatively little work. + + We will document the new ACL module more fully with a subsequent + release. Please note that the new ACL module should be considered a + preview only (ie do not use in production without proper prior + testing), and there is a small chance there may be changes between + 1.0.3 and 1.1.0 when it will become final. Nevertheless, + compatibility-affecting changes are considered quite unlikely, + especially given the module is already based on several years of + feedback from users of the original ACL module. +
+ + + + Domain Object Security (old ACL module) + +
+ Overview + + PLEASE NOTE: Acegi Security 1.0.3 contains a preview of a new + ACL module. The new ACL module is a significant rewrite of the + existing ACL module. The new module can be found under the + org.acegisecurity.acls package, with the old ACL + module under org.acegisecurity.acl. We encourage + users to consider testing with the new ACL module and build + applications with it. The old ACL module should be considered + deprecated and may be removed from a future release. + + Complex applications often will find the need to define access + permissions not simply at a web request or method invocation level. + Instead, security decisions need to comprise both who + (Authentication), where + (MethodInvocation) and what + (SomeDomainObject). In other words, authorization + decisions also need to consider the actual domain object instance + subject of a method invocation. + + Imagine you're designing an application for a pet clinic. There + will be two main groups of users of your Spring-based application: + staff of the pet clinic, as well as the pet clinic's customers. The + staff will have access to all of the data, whilst your customers will + only be able to see their own customer records. To make it a little + more interesting, your customers can allow other users to see their + customer records, such as their "puppy preschool "mentor or president + of their local "Pony Club". Using Acegi Security as the foundation, + you have several approaches that can be used: + + Write your business methods to enforce the security. You + could consult a collection within the + Customer domain object instance to determine + which users have access. By using the + SecurityContextHolder.getContext().getAuthentication(), + you'll be able to access the Authentication + object. + + + + Write an AccessDecisionVoter to enforce + the security from the GrantedAuthority[]s + stored in the Authentication object. This + would mean your AuthenticationManager would + need to populate the Authentication with + custom GrantedAuthority[]s representing each + of the Customer domain object instances the + principal has access to. + + + + Write an AccessDecisionVoter to enforce + the security and open the target Customer + domain object directly. This would mean your voter needs access + to a DAO that allows it to retrieve the + Customer object. It would then access the + Customer object's collection of approved + users and make the appropriate decision. + + + + Each one of these approaches is perfectly legitimate. However, + the first couples your authorization checking to your business code. + The main problems with this include the enhanced difficulty of unit + testing and the fact it would be more difficult to reuse the + Customer authorization logic elsewhere. Obtaining + the GrantedAuthority[]s from the + Authentication object is also fine, but will not + scale to large numbers of Customers. If a user + might be able to access 5,000 Customers (unlikely + in this case, but imagine if it were a popular vet for a large Pony + Club!) the amount of memory consumed and time required to construct + the Authentication object would be undesirable. The + final method, opening the Customer directly from + external code, is probably the best of the three. It achieves + separation of concerns, and doesn't misuse memory or CPU cycles, but + it is still inefficient in that both the + AccessDecisionVoter and the eventual business + method itself will perform a call to the DAO responsible for + retrieving the Customer object. Two accesses per + method invocation is clearly undesirable. In addition, with every + approach listed you'll need to write your own access control list + (ACL) persistence and business logic from scratch. + + Fortunately, there is another alternative, which we'll talk + about below. +
+ +
Basic ACL Package Please note that our Basic ACL services are currently being diff --git a/project.xml b/project.xml index 64afaa2b78..3aeb084e45 100644 --- a/project.xml +++ b/project.xml @@ -265,7 +265,7 @@ hsqldb hsqldb - 1.7.3.0 + 1.8.0.4 jar http://hsqldb.sourceforge.net/ diff --git a/samples/contacts-tiger/src/main/java/sample/contact/annotation/ContactManagerBackend.java b/samples/contacts-tiger/src/main/java/sample/contact/annotation/ContactManagerBackend.java index ecbb31b07c..6edbea4c56 100644 --- a/samples/contacts-tiger/src/main/java/sample/contact/annotation/ContactManagerBackend.java +++ b/samples/contacts-tiger/src/main/java/sample/contact/annotation/ContactManagerBackend.java @@ -12,15 +12,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package sample.contact.annotation; import org.acegisecurity.Authentication; -import org.acegisecurity.acl.basic.AclObjectIdentity; -import org.acegisecurity.acl.basic.BasicAclExtendedDao; -import org.acegisecurity.acl.basic.NamedEntityObjectIdentity; -import org.acegisecurity.acl.basic.SimpleAclEntry; +import org.acegisecurity.acls.AccessControlEntry; +import org.acegisecurity.acls.MutableAcl; +import org.acegisecurity.acls.MutableAclService; +import org.acegisecurity.acls.NotFoundException; +import org.acegisecurity.acls.Permission; +import org.acegisecurity.acls.domain.BasePermission; +import org.acegisecurity.acls.objectidentity.ObjectIdentity; +import org.acegisecurity.acls.objectidentity.ObjectIdentityImpl; +import org.acegisecurity.acls.sid.PrincipalSid; +import org.acegisecurity.acls.sid.Sid; import org.acegisecurity.annotation.Secured; @@ -54,28 +59,36 @@ import java.util.Random; public class ContactManagerBackend extends ApplicationObjectSupport implements ContactManager, InitializingBean { //~ Instance fields ================================================================================================ - private BasicAclExtendedDao basicAclExtendedDao; private ContactDao contactDao; - private int counter = 100; + + // TODO: Assignment of annotations against class does not result in match in sample application + private MutableAclService mutableAclService; + private int counter = 1000; //~ Methods ======================================================================================================== @Secured({"ACL_CONTACT_ADMIN"}) - public void addPermission(Contact contact, String recipient, Integer permission) { - SimpleAclEntry simpleAclEntry = new SimpleAclEntry(); - simpleAclEntry.setAclObjectIdentity(makeObjectIdentity(contact)); - simpleAclEntry.setMask(permission.intValue()); - simpleAclEntry.setRecipient(recipient); - basicAclExtendedDao.create(simpleAclEntry); + public void addPermission(Contact contact, Sid recipient, Permission permission) { + MutableAcl acl; + ObjectIdentity oid = new ObjectIdentityImpl(Contact.class, contact.getId()); + + try { + acl = (MutableAcl) mutableAclService.readAclById(oid); + } catch (NotFoundException nfe) { + acl = mutableAclService.createAcl(oid); + } + + acl.insertAce(null, permission, recipient, true); + mutableAclService.updateAcl(acl); if (logger.isDebugEnabled()) { - logger.debug("Added permission " + permission + " for recipient " + recipient + " contact " + contact); + logger.debug("Added permission " + permission + " for Sid " + recipient + " contact " + contact); } } public void afterPropertiesSet() throws Exception { Assert.notNull(contactDao, "contactDao required"); - Assert.notNull(basicAclExtendedDao, "basicAclExtendedDao required"); + Assert.notNull(mutableAclService, "mutableAclService required"); } @Secured({"ROLE_USER"}) @@ -84,8 +97,8 @@ public class ContactManagerBackend extends ApplicationObjectSupport implements C contact.setId(new Long(counter++)); contactDao.create(contact); - // Grant the current principal access to the contact - addPermission(contact, getUsername(), new Integer(SimpleAclEntry.ADMINISTRATION)); + // Grant the current principal administrative permission to the contact + addPermission(contact, new PrincipalSid(getUsername()), BasePermission.ADMINISTRATION); if (logger.isDebugEnabled()) { logger.debug("Created contact " + contact + " and granted admin permission to recipient " + getUsername()); @@ -97,7 +110,8 @@ public class ContactManagerBackend extends ApplicationObjectSupport implements C contactDao.delete(contact.getId()); // Delete the ACL information as well - basicAclExtendedDao.delete(makeObjectIdentity(contact)); + ObjectIdentity oid = new ObjectIdentityImpl(Contact.class, contact.getId()); + mutableAclService.deleteAcl(oid, false); if (logger.isDebugEnabled()) { logger.debug("Deleted contact " + contact + " including ACL permissions"); @@ -105,8 +119,20 @@ public class ContactManagerBackend extends ApplicationObjectSupport implements C } @Secured({"ACL_CONTACT_ADMIN"}) - public void deletePermission(Contact contact, String recipient) { - basicAclExtendedDao.delete(makeObjectIdentity(contact), recipient); + public void deletePermission(Contact contact, Sid recipient, Permission permission) { + ObjectIdentity oid = new ObjectIdentityImpl(Contact.class, contact.getId()); + MutableAcl acl = (MutableAcl) mutableAclService.readAclById(oid); + + // Remove all permissions associated with this particular recipient (string equality to KISS) + AccessControlEntry[] entries = acl.getEntries(); + + for (int i = 0; i < entries.length; i++) { + if (entries[i].getSid().equals(recipient) && entries[i].getPermission().equals(permission)) { + acl.deleteAce(entries[i].getId()); + } + } + + mutableAclService.updateAcl(acl); if (logger.isDebugEnabled()) { logger.debug("Deleted contact " + contact + " ACL permissions for recipient " + recipient); @@ -131,15 +157,10 @@ public class ContactManagerBackend extends ApplicationObjectSupport implements C } List list = contactDao.findAllPrincipals(); - list.addAll(contactDao.findAllRoles()); return list; } - public BasicAclExtendedDao getBasicAclExtendedDao() { - return basicAclExtendedDao; - } - @Secured({"ROLE_USER", "AFTER_ACL_READ"}) @Transactional(readOnly = true) public Contact getById(Long id) { @@ -150,10 +171,6 @@ public class ContactManagerBackend extends ApplicationObjectSupport implements C return contactDao.getById(id); } - public ContactDao getContactDao() { - return contactDao; - } - /** * This is a public method. * @@ -181,18 +198,14 @@ public class ContactManagerBackend extends ApplicationObjectSupport implements C } } - private AclObjectIdentity makeObjectIdentity(Contact contact) { - return new NamedEntityObjectIdentity(contact.getClass().getName(), contact.getId().toString()); - } - - public void setBasicAclExtendedDao(BasicAclExtendedDao basicAclExtendedDao) { - this.basicAclExtendedDao = basicAclExtendedDao; - } - public void setContactDao(ContactDao contactDao) { this.contactDao = contactDao; } + public void setMutableAclService(MutableAclService mutableAclService) { + this.mutableAclService = mutableAclService; + } + public void update(Contact contact) { contactDao.update(contact); diff --git a/samples/contacts-tiger/src/main/webapp/common/WEB-INF/applicationContext-common-authorization.xml b/samples/contacts-tiger/src/main/webapp/common/WEB-INF/applicationContext-common-authorization.xml deleted file mode 100644 index 57c4d3b318..0000000000 --- a/samples/contacts-tiger/src/main/webapp/common/WEB-INF/applicationContext-common-authorization.xml +++ /dev/null @@ -1,163 +0,0 @@ - - - - - - - - - - - - net.sf.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION - - - net.sf.acegisecurity.acl.basic.SimpleAclEntry.READ - - - net.sf.acegisecurity.acl.basic.SimpleAclEntry.DELETE - - - - - - - - - ACL_CONTACT_READ - sample.contact.Contact - - - - - - - - - - - - ACL_CONTACT_DELETE - sample.contact.Contact - - - - - - - - - - - - ACL_CONTACT_ADMIN - sample.contact.Contact - - - - - - - - - - - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sample.contact.ContactManager.create=ROLE_USER - sample.contact.ContactManager.getAllRecipients=ROLE_USER - sample.contact.ContactManager.getAll=ROLE_USER,AFTER_ACL_COLLECTION_READ - sample.contact.ContactManager.getById=ROLE_USER,AFTER_ACL_READ - sample.contact.ContactManager.delete=ACL_CONTACT_DELETE - sample.contact.ContactManager.deletePermission=ACL_CONTACT_ADMIN - sample.contact.ContactManager.addPermission=ACL_CONTACT_ADMIN - - - - - diff --git a/samples/contacts-tiger/src/main/webapp/common/WEB-INF/applicationContext-common-business.xml b/samples/contacts-tiger/src/main/webapp/common/WEB-INF/applicationContext-common-business.xml deleted file mode 100644 index 6666a49cee..0000000000 --- a/samples/contacts-tiger/src/main/webapp/common/WEB-INF/applicationContext-common-business.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - org.hsqldb.jdbcDriver - - - jdbc:hsqldb:mem:contacts - - - sa - - - - - - - - - - - - - - - sample.contact.ContactManager.create=PROPAGATION_REQUIRED - sample.contact.ContactManager.getAllRecipients=PROPAGATION_REQUIRED,readOnly - sample.contact.ContactManager.getAll=PROPAGATION_REQUIRED,readOnly - sample.contact.ContactManager.getById=PROPAGATION_REQUIRED,readOnly - sample.contact.ContactManager.delete=PROPAGATION_REQUIRED - sample.contact.ContactManager.deletePermission=PROPAGATION_REQUIRED - sample.contact.ContactManager.addPermission=PROPAGATION_REQUIRED - - - - - - - - - - - - - - sample.contact.ContactManager - - - - - - - - - - - - - - - diff --git a/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/applicationContext-acegi-security.xml b/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/applicationContext-acegi-security.xml index ac3a50b40f..d185b84fef 100644 --- a/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/applicationContext-acegi-security.xml +++ b/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/applicationContext-acegi-security.xml @@ -21,7 +21,7 @@ CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT - /**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,basicProcessingFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor + /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,switchUserProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor @@ -38,7 +38,7 @@ - + @@ -90,6 +90,7 @@ + @@ -101,6 +102,18 @@ springRocks + + + + + + + + + + + + @@ -136,6 +149,11 @@ + + + + + @@ -173,10 +191,22 @@ /index.jsp=ROLE_ANONYMOUS,ROLE_USER /hello.htm=ROLE_ANONYMOUS,ROLE_USER /logoff.jsp=ROLE_ANONYMOUS,ROLE_USER + /switchuser.jsp=ROLE_SUPERVISOR + /j_acegi_switch_user=ROLE_SUPERVISOR /acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER /**=ROLE_USER + + + + + /j_acegi_switch_user + /j_acegi_exit_user + /acegi-security-sample-contacts-filter/secure/index.htm + + diff --git a/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/applicationContext-annotations.xml b/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/applicationContext-annotations.xml index 1bac89db5e..db2ccac1b8 100644 --- a/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/applicationContext-annotations.xml +++ b/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/applicationContext-annotations.xml @@ -12,60 +12,72 @@ - - net.sf.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION + + org.acegisecurity.acls.domain.BasePermission.ADMINISTRATION - - net.sf.acegisecurity.acl.basic.SimpleAclEntry.READ + + org.acegisecurity.acls.domain.BasePermission.READ - - net.sf.acegisecurity.acl.basic.SimpleAclEntry.DELETE + + org.acegisecurity.acls.domain.BasePermission.DELETE - + - - ACL_CONTACT_READ + + + + + + ACL_CONTACT_READ + + + + + + + sample.contact.Contact - - - - - - - - - ACL_CONTACT_DELETE + + + + + + ACL_CONTACT_DELETE + + + + + + + sample.contact.Contact - - - - - - - - - ACL_CONTACT_ADMIN + + + + + + ACL_CONTACT_ADMIN + + + + + + sample.contact.Contact - - - - - - - + false @@ -79,25 +91,53 @@ - - - - - - - - - - - - - - - + + + + + + + + aclCache + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -107,38 +147,41 @@ - - - - - - - - + + + + + + + + + + - - - - - - - - + + + + + + + + + + - - + - + - + false @@ -160,7 +203,7 @@ which in the above configuration is a JDK 5 Annotations Attributes-based source. --> diff --git a/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/applicationContext-business.xml b/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/applicationContext-business.xml index 5469acf88a..539fa69fde 100644 --- a/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/applicationContext-business.xml +++ b/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/applicationContext-business.xml @@ -31,7 +31,9 @@ - + + + @@ -42,7 +44,7 @@ - + diff --git a/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/web.xml b/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/web.xml index 7b067cd65f..f4ab0cf7bf 100644 --- a/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/web.xml +++ b/samples/contacts-tiger/src/main/webapp/filter/WEB-INF/web.xml @@ -33,10 +33,10 @@ Acegi Filter Chain Proxy - net.sf.acegisecurity.util.FilterToBeanProxy + org.acegisecurity.util.FilterToBeanProxy targetClass - net.sf.acegisecurity.util.FilterChainProxy + org.acegisecurity.util.FilterChainProxy @@ -64,7 +64,7 @@ to the WebApplicationContext --> - net.sf.acegisecurity.ui.session.HttpSessionEventPublisher + org.acegisecurity.ui.session.HttpSessionEventPublisher - - org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION + + org.acegisecurity.acls.domain.BasePermission.ADMINISTRATION - - org.acegisecurity.acl.basic.SimpleAclEntry.READ + + org.acegisecurity.acls.domain.BasePermission.READ - - org.acegisecurity.acl.basic.SimpleAclEntry.DELETE + + org.acegisecurity.acls.domain.BasePermission.DELETE @@ -37,41 +37,53 @@ - - ACL_CONTACT_READ + + + + + + ACL_CONTACT_READ + + + + + + + sample.contact.Contact - - - - - - - - - ACL_CONTACT_DELETE + + + + + + ACL_CONTACT_DELETE + + + + + + + sample.contact.Contact - - - - - - - - - ACL_CONTACT_ADMIN + + + + + + ACL_CONTACT_ADMIN + + + + + + sample.contact.Contact - - - - - - @@ -89,21 +101,49 @@ - - - - - - - - - - - - - - - + + + + + + + + aclCache + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -117,28 +157,31 @@ - - - - - - - - + + + + + + + + + + - - - - - - - - + + + + + + + + + + - sa @@ -46,7 +47,9 @@ - + + + @@ -66,7 +69,7 @@ - + diff --git a/samples/contacts/src/main/webapp/common/WEB-INF/contacts-servlet.xml b/samples/contacts/src/main/webapp/common/WEB-INF/contacts-servlet.xml index a796a6b16b..3176dec545 100644 --- a/samples/contacts/src/main/webapp/common/WEB-INF/contacts-servlet.xml +++ b/samples/contacts/src/main/webapp/common/WEB-INF/contacts-servlet.xml @@ -29,12 +29,12 @@ - + - + diff --git a/samples/contacts/src/main/webapp/common/WEB-INF/jsp/adminPermission.jsp b/samples/contacts/src/main/webapp/common/WEB-INF/jsp/adminPermission.jsp index 86bc15afee..c6aa83063d 100644 --- a/samples/contacts/src/main/webapp/common/WEB-INF/jsp/adminPermission.jsp +++ b/samples/contacts/src/main/webapp/common/WEB-INF/jsp/adminPermission.jsp @@ -1,4 +1,3 @@ -<%@ page import="org.acegisecurity.acl.basic.SimpleAclEntry" %> <%@ include file="/WEB-INF/jsp/include.jsp" %> @@ -11,27 +10,17 @@

- - + -
- <% - SimpleAclEntry simpleAcl = ((SimpleAclEntry) pageContext.getAttribute("acl")); - String permissionBlock = simpleAcl.printPermissionsBlock(); - %> - <%= permissionBlock %> - [] - + - - ">Del + ">Del

">Add Permission ">Manage diff --git a/samples/contacts/src/main/webapp/common/WEB-INF/jsp/deletePermission.jsp b/samples/contacts/src/main/webapp/common/WEB-INF/jsp/deletePermission.jsp index 73fd169d41..9fdb393ace 100644 --- a/samples/contacts/src/main/webapp/common/WEB-INF/jsp/deletePermission.jsp +++ b/samples/contacts/src/main/webapp/common/WEB-INF/jsp/deletePermission.jsp @@ -1,4 +1,3 @@ -<%@ page import="org.acegisecurity.acl.basic.SimpleAclEntry" %> <%@ include file="/WEB-INF/jsp/include.jsp" %> @@ -11,8 +10,11 @@

- + + + +

">Manage diff --git a/samples/contacts/src/main/webapp/common/WEB-INF/jsp/index.jsp b/samples/contacts/src/main/webapp/common/WEB-INF/jsp/index.jsp index 024bef7e72..98756513e5 100644 --- a/samples/contacts/src/main/webapp/common/WEB-INF/jsp/index.jsp +++ b/samples/contacts/src/main/webapp/common/WEB-INF/jsp/index.jsp @@ -18,12 +18,12 @@ - + ">Del - - + + ">Admin Permission - + diff --git a/samples/contacts/src/test/java/sample/contact/AbstractContactsSampleTest.java b/samples/contacts/src/test/java/sample/contact/AbstractContactsSampleTest.java deleted file mode 100644 index 0b170ada3b..0000000000 --- a/samples/contacts/src/test/java/sample/contact/AbstractContactsSampleTest.java +++ /dev/null @@ -1,115 +0,0 @@ -package sample.contact; - -import java.util.Iterator; -import java.util.List; - -import org.acegisecurity.Authentication; -import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; -import org.acegisecurity.context.SecurityContextImpl; -import org.acegisecurity.context.SecurityContextHolder; -import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.test.AbstractTransactionalSpringContextTests; - -/** - * Provides simplified access to the ContactManager bean and - * convenience test support methods. - * - * @author David Leal - * @author Ben Alex - */ -public abstract class AbstractContactsSampleTest extends AbstractTransactionalSpringContextTests { - - protected ContactManager contactManager; - - protected String[] getConfigLocations() { - setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME); - return new String[] { "applicationContext-common-authorization.xml", - "applicationContext-common-business.xml", - "applicationContext-contacts-test.xml" }; - } - - /** - * Locates the first Contact of the exact name specified. - * - *

- * Uses the {@link ContactManager#getAll()} method. - *

- * - * @param id - * Identify of the contact to locate (must be an exact match) - * - * @return the domain or null if not found - */ - protected Contact getContact(String id) { - List contacts = contactManager.getAll(); - Iterator iter = contacts.iterator(); - - while (iter.hasNext()) { - Contact contact = (Contact) iter.next(); - - if (contact.getId().equals(id)) { - return contact; - } - } - - return null; - } - - protected void assertContainsContact(String id, List contacts) { - Iterator iter = contacts.iterator(); - System.out.println(contacts); - while (iter.hasNext()) { - Contact contact = (Contact) iter.next(); - - if (contact.getId().toString().equals(id)) { - return; - } - } - - fail("List of contacts should have contained: " + id); - } - - protected void assertNotContainsContact(String id, List contacts) { - Iterator iter = contacts.iterator(); - - while (iter.hasNext()) { - Contact domain = (Contact) iter.next(); - - if (domain.getId().toString().equals(id)) { - fail("List of contact should NOT (but did) contain: " + id); - } - } - } - - protected void makeActiveUser(String username) { - String password = ""; - - if ("marissa".equals(username)) { - password = "koala"; - } else if ("dianne".equals(username)) { - password = "emu"; - } else if ("scott".equals(username)) { - password = "wombat"; - } else if ("peter".equals(username)) { - password = "opal"; - } - - Authentication authRequest = new UsernamePasswordAuthenticationToken( - username, password); - SecurityContextImpl secureContext = new SecurityContextImpl(); - secureContext.setAuthentication(authRequest); - SecurityContextHolder.setContext(secureContext); - } - - protected void onTearDownInTransaction() { - destroySecureContext(); - } - - private static void destroySecureContext() { - SecurityContextHolder.setContext(new SecurityContextImpl()); - } - - public void setContactManager(ContactManager contactManager) { - this.contactManager = contactManager; - } -} diff --git a/samples/contacts/src/test/java/sample/contact/GetAllContactsTests.java b/samples/contacts/src/test/java/sample/contact/GetAllContactsTests.java index f4f5062b94..462a4c536d 100644 --- a/samples/contacts/src/test/java/sample/contact/GetAllContactsTests.java +++ b/samples/contacts/src/test/java/sample/contact/GetAllContactsTests.java @@ -12,68 +12,174 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package sample.contact; +import org.acegisecurity.Authentication; + +import org.acegisecurity.acls.domain.BasePermission; +import org.acegisecurity.acls.sid.PrincipalSid; + +import org.acegisecurity.context.SecurityContextHolder; + +import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; + +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; + +import org.springframework.test.AbstractTransactionalSpringContextTests; + +import java.util.Iterator; import java.util.List; /** - * Tests {@link - * com.acegitech.dns.domain.DomainManager#findAllDomainsLike(String)}. + * Tests {@link ContactManager}. * * @author David Leal + * @author Ben Alex */ -public class GetAllContactsTests extends AbstractContactsSampleTest { - //~ Methods ================================================================ +public class GetAllContactsTests extends AbstractTransactionalSpringContextTests { + //~ Instance fields ================================================================================================ - public void testFindAllDomainsLikeAsDianne() { + protected ContactManager contactManager; + + //~ Methods ======================================================================================================== + + protected void assertContainsContact(String id, List contacts) { + Iterator iter = contacts.iterator(); + System.out.println(contacts); + + while (iter.hasNext()) { + Contact contact = (Contact) iter.next(); + + if (contact.getId().toString().equals(id)) { + return; + } + } + + fail("List of contacts should have contained: " + id); + } + + protected void assertNotContainsContact(String id, List contacts) { + Iterator iter = contacts.iterator(); + + while (iter.hasNext()) { + Contact domain = (Contact) iter.next(); + + if (domain.getId().toString().equals(id)) { + fail("List of contact should NOT (but did) contain: " + id); + } + } + } + + private static void destroySecureContext() { + SecurityContextHolder.clearContext(); + } + + protected String[] getConfigLocations() { + setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME); + + return new String[] { + "applicationContext-common-authorization.xml", "applicationContext-common-business.xml", + "applicationContext-contacts-test.xml" + }; + } + + /** + * Locates the first Contact of the exact name specified.

Uses the {@link + * ContactManager#getAll()} method.

+ * + * @param id Identify of the contact to locate (must be an exact match) + * + * @return the domain or null if not found + */ + protected Contact getContact(String id) { + List contacts = contactManager.getAll(); + Iterator iter = contacts.iterator(); + + while (iter.hasNext()) { + Contact contact = (Contact) iter.next(); + + if (contact.getId().equals(id)) { + return contact; + } + } + + return null; + } + + protected void makeActiveUser(String username) { + String password = ""; + + if ("marissa".equals(username)) { + password = "koala"; + } else if ("dianne".equals(username)) { + password = "emu"; + } else if ("scott".equals(username)) { + password = "wombat"; + } else if ("peter".equals(username)) { + password = "opal"; + } + + Authentication authRequest = new UsernamePasswordAuthenticationToken(username, password); + SecurityContextHolder.getContext().setAuthentication(authRequest); + } + + protected void onTearDownInTransaction() { + destroySecureContext(); + } + + public void setContactManager(ContactManager contactManager) { + this.contactManager = contactManager; + } + + public void testDianne() { makeActiveUser("dianne"); // has ROLE_USER - + List contacts = contactManager.getAll(); assertEquals(4, contacts.size()); - + assertContainsContact(Long.toString(4), contacts); assertContainsContact(Long.toString(5), contacts); assertContainsContact(Long.toString(6), contacts); assertContainsContact(Long.toString(8), contacts); - + assertNotContainsContact(Long.toString(1), contacts); assertNotContainsContact(Long.toString(2), contacts); assertNotContainsContact(Long.toString(3), contacts); - } - public void testFindAllDomainsLikeAsMarissa() { + public void testMarissa() { makeActiveUser("marissa"); // has ROLE_SUPERVISOR - - List contacts = contactManager.getAll(); - + + List contacts = contactManager.getAll(); + assertEquals(4, contacts.size()); - + assertContainsContact(Long.toString(1), contacts); assertContainsContact(Long.toString(2), contacts); assertContainsContact(Long.toString(3), contacts); assertContainsContact(Long.toString(4), contacts); - - assertNotContainsContact(Long.toString(5), contacts); - + + assertNotContainsContact(Long.toString(5), contacts); + + Contact c1 = contactManager.getById(new Long(4)); + + contactManager.deletePermission(c1, new PrincipalSid("bob"), BasePermission.ADMINISTRATION); } - public void testFindAllDomainsLikeAsScott() { + public void testScott() { makeActiveUser("scott"); // has ROLE_USER - + List contacts = contactManager.getAll(); - - assertEquals(5, contacts.size()); - + + assertEquals(5, contacts.size()); + assertContainsContact(Long.toString(4), contacts); assertContainsContact(Long.toString(6), contacts); assertContainsContact(Long.toString(7), contacts); assertContainsContact(Long.toString(8), contacts); assertContainsContact(Long.toString(9), contacts); - - assertNotContainsContact(Long.toString(1), contacts); - + + assertNotContainsContact(Long.toString(1), contacts); } } diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/domain/BasePermission.java b/sandbox/other/src/main/java/org/acegisecurity/acls/domain/BasePermission.java deleted file mode 100644 index b8398aec23..0000000000 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/domain/BasePermission.java +++ /dev/null @@ -1,106 +0,0 @@ -/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.acegisecurity.acls.domain; - -import org.acegisecurity.acls.AclFormattingUtils; -import org.acegisecurity.acls.Permission; - -/** - * DOCUMENT ME! - * - * @author $author$ - * @version $Revision$ - */ -public class BasePermission implements Permission { - //~ Static fields/initializers ===================================================================================== - - public static final Permission READ = new BasePermission(1 << 0, 'R'); // 1 - public static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2 - public static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4 - public static final Permission DELETE = new BasePermission(1 << 3, 'D'); // 8 - public static final Permission ADMINISTRATION = new BasePermission(1 << 4, 'A'); // 16 - - //~ Instance fields ================================================================================================ - - private char code; - private int mask; - - //~ Constructors =================================================================================================== - - private BasePermission(int mask, char code) { - this.mask = mask; - this.code = code; - } - - //~ Methods ======================================================================================================== - - /** - * Dynamically creates a CumulativePermission representing the active bits in the passed mask. - * NB: Only uses BasePermission! - * - * @param mask to review - * - * @return DOCUMENT ME! - */ - public static Permission buildFromMask(int mask) { - CumulativePermission permission = new CumulativePermission(); - - // TODO: Write the rest of it to iterate through the 32 bits and instantiate BasePermissions - if (mask == 1) { - permission.set(READ); - } - - if (mask == 2) { - permission.set(WRITE); - } - - if (mask == 4) { - permission.set(CREATE); - } - - if (mask == 8) { - permission.set(DELETE); - } - - if (mask == 16) { - permission.set(ADMINISTRATION); - } - - return permission; - } - - public boolean equals(Object arg0) { - if (!(arg0 instanceof BasePermission)) { - return false; - } - - BasePermission rhs = (BasePermission) arg0; - - return (this.mask == rhs.getMask()); - } - - public int getMask() { - return mask; - } - - public String getPattern() { - return AclFormattingUtils.printBinary(mask, code); - } - - public String toString() { - return "BasePermission[" + getPattern() + "=" + mask + "]"; - } -} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/JdbcAclService.java b/sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/JdbcAclService.java deleted file mode 100644 index c27d26dcba..0000000000 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/jdbc/JdbcAclService.java +++ /dev/null @@ -1,78 +0,0 @@ -/* 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.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; - -import java.util.Map; - -import javax.sql.DataSource; - - -/** - * Simple JDBC-based implementation of AclService.

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 */ { - //~ Instance fields ================================================================================================ - - private AclCache aclCache; - private JdbcTemplate template; - private LookupStrategy lookupStrategy; - - //~ Constructors =================================================================================================== - - 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; - } - - //~ Methods ======================================================================================================== - - public Map readAclsById(ObjectIdentity[] objects) { - return readAclsById(objects, null); - } - - /** - * Method required by interface. - * - * @param objects DOCUMENT ME! - * @param sids DOCUMENT ME! - * - * @return DOCUMENT ME! - * - * @throws NotFoundException DOCUMENT ME! - */ - public Map readAclsById(ObjectIdentity[] objects, Sid[] sids) - throws NotFoundException { - return lookupStrategy.readAclsById(objects, sids); - } -} diff --git a/sandbox/other/src/main/java/org/acegisecurity/acls/package.html b/sandbox/other/src/main/java/org/acegisecurity/acls/package.html deleted file mode 100644 index 692d065470..0000000000 --- a/sandbox/other/src/main/java/org/acegisecurity/acls/package.html +++ /dev/null @@ -1,5 +0,0 @@ - - -Enables retrieval of access control lists (ACLs) for domain object instances. - - diff --git a/sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/JdbcAclServiceTests.java b/sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/JdbcAclServiceTests.java deleted file mode 100644 index 88e4266a46..0000000000 --- a/sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/JdbcAclServiceTests.java +++ /dev/null @@ -1,65 +0,0 @@ -/* 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.Acl; -import org.acegisecurity.acls.objectidentity.ObjectIdentity; -import org.acegisecurity.acls.objectidentity.ObjectIdentityImpl; - -import org.springframework.test.AbstractDependencyInjectionSpringContextTests; - -import java.util.Iterator; -import java.util.Map; - - -/** - * DOCUMENT ME! - * - * @author $author$ - * @version $Revision$ - */ -public class JdbcAclServiceTests extends AbstractDependencyInjectionSpringContextTests { - //~ Instance fields ================================================================================================ - - private JdbcAclService jdbcAclService; - - //~ Methods ======================================================================================================== - - protected String[] getConfigLocations() { - return new String[] {"classpath:org/acegisecurity/acls/jdbc/applicationContext-test.xml"}; - } - - public void setJdbcAclService(JdbcAclService jdbcAclService) { - this.jdbcAclService = jdbcAclService; - } - - public void testStub() { - ObjectIdentity id1 = new ObjectIdentityImpl("java.lang.Object", new Long(1)); - ObjectIdentity id2 = new ObjectIdentityImpl("java.lang.Object", new Long(2)); - ObjectIdentity id3 = new ObjectIdentityImpl("java.lang.Object", new Long(3)); - ObjectIdentity id4 = new ObjectIdentityImpl("java.lang.Object", new Long(4)); - ObjectIdentity id5 = new ObjectIdentityImpl("java.lang.Object", new Long(5)); - ObjectIdentity id6 = new ObjectIdentityImpl("java.lang.Object", 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)); - } - } -} diff --git a/sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/testData.sql b/sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/testData.sql deleted file mode 100644 index fe9d2d2ecc..0000000000 --- a/sandbox/other/src/test/java/org/acegisecurity/acls/jdbc/testData.sql +++ /dev/null @@ -1,70 +0,0 @@ -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);