Explorar el Código

Make MethodDefinitionMap query interfaces defined by secure objects, to properly support MethodDefinitionSourceAdvisor.

Ben Alex hace 21 años
padre
commit
f123e9c333

+ 2 - 0
changelog.txt

@@ -2,6 +2,8 @@ Changes in version 0.7 (2004-xx-xx)
 -----------------------------------
 
 * Added MethodDefinitionSourceAdvisor for performance and autoproxying
+* Added MethodDefinitionMap querying of interfaces defined by secure objects
+* Documentation improvements
 
 Changes in version 0.6.1 (2004-09-25)
 -------------------------------------

+ 90 - 1
core/src/main/java/org/acegisecurity/intercept/method/MethodDefinitionMap.java

@@ -15,6 +15,7 @@
 
 package net.sf.acegisecurity.intercept.method;
 
+import net.sf.acegisecurity.ConfigAttribute;
 import net.sf.acegisecurity.ConfigAttributeDefinition;
 
 import org.aopalliance.intercept.MethodInvocation;
@@ -34,6 +35,32 @@ import java.util.Map;
 /**
  * Stores a {@link ConfigAttributeDefinition} for each method signature defined
  * in a bean context.
+ * 
+ * <p>
+ * For consistency with {@link MethodDefinitionAttributes} as well as support
+ * for {@link MethodDefinitionSourceAdvisor}, this implementation will return
+ * a <code>ConfigAttributeDefinition</code> containing all configuration
+ * attributes defined against:
+ * 
+ * <ul>
+ * <li>
+ * The method-specific attributes defined for the intercepted method of the
+ * intercepted class.
+ * </li>
+ * <li>
+ * The method-specific attributes defined by any explicitly implemented
+ * interface if that interface contains a method signature matching that of
+ * the intercepted method.
+ * </li>
+ * </ul>
+ * </p>
+ * 
+ * <p>
+ * In general you should therefore define the <b>interface method</b>s of your
+ * secure objects, not the implementations. For example, define
+ * <code>com.company.Foo.findAll=ROLE_TEST</code> but not
+ * <code>com.company.FooImpl.findAll=ROLE_TEST</code>.
+ * </p>
  *
  * @author Ben Alex
  * @version $Id$
@@ -53,10 +80,28 @@ public class MethodDefinitionMap extends AbstractMethodDefinitionSource {
 
     //~ Methods ================================================================
 
+    /**
+     * Obtains the configuration attributes explicitly defined against this
+     * bean. This method will not return implicit configuration attributes
+     * that may be returned by {@link #lookupAttributes(MethodInvocation)} as
+     * it does not have access to a method invocation at this time.
+     *
+     * @return the attributes explicitly defined against this bean
+     */
     public Iterator getConfigAttributeDefinitions() {
         return methodMap.values().iterator();
     }
 
+    /**
+     * Obtains the number of configuration attributes explicitly defined
+     * against this bean. This method will not return implicit configuration
+     * attributes that may be returned by {@link
+     * #lookupAttributes(MethodInvocation)} as it does not have access to a
+     * method invocation at this time.
+     *
+     * @return the number of configuration attributes explicitly defined
+     *         against this bean
+     */
     public int getMethodMapSize() {
         return this.methodMap.size();
     }
