Extend After ACL provider to also filter arrays. Thanks to Joni Suominen.
This commit is contained in:
parent
93ec9e20a3
commit
c5ea35d093
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
23
project.xml
23
project.xml
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue