diff --git a/core/src/main/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationCollectionFilteringProvider.java b/core/src/main/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationCollectionFilteringProvider.java
index 83bee24bb6..4877c0b5bf 100644
--- a/core/src/main/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationCollectionFilteringProvider.java
+++ b/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);
+ }
+}
diff --git a/core/src/test/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationCollectionFilteringProviderTests.java b/core/src/test/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationCollectionFilteringProviderTests.java
index 9d4bc3e160..339d527af6 100644
--- a/core/src/test/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationCollectionFilteringProviderTests.java
+++ b/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
diff --git a/project.xml b/project.xml
index b866fca1e7..06cb790e3d 100644
--- a/project.xml
+++ b/project.xml
@@ -129,6 +129,9 @@
Orlando Garcia Carmona
+
+ Joni Suominen
+
@@ -151,16 +154,6 @@
true
-
- jetty
- org.mortbay.jetty
- 4.2.22
- jar
- http://jetty.mortbay.org
-
- true
-
-
retroweaver
retroweaver
@@ -258,6 +251,16 @@
3.8.1
jar
+
+ jetty
+ org.mortbay.jetty
+ 4.2.22
+ jar
+ http://jetty.mortbay.org
+
+ true
+
+
springframework
spring-core