@@ -165,7 +210,38 @@ public class MethodDefinitionMap extends AbstractMethodDefinitionSource {
     }
 
     protected ConfigAttributeDefinition lookupAttributes(MethodInvocation mi) {
-        return (ConfigAttributeDefinition) this.methodMap.get(mi.getMethod());
+        ConfigAttributeDefinition definition = new ConfigAttributeDefinition();
+
+        // Add attributes explictly defined for this method invocation
+        ConfigAttributeDefinition directlyAssigned = (ConfigAttributeDefinition) this.methodMap
+            .get(mi.getMethod());
+        merge(definition, directlyAssigned);
+
+        // Add attributes explicitly defined for this method invocation's interfaces
+        Class[] interfaces = mi.getMethod().getDeclaringClass().getInterfaces();
+
+        for (int i = 0; i < interfaces.length; i++) {
+            Class clazz = interfaces[i];
+
+            try {
+                // Look for the method on the current interface
+                Method interfaceMethod = clazz.getDeclaredMethod(mi.getMethod()
+                                                                   .getName(),
+                        mi.getMethod().getParameterTypes());
+                ConfigAttributeDefinition interfaceAssigned = (ConfigAttributeDefinition) this.methodMap
+                    .get(interfaceMethod);
+                merge(definition, interfaceAssigned);
+            } catch (Exception e) {
+                // skip this interface
+            }
+        }
+
+        // Return null if empty, as per abstract superclass contract
+        if (definition.size() == 0) {
+            return null;
+        } else {
+            return definition;
+        }
     }
 
     /**
@@ -184,4 +260,17 @@ public class MethodDefinitionMap extends AbstractMethodDefinitionSource {
         || (mappedName.startsWith("*")
         && methodName.endsWith(mappedName.substring(1, mappedName.length())));
     }
+
+    private void merge(ConfigAttributeDefinition definition,
+        ConfigAttributeDefinition toMerge) {
+        if (toMerge == null) {
+            return;
+        }
+
+        Iterator attribs = toMerge.getConfigAttributes();
+
+        while (attribs.hasNext()) {
+            definition.addConfigAttribute((ConfigAttribute) attribs.next());
+        }
+    }
 }

+ 28 - 0
core/src/test/java/org/acegisecurity/intercept/method/MethodDefinitionSourceEditorTests.java

@@ -91,6 +91,34 @@ public class MethodDefinitionSourceEditorTests extends TestCase {
         }
     }
 
+    public void testConcreteClassInvocationsAlsoReturnDefinitionsAgainstInterface()
+        throws Exception {
+        MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
+        editor.setAsText(
+            "net.sf.acegisecurity.ITargetObject.makeLower*=ROLE_FROM_INTERFACE\r\nnet.sf.acegisecurity.ITargetObject.makeUpper*=ROLE_FROM_INTERFACE\r\nnet.sf.acegisecurity.TargetObject.makeUpper*=ROLE_FROM_IMPLEMENTATION");
+
+        MethodDefinitionMap map = (MethodDefinitionMap) editor.getValue();
+        assertEquals(3, map.getMethodMapSize());
+
+        ConfigAttributeDefinition returnedMakeLower = map.getAttributes(new MockMethodInvocation(
+                    TargetObject.class, "makeLowerCase",
+                    new Class[] {String.class}));
+        ConfigAttributeDefinition expectedMakeLower = new ConfigAttributeDefinition();
+        expectedMakeLower.addConfigAttribute(new SecurityConfig(
+                "ROLE_FROM_INTERFACE"));
+        assertEquals(expectedMakeLower, returnedMakeLower);
+
+        ConfigAttributeDefinition returnedMakeUpper = map.getAttributes(new MockMethodInvocation(
+                    TargetObject.class, "makeUpperCase",
+                    new Class[] {String.class}));
+        ConfigAttributeDefinition expectedMakeUpper = new ConfigAttributeDefinition();
+        expectedMakeUpper.addConfigAttribute(new SecurityConfig(
+                "ROLE_FROM_IMPLEMENTATION"));
+        expectedMakeUpper.addConfigAttribute(new SecurityConfig(
+                "ROLE_FROM_INTERFACE"));
+        assertEquals(expectedMakeUpper, returnedMakeUpper);
+    }
+
     public void testEmptyStringReturnsEmptyMap() {
         MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
         editor.setAsText("");

+ 29 - 0
upgrade-06-07.txt

@@ -0,0 +1,29 @@
+===============================================================================
+          ACEGI SECURITY SYSTEM FOR SPRING - UPGRADING FROM 0.6 TO 0.7
+===============================================================================
+
+The following should help most casual users of the project update their
+applications:
+
+- MethodDefinitionMap, which is usually used by MethodSecurityInterceptor
+  for its objectDefinitionSource property, has been changed. From 0.7, when
+  MethodDefinitionMap is queried for configuration attributes associated with
+  secure MethodInvocations, it will use any method matching in the method
+  invocation class (as it always has) plus any method matching any interface
+  the MethodInvocation class directly implements. So consider a PersonManager
+  interface, a PersonManagerImpl class that implements it, and a definition of
+  PersonManager.findAll=ROLE_FOO. In this example, any query for either
+  PersonManager.findAll OR PersonManagerImpl.findAll will return ROLE_FOO.
+  As we have always encouraged definition against the interface names (as per
+  this example), this change should not adversely impact users. This change
+  was necessary because of the new MethodDefinitionSourceAdvisor (see below).
+  Refer to the MethodDefinitionMap JavaDocs for further clarification.
+
+- MethodDefinitionSourceAdvisor can now be used instead of defining proxies
+  for secure business objects. The advisor is fully compatible with both
+  MethodDefinitionMap and MethodDefinitionAttributes. Using an advisor allows
+  caching of which methods the MethodSecurityInterceptor should handle, thus
+  providing a performance benefit as MethodSecurityInterceptor is not called
+  for public (non-secure) objects. It also simplifies configuration.
+
+$Id$