소스 검색

Extend After ACL provider to also filter arrays. Thanks to Joni Suominen.

Ben Alex 20 년 전
부모
커밋
c5ea35d093

+ 183 - 31
core/src/main/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationCollectionFilteringProvider.java

@@ -25,11 +25,14 @@ import net.sf.acegisecurity.acl.AclManager;
 import net.sf.acegisecurity.acl.basic.AbstractBasicAclEntry;
 import net.sf.acegisecurity.acl.basic.SimpleAclEntry;
 
+import org.apache.commons.collections.iterators.ArrayIterator;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import org.springframework.beans.factory.InitializingBean;
 
+import java.lang.reflect.Array;
+
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -168,25 +171,22 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider
                     return null;
                 }
 
-                if (!(returnedObject instanceof Collection)) {
+                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 null) was required as the returnedObject, but the returnedObject was: "
+                        "A Collection or an array (or null) was required as the returnedObject, but the returnedObject was: "
                         + returnedObject);
                 }
 
-                Collection collection = (Collection) returnedObject;
-
-                // 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)
-                Set removeList = new HashSet();
-
                 // Locate unauthorised Collection elements
-                Iterator collectionIter = collection.iterator();
+                Iterator collectionIter = filterer.iterator();
 
                 while (collectionIter.hasNext()) {
                     Object domainObject = collectionIter.next();
@@ -228,7 +228,7 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider
                     }
 
                     if (!hasPermission) {
-                        removeList.add(domainObject);
+                        filterer.remove(domainObject);
 
                         if (logger.isDebugEnabled()) {
                             logger.debug(
@@ -238,22 +238,7 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider
                     }
                 }
 
-                // 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;
+                return filterer.getFilteredObject();
             }
         }
 
@@ -281,3 +266,170 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider
         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;
+    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 net.sf.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 net.sf.acegisecurity.afterinvocation.Filterer#iterator()
+     */
+    public Iterator iterator() {
+        return collection.iterator();
+    }
+
+    /**
+     * @see net.sf.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 net.sf.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 net.sf.acegisecurity.afterinvocation.Filterer#iterator()
+     */
+    public Iterator iterator() {
+        return new ArrayIterator(list);
+    }
+
+    /**
+     * @see net.sf.acegisecurity.afterinvocation.Filterer#remove(java.lang.Object)
+     */
+    public void remove(Object object) {
+        removeList.add(object);
+    }
+}

+ 38 - 0
core/src/test/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationCollectionFilteringProviderTests.java

@@ -167,6 +167,44 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProviderTests
         assertEquals("belmont", filteredList.get(0));
     }
 
+    public void testCorrectOperationWhenReturnedObjectIsArray()
+        throws Exception {
+        // Create an AclManager
+        AclManager aclManager = new MockAclManager("belmont", "marissa",
+                new AclEntry[] {new MockAclEntry(), new SimpleAclEntry(
+                        "marissa", new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.ADMINISTRATION), new SimpleAclEntry(
+                        "marissa", new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.READ), new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null, SimpleAclEntry.DELETE)});
+
+        BasicAclEntryAfterInvocationCollectionFilteringProvider provider = new BasicAclEntryAfterInvocationCollectionFilteringProvider();
+        provider.setAclManager(aclManager);
+        assertEquals(aclManager, provider.getAclManager());
+        provider.afterPropertiesSet();
+
+        // Create a Collection containing many items, which only "belmont"
+        // should remain in after filtering by provider
+        String[] list = new String[4];
+        list[0] = "sydney";
+        list[1] = "melbourne";
+        list[2] = "belmont";
+        list[3] = "brisbane";
+
+        // Create the Authentication and Config Attribs we'll be presenting
+        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("marissa",
+                "NOT_USED");
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("AFTER_ACL_COLLECTION_READ"));
+
+        // Filter
+        String[] filteredList = (String[]) provider.decide(auth,
+                new MockMethodInvocation(), attr, list);
+
+        assertEquals(1, filteredList.length);
+        assertEquals("belmont", filteredList[0]);
+    }
+
     public void testDetectsIfReturnedObjectIsNotACollection()
         throws Exception {
         // Create an AclManager

+ 13 - 10
project.xml

@@ -129,6 +129,9 @@
     <contributor>
       <name>Orlando Garcia Carmona</name>
     </contributor>
+    <contributor>
+      <name>Joni Suominen</name>
+    </contributor>
   </contributors>
   <dependencies>
     <dependency>
@@ -151,16 +154,6 @@
         <war.bundle>true</war.bundle>
       </properties>
     </dependency>
-    <dependency>
-      <groupId>jetty</groupId>
-      <artifactId>org.mortbay.jetty</artifactId>
-      <version>4.2.22</version>
-      <type>jar</type>
-      <url>http://jetty.mortbay.org</url>
-      <properties>
-        <war.bundle>true</war.bundle>
-      </properties>
-    </dependency>
     <dependency>
       <groupId>retroweaver</groupId>
       <artifactId>retroweaver</artifactId>
@@ -258,6 +251,16 @@
       <version>3.8.1</version>
       <type>jar</type>
     </dependency>
+    <dependency>
+      <groupId>jetty</groupId>
+      <artifactId>org.mortbay.jetty</artifactId>
+      <version>4.2.22</version>
+      <type>jar</type>
+      <url>http://jetty.mortbay.org</url>
+      <properties>
+        <war.bundle>true</war.bundle>
+      </properties>
+    </dependency>
     <dependency>
       <groupId>springframework</groupId>
       <artifactId>spring-core</artifactId>