diff --git a/acl/src/main/java/org/springframework/security/acls/domain/AbstractPermission.java b/acl/src/main/java/org/springframework/security/acls/domain/AbstractPermission.java new file mode 100644 index 0000000000..8cd7c7f371 --- /dev/null +++ b/acl/src/main/java/org/springframework/security/acls/domain/AbstractPermission.java @@ -0,0 +1,59 @@ +package org.springframework.security.acls.domain; + +import org.springframework.security.acls.AclFormattingUtils; +import org.springframework.security.acls.Permission; + +/** + * Provides an abstract superclass for {@link Permission} implementations. + * + * @author Ben Alex + * @since 2.0.3 + * @see AbstractRegisteredPermission + * + */ +public abstract class AbstractPermission implements Permission { + + //~ Instance fields ================================================================================================ + + protected char code; + protected int mask; + + //~ Constructors =================================================================================================== + + protected AbstractPermission(int mask, char code) { + this.mask = mask; + this.code = code; + } + + //~ Methods ======================================================================================================== + + public final boolean equals(Object arg0) { + if (arg0 == null) { + return false; + } + + if (!(arg0 instanceof Permission)) { + return false; + } + + Permission rhs = (Permission) arg0; + + return (this.mask == rhs.getMask()); + } + + public final int getMask() { + return mask; + } + + public String getPattern() { + return AclFormattingUtils.printBinary(mask, code); + } + + public final String toString() { + return this.getClass().getSimpleName() + "[" + getPattern() + "=" + mask + "]"; + } + + public final int hashCode() { + return this.mask; + } +} diff --git a/acl/src/main/java/org/springframework/security/acls/domain/AbstractRegisteredPermission.java b/acl/src/main/java/org/springframework/security/acls/domain/AbstractRegisteredPermission.java new file mode 100644 index 0000000000..3dcab21e1d --- /dev/null +++ b/acl/src/main/java/org/springframework/security/acls/domain/AbstractRegisteredPermission.java @@ -0,0 +1,39 @@ +package org.springframework.security.acls.domain; + +import org.springframework.security.acls.Permission; + +/** + * Provides an abstract base for standard {@link Permission} instances that wish to offer static convenience + * methods to callers via delegation to {@link DefaultPermissionFactory}. + * + * @author Ben Alex + * @since 2.0.3 + * + */ +public abstract class AbstractRegisteredPermission extends AbstractPermission { + protected static DefaultPermissionFactory defaultPermissionFactory = new DefaultPermissionFactory(); + + protected AbstractRegisteredPermission(int mask, char code) { + super(mask, code); + } + + protected final static void registerPermissionsFor(Class subClass) { + defaultPermissionFactory.registerPublicPermissions(subClass); + } + + public final static Permission buildFromMask(int mask) { + return defaultPermissionFactory.buildFromMask(mask); + } + + public final static Permission[] buildFromMask(int[] masks) { + return defaultPermissionFactory.buildFromMask(masks); + } + + public final static Permission buildFromName(String name) { + return defaultPermissionFactory.buildFromName(name); + } + + public final static Permission[] buildFromName(String[] names) { + return defaultPermissionFactory.buildFromName(names); + } +} diff --git a/acl/src/main/java/org/springframework/security/acls/domain/BasePermission.java b/acl/src/main/java/org/springframework/security/acls/domain/BasePermission.java index f585dac306..592f32eed5 100644 --- a/acl/src/main/java/org/springframework/security/acls/domain/BasePermission.java +++ b/acl/src/main/java/org/springframework/security/acls/domain/BasePermission.java @@ -14,157 +14,36 @@ */ package org.springframework.security.acls.domain; -import org.springframework.security.acls.AclFormattingUtils; import org.springframework.security.acls.Permission; -import org.springframework.util.Assert; - -import java.lang.reflect.Field; - -import java.util.HashMap; -import java.util.Map; - /** * A set of standard permissions. - * + * + *
+ * You may subclass this class to add additional permissions, or use this class as a guide + * for creating your own permission classes. + *
+ * * @author Ben Alex * @version $Id$ */ -public final class BasePermission implements Permission { - //~ Static fields/initializers ===================================================================================== - +public class BasePermission extends AbstractRegisteredPermission { 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 aCumulativePermission
or BasePermission
representing the
- * active bits in the passed mask.
- *
- * @param mask to build
- *
- * @return a Permission representing the requested object
+ * Registers the public static permissions defined on this class. This is mandatory so
+ * that the static methods will operate correctly.
*/
- 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;
+ static {
+ registerPermissionsFor(BasePermission.class);
}
- public static Permission[] buildFromMask(int[] masks) {
- if ((masks == null) || (masks.length == 0)) {
- return new Permission[0];
- }
-
- Permission[] permissions = new Permission[masks.length];
-
- for (int i = 0; i < masks.length; i++) {
- permissions[i] = buildFromMask(masks[i]);
- }
-
- return permissions;
+ protected BasePermission(int mask, char code) {
+ super(mask, code);
}
-
- 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[0];
- }
-
- Permission[] permissions = new Permission[names.length];
-
- for (int i = 0; i < names.length; i++) {
- permissions[i] = buildFromName(names[i]);
- }
-
- return permissions;
- }
-
- public boolean equals(Object arg0) {
- if (arg0 == null) {
- return false;
- }
-
- if (!(arg0 instanceof Permission)) {
- return false;
- }
-
- Permission rhs = (Permission) 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 + "]";
- }
-
- public int hashCode() {
- return this.mask;
- }
}
diff --git a/acl/src/main/java/org/springframework/security/acls/domain/CumulativePermission.java b/acl/src/main/java/org/springframework/security/acls/domain/CumulativePermission.java
index 29c676b270..5dca663072 100644
--- a/acl/src/main/java/org/springframework/security/acls/domain/CumulativePermission.java
+++ b/acl/src/main/java/org/springframework/security/acls/domain/CumulativePermission.java
@@ -19,20 +19,21 @@ import org.springframework.security.acls.Permission;
/**
- * Represents a Permission
that is constructed at runtime from other permissions.Methods return
- * this
, in order to facilitate method chaining.
Permission
that is constructed at runtime from other permissions.
+ *
+ * Methods return this
, in order to facilitate method chaining.
+ * Generally this class will be used by a {@link Permission} instance, as opposed to being dependency + * injected into a {@link LookupStrategy} or similar. Nevertheless, the latter mode of operation is + * fully supported (in which case your {@link Permission} implementations probably should extend + * {@link AbstractPermission} instead of {@link AbstractRegisteredPermission}). + *
+ * + * @author Ben Alex + * @since 2.0.3 + * + */ +public class DefaultPermissionFactory implements PermissionFactory { + private Map registeredPermissionsByInteger = new HashMap(); + private Map registeredPermissionsByName = new HashMap(); + + /** + * Permit registration of a {@link DefaultPermissionFactory} class. The class must provide + * public static fields of type {@link Permission} to represent the possible permissions. + * + * @param clazz a {@link Permission} class with public static fields to register + */ + public void registerPublicPermissions(Class clazz) { + Assert.notNull(clazz, "Class required"); + Assert.isAssignable(Permission.class, clazz); + + Field[] fields = clazz.getFields(); + + for (int i = 0; i < fields.length; i++) { + try { + Object fieldValue = fields[i].get(null); + + if (Permission.class.isAssignableFrom(fieldValue.getClass())) { + // Found a Permission static field + Permission perm = (Permission) fieldValue; + String permissionName = fields[i].getName(); + + registerPermission(perm, permissionName); + } + } catch (Exception ignore) {} + } + } + + public void registerPermission(Permission perm, String permissionName) { + Assert.notNull(perm, "Permission required"); + Assert.hasText(permissionName, "Permission name required"); + + Integer mask = new Integer(perm.getMask()); + + // Ensure no existing Permission uses this integer or code + Assert.isTrue(!registeredPermissionsByInteger.containsKey(mask), "An existing Permission already provides mask " + mask); + Assert.isTrue(!registeredPermissionsByName.containsKey(permissionName), "An existing Permission already provides name '" + permissionName + "'"); + + // Register the new Permission + registeredPermissionsByInteger.put(mask, perm); + registeredPermissionsByName.put(permissionName, perm); + } + + public Permission buildFromMask(int mask) { + if (registeredPermissionsByInteger.containsKey(new Integer(mask))) { + // The requested mask has an exactly match against a statically-defined Permission, so return it + return (Permission) registeredPermissionsByInteger.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) registeredPermissionsByInteger.get(new Integer(permissionToCheck)); + Assert.state(p != null, "Mask " + permissionToCheck + " does not have a corresponding static Permission"); + permission.set(p); + } + } + + return permission; + } + + public Permission[] buildFromMask(int[] masks) { + if ((masks == null) || (masks.length == 0)) { + return new Permission[0]; + } + + Permission[] permissions = new Permission[masks.length]; + + for (int i = 0; i < masks.length; i++) { + permissions[i] = buildFromMask(masks[i]); + } + + return permissions; + } + + public Permission buildFromName(String name) { + Assert.isTrue(registeredPermissionsByName.containsKey(name), "Unknown permission '" + name + "'"); + + return (Permission) registeredPermissionsByName.get(name); + } + + public Permission[] buildFromName(String[] names) { + if ((names == null) || (names.length == 0)) { + return new Permission[0]; + } + + Permission[] permissions = new Permission[names.length]; + + for (int i = 0; i < names.length; i++) { + permissions[i] = buildFromName(names[i]); + } + + return permissions; + } + +} diff --git a/acl/src/main/java/org/springframework/security/acls/domain/PermissionFactory.java b/acl/src/main/java/org/springframework/security/acls/domain/PermissionFactory.java new file mode 100644 index 0000000000..86c303aa1c --- /dev/null +++ b/acl/src/main/java/org/springframework/security/acls/domain/PermissionFactory.java @@ -0,0 +1,24 @@ +package org.springframework.security.acls.domain; + +import org.springframework.security.acls.Permission; + +/** + * Provides a simple mechanism to retrieve {@link Permission} instances from integer masks. + * + * @author Ben Alex + * @since 2.0.3 + * + */ +public interface PermissionFactory { + + /** + * Dynamically creates aCumulativePermission
or BasePermission
representing the
+ * active bits in the passed mask.
+ *
+ * @param mask to build
+ *
+ * @return a Permission representing the requested object
+ */
+ public abstract Permission buildFromMask(int mask);
+
+}
\ No newline at end of file
diff --git a/acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java b/acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java
index 4f424ac000..c8606be7d4 100644
--- a/acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java
+++ b/acl/src/main/java/org/springframework/security/acls/jdbc/BasicLookupStrategy.java
@@ -240,7 +240,8 @@ public final class BasicLookupStrategy implements LookupStrategy {
recipient = new GrantedAuthoritySid(rs.getString("ace_sid"));
}
- Permission permission = BasePermission.buildFromMask(rs.getInt("mask"));
+ int mask = rs.getInt("mask");
+ Permission permission = convertMaskIntoPermission(mask);
boolean granting = rs.getBoolean("granting");
boolean auditSuccess = rs.getBoolean("audit_success");
boolean auditFailure = rs.getBoolean("audit_failure");
@@ -265,6 +266,10 @@ public final class BasicLookupStrategy implements LookupStrategy {
}
}
+ protected Permission convertMaskIntoPermission(int mask) {
+ return BasePermission.buildFromMask(mask);
+ }
+
/**
* Looks up a batch of ObjectIdentity
s 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 diff --git a/acl/src/test/java/org/springframework/security/acls/domain/PermissionTests.java b/acl/src/test/java/org/springframework/security/acls/domain/PermissionTests.java index 5c56d955fc..c36bdfdf1d 100644 --- a/acl/src/test/java/org/springframework/security/acls/domain/PermissionTests.java +++ b/acl/src/test/java/org/springframework/security/acls/domain/PermissionTests.java @@ -22,7 +22,7 @@ import org.springframework.security.acls.Permission; /** - * Tests BasePermission and CumulativePermission. + * Tests classes associated with Permission. * * @author Ben Alex * @version $Id${date} @@ -63,9 +63,9 @@ public class PermissionTests { assertEquals("CumulativePermission[...............................R=1]", new CumulativePermission().set(BasePermission.READ).toString()); - System.out.println("A = " + new CumulativePermission().set(BasePermission.ADMINISTRATION).toString()); - assertEquals("CumulativePermission[...........................A....=16]", - new CumulativePermission().set(BasePermission.ADMINISTRATION).toString()); + System.out.println("A = " + new CumulativePermission().set(SpecialPermission.ENTER).set(BasePermission.ADMINISTRATION).toString()); + assertEquals("CumulativePermission[..........................EA....=48]", + new CumulativePermission().set(SpecialPermission.ENTER).set(BasePermission.ADMINISTRATION).toString()); System.out.println("RA = " + new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ).toString()); diff --git a/acl/src/test/java/org/springframework/security/acls/domain/SpecialPermission.java b/acl/src/test/java/org/springframework/security/acls/domain/SpecialPermission.java new file mode 100644 index 0000000000..ce5cb0c9d5 --- /dev/null +++ b/acl/src/test/java/org/springframework/security/acls/domain/SpecialPermission.java @@ -0,0 +1,40 @@ +/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.acls.domain; + +import org.springframework.security.acls.Permission; + + +/** + * A test permission. + * + * @author Ben Alex + * @version $Id$ + */ +public class SpecialPermission extends BasePermission { + public static final Permission ENTER = new SpecialPermission(1 << 5, 'E'); // 32 + + /** + * Registers the public static permissions defined on this class. This is mandatory so + * that the static methods will operate correctly. + */ + static { + registerPermissionsFor(SpecialPermission.class); + } + + protected SpecialPermission(int mask, char code) { + super(mask, code); + } +}