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

This commit is contained in:
Ben Alex 2004-12-17 21:56:05 +00:00
parent 93ec9e20a3
commit c5ea35d093
3 changed files with 234 additions and 41 deletions

View File

@ -25,11 +25,14 @@ import net.sf.acegisecurity.acl.AclManager;
import net.sf.acegisecurity.acl.basic.AbstractBasicAclEntry; import net.sf.acegisecurity.acl.basic.AbstractBasicAclEntry;
import net.sf.acegisecurity.acl.basic.SimpleAclEntry; 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.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import java.lang.reflect.Array;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -168,25 +171,22 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider
return null; 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( 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); + 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 // Locate unauthorised Collection elements
Iterator collectionIter = collection.iterator(); Iterator collectionIter = filterer.iterator();
while (collectionIter.hasNext()) { while (collectionIter.hasNext()) {
Object domainObject = collectionIter.next(); Object domainObject = collectionIter.next();
@ -228,7 +228,7 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider
} }
if (!hasPermission) { if (!hasPermission) {
removeList.add(domainObject); filterer.remove(domainObject);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug( logger.debug(
@ -238,22 +238,7 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider
} }
} }
// Now the Iterator has ended, remove Objects from Collection return filterer.getFilteredObject();
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;
} }
} }
@ -281,3 +266,170 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProvider
return true; 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);
}
}

View File

@ -167,6 +167,44 @@ public class BasicAclEntryAfterInvocationCollectionFilteringProviderTests
assertEquals("belmont", filteredList.get(0)); 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() public void testDetectsIfReturnedObjectIsNotACollection()
throws Exception { throws Exception {
// Create an AclManager // Create an AclManager

View File

@ -129,6 +129,9 @@
<contributor> <contributor>
<name>Orlando Garcia Carmona</name> <name>Orlando Garcia Carmona</name>
</contributor> </contributor>
<contributor>
<name>Joni Suominen</name>
</contributor>
</contributors> </contributors>
<dependencies> <dependencies>
<dependency> <dependency>
@ -151,16 +154,6 @@
<war.bundle>true</war.bundle> <war.bundle>true</war.bundle>
</properties> </properties>
</dependency> </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> <dependency>
<groupId>retroweaver</groupId> <groupId>retroweaver</groupId>
<artifactId>retroweaver</artifactId> <artifactId>retroweaver</artifactId>
@ -258,6 +251,16 @@
<version>3.8.1</version> <version>3.8.1</version>
<type>jar</type> <type>jar</type>
</dependency> </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> <dependency>
<groupId>springframework</groupId> <groupId>springframework</groupId>
<artifactId>spring-core</artifactId> <artifactId>spring-core</artifactId>