Quellcode durchsuchen

SEC-813: Allow custom Permission classes to be used.

Ben Alex vor 17 Jahren
Ursprung
Commit
e38d5dfd87

+ 59 - 0
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;
+	}
+}

+ 39 - 0
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);
+    }
+}

+ 14 - 135
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.
- *
+ * 
+ * <p>
+ * You may subclass this class to add additional permissions, or use this class as a guide
+ * for creating your own permission classes.
+ * </p>
+ * 
  * @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 a <code>CumulativePermission</code> or <code>BasePermission</code> 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;
-    }
-
-    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;
-    }
-
-    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);
+    static {
+    	registerPermissionsFor(BasePermission.class);
     }
 
-    public String toString() {
-        return "BasePermission[" + getPattern() + "=" + mask + "]";
+    protected BasePermission(int mask, char code) {
+    	super(mask, code);
     }
-
-	public int hashCode() {
-		return this.mask;
-	}
 }

+ 12 - 38
acl/src/main/java/org/springframework/security/acls/domain/CumulativePermission.java

@@ -19,20 +19,21 @@ import org.springframework.security.acls.Permission;
 
 
 /**
- * Represents a <code>Permission</code> that is constructed at runtime from other permissions.<p>Methods return
- * <code>this</code>, in order to facilitate method chaining.</p>
+ * Represents a <code>Permission</code> that is constructed at runtime from other permissions.
+ * 
+ * <p>Methods return <code>this</code>, in order to facilitate method chaining.</p>
  *
  * @author Ben Alex
  * @version $Id$
  */
-public class CumulativePermission implements Permission {
-    //~ Instance fields ================================================================================================
+public class CumulativePermission extends AbstractPermission {
 
     private String pattern = THIRTY_TWO_RESERVED_OFF;
-    private int mask = 0;
-
-    //~ Methods ========================================================================================================
 
+    public CumulativePermission() {
+    	super(0, ' ');
+    }
+    
     public CumulativePermission clear(Permission permission) {
         this.mask &= ~permission.getMask();
         this.pattern = AclFormattingUtils.demergePatterns(this.pattern, permission.getPattern());
@@ -46,43 +47,16 @@ public class CumulativePermission implements Permission {
 
         return this;
     }
-
     
-    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 hashCode() {
-		return this.mask;
-	}
-
-	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 + "]";
+    
+    public String getPattern() {
+        return this.pattern;
     }
+
 }

+ 127 - 0
acl/src/main/java/org/springframework/security/acls/domain/DefaultPermissionFactory.java

@@ -0,0 +1,127 @@
+package org.springframework.security.acls.domain;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.security.acls.Permission;
+import org.springframework.security.acls.jdbc.LookupStrategy;
+import org.springframework.util.Assert;
+
+/**
+ * Default implementation of {@link PermissionFactory}.
+ * 
+ * <p>
+ * 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}).
+ * </p>
+ * 
+ * @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;
+    }
+    
+}

+ 24 - 0
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 a <code>CumulativePermission</code> or <code>BasePermission</code> 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);
+
+}

+ 6 - 1
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 <code>ObjectIdentity</code>s directly from the database.<p>The caller is responsible
      * for optimization issues, such as selecting the identities to lookup, ensuring the cache doesn't contain them

+ 4 - 4
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());

+ 40 - 0
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);
+    }
+}