Bladeren bron

Expand test coverage. Clover via Maven (without excluding appropriate patterns like *Exception and debug messages) has modified coverage from 77.2% to 95%.

Ben Alex 21 jaren geleden
bovenliggende
commit
41b41ba316
44 gewijzigde bestanden met toevoegingen van 3940 en 75 verwijderingen
  1. 2 0
      core/src/test/java/org/acegisecurity/ITargetObject.java
  2. 66 0
      core/src/test/java/org/acegisecurity/MockAclManager.java
  3. 60 0
      core/src/test/java/org/acegisecurity/MockAfterInvocationManager.java
  4. 36 0
      core/src/test/java/org/acegisecurity/MockApplicationContext.java
  5. 162 0
      core/src/test/java/org/acegisecurity/MockJoinPoint.java
  6. 17 3
      core/src/test/java/org/acegisecurity/MockMethodInvocation.java
  7. 1 1
      core/src/test/java/org/acegisecurity/MockRunAsManager.java
  8. 4 0
      core/src/test/java/org/acegisecurity/TargetObject.java
  9. 5 0
      core/src/test/java/org/acegisecurity/acl/basic/BasicAclProviderTests.java
  10. 4 0
      core/src/test/java/org/acegisecurity/acl/basic/SimpleAclEntryTests.java
  11. 336 0
      core/src/test/java/org/acegisecurity/acl/basic/jdbc/JdbcExtendedDaoImplTests.java
  12. 223 0
      core/src/test/java/org/acegisecurity/afterinvocation/AfterInvocationProviderManagerTests.java
  13. 373 0
      core/src/test/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationCollectionFilteringProviderTests.java
  14. 284 0
      core/src/test/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationProviderTests.java
  15. 125 0
      core/src/test/java/org/acegisecurity/intercept/AbstractSecurityInterceptorTests.java
  16. 74 0
      core/src/test/java/org/acegisecurity/intercept/InterceptorStatusTokenTests.java
  17. 74 0
      core/src/test/java/org/acegisecurity/intercept/event/AuthenticationCredentialsNotFoundEventTests.java
  18. 88 0
      core/src/test/java/org/acegisecurity/intercept/event/AuthenticationFailureEventTests.java
  19. 88 0
      core/src/test/java/org/acegisecurity/intercept/event/AuthorizationFailureEventTests.java
  20. 74 0
      core/src/test/java/org/acegisecurity/intercept/event/AuthorizedEventTests.java
  21. 5 45
      core/src/test/java/org/acegisecurity/intercept/method/MethodDefinitionAttributesTests.java
  22. 24 1
      core/src/test/java/org/acegisecurity/intercept/method/MethodDefinitionSourceEditorTests.java
  23. 43 0
      core/src/test/java/org/acegisecurity/intercept/method/aopalliance/MethodDefinitionSourceAdvisorTests.java
  24. 111 10
      core/src/test/java/org/acegisecurity/intercept/method/aopalliance/MethodSecurityInterceptorTests.java
  25. 163 0
      core/src/test/java/org/acegisecurity/intercept/method/aspectj/AspectJSecurityInterceptorTests.java
  26. 51 3
      core/src/test/java/org/acegisecurity/intercept/web/FilterSecurityInterceptorTests.java
  27. 2 0
      core/src/test/java/org/acegisecurity/providers/cas/CasAuthenticationTokenTests.java
  28. 81 1
      core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java
  29. 21 0
      core/src/test/java/org/acegisecurity/providers/dao/PasswordDaoAuthenticationProviderTests.java
  30. 16 0
      core/src/test/java/org/acegisecurity/providers/dao/event/LoggerListenerTests.java
  31. 226 0
      core/src/test/java/org/acegisecurity/taglibs/authz/AclTagTests.java
  32. 133 0
      core/src/test/java/org/acegisecurity/taglibs/authz/AuthenticationTagTests.java
  33. 6 0
      core/src/test/java/org/acegisecurity/ui/AbstractProcessingFilterTests.java
  34. 143 0
      core/src/test/java/org/acegisecurity/ui/httpinvoker/AuthenticationSimpleHttpInvokerRequestExecutorTests.java
  35. 102 0
      core/src/test/java/org/acegisecurity/ui/rmi/ContextPropagatingRemoteInvocationTests.java
  36. 32 1
      core/src/test/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilterEntryPointTests.java
  37. 20 0
      core/src/test/java/org/acegisecurity/util/FilterToBeanProxyTests.java
  38. 12 0
      core/src/test/java/org/acegisecurity/util/PortResolverImplTests.java
  39. 485 0
      core/src/test/java/org/acegisecurity/vote/BasicAclEntryVoterTests.java
  40. 42 0
      core/src/test/java/org/acegisecurity/vote/SomeDomainObject.java
  41. 29 0
      core/src/test/java/org/acegisecurity/vote/SomeDomainObjectManager.java
  42. 29 0
      core/src/test/resources/org/acegisecurity/applicationContext.xml
  43. 13 10
      core/src/test/resources/org/acegisecurity/intercept/method/aopalliance/applicationContext.xml
  44. 55 0
      core/src/test/resources/org/acegisecurity/intercept/method/applicationContext.xml

+ 2 - 0
core/src/test/java/org/acegisecurity/ITargetObject.java

@@ -24,6 +24,8 @@ package net.sf.acegisecurity;
 public interface ITargetObject {
     //~ Methods ================================================================
 
+    public Integer computeHashCode(String input);
+
     public int countLength(String input);
 
     public String makeLowerCase(String input);

+ 66 - 0
core/src/test/java/org/acegisecurity/MockAclManager.java

@@ -0,0 +1,66 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity;
+
+import net.sf.acegisecurity.acl.AclEntry;
+import net.sf.acegisecurity.acl.AclManager;
+
+
+/**
+ * Returns the indicated collection of <code>AclEntry</code>s when the given
+ * <code>Authentication</code> principal is presented for the indicated domain
+ * <code>Object</code> instance.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MockAclManager implements AclManager {
+    //~ Instance fields ========================================================
+
+    private Object object;
+    private Object principal;
+    private AclEntry[] acls;
+
+    //~ Constructors ===========================================================
+
+    public MockAclManager(Object domainObject, Object principal, AclEntry[] acls) {
+        this.object = domainObject;
+        this.principal = principal;
+        this.acls = acls;
+    }
+
+    private MockAclManager() {}
+
+    //~ Methods ================================================================
+
+    public AclEntry[] getAcls(Object domainInstance,
+        Authentication authentication) {
+        if (domainInstance.equals(object)
+            && authentication.getPrincipal().equals(principal)) {
+            return acls;
+        } else {
+            return null;
+        }
+    }
+
+    public AclEntry[] getAcls(Object domainInstance) {
+        if (domainInstance.equals(object)) {
+            return acls;
+        } else {
+            return null;
+        }
+    }
+}

+ 60 - 0
core/src/test/java/org/acegisecurity/MockAfterInvocationManager.java

@@ -0,0 +1,60 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity;
+
+import java.util.Iterator;
+
+
+/**
+ * If there is a configuration attribute of "AFTER_INVOCATION_MOCK", modifies
+ * the return value to null.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MockAfterInvocationManager implements AfterInvocationManager {
+    //~ Methods ================================================================
+
+    public Object decide(Authentication authentication, Object object,
+        ConfigAttributeDefinition config, Object returnedObject)
+        throws AccessDeniedException {
+        Iterator iter = config.getConfigAttributes();
+
+        while (iter.hasNext()) {
+            ConfigAttribute attr = (ConfigAttribute) iter.next();
+
+            if (this.supports(attr)) {
+                return null;
+            }
+        }
+
+        // this "after invocation" hasn't got a config attribute asking
+        // for this mock to modify the returned object
+        return returnedObject;
+    }
+
+    public boolean supports(ConfigAttribute attribute) {
+        if (attribute.getAttribute().equals("AFTER_INVOCATION_MOCK")) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public boolean supports(Class clazz) {
+        return true;
+    }
+}

+ 36 - 0
core/src/test/java/org/acegisecurity/MockApplicationContext.java

@@ -0,0 +1,36 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity;
+
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+
+/**
+ * Simply returns an <code>ApplicationContext</code> which has a couple of
+ * <code>ApplicationEvent</code> listeners.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MockApplicationContext {
+    //~ Methods ================================================================
+
+    public static ConfigurableApplicationContext getContext() {
+        return new ClassPathXmlApplicationContext(
+            "net/sf/acegisecurity/applicationContext.xml");
+    }
+}

+ 162 - 0
core/src/test/java/org/acegisecurity/MockJoinPoint.java

@@ -0,0 +1,162 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.reflect.CodeSignature;
+import org.aspectj.lang.reflect.SourceLocation;
+
+import java.lang.reflect.Method;
+
+
+/**
+ * A mock AspectJ <code>JoinPoint</code>.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MockJoinPoint implements JoinPoint {
+    //~ Instance fields ========================================================
+
+    private Method beingInvoked;
+    private Object object;
+
+    //~ Constructors ===========================================================
+
+    public MockJoinPoint(Object object, Method beingInvoked) {
+        this.object = object;
+        this.beingInvoked = beingInvoked;
+    }
+
+    private MockJoinPoint() {}
+
+    //~ Methods ================================================================
+
+    public Object[] getArgs() {
+        throw new UnsupportedOperationException("mock not implemented");
+    }
+
+    public String getKind() {
+        throw new UnsupportedOperationException("mock not implemented");
+    }
+
+    public Signature getSignature() {
+        throw new UnsupportedOperationException("mock not implemented");
+    }
+
+    public SourceLocation getSourceLocation() {
+        throw new UnsupportedOperationException("mock not implemented");
+    }
+
+    public StaticPart getStaticPart() {
+        return new MockStaticPart(beingInvoked);
+    }
+
+    public Object getTarget() {
+        return object;
+    }
+
+    public Object getThis() {
+        throw new UnsupportedOperationException("mock not implemented");
+    }
+
+    public String toLongString() {
+        throw new UnsupportedOperationException("mock not implemented");
+    }
+
+    public String toShortString() {
+        throw new UnsupportedOperationException("mock not implemented");
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockCodeSignature implements CodeSignature {
+        private Method beingInvoked;
+
+        public MockCodeSignature(Method beingInvoked) {
+            this.beingInvoked = beingInvoked;
+        }
+
+        private MockCodeSignature() {}
+
+        public Class getDeclaringType() {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+
+        public String getDeclaringTypeName() {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+
+        public Class[] getExceptionTypes() {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+
+        public int getModifiers() {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+
+        public String getName() {
+            return beingInvoked.getName();
+        }
+
+        public String[] getParameterNames() {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+
+        public Class[] getParameterTypes() {
+            return beingInvoked.getParameterTypes();
+        }
+
+        public String toLongString() {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+
+        public String toShortString() {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+    }
+
+    private class MockStaticPart implements StaticPart {
+        private Method beingInvoked;
+
+        public MockStaticPart(Method beingInvoked) {
+            this.beingInvoked = beingInvoked;
+        }
+
+        private MockStaticPart() {}
+
+        public String getKind() {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+
+        public Signature getSignature() {
+            return new MockCodeSignature(beingInvoked);
+        }
+
+        public SourceLocation getSourceLocation() {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+
+        public String toLongString() {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+
+        public String toShortString() {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+    }
+}

+ 17 - 3
core/src/test/java/org/acegisecurity/MockMethodInvocation.java

@@ -22,20 +22,34 @@ import java.lang.reflect.Method;
 
 
 /**
- * DOCUMENT ME!
+ * Represents the AOP Alliance <code>MethodInvocation</code> secure object.
  *
  * @author Ben Alex
  * @version $Id$
  */
 public class MockMethodInvocation implements MethodInvocation {
+    //~ Instance fields ========================================================
+
+    private Method method;
+    private Object[] arguments;
+
+    //~ Constructors ===========================================================
+
+    public MockMethodInvocation(Method method, Object[] arguments) {
+        this.method = method;
+        this.arguments = arguments;
+    }
+
+    public MockMethodInvocation() {}
+
     //~ Methods ================================================================
 
     public Object[] getArguments() {
-        throw new UnsupportedOperationException("mock method not implemented");
+        return arguments;
     }
 
     public Method getMethod() {
-        throw new UnsupportedOperationException("mock method not implemented");
+        return method;
     }
 
     public AccessibleObject getStaticPart() {

+ 1 - 1
core/src/test/java/org/acegisecurity/MockRunAsManager.java

@@ -47,7 +47,7 @@ public class MockRunAsManager implements RunAsManager {
     }
 
     public boolean supports(ConfigAttribute attribute) {
-        if ("RUN_AS".equals(attribute.getAttribute())) {
+        if (attribute.getAttribute().startsWith("RUN_AS")) {
             return true;
         } else {
             return false;

+ 4 - 0
core/src/test/java/org/acegisecurity/TargetObject.java

@@ -29,6 +29,10 @@ import net.sf.acegisecurity.context.SecureContext;
 public class TargetObject implements ITargetObject {
     //~ Methods ================================================================
 
+    public Integer computeHashCode(String input) {
+        return new Integer(input.hashCode());
+    }
+
     public int countLength(String input) {
         return input.length();
     }

+ 5 - 0
core/src/test/java/org/acegisecurity/acl/basic/BasicAclProviderTests.java

@@ -292,6 +292,11 @@ public class BasicAclProviderTests extends TestCase {
         assertFalse(provider.supports(new MockDomain(4)));
     }
 
+    public void testSupportsReturnsNullIfObjectNull() {
+        BasicAclProvider provider = new BasicAclProvider();
+        assertFalse(provider.supports(new Integer(34)));
+    }
+
     private JdbcDaoImpl makePopulatedJdbcDao() throws Exception {
         JdbcDaoImpl dao = new JdbcDaoImpl();
         dao.setDataSource(PopulatedDatabase.getDataSource());

+ 4 - 0
core/src/test/java/org/acegisecurity/acl/basic/SimpleAclEntryTests.java

@@ -82,6 +82,10 @@ public class SimpleAclEntryTests extends TestCase {
         assertFalse(acl.isPermitted(SimpleAclEntry.ADMINISTRATION));
         acl.togglePermission(SimpleAclEntry.CREATE);
         assertFalse(acl.isPermitted(SimpleAclEntry.CREATE));
+
+        acl.togglePermission(SimpleAclEntry.DELETE);
+        assertTrue(acl.isPermitted(SimpleAclEntry.DELETE));
+        assertEquals("----D", acl.printPermissionsBlock());
     }
 
     public void testDetectsNullOnMainConstructor() {

+ 336 - 0
core/src/test/java/org/acegisecurity/acl/basic/jdbc/JdbcExtendedDaoImplTests.java

@@ -0,0 +1,336 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.acl.basic.jdbc;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.PopulatedDatabase;
+import net.sf.acegisecurity.acl.basic.AclObjectIdentity;
+import net.sf.acegisecurity.acl.basic.BasicAclEntry;
+import net.sf.acegisecurity.acl.basic.NamedEntityObjectIdentity;
+import net.sf.acegisecurity.acl.basic.SimpleAclEntry;
+
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.dao.DataRetrievalFailureException;
+
+import org.springframework.jdbc.object.MappingSqlQuery;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+
+/**
+ * Tests {@link JdbcExtendedDaoImpl}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class JdbcExtendedDaoImplTests extends TestCase {
+    //~ Static fields/initializers =============================================
+
+    public static final String OBJECT_IDENTITY = "net.sf.acegisecurity.acl.DomainObject";
+
+    //~ Constructors ===========================================================
+
+    public JdbcExtendedDaoImplTests() {
+        super();
+    }
+
+    public JdbcExtendedDaoImplTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(JdbcExtendedDaoImplTests.class);
+    }
+
+    public void testChangeMask() throws Exception {
+        JdbcExtendedDaoImpl dao = makePopulatedJdbcDao();
+        AclObjectIdentity identity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
+                "204");
+        AclObjectIdentity parentIdentity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
+                "1");
+
+        // Create a BasicAclEntry for this AclObjectIdentity
+        SimpleAclEntry simpleAcl1 = new SimpleAclEntry("marissa", identity,
+                parentIdentity, SimpleAclEntry.CREATE);
+        dao.create(simpleAcl1);
+
+        // Create another BasicAclEntry for this AclObjectIdentity
+        SimpleAclEntry simpleAcl2 = new SimpleAclEntry("scott", identity,
+                parentIdentity, SimpleAclEntry.READ);
+        dao.create(simpleAcl2);
+
+        // Check creation was successful
+        BasicAclEntry[] acls = dao.getAcls(identity);
+        assertEquals(2, acls.length);
+        assertEquals(SimpleAclEntry.CREATE, acls[0].getMask());
+        assertEquals(SimpleAclEntry.READ, acls[1].getMask());
+
+        // Attempt to change mask
+        dao.changeMask(identity, "marissa",
+            new Integer(SimpleAclEntry.ADMINISTRATION));
+        dao.changeMask(identity, "scott", new Integer(SimpleAclEntry.NOTHING));
+        acls = dao.getAcls(identity);
+        assertEquals(2, acls.length);
+        assertEquals("marissa", acls[0].getRecipient());
+        assertEquals(SimpleAclEntry.ADMINISTRATION, acls[0].getMask());
+        assertEquals("scott", acls[1].getRecipient());
+        assertEquals(SimpleAclEntry.NOTHING, acls[1].getMask());
+    }
+
+    public void testChangeMaskThrowsExceptionWhenExistingRecordNotFound()
+        throws Exception {
+        JdbcExtendedDaoImpl dao = makePopulatedJdbcDao();
+        AclObjectIdentity identity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
+                "205");
+        AclObjectIdentity parentIdentity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
+                "1");
+
+        // Create at least one record for this AclObjectIdentity
+        SimpleAclEntry simpleAcl1 = new SimpleAclEntry("marissa", identity,
+                parentIdentity, SimpleAclEntry.CREATE);
+        dao.create(simpleAcl1);
+
+        // Attempt to change mask, but for a recipient we don't have
+        try {
+            dao.changeMask(identity, "scott",
+                new Integer(SimpleAclEntry.ADMINISTRATION));
+            fail("Should have thrown DataRetrievalFailureException");
+        } catch (DataRetrievalFailureException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testConvertAclObjectIdentity() throws Exception {
+        JdbcExtendedDaoImpl dao = makePopulatedJdbcDao();
+
+        try {
+            dao.convertAclObjectIdentityToString(new AclObjectIdentity() {
+                    // not a NamedEntityObjectIdentity
+                });
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testCreationOfIdentityThenAclInSeparateInvocations()
+        throws Exception {
+        JdbcExtendedDaoImpl dao = makePopulatedJdbcDao();
+        AclObjectIdentity identity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
+                "206");
+        AclObjectIdentity parentIdentity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
+                "1");
+
+        // Create just the object identity (NB: recipient and mask is null)
+        SimpleAclEntry simpleAcl1 = new SimpleAclEntry();
+        simpleAcl1.setAclObjectIdentity(identity);
+        simpleAcl1.setAclObjectParentIdentity(parentIdentity);
+        dao.create(simpleAcl1);
+
+        // Delete it
+        dao.delete(identity);
+    }
+
+    public void testDeletionOfAllRecipients() throws Exception {
+        JdbcExtendedDaoImpl dao = makePopulatedJdbcDao();
+        AclObjectIdentity identity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
+                "203");
+
+        // Create a BasicAclEntry for this AclObjectIdentity
+        SimpleAclEntry simpleAcl1 = new SimpleAclEntry("marissa", identity,
+                null, SimpleAclEntry.CREATE);
+        dao.create(simpleAcl1);
+
+        // Create another BasicAclEntry for this AclObjectIdentity
+        SimpleAclEntry simpleAcl2 = new SimpleAclEntry("scott", identity, null,
+                SimpleAclEntry.READ);
+        dao.create(simpleAcl2);
+
+        // Check creation was successful
+        BasicAclEntry[] acls = dao.getAcls(identity);
+        assertEquals(2, acls.length);
+
+        // Attempt deletion and check delete successful
+        dao.delete(identity);
+        assertNull(dao.getAcls(identity));
+    }
+
+    public void testDeletionOfSpecificRecipient() throws Exception {
+        JdbcExtendedDaoImpl dao = makePopulatedJdbcDao();
+        AclObjectIdentity identity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
+                "202");
+        AclObjectIdentity parentIdentity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
+                "1");
+
+        // Create a BasicAclEntry for this AclObjectIdentity
+        SimpleAclEntry simpleAcl1 = new SimpleAclEntry("marissa", identity,
+                parentIdentity, SimpleAclEntry.CREATE);
+        dao.create(simpleAcl1);
+
+        // Create another BasicAclEntry for this AclObjectIdentity
+        SimpleAclEntry simpleAcl2 = new SimpleAclEntry("scott", identity,
+                parentIdentity, SimpleAclEntry.READ);
+        dao.create(simpleAcl2);
+
+        // Check creation was successful
+        BasicAclEntry[] acls = dao.getAcls(identity);
+        assertEquals(2, acls.length);
+
+        // Attempt deletion and check delete successful
+        dao.delete(identity, "scott");
+        acls = dao.getAcls(identity);
+        assertEquals(1, acls.length);
+        assertEquals(simpleAcl1.getRecipient(), acls[0].getRecipient());
+    }
+
+    public void testGettersSetters() throws Exception {
+        JdbcExtendedDaoImpl dao = makePopulatedJdbcDao();
+
+        assertNotNull(dao.getAclObjectIdentityDelete());
+        dao.setAclObjectIdentityDelete(null);
+        assertNull(dao.getAclObjectIdentityDelete());
+
+        assertNotNull(dao.getAclObjectIdentityInsert());
+        dao.setAclObjectIdentityInsert(null);
+        assertNull(dao.getAclObjectIdentityInsert());
+
+        assertNotNull(dao.getAclPermissionDelete());
+        dao.setAclPermissionDelete(null);
+        assertNull(dao.getAclPermissionDelete());
+
+        assertNotNull(dao.getAclPermissionInsert());
+        dao.setAclPermissionInsert(null);
+        assertNull(dao.getAclPermissionInsert());
+
+        assertNotNull(dao.getAclPermissionUpdate());
+        dao.setAclPermissionUpdate(null);
+        assertNull(dao.getAclPermissionUpdate());
+
+        assertNotNull(dao.getAclsByObjectIdentity());
+        dao.setAclsByObjectIdentity(null);
+        assertNull(dao.getAclsByObjectIdentity());
+
+        assertNotNull(dao.getLookupPermissionIdMapping());
+        dao.setLookupPermissionIdMapping(null);
+        assertNull(dao.getLookupPermissionIdMapping());
+
+        assertNotNull(dao.getAclObjectIdentityDeleteStatement());
+        dao.setAclObjectIdentityDeleteStatement("SELECT ...");
+        assertEquals("SELECT ...", dao.getAclObjectIdentityDeleteStatement());
+
+        assertNotNull(dao.getAclObjectIdentityInsertStatement());
+        dao.setAclObjectIdentityInsertStatement("SELECT ...");
+        assertEquals("SELECT ...", dao.getAclObjectIdentityInsertStatement());
+
+        assertNotNull(dao.getAclPermissionDeleteStatement());
+        dao.setAclPermissionDeleteStatement("SELECT ...");
+        assertEquals("SELECT ...", dao.getAclPermissionDeleteStatement());
+
+        assertNotNull(dao.getAclPermissionInsertStatement());
+        dao.setAclPermissionInsertStatement("SELECT ...");
+        assertEquals("SELECT ...", dao.getAclPermissionInsertStatement());
+
+        assertNotNull(dao.getAclPermissionUpdateStatement());
+        dao.setAclPermissionUpdateStatement("SELECT ...");
+        assertEquals("SELECT ...", dao.getAclPermissionUpdateStatement());
+
+        assertNotNull(dao.getAclsByObjectIdentityQuery());
+        dao.setAclsByObjectIdentityQuery("SELECT ...");
+        assertEquals("SELECT ...", dao.getAclsByObjectIdentityQuery());
+
+        assertNotNull(dao.getLookupPermissionIdQuery());
+        dao.setLookupPermissionIdQuery("SELECT ...");
+        assertEquals("SELECT ...", dao.getLookupPermissionIdQuery());
+    }
+
+    public void testNormalCreationAndDuplicateDetection()
+        throws Exception {
+        JdbcExtendedDaoImpl dao = makePopulatedJdbcDao();
+        AclObjectIdentity identity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
+                "200");
+        AclObjectIdentity parentIdentity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
+                "1");
+
+        // Create a BasicAclEntry for this AclObjectIdentity
+        SimpleAclEntry simpleAcl1 = new SimpleAclEntry("marissa", identity,
+                parentIdentity, SimpleAclEntry.CREATE);
+        dao.create(simpleAcl1);
+
+        // Create another BasicAclEntry for this AclObjectIdentity
+        SimpleAclEntry simpleAcl2 = new SimpleAclEntry("scott", identity,
+                parentIdentity, SimpleAclEntry.READ);
+        dao.create(simpleAcl2);
+
+        // Check creation was successful
+        BasicAclEntry[] acls = dao.getAcls(identity);
+        assertEquals(2, acls.length);
+        assertEquals(simpleAcl1.getRecipient(), acls[0].getRecipient());
+        assertEquals(simpleAcl1.getMask(), acls[0].getMask());
+        assertEquals(simpleAcl2.getRecipient(), acls[1].getRecipient());
+        assertEquals(simpleAcl2.getMask(), acls[1].getMask());
+
+        // Check it rejects an attempt to create another identical entry
+        try {
+            dao.create(simpleAcl1);
+            fail("Should have thrown DataIntegrityViolationException");
+        } catch (DataIntegrityViolationException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testRejectsInvalidParent() throws Exception {
+        JdbcExtendedDaoImpl dao = makePopulatedJdbcDao();
+        AclObjectIdentity identity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
+                "201");
+        AclObjectIdentity parentIdentity = new NamedEntityObjectIdentity(OBJECT_IDENTITY,
+                "987987987987986");
+        SimpleAclEntry simpleAcl = new SimpleAclEntry("marissa", identity,
+                parentIdentity, SimpleAclEntry.CREATE);
+
+        try {
+            dao.create(simpleAcl);
+            fail("Should have thrown DataRetrievalFailureException");
+        } catch (DataRetrievalFailureException expected) {
+            assertTrue(true);
+        }
+    }
+
+    private JdbcExtendedDaoImpl makePopulatedJdbcDao()
+        throws Exception {
+        JdbcExtendedDaoImpl dao = new JdbcExtendedDaoImpl();
+        dao.setDataSource(PopulatedDatabase.getDataSource());
+        dao.afterPropertiesSet();
+
+        return dao;
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockMappingSqlQuery extends MappingSqlQuery {
+        protected Object mapRow(ResultSet arg0, int arg1)
+            throws SQLException {
+            return null;
+        }
+    }
+}

+ 223 - 0
core/src/test/java/org/acegisecurity/afterinvocation/AfterInvocationProviderManagerTests.java

@@ -0,0 +1,223 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.afterinvocation;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.AccessDeniedException;
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.ConfigAttribute;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockMethodInvocation;
+import net.sf.acegisecurity.SecurityConfig;
+import net.sf.acegisecurity.intercept.web.FilterInvocation;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import java.util.List;
+import java.util.Vector;
+
+
+/**
+ * Tests {@link AfterInvocationProviderManager}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AfterInvocationProviderManagerTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public AfterInvocationProviderManagerTests() {
+        super();
+    }
+
+    public AfterInvocationProviderManagerTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AfterInvocationProviderManagerTests.class);
+    }
+
+    public void testCorrectOperation() throws Exception {
+        AfterInvocationProviderManager manager = new AfterInvocationProviderManager();
+        List list = new Vector();
+        list.add(new MockAfterInvocationProvider("swap1",
+                MethodInvocation.class, new SecurityConfig("GIVE_ME_SWAP1")));
+        list.add(new MockAfterInvocationProvider("swap2",
+                MethodInvocation.class, new SecurityConfig("GIVE_ME_SWAP2")));
+        list.add(new MockAfterInvocationProvider("swap3",
+                MethodInvocation.class, new SecurityConfig("GIVE_ME_SWAP3")));
+        manager.setProviders(list);
+        assertEquals(list, manager.getProviders());
+        manager.afterPropertiesSet();
+
+        ConfigAttributeDefinition attr1 = new ConfigAttributeDefinition();
+        attr1.addConfigAttribute(new SecurityConfig("GIVE_ME_SWAP1"));
+
+        ConfigAttributeDefinition attr2 = new ConfigAttributeDefinition();
+        attr2.addConfigAttribute(new SecurityConfig("GIVE_ME_SWAP2"));
+
+        ConfigAttributeDefinition attr3 = new ConfigAttributeDefinition();
+        attr3.addConfigAttribute(new SecurityConfig("GIVE_ME_SWAP3"));
+
+        ConfigAttributeDefinition attr2and3 = new ConfigAttributeDefinition();
+        attr2and3.addConfigAttribute(new SecurityConfig("GIVE_ME_SWAP2"));
+        attr2and3.addConfigAttribute(new SecurityConfig("GIVE_ME_SWAP3"));
+
+        ConfigAttributeDefinition attr4 = new ConfigAttributeDefinition();
+        attr4.addConfigAttribute(new SecurityConfig("NEVER_CAUSES_SWAP"));
+
+        assertEquals("swap1",
+            manager.decide(null, new MockMethodInvocation(), attr1,
+                "content-before-swapping"));
+
+        assertEquals("swap2",
+            manager.decide(null, new MockMethodInvocation(), attr2,
+                "content-before-swapping"));
+
+        assertEquals("swap3",
+            manager.decide(null, new MockMethodInvocation(), attr3,
+                "content-before-swapping"));
+
+        assertEquals("content-before-swapping",
+            manager.decide(null, new MockMethodInvocation(), attr4,
+                "content-before-swapping"));
+
+        assertEquals("swap3",
+            manager.decide(null, new MockMethodInvocation(), attr2and3,
+                "content-before-swapping"));
+    }
+
+    public void testRejectsEmptyProvidersList() {
+        AfterInvocationProviderManager manager = new AfterInvocationProviderManager();
+        List list = new Vector();
+
+        try {
+            manager.setProviders(list);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testRejectsNonAfterInvocationProviders() {
+        AfterInvocationProviderManager manager = new AfterInvocationProviderManager();
+        List list = new Vector();
+        list.add(new MockAfterInvocationProvider("swap1",
+                MethodInvocation.class, new SecurityConfig("GIVE_ME_SWAP1")));
+        list.add(new Integer(45));
+        list.add(new MockAfterInvocationProvider("swap3",
+                MethodInvocation.class, new SecurityConfig("GIVE_ME_SWAP3")));
+
+        try {
+            manager.setProviders(list);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testRejectsNullProvidersList() throws Exception {
+        AfterInvocationProviderManager manager = new AfterInvocationProviderManager();
+
+        try {
+            manager.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testSupportsConfigAttributeIteration()
+        throws Exception {
+        AfterInvocationProviderManager manager = new AfterInvocationProviderManager();
+        List list = new Vector();
+        list.add(new MockAfterInvocationProvider("swap1",
+                MethodInvocation.class, new SecurityConfig("GIVE_ME_SWAP1")));
+        list.add(new MockAfterInvocationProvider("swap2",
+                MethodInvocation.class, new SecurityConfig("GIVE_ME_SWAP2")));
+        list.add(new MockAfterInvocationProvider("swap3",
+                MethodInvocation.class, new SecurityConfig("GIVE_ME_SWAP3")));
+        manager.setProviders(list);
+        manager.afterPropertiesSet();
+
+        assertFalse(manager.supports(new SecurityConfig("UNKNOWN_ATTRIB")));
+        assertTrue(manager.supports(new SecurityConfig("GIVE_ME_SWAP2")));
+    }
+
+    public void testSupportsSecureObjectIteration() throws Exception {
+        AfterInvocationProviderManager manager = new AfterInvocationProviderManager();
+        List list = new Vector();
+        list.add(new MockAfterInvocationProvider("swap1",
+                MethodInvocation.class, new SecurityConfig("GIVE_ME_SWAP1")));
+        list.add(new MockAfterInvocationProvider("swap2",
+                MethodInvocation.class, new SecurityConfig("GIVE_ME_SWAP2")));
+        list.add(new MockAfterInvocationProvider("swap3",
+                MethodInvocation.class, new SecurityConfig("GIVE_ME_SWAP3")));
+        manager.setProviders(list);
+        manager.afterPropertiesSet();
+
+        assertFalse(manager.supports(FilterInvocation.class));
+        assertTrue(manager.supports(MethodInvocation.class));
+    }
+
+    //~ Inner Classes ==========================================================
+
+    /**
+     * Always returns the constructor-defined <code>forceReturnObject</code>,
+     * provided the same configuration attribute was provided. Also stores the
+     * secure object it supports.
+     */
+    private class MockAfterInvocationProvider implements AfterInvocationProvider {
+        private Class secureObject;
+        private ConfigAttribute configAttribute;
+        private Object forceReturnObject;
+
+        public MockAfterInvocationProvider(Object forceReturnObject,
+            Class secureObject, ConfigAttribute configAttribute) {
+            this.forceReturnObject = forceReturnObject;
+            this.secureObject = secureObject;
+            this.configAttribute = configAttribute;
+        }
+
+        private MockAfterInvocationProvider() {}
+
+        public Object decide(Authentication authentication, Object object,
+            ConfigAttributeDefinition config, Object returnedObject)
+            throws AccessDeniedException {
+            if (config.contains(configAttribute)) {
+                return forceReturnObject;
+            }
+
+            return returnedObject;
+        }
+
+        public boolean supports(Class clazz) {
+            return secureObject.isAssignableFrom(clazz);
+        }
+
+        public boolean supports(ConfigAttribute attribute) {
+            return attribute.equals(configAttribute);
+        }
+    }
+}

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

@@ -0,0 +1,373 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.afterinvocation;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.AuthorizationServiceException;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockAclManager;
+import net.sf.acegisecurity.MockMethodInvocation;
+import net.sf.acegisecurity.SecurityConfig;
+import net.sf.acegisecurity.acl.AclEntry;
+import net.sf.acegisecurity.acl.AclManager;
+import net.sf.acegisecurity.acl.basic.MockAclObjectIdentity;
+import net.sf.acegisecurity.acl.basic.SimpleAclEntry;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import java.util.List;
+import java.util.Vector;
+
+
+/**
+ * Tests {@link BasicAclEntryAfterInvocationCollectionFilteringProvider}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class BasicAclEntryAfterInvocationCollectionFilteringProviderTests
+    extends TestCase {
+    //~ Constructors ===========================================================
+
+    public BasicAclEntryAfterInvocationCollectionFilteringProviderTests() {
+        super();
+    }
+
+    public BasicAclEntryAfterInvocationCollectionFilteringProviderTests(
+        String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(BasicAclEntryAfterInvocationCollectionFilteringProviderTests.class);
+    }
+
+    public void testCorrectOperationWhenPrincipalHasIncorrectPermissionToDomainObject()
+        throws Exception {
+        // Create an AclManager, granting scott only ADMINISTRATION rights
+        AclManager aclManager = new MockAclManager("belmont", "scott",
+                new AclEntry[] {new SimpleAclEntry("scott",
+                        new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.ADMINISTRATION)});
+
+        BasicAclEntryAfterInvocationCollectionFilteringProvider provider = new BasicAclEntryAfterInvocationCollectionFilteringProvider();
+        provider.setAclManager(aclManager);
+        provider.afterPropertiesSet();
+
+        // Create a Collection containing many items
+        List list = new Vector();
+        list.add("sydney");
+        list.add("melbourne");
+        list.add("belmont");
+        list.add("brisbane");
+
+        // Create the Authentication and Config Attribs we'll be presenting
+        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("scott",
+                "NOT_USED");
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("AFTER_ACL_COLLECTION_READ"));
+
+        // Filter
+        List filteredList = (List) provider.decide(auth,
+                new MockMethodInvocation(), attr, list);
+
+        assertEquals(0, filteredList.size());
+    }
+
+    public void testCorrectOperationWhenPrincipalHasNoPermissionToDomainObject()
+        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);
+        provider.afterPropertiesSet();
+
+        // Create a Collection containing many items, which only "belmont"
+        // should remain in after filtering by provider
+        List list = new Vector();
+        list.add("sydney");
+        list.add("melbourne");
+        list.add("belmont");
+        list.add("brisbane");
+
+        // Create the Authentication and Config Attribs we'll be presenting
+        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("scott",
+                "NOT_USED");
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("AFTER_ACL_COLLECTION_READ"));
+
+        // Filter
+        List filteredList = (List) provider.decide(auth,
+                new MockMethodInvocation(), attr, list);
+
+        assertEquals(0, filteredList.size());
+    }
+
+    public void testCorrectOperationWhenPrincipalIsAuthorised()
+        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
+        List list = new Vector();
+        list.add("sydney");
+        list.add("melbourne");
+        list.add("belmont");
+        list.add("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
+        List filteredList = (List) provider.decide(auth,
+                new MockMethodInvocation(), attr, list);
+
+        assertEquals(1, filteredList.size());
+        assertEquals("belmont", filteredList.get(0));
+    }
+
+    public void testDetectsIfReturnedObjectIsNotACollection()
+        throws Exception {
+        // Create an AclManager
+        AclManager aclManager = new MockAclManager("belmont", "marissa",
+                new AclEntry[] {new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.ADMINISTRATION), new SimpleAclEntry(
+                        "marissa", new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.READ), new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null, SimpleAclEntry.DELETE), new MockAclEntry()});
+
+        BasicAclEntryAfterInvocationCollectionFilteringProvider provider = new BasicAclEntryAfterInvocationCollectionFilteringProvider();
+        provider.setAclManager(aclManager);
+        provider.afterPropertiesSet();
+
+        // 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
+        try {
+            provider.decide(auth, new MockMethodInvocation(), attr,
+                new String("RETURN_OBJECT_NOT_COLLECTION"));
+            fail("Should have thrown AuthorizationServiceException");
+        } catch (AuthorizationServiceException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testGrantsAccessIfReturnedObjectIsNull()
+        throws Exception {
+        // Create an AclManager
+        AclManager aclManager = new MockAclManager("belmont", "marissa",
+                new AclEntry[] {new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.ADMINISTRATION), new SimpleAclEntry(
+                        "marissa", new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.READ), new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null, SimpleAclEntry.DELETE), new MockAclEntry()});
+
+        BasicAclEntryAfterInvocationCollectionFilteringProvider provider = new BasicAclEntryAfterInvocationCollectionFilteringProvider();
+        provider.setAclManager(aclManager);
+        provider.afterPropertiesSet();
+
+        // 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
+        List filteredList = (List) provider.decide(auth,
+                new MockMethodInvocation(), attr, null);
+
+        assertNull(filteredList);
+    }
+
+    public void testRespectsModificationsToProcessConfigAttribute()
+        throws Exception {
+        // Create an AclManager
+        AclManager aclManager = new MockAclManager("sydney", "marissa",
+                new AclEntry[] {new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null, SimpleAclEntry.READ), new MockAclEntry()});
+
+        BasicAclEntryAfterInvocationCollectionFilteringProvider provider = new BasicAclEntryAfterInvocationCollectionFilteringProvider();
+        provider.setAclManager(aclManager);
+        assertEquals("AFTER_ACL_COLLECTION_READ",
+            provider.getProcessConfigAttribute());
+        provider.setProcessConfigAttribute("AFTER_ACL_COLLECTION_ADMIN");
+        assertEquals("AFTER_ACL_COLLECTION_ADMIN",
+            provider.getProcessConfigAttribute());
+        provider.afterPropertiesSet();
+
+        // Create a Collection containing many items, which only "sydney"
+        // should remain in after filtering by provider
+        List list = new Vector();
+        list.add("sydney");
+        list.add("melbourne");
+        list.add("belmont");
+        list.add("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"));
+
+        // As no matching config attrib, ensure provider doesn't change list
+        assertEquals(4,
+            ((List) provider.decide(auth, new MockMethodInvocation(), attr, list))
+            .size());
+
+        // Filter, this time with the conf attrib provider setup to answer
+        attr.addConfigAttribute(new SecurityConfig("AFTER_ACL_COLLECTION_ADMIN"));
+
+        List filteredList = (List) provider.decide(auth,
+                new MockMethodInvocation(), attr, list);
+
+        assertEquals(1, filteredList.size());
+        assertEquals("sydney", filteredList.get(0));
+    }
+
+    public void testRespectsModificationsToRequirePermissions()
+        throws Exception {
+        // Create an AclManager
+        AclManager aclManager = new MockAclManager("sydney", "marissa",
+                new AclEntry[] {new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.ADMINISTRATION), new MockAclEntry()});
+
+        BasicAclEntryAfterInvocationCollectionFilteringProvider provider = new BasicAclEntryAfterInvocationCollectionFilteringProvider();
+        provider.setAclManager(aclManager);
+        assertEquals(SimpleAclEntry.READ, provider.getRequirePermission()[0]);
+        provider.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION});
+        assertEquals(SimpleAclEntry.ADMINISTRATION,
+            provider.getRequirePermission()[0]);
+        provider.afterPropertiesSet();
+
+        // Create a Collection containing many items, which only "sydney"
+        // should remain in after filtering by provider
+        List list = new Vector();
+        list.add("sydney");
+        list.add("melbourne");
+        list.add("belmont");
+        list.add("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
+        List filteredList = (List) provider.decide(auth,
+                new MockMethodInvocation(), attr, list);
+
+        assertEquals(1, filteredList.size());
+        assertEquals("sydney", filteredList.get(0));
+    }
+
+    public void testStartupDetectsMissingAclManager() throws Exception {
+        BasicAclEntryAfterInvocationCollectionFilteringProvider provider = new BasicAclEntryAfterInvocationCollectionFilteringProvider();
+
+        try {
+            provider.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("An aclManager is mandatory", expected.getMessage());
+        }
+    }
+
+    public void testStartupDetectsMissingProcessConfigAttribute()
+        throws Exception {
+        BasicAclEntryAfterInvocationCollectionFilteringProvider provider = new BasicAclEntryAfterInvocationCollectionFilteringProvider();
+        AclManager aclManager = new MockAclManager("sydney", "marissa",
+                new AclEntry[] {new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.ADMINISTRATION), new MockAclEntry()});
+        provider.setAclManager(aclManager);
+
+        provider.setProcessConfigAttribute(null);
+
+        try {
+            provider.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("A processConfigAttribute is mandatory",
+                expected.getMessage());
+        }
+    }
+
+    public void testStartupDetectsMissingRequirePermission()
+        throws Exception {
+        BasicAclEntryAfterInvocationCollectionFilteringProvider provider = new BasicAclEntryAfterInvocationCollectionFilteringProvider();
+        AclManager aclManager = new MockAclManager("sydney", "marissa",
+                new AclEntry[] {new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.ADMINISTRATION), new MockAclEntry()});
+        provider.setAclManager(aclManager);
+
+        provider.setRequirePermission(null);
+
+        try {
+            provider.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("One or more requirePermission entries is mandatory",
+                expected.getMessage());
+        }
+    }
+
+    public void testSupportsAnything() {
+        assertTrue(new BasicAclEntryAfterInvocationCollectionFilteringProvider()
+            .supports(String.class));
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockAclEntry implements AclEntry {
+        // just so AclTag iterates some different types of AclEntrys
+    }
+}

+ 284 - 0
core/src/test/java/org/acegisecurity/afterinvocation/BasicAclEntryAfterInvocationProviderTests.java

@@ -0,0 +1,284 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.afterinvocation;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.AccessDeniedException;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockAclManager;
+import net.sf.acegisecurity.MockMethodInvocation;
+import net.sf.acegisecurity.SecurityConfig;
+import net.sf.acegisecurity.acl.AclEntry;
+import net.sf.acegisecurity.acl.AclManager;
+import net.sf.acegisecurity.acl.basic.MockAclObjectIdentity;
+import net.sf.acegisecurity.acl.basic.SimpleAclEntry;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+
+/**
+ * Tests {@link BasicAclEntryAfterInvocationProvider}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class BasicAclEntryAfterInvocationProviderTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public BasicAclEntryAfterInvocationProviderTests() {
+        super();
+    }
+
+    public BasicAclEntryAfterInvocationProviderTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(BasicAclEntryAfterInvocationProviderTests.class);
+    }
+
+    public void testCorrectOperationWhenPrincipalHasIncorrectPermissionToDomainObject()
+        throws Exception {
+        // Create an AclManager, granting scott only ADMINISTRATION rights
+        AclManager aclManager = new MockAclManager("belmont", "scott",
+                new AclEntry[] {new SimpleAclEntry("scott",
+                        new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.ADMINISTRATION)});
+
+        BasicAclEntryAfterInvocationProvider provider = new BasicAclEntryAfterInvocationProvider();
+        provider.setAclManager(aclManager);
+        provider.afterPropertiesSet();
+
+        // Create the Authentication and Config Attribs we'll be presenting
+        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("scott",
+                "NOT_USED");
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("AFTER_ACL_READ"));
+
+        try {
+            provider.decide(auth, new MockMethodInvocation(), attr, "belmont");
+            fail("Should have thrown AccessDeniedException");
+        } catch (AccessDeniedException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testCorrectOperationWhenPrincipalHasNoPermissionToDomainObject()
+        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)});
+
+        BasicAclEntryAfterInvocationProvider provider = new BasicAclEntryAfterInvocationProvider();
+        provider.setAclManager(aclManager);
+        provider.afterPropertiesSet();
+
+        // Create the Authentication and Config Attribs we'll be presenting
+        UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("scott",
+                "NOT_USED");
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("AFTER_ACL_READ"));
+
+        try {
+            provider.decide(auth, new MockMethodInvocation(), attr, "belmont");
+            fail("Should have thrown AccessDeniedException");
+        } catch (AccessDeniedException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testCorrectOperationWhenPrincipalIsAuthorised()
+        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)});
+
+        BasicAclEntryAfterInvocationProvider provider = new BasicAclEntryAfterInvocationProvider();
+        provider.setAclManager(aclManager);
+        assertEquals(aclManager, provider.getAclManager());
+        provider.afterPropertiesSet();
+
+        // 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_READ"));
+
+        // Filter
+        assertEquals("belmont",
+            provider.decide(auth, new MockMethodInvocation(), attr, "belmont"));
+    }
+
+    public void testGrantsAccessIfReturnedObjectIsNull()
+        throws Exception {
+        // Create an AclManager
+        AclManager aclManager = new MockAclManager("belmont", "marissa",
+                new AclEntry[] {new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.ADMINISTRATION), new SimpleAclEntry(
+                        "marissa", new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.READ), new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null, SimpleAclEntry.DELETE), new MockAclEntry()});
+
+        BasicAclEntryAfterInvocationProvider provider = new BasicAclEntryAfterInvocationProvider();
+        provider.setAclManager(aclManager);
+        provider.afterPropertiesSet();
+
+        // 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_READ"));
+
+        // Filter
+        assertNull(provider.decide(auth, new MockMethodInvocation(), attr, null));
+    }
+
+    public void testRespectsModificationsToProcessConfigAttribute()
+        throws Exception {
+        // Create an AclManager
+        AclManager aclManager = new MockAclManager("sydney", "marissa",
+                new AclEntry[] {new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null, SimpleAclEntry.READ), new MockAclEntry()});
+
+        BasicAclEntryAfterInvocationProvider provider = new BasicAclEntryAfterInvocationProvider();
+        provider.setAclManager(aclManager);
+        assertEquals("AFTER_ACL_READ", provider.getProcessConfigAttribute());
+        provider.setProcessConfigAttribute("AFTER_ACL_ADMIN");
+        assertEquals("AFTER_ACL_ADMIN", provider.getProcessConfigAttribute());
+        provider.afterPropertiesSet();
+
+        // 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_READ"));
+
+        // As no matching config attrib, ensure provider returns original obj
+        assertEquals("sydney",
+            provider.decide(auth, new MockMethodInvocation(), attr, "sydney"));
+
+        // Filter, this time with the conf attrib provider setup to answer
+        attr.addConfigAttribute(new SecurityConfig("AFTER_ACL_ADMIN"));
+        assertEquals("sydney",
+            provider.decide(auth, new MockMethodInvocation(), attr, "sydney"));
+    }
+
+    public void testRespectsModificationsToRequirePermissions()
+        throws Exception {
+        // Create an AclManager
+        AclManager aclManager = new MockAclManager("sydney", "marissa",
+                new AclEntry[] {new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.ADMINISTRATION), new MockAclEntry()});
+
+        BasicAclEntryAfterInvocationProvider provider = new BasicAclEntryAfterInvocationProvider();
+        provider.setAclManager(aclManager);
+        assertEquals(SimpleAclEntry.READ, provider.getRequirePermission()[0]);
+        provider.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION});
+        assertEquals(SimpleAclEntry.ADMINISTRATION,
+            provider.getRequirePermission()[0]);
+        provider.afterPropertiesSet();
+
+        // 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_READ"));
+
+        // Filter
+        assertEquals("sydney",
+            provider.decide(auth, new MockMethodInvocation(), attr, "sydney"));
+    }
+
+    public void testStartupDetectsMissingAclManager() throws Exception {
+        BasicAclEntryAfterInvocationProvider provider = new BasicAclEntryAfterInvocationProvider();
+
+        try {
+            provider.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("An aclManager is mandatory", expected.getMessage());
+        }
+    }
+
+    public void testStartupDetectsMissingProcessConfigAttribute()
+        throws Exception {
+        BasicAclEntryAfterInvocationProvider provider = new BasicAclEntryAfterInvocationProvider();
+        AclManager aclManager = new MockAclManager("sydney", "marissa",
+                new AclEntry[] {new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.ADMINISTRATION), new MockAclEntry()});
+        provider.setAclManager(aclManager);
+
+        provider.setProcessConfigAttribute(null);
+
+        try {
+            provider.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("A processConfigAttribute is mandatory",
+                expected.getMessage());
+        }
+    }
+
+    public void testStartupDetectsMissingRequirePermission()
+        throws Exception {
+        BasicAclEntryAfterInvocationProvider provider = new BasicAclEntryAfterInvocationProvider();
+        AclManager aclManager = new MockAclManager("sydney", "marissa",
+                new AclEntry[] {new SimpleAclEntry("marissa",
+                        new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.ADMINISTRATION), new MockAclEntry()});
+        provider.setAclManager(aclManager);
+
+        provider.setRequirePermission(null);
+
+        try {
+            provider.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("One or more requirePermission entries is mandatory",
+                expected.getMessage());
+        }
+    }
+
+    public void testSupportsAnything() {
+        assertTrue(new BasicAclEntryAfterInvocationProvider().supports(
+                String.class));
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockAclEntry implements AclEntry {
+        // just so AclTag iterates some different types of AclEntrys
+    }
+}

+ 125 - 0
core/src/test/java/org/acegisecurity/intercept/AbstractSecurityInterceptorTests.java

@@ -0,0 +1,125 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.intercept;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.MockAccessDecisionManager;
+import net.sf.acegisecurity.MockAfterInvocationManager;
+import net.sf.acegisecurity.MockAuthenticationManager;
+import net.sf.acegisecurity.MockMethodInvocation;
+import net.sf.acegisecurity.MockRunAsManager;
+import net.sf.acegisecurity.intercept.method.MockMethodDefinitionSource;
+
+
+/**
+ * Tests some {@link AbstractSecurityInterceptor} methods. Most of the  testing
+ * for this class is found in the <code>MethodSecurityInterceptorTests</code>
+ * class.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AbstractSecurityInterceptorTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public AbstractSecurityInterceptorTests() {
+        super();
+    }
+
+    public AbstractSecurityInterceptorTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AbstractSecurityInterceptorTests.class);
+    }
+
+    public void testDetectsIfInvocationPassedIncompatibleSecureObject()
+        throws Exception {
+        MockSecurityInterceptorWhichOnlySupportsStrings si = new MockSecurityInterceptorWhichOnlySupportsStrings();
+        si.setRunAsManager(new MockRunAsManager());
+        si.setAuthenticationManager(new MockAuthenticationManager());
+        si.setAfterInvocationManager(new MockAfterInvocationManager());
+        si.setAccessDecisionManager(new MockAccessDecisionManager());
+        si.setObjectDefinitionSource(new MockMethodDefinitionSource(false, true));
+
+        try {
+            si.beforeInvocation(new MockMethodInvocation());
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(expected.getMessage().startsWith("Security invocation attempted for object"));
+        }
+    }
+
+    public void testDetectsViolationOfGetSecureObjectClassMethod()
+        throws Exception {
+        MockSecurityInterceptorReturnsNull si = new MockSecurityInterceptorReturnsNull();
+        si.setRunAsManager(new MockRunAsManager());
+        si.setAuthenticationManager(new MockAuthenticationManager());
+        si.setAfterInvocationManager(new MockAfterInvocationManager());
+        si.setAccessDecisionManager(new MockAccessDecisionManager());
+        si.setObjectDefinitionSource(new MockMethodDefinitionSource(false, true));
+
+        try {
+            si.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("Subclass must provide a non-null response to getSecureObjectClass()",
+                expected.getMessage());
+        }
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockSecurityInterceptorReturnsNull
+        extends AbstractSecurityInterceptor {
+        private ObjectDefinitionSource objectDefinitionSource;
+
+        public void setObjectDefinitionSource(
+            ObjectDefinitionSource objectDefinitionSource) {
+            this.objectDefinitionSource = objectDefinitionSource;
+        }
+
+        public Class getSecureObjectClass() {
+            return null;
+        }
+
+        public ObjectDefinitionSource obtainObjectDefinitionSource() {
+            return objectDefinitionSource;
+        }
+    }
+
+    private class MockSecurityInterceptorWhichOnlySupportsStrings
+        extends AbstractSecurityInterceptor {
+        private ObjectDefinitionSource objectDefinitionSource;
+
+        public void setObjectDefinitionSource(
+            ObjectDefinitionSource objectDefinitionSource) {
+            this.objectDefinitionSource = objectDefinitionSource;
+        }
+
+        public Class getSecureObjectClass() {
+            return String.class;
+        }
+
+        public ObjectDefinitionSource obtainObjectDefinitionSource() {
+            return objectDefinitionSource;
+        }
+    }
+}

+ 74 - 0
core/src/test/java/org/acegisecurity/intercept/InterceptorStatusTokenTests.java

@@ -0,0 +1,74 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.intercept;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockMethodInvocation;
+import net.sf.acegisecurity.SecurityConfig;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+
+/**
+ * Tests {@link InterceptorStatusToken}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class InterceptorStatusTokenTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public InterceptorStatusTokenTests() {
+        super();
+    }
+
+    public InterceptorStatusTokenTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(InterceptorStatusTokenTests.class);
+    }
+
+    public void testDefaultConstructor() {
+        try {
+            new InterceptorStatusToken();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testOperation() {
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("FOO"));
+
+        MethodInvocation mi = new MockMethodInvocation();
+
+        InterceptorStatusToken token = new InterceptorStatusToken(new UsernamePasswordAuthenticationToken(
+                    "marissa", "koala"), true, attr, mi);
+
+        assertTrue(token.isContextHolderRefreshRequired());
+        assertEquals(attr, token.getAttr());
+        assertEquals(mi, token.getSecureObject());
+        assertEquals("marissa", token.getAuthentication().getPrincipal());
+    }
+}

+ 74 - 0
core/src/test/java/org/acegisecurity/intercept/event/AuthenticationCredentialsNotFoundEventTests.java

@@ -0,0 +1,74 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.intercept.event;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.AuthenticationCredentialsNotFoundException;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockMethodInvocation;
+
+
+/**
+ * Tests {@link AuthenticationCredentialsNotFoundEvent}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthenticationCredentialsNotFoundEventTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public AuthenticationCredentialsNotFoundEventTests() {
+        super();
+    }
+
+    public AuthenticationCredentialsNotFoundEventTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AuthenticationCredentialsNotFoundEventTests.class);
+    }
+
+    public void testRejectsNulls() {
+        try {
+            AuthenticationCredentialsNotFoundEvent event = new AuthenticationCredentialsNotFoundEvent(null,
+                    new ConfigAttributeDefinition(),
+                    new AuthenticationCredentialsNotFoundException("test"));
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            AuthenticationCredentialsNotFoundEvent event = new AuthenticationCredentialsNotFoundEvent(new MockMethodInvocation(),
+                    null, new AuthenticationCredentialsNotFoundException("test"));
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            AuthenticationCredentialsNotFoundEvent event = new AuthenticationCredentialsNotFoundEvent(new MockMethodInvocation(),
+                    new ConfigAttributeDefinition(), null);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+}

+ 88 - 0
core/src/test/java/org/acegisecurity/intercept/event/AuthenticationFailureEventTests.java

@@ -0,0 +1,88 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.intercept.event;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.BadCredentialsException;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockMethodInvocation;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+
+/**
+ * Tests {@link AuthenticationFailureEvent}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthenticationFailureEventTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public AuthenticationFailureEventTests() {
+        super();
+    }
+
+    public AuthenticationFailureEventTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AuthenticationFailureEventTests.class);
+    }
+
+    public void testRejectsNulls() {
+        try {
+            AuthenticationFailureEvent event = new AuthenticationFailureEvent(null,
+                    new ConfigAttributeDefinition(),
+                    new UsernamePasswordAuthenticationToken("foo", "bar"),
+                    new BadCredentialsException("error"));
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            AuthenticationFailureEvent event = new AuthenticationFailureEvent(new MockMethodInvocation(),
+                    null,
+                    new UsernamePasswordAuthenticationToken("foo", "bar"),
+                    new BadCredentialsException("error"));
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            AuthenticationFailureEvent event = new AuthenticationFailureEvent(new MockMethodInvocation(),
+                    new ConfigAttributeDefinition(), null,
+                    new BadCredentialsException("error"));
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            AuthenticationFailureEvent event = new AuthenticationFailureEvent(new MockMethodInvocation(),
+                    new ConfigAttributeDefinition(),
+                    new UsernamePasswordAuthenticationToken("foo", "bar"), null);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+}

+ 88 - 0
core/src/test/java/org/acegisecurity/intercept/event/AuthorizationFailureEventTests.java

@@ -0,0 +1,88 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.intercept.event;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.AccessDeniedException;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockMethodInvocation;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+
+/**
+ * Tests {@link AuthorizationFailureEvent}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthorizationFailureEventTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public AuthorizationFailureEventTests() {
+        super();
+    }
+
+    public AuthorizationFailureEventTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AuthorizationFailureEventTests.class);
+    }
+
+    public void testRejectsNulls() {
+        try {
+            AuthorizationFailureEvent event = new AuthorizationFailureEvent(null,
+                    new ConfigAttributeDefinition(),
+                    new UsernamePasswordAuthenticationToken("foo", "bar"),
+                    new AccessDeniedException("error"));
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            AuthorizationFailureEvent event = new AuthorizationFailureEvent(new MockMethodInvocation(),
+                    null,
+                    new UsernamePasswordAuthenticationToken("foo", "bar"),
+                    new AccessDeniedException("error"));
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            AuthorizationFailureEvent event = new AuthorizationFailureEvent(new MockMethodInvocation(),
+                    new ConfigAttributeDefinition(), null,
+                    new AccessDeniedException("error"));
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            AuthorizationFailureEvent event = new AuthorizationFailureEvent(new MockMethodInvocation(),
+                    new ConfigAttributeDefinition(),
+                    new UsernamePasswordAuthenticationToken("foo", "bar"), null);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+}

+ 74 - 0
core/src/test/java/org/acegisecurity/intercept/event/AuthorizedEventTests.java

@@ -0,0 +1,74 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.intercept.event;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockMethodInvocation;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+
+/**
+ * Tests {@link AuthorizedEvent}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthorizedEventTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public AuthorizedEventTests() {
+        super();
+    }
+
+    public AuthorizedEventTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AuthorizedEventTests.class);
+    }
+
+    public void testRejectsNulls() {
+        try {
+            AuthorizedEvent event = new AuthorizedEvent(null,
+                    new ConfigAttributeDefinition(),
+                    new UsernamePasswordAuthenticationToken("foo", "bar"));
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            AuthorizedEvent event = new AuthorizedEvent(new MockMethodInvocation(),
+                    null, new UsernamePasswordAuthenticationToken("foo", "bar"));
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            AuthorizedEvent event = new AuthorizedEvent(new MockMethodInvocation(),
+                    new ConfigAttributeDefinition(), null);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+}

+ 5 - 45
core/src/test/java/org/acegisecurity/intercept/method/MethodDefinitionAttributesTests.java

@@ -32,16 +32,13 @@ import net.sf.acegisecurity.context.SecureContext;
 import net.sf.acegisecurity.context.SecureContextImpl;
 import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
 
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
-import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;
-
+import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;
 
 import java.lang.reflect.Method;
 
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.Properties;
 import java.util.Set;
 
 
@@ -228,47 +225,10 @@ public class MethodDefinitionAttributesTests extends TestCase {
     }
 
     private ITargetObject makeInterceptedTarget() {
-        String PREFIX = "beans.";
-        DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
-        Properties p = new Properties();
-        p.setProperty(PREFIX + "authentication.class",
-            "net.sf.acegisecurity.MockAuthenticationManager");
-        p.setProperty(PREFIX + "accessDecision.class",
-            "net.sf.acegisecurity.MockAccessDecisionManager");
-        p.setProperty(PREFIX + "runAs.class",
-            "net.sf.acegisecurity.MockRunAsManager");
-        p.setProperty(PREFIX + "attributes.class",
-            "net.sf.acegisecurity.intercept.method.MockAttributes");
-
-        p.setProperty(PREFIX + "objectDefinitionSource.class",
-            "net.sf.acegisecurity.intercept.method.MethodDefinitionAttributes");
-        p.setProperty(PREFIX + "objectDefinitionSource.attributes(ref)",
-            "attributes");
-
-        p.setProperty(PREFIX + "securityInterceptor.class",
-            "net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor");
-        p.setProperty(PREFIX + "securityInterceptor.authenticationManager(ref)",
-            "authentication");
-        p.setProperty(PREFIX + "securityInterceptor.accessDecisionManager(ref)",
-            "accessDecision");
-        p.setProperty(PREFIX + "securityInterceptor.runAsManager(ref)", "runAs");
-        p.setProperty(PREFIX
-            + "securityInterceptor.objectDefinitionSource(ref)",
-            "objectDefinitionSource");
-
-        p.setProperty(PREFIX + "targetObject.class",
-            "net.sf.acegisecurity.TargetObject");
-        p.setProperty(PREFIX + "target.class",
-            "org.springframework.aop.framework.ProxyFactoryBean");
-        p.setProperty(PREFIX + "target.proxyInterfaces",
-            "net.sf.acegisecurity.ITargetObject");
-        p.setProperty(PREFIX + "target.interceptorNames",
-            "securityInterceptor,targetObject");
-
-        (new PropertiesBeanDefinitionReader(lbf)).registerBeanDefinitions(p,
-            PREFIX);
-
-        return (ITargetObject) lbf.getBean("target");
+        ApplicationContext context = new ClassPathXmlApplicationContext(
+                "net/sf/acegisecurity/intercept/method/applicationContext.xml");
+
+        return (ITargetObject) context.getBean("target");
     }
 
     /**

+ 24 - 1
core/src/test/java/org/acegisecurity/intercept/method/MethodDefinitionSourceEditorTests.java

@@ -18,6 +18,7 @@ package net.sf.acegisecurity.intercept.method;
 import junit.framework.TestCase;
 
 import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockJoinPoint;
 import net.sf.acegisecurity.SecurityConfig;
 import net.sf.acegisecurity.TargetObject;
 
@@ -57,6 +58,28 @@ public class MethodDefinitionSourceEditorTests extends TestCase {
         junit.textui.TestRunner.run(MethodDefinitionSourceEditorTests.class);
     }
 
+    public void testAspectJJointPointLookup() throws Exception {
+        MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
+        editor.setAsText(
+            "net.sf.acegisecurity.TargetObject.countLength=ROLE_ONE,ROLE_TWO,RUN_AS_ENTRY");
+
+        MethodDefinitionMap map = (MethodDefinitionMap) editor.getValue();
+
+        Class clazz = TargetObject.class;
+        Method method = clazz.getMethod("countLength",
+                new Class[] {String.class});
+        MockJoinPoint joinPoint = new MockJoinPoint(new TargetObject(), method);
+
+        ConfigAttributeDefinition returnedCountLength = map.getAttributes(joinPoint);
+
+        ConfigAttributeDefinition expectedCountLength = new ConfigAttributeDefinition();
+        expectedCountLength.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
+        expectedCountLength.addConfigAttribute(new SecurityConfig("ROLE_TWO"));
+        expectedCountLength.addConfigAttribute(new SecurityConfig(
+                "RUN_AS_ENTRY"));
+        assertEquals(expectedCountLength, returnedCountLength);
+    }
+
     public void testClassNameNotFoundResultsInException() {
         MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
 
@@ -160,7 +183,7 @@ public class MethodDefinitionSourceEditorTests extends TestCase {
             "net.sf.acegisecurity.TargetObject.*=ROLE_GENERAL\r\nnet.sf.acegisecurity.TargetObject.makeLower*=ROLE_LOWER\r\nnet.sf.acegisecurity.TargetObject.make*=ROLE_MAKE\r\nnet.sf.acegisecurity.TargetObject.makeUpper*=ROLE_UPPER");
 
         MethodDefinitionMap map = (MethodDefinitionMap) editor.getValue();
-        assertEquals(4, map.getMethodMapSize());
+        assertEquals(5, map.getMethodMapSize());
 
         ConfigAttributeDefinition returnedMakeLower = map.getAttributes(new MockMethodInvocation(
                     TargetObject.class, "makeLowerCase",

+ 43 - 0
core/src/test/java/org/acegisecurity/intercept/method/aopalliance/MethodDefinitionSourceAdvisorTests.java

@@ -85,6 +85,49 @@ public class MethodDefinitionSourceAdvisorTests extends TestCase {
         }
     }
 
+    public void testUnsupportedOperations() throws Throwable {
+        Class clazz = TargetObject.class;
+        Method method = clazz.getMethod("countLength",
+                new Class[] {String.class});
+
+        MethodDefinitionSourceAdvisor.InternalMethodInvocation imi = new MethodDefinitionSourceAdvisor(getInterceptor()).new InternalMethodInvocation(method);
+
+        try {
+            imi.getArguments();
+            fail("Should have thrown UnsupportedOperationException");
+        } catch (UnsupportedOperationException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            imi.getStaticPart();
+            fail("Should have thrown UnsupportedOperationException");
+        } catch (UnsupportedOperationException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            imi.getThis();
+            fail("Should have thrown UnsupportedOperationException");
+        } catch (UnsupportedOperationException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            imi.proceed();
+            fail("Should have thrown UnsupportedOperationException");
+        } catch (UnsupportedOperationException expected) {
+            assertTrue(true);
+        }
+
+        try {
+            new MethodDefinitionSourceAdvisor(getInterceptor()).new InternalMethodInvocation();
+            fail("Should have thrown UnsupportedOperationException");
+        } catch (UnsupportedOperationException expected) {
+            assertTrue(true);
+        }
+    }
+
     private MethodSecurityInterceptor getInterceptor() {
         MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
         editor.setAsText(

+ 111 - 10
core/src/test/java/org/acegisecurity/intercept/method/aopalliance/MethodSecurityInterceptorTests.java

@@ -19,14 +19,17 @@ import junit.framework.TestCase;
 
 import net.sf.acegisecurity.AccessDecisionManager;
 import net.sf.acegisecurity.AccessDeniedException;
+import net.sf.acegisecurity.AfterInvocationManager;
 import net.sf.acegisecurity.Authentication;
 import net.sf.acegisecurity.AuthenticationCredentialsNotFoundException;
+import net.sf.acegisecurity.AuthenticationException;
 import net.sf.acegisecurity.ConfigAttribute;
 import net.sf.acegisecurity.ConfigAttributeDefinition;
 import net.sf.acegisecurity.GrantedAuthority;
 import net.sf.acegisecurity.GrantedAuthorityImpl;
 import net.sf.acegisecurity.ITargetObject;
 import net.sf.acegisecurity.MockAccessDecisionManager;
+import net.sf.acegisecurity.MockAfterInvocationManager;
 import net.sf.acegisecurity.MockAuthenticationManager;
 import net.sf.acegisecurity.MockRunAsManager;
 import net.sf.acegisecurity.RunAsManager;
@@ -139,17 +142,20 @@ public class MethodSecurityInterceptorTests extends TestCase {
         MockAuthenticationManager authManager = new MockAuthenticationManager();
         MockMethodDefinitionSource methodSource = new MockMethodDefinitionSource(false,
                 true);
+        MockAfterInvocationManager afterInvocation = new MockAfterInvocationManager();
 
         MethodSecurityInterceptor si = new MethodSecurityInterceptor();
         si.setAccessDecisionManager(accessDecision);
         si.setRunAsManager(runAs);
         si.setAuthenticationManager(authManager);
         si.setObjectDefinitionSource(methodSource);
+        si.setAfterInvocationManager(afterInvocation);
 
         assertEquals(accessDecision, si.getAccessDecisionManager());
         assertEquals(runAs, si.getRunAsManager());
         assertEquals(authManager, si.getAuthenticationManager());
         assertEquals(methodSource, si.getObjectDefinitionSource());
+        assertEquals(afterInvocation, si.getAfterInvocationManager());
     }
 
     public void testMethodCallWithRunAsReplacement() throws Exception {
@@ -178,7 +184,7 @@ public class MethodSecurityInterceptorTests extends TestCase {
         context.setAuthentication(token);
         ContextHolder.setContext(context);
 
-        ITargetObject target = makeInterceptedTarget();
+        ITargetObject target = makeInterceptedTargetWithoutAnAfterInvocationManager();
         String result = target.makeLowerCase("HELLO");
 
         // Note we check the isAuthenticated becomes true in following line
@@ -234,7 +240,8 @@ public class MethodSecurityInterceptorTests extends TestCase {
         ContextHolder.setContext(null);
     }
 
-    public void testRejectsAccessDecisionManagersThatDoNotSupportMethodInvocation() {
+    public void testRejectsAccessDecisionManagersThatDoNotSupportMethodInvocation()
+        throws Exception {
         MethodSecurityInterceptor si = new MethodSecurityInterceptor();
         si.setAccessDecisionManager(new MockAccessDecisionManagerWhichOnlySupportsStrings());
         si.setAuthenticationManager(new MockAuthenticationManager());
@@ -250,6 +257,28 @@ public class MethodSecurityInterceptorTests extends TestCase {
         }
     }
 
+    public void testRejectsCallsWhenAuthenticationIsIncorrect()
+        throws Exception {
+        SecureContext context = new SecureContextImpl();
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
+                "Password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_LOWER")});
+        assertTrue(!token.isAuthenticated());
+        context.setAuthentication(token);
+        ContextHolder.setContext(context);
+
+        ITargetObject target = makeInterceptedTargetRejectsAuthentication();
+
+        try {
+            target.makeLowerCase("HELLO");
+            fail("Should have thrown AuthenticationException");
+        } catch (AuthenticationException expected) {
+            assertTrue(true);
+        }
+
+        ContextHolder.setContext(null);
+    }
+
     public void testRejectsCallsWhenObjectDefinitionSourceDoesNotSupportObject()
         throws Throwable {
         MethodSecurityInterceptor interceptor = new MethodSecurityInterceptor();
@@ -278,12 +307,14 @@ public class MethodSecurityInterceptorTests extends TestCase {
         }
     }
 
-    public void testRejectsRunAsManagersThatDoNotSupportMethodInvocation() {
+    public void testRejectsRunAsManagersThatDoNotSupportMethodInvocation()
+        throws Exception {
         MethodSecurityInterceptor si = new MethodSecurityInterceptor();
         si.setAccessDecisionManager(new MockAccessDecisionManager());
         si.setAuthenticationManager(new MockAuthenticationManager());
         si.setObjectDefinitionSource(new MockMethodDefinitionSource(false, true));
         si.setRunAsManager(new MockRunAsManagerWhichOnlySupportsStrings());
+        si.setAfterInvocationManager(new MockAfterInvocationManager());
 
         try {
             si.afterPropertiesSet();
@@ -294,10 +325,12 @@ public class MethodSecurityInterceptorTests extends TestCase {
         }
     }
 
-    public void testStartupCheckForAccessDecisionManager() {
+    public void testStartupCheckForAccessDecisionManager()
+        throws Exception {
         MethodSecurityInterceptor si = new MethodSecurityInterceptor();
         si.setRunAsManager(new MockRunAsManager());
         si.setAuthenticationManager(new MockAuthenticationManager());
+        si.setAfterInvocationManager(new MockAfterInvocationManager());
 
         si.setObjectDefinitionSource(new MockMethodDefinitionSource(false, true));
 
@@ -310,10 +343,12 @@ public class MethodSecurityInterceptorTests extends TestCase {
         }
     }
 
-    public void testStartupCheckForAuthenticationManager() {
+    public void testStartupCheckForAuthenticationManager()
+        throws Exception {
         MethodSecurityInterceptor si = new MethodSecurityInterceptor();
         si.setAccessDecisionManager(new MockAccessDecisionManager());
         si.setRunAsManager(new MockRunAsManager());
+        si.setAfterInvocationManager(new MockAfterInvocationManager());
 
         si.setObjectDefinitionSource(new MockMethodDefinitionSource(false, true));
 
@@ -326,7 +361,8 @@ public class MethodSecurityInterceptorTests extends TestCase {
         }
     }
 
-    public void testStartupCheckForMethodDefinitionSource() {
+    public void testStartupCheckForMethodDefinitionSource()
+        throws Exception {
         MethodSecurityInterceptor si = new MethodSecurityInterceptor();
         si.setAccessDecisionManager(new MockAccessDecisionManager());
         si.setAuthenticationManager(new MockAuthenticationManager());
@@ -340,7 +376,7 @@ public class MethodSecurityInterceptorTests extends TestCase {
         }
     }
 
-    public void testStartupCheckForRunAsManager() {
+    public void testStartupCheckForRunAsManager() throws Exception {
         MethodSecurityInterceptor si = new MethodSecurityInterceptor();
         si.setAccessDecisionManager(new MockAccessDecisionManager());
         si.setAuthenticationManager(new MockAuthenticationManager());
@@ -356,7 +392,25 @@ public class MethodSecurityInterceptorTests extends TestCase {
         }
     }
 
-    public void testValidationFailsIfInvalidAttributePresented() {
+    public void testStartupCheckForValidAfterInvocationManager()
+        throws Exception {
+        MethodSecurityInterceptor si = new MethodSecurityInterceptor();
+        si.setRunAsManager(new MockRunAsManager());
+        si.setAuthenticationManager(new MockAuthenticationManager());
+        si.setAfterInvocationManager(new MockAfterInvocationManagerWhichOnlySupportsStrings());
+        si.setAccessDecisionManager(new MockAccessDecisionManager());
+        si.setObjectDefinitionSource(new MockMethodDefinitionSource(false, true));
+
+        try {
+            si.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(expected.getMessage().startsWith("AfterInvocationManager does not support secure object class:"));
+        }
+    }
+
+    public void testValidationFailsIfInvalidAttributePresented()
+        throws Exception {
         MethodSecurityInterceptor si = new MethodSecurityInterceptor();
         si.setAccessDecisionManager(new MockAccessDecisionManager());
         si.setAuthenticationManager(new MockAuthenticationManager());
@@ -374,7 +428,8 @@ public class MethodSecurityInterceptorTests extends TestCase {
         }
     }
 
-    public void testValidationNotAttemptedIfIsValidateConfigAttributesSetToFalse() {
+    public void testValidationNotAttemptedIfIsValidateConfigAttributesSetToFalse()
+        throws Exception {
         MethodSecurityInterceptor si = new MethodSecurityInterceptor();
         si.setAccessDecisionManager(new MockAccessDecisionManager());
         si.setAuthenticationManager(new MockAuthenticationManager());
@@ -388,7 +443,8 @@ public class MethodSecurityInterceptorTests extends TestCase {
         assertTrue(true);
     }
 
-    public void testValidationNotAttemptedIfMethodDefinitionSourceCannotReturnIterator() {
+    public void testValidationNotAttemptedIfMethodDefinitionSourceCannotReturnIterator()
+        throws Exception {
         MethodSecurityInterceptor si = new MethodSecurityInterceptor();
         si.setAccessDecisionManager(new MockAccessDecisionManager());
         si.setRunAsManager(new MockRunAsManager());
@@ -407,6 +463,29 @@ public class MethodSecurityInterceptorTests extends TestCase {
         return (ITargetObject) context.getBean("target");
     }
 
+    private ITargetObject makeInterceptedTargetRejectsAuthentication() {
+        ApplicationContext context = new ClassPathXmlApplicationContext(
+                "net/sf/acegisecurity/intercept/method/aopalliance/applicationContext.xml");
+
+        MockAuthenticationManager authenticationManager = new MockAuthenticationManager(false);
+        MethodSecurityInterceptor si = (MethodSecurityInterceptor) context
+            .getBean("securityInterceptor");
+        si.setAuthenticationManager(authenticationManager);
+
+        return (ITargetObject) context.getBean("target");
+    }
+
+    private ITargetObject makeInterceptedTargetWithoutAnAfterInvocationManager() {
+        ApplicationContext context = new ClassPathXmlApplicationContext(
+                "net/sf/acegisecurity/intercept/method/aopalliance/applicationContext.xml");
+
+        MethodSecurityInterceptor si = (MethodSecurityInterceptor) context
+            .getBean("securityInterceptor");
+        si.setAfterInvocationManager(null);
+
+        return (ITargetObject) context.getBean("target");
+    }
+
     //~ Inner Classes ==========================================================
 
     private class MockAccessDecisionManagerWhichOnlySupportsStrings
@@ -430,6 +509,28 @@ public class MethodSecurityInterceptorTests extends TestCase {
         }
     }
 
+    private class MockAfterInvocationManagerWhichOnlySupportsStrings
+        implements AfterInvocationManager {
+        public Object decide(Authentication authentication, Object object,
+            ConfigAttributeDefinition config, Object returnedObject)
+            throws AccessDeniedException {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public boolean supports(Class clazz) {
+            if (String.class.isAssignableFrom(clazz)) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        public boolean supports(ConfigAttribute attribute) {
+            return true;
+        }
+    }
+
     private class MockObjectDefinitionSourceWhichOnlySupportsStrings
         extends AbstractMethodDefinitionSource {
         public Iterator getConfigAttributeDefinitions() {

+ 163 - 0
core/src/test/java/org/acegisecurity/intercept/method/aspectj/AspectJSecurityInterceptorTests.java

@@ -0,0 +1,163 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.intercept.method.aspectj;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.AccessDeniedException;
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+import net.sf.acegisecurity.MockAccessDecisionManager;
+import net.sf.acegisecurity.MockApplicationContext;
+import net.sf.acegisecurity.MockAuthenticationManager;
+import net.sf.acegisecurity.MockJoinPoint;
+import net.sf.acegisecurity.MockRunAsManager;
+import net.sf.acegisecurity.TargetObject;
+import net.sf.acegisecurity.context.ContextHolder;
+import net.sf.acegisecurity.context.SecureContext;
+import net.sf.acegisecurity.context.SecureContextImpl;
+import net.sf.acegisecurity.intercept.method.MethodDefinitionMap;
+import net.sf.acegisecurity.intercept.method.MethodDefinitionSourceEditor;
+import net.sf.acegisecurity.providers.TestingAuthenticationToken;
+
+import java.lang.reflect.Method;
+
+
+/**
+ * Tests {@link AspectJSecurityInterceptor}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AspectJSecurityInterceptorTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public AspectJSecurityInterceptorTests() {
+        super();
+    }
+
+    public AspectJSecurityInterceptorTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AspectJSecurityInterceptorTests.class);
+    }
+
+    public void testCallbackIsInvokedWhenPermissionGranted()
+        throws Exception {
+        AspectJSecurityInterceptor si = new AspectJSecurityInterceptor();
+        si.setApplicationContext(MockApplicationContext.getContext());
+        si.setAccessDecisionManager(new MockAccessDecisionManager());
+        si.setAuthenticationManager(new MockAuthenticationManager());
+        si.setRunAsManager(new MockRunAsManager());
+
+        MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
+        editor.setAsText(
+            "net.sf.acegisecurity.TargetObject.countLength=MOCK_ONE,MOCK_TWO");
+
+        MethodDefinitionMap map = (MethodDefinitionMap) editor.getValue();
+        si.setObjectDefinitionSource(map);
+        assertEquals(map, si.getObjectDefinitionSource());
+
+        si.afterPropertiesSet();
+
+        Class clazz = TargetObject.class;
+        Method method = clazz.getMethod("countLength",
+                new Class[] {String.class});
+        MockJoinPoint joinPoint = new MockJoinPoint(new TargetObject(), method);
+
+        MockAspectJCallback aspectJCallback = new MockAspectJCallback();
+
+        SecureContext secureContext = new SecureContextImpl();
+        secureContext.setAuthentication(new TestingAuthenticationToken(
+                "marissa", "koala",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_ONE")}));
+        ContextHolder.setContext(secureContext);
+
+        Object result = si.invoke(joinPoint, aspectJCallback);
+
+        assertEquals("object proceeded", result);
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testCallbackIsNotInvokedWhenPermissionDenied()
+        throws Exception {
+        AspectJSecurityInterceptor si = new AspectJSecurityInterceptor();
+        si.setApplicationContext(MockApplicationContext.getContext());
+        si.setAccessDecisionManager(new MockAccessDecisionManager());
+        si.setAuthenticationManager(new MockAuthenticationManager());
+        si.setRunAsManager(new MockRunAsManager());
+
+        MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
+        editor.setAsText(
+            "net.sf.acegisecurity.TargetObject.countLength=MOCK_ONE,MOCK_TWO");
+
+        MethodDefinitionMap map = (MethodDefinitionMap) editor.getValue();
+        si.setObjectDefinitionSource(map);
+
+        si.afterPropertiesSet();
+
+        Class clazz = TargetObject.class;
+        Method method = clazz.getMethod("countLength",
+                new Class[] {String.class});
+        MockJoinPoint joinPoint = new MockJoinPoint(new TargetObject(), method);
+
+        MockAspectJCallback aspectJCallback = new MockAspectJCallback();
+        aspectJCallback.setThrowExceptionIfInvoked(true);
+
+        SecureContext secureContext = new SecureContextImpl();
+        secureContext.setAuthentication(new TestingAuthenticationToken(
+                "marissa", "koala", new GrantedAuthority[] {}));
+        ContextHolder.setContext(secureContext);
+
+        try {
+            si.invoke(joinPoint, aspectJCallback);
+            fail("Should have thrown AccessDeniedException");
+        } catch (AccessDeniedException expected) {
+            assertTrue(true);
+        }
+
+        ContextHolder.setContext(null);
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockAspectJCallback implements AspectJCallback {
+        private boolean throwExceptionIfInvoked = false;
+
+        private MockAspectJCallback() {}
+
+        public void setThrowExceptionIfInvoked(boolean throwExceptionIfInvoked) {
+            this.throwExceptionIfInvoked = throwExceptionIfInvoked;
+        }
+
+        public Object proceedWithObject() {
+            if (throwExceptionIfInvoked) {
+                throw new IllegalStateException("AspectJCallback proceeded");
+            }
+
+            return "object proceeded";
+        }
+    }
+}

+ 51 - 3
core/src/test/java/org/acegisecurity/intercept/web/FilterSecurityInterceptorTests.java

@@ -25,6 +25,7 @@ import net.sf.acegisecurity.ConfigAttributeDefinition;
 import net.sf.acegisecurity.GrantedAuthority;
 import net.sf.acegisecurity.GrantedAuthorityImpl;
 import net.sf.acegisecurity.MockAccessDecisionManager;
+import net.sf.acegisecurity.MockApplicationContext;
 import net.sf.acegisecurity.MockAuthenticationManager;
 import net.sf.acegisecurity.MockHttpServletRequest;
 import net.sf.acegisecurity.MockHttpServletResponse;
@@ -74,7 +75,8 @@ public class FilterSecurityInterceptorTests extends TestCase {
         junit.textui.TestRunner.run(FilterSecurityInterceptorTests.class);
     }
 
-    public void testEnsuresAccessDecisionManagerSupportsFilterInvocationClass() {
+    public void testEnsuresAccessDecisionManagerSupportsFilterInvocationClass()
+        throws Exception {
         FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
         interceptor.setAuthenticationManager(new MockAuthenticationManager());
         interceptor.setObjectDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
@@ -106,7 +108,8 @@ public class FilterSecurityInterceptorTests extends TestCase {
         }
     }
 
-    public void testEnsuresRunAsManagerSupportsFilterInvocationClass() {
+    public void testEnsuresRunAsManagerSupportsFilterInvocationClass()
+        throws Exception {
         FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
         interceptor.setAccessDecisionManager(new MockAccessDecisionManager());
         interceptor.setAuthenticationManager(new MockAuthenticationManager());
@@ -138,7 +141,51 @@ public class FilterSecurityInterceptorTests extends TestCase {
         }
     }
 
-    public void testNormalStartupAndGetter() {
+    public void testHttpsInvocationReflectsPortNumber()
+        throws Throwable {
+        // Setup the FilterSecurityInterceptor
+        FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
+        interceptor.setAccessDecisionManager(new MockAccessDecisionManager());
+        interceptor.setAuthenticationManager(new MockAuthenticationManager());
+        interceptor.setRunAsManager(new MockRunAsManager());
+        interceptor.setApplicationContext(MockApplicationContext.getContext());
+
+        // Setup a mock config attribute definition
+        ConfigAttributeDefinition def = new ConfigAttributeDefinition();
+        def.addConfigAttribute(new SecurityConfig("MOCK_OK"));
+
+        MockFilterInvocationDefinitionMap mockSource = new MockFilterInvocationDefinitionMap("/secure/page.html",
+                def);
+        interceptor.setObjectDefinitionSource(mockSource);
+
+        // Setup our expectation that the filter chain will be invoked, as access is granted
+        MockFilterChain chain = new MockFilterChain(true);
+
+        // Setup our HTTPS request and response
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockHttpServletRequest request = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request.setServletPath("/secure/page.html");
+        request.setScheme("https");
+        request.setServerPort(443);
+
+        // Setup a Context
+        SecureContext context = new SecureContextImpl();
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
+                "Password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_OK")});
+        context.setAuthentication(token);
+        ContextHolder.setContext(context);
+
+        // Create and test our secure object
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+        interceptor.invoke(fi);
+
+        // Destroy the Context
+        ContextHolder.setContext(null);
+    }
+
+    public void testNormalStartupAndGetter() throws Exception {
         FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
         interceptor.setAccessDecisionManager(new MockAccessDecisionManager());
         interceptor.setAuthenticationManager(new MockAuthenticationManager());
@@ -164,6 +211,7 @@ public class FilterSecurityInterceptorTests extends TestCase {
         interceptor.setAccessDecisionManager(new MockAccessDecisionManager());
         interceptor.setAuthenticationManager(new MockAuthenticationManager());
         interceptor.setRunAsManager(new MockRunAsManager());
+        interceptor.setApplicationContext(MockApplicationContext.getContext());
 
         // Setup a mock config attribute definition
         ConfigAttributeDefinition def = new ConfigAttributeDefinition();

+ 2 - 0
core/src/test/java/org/acegisecurity/providers/cas/CasAuthenticationTokenTests.java

@@ -174,6 +174,8 @@ public class CasAuthenticationTokenTests extends TestCase {
         assertEquals("PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt",
             token.getProxyGrantingTicketIou());
         assertEquals(proxyList, token.getProxyList());
+        assertEquals(makeUserDetails().getUsername(),
+            token.getUserDetails().getUsername());
     }
 
     public void testNoArgConstructor() {

+ 81 - 1
core/src/test/java/org/acegisecurity/providers/dao/DaoAuthenticationProviderTests.java

@@ -31,6 +31,8 @@ import net.sf.acegisecurity.providers.dao.cache.NullUserCache;
 import net.sf.acegisecurity.providers.dao.salt.SystemWideSaltSource;
 import net.sf.acegisecurity.providers.encoding.ShaPasswordEncoder;
 
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
 import org.springframework.dao.DataAccessException;
 import org.springframework.dao.DataRetrievalFailureException;
 
@@ -103,6 +105,22 @@ public class DaoAuthenticationProviderTests extends TestCase {
         }
     }
 
+    public void testAuthenticateFailsWithEmptyUsername() {
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null,
+                "koala");
+
+        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
+        provider.setAuthenticationDao(new MockAuthenticationDaoUserMarissa());
+        provider.setUserCache(new MockUserCache());
+
+        try {
+            provider.authenticate(token);
+            fail("Should have thrown BadCredentialsException");
+        } catch (BadCredentialsException expected) {
+            assertTrue(true);
+        }
+    }
+
     public void testAuthenticateFailsWithInvalidPassword() {
         UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa",
                 "INVALID_PASSWORD");
@@ -247,6 +265,27 @@ public class DaoAuthenticationProviderTests extends TestCase {
         assertEquals("ROLE_TWO", castResult.getAuthorities()[1].getAuthority());
     }
 
+    public void testAuthenticatesWithForcePrincipalAsString() {
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa",
+                "koala");
+
+        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
+        provider.setAuthenticationDao(new MockAuthenticationDaoUserMarissa());
+        provider.setUserCache(new MockUserCache());
+        provider.setForcePrincipalAsString(true);
+
+        Authentication result = provider.authenticate(token);
+
+        if (!(result instanceof UsernamePasswordAuthenticationToken)) {
+            fail(
+                "Should have returned instance of UsernamePasswordAuthenticationToken");
+        }
+
+        UsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result;
+        assertEquals(String.class, castResult.getPrincipal().getClass());
+        assertEquals("marissa", castResult.getPrincipal());
+    }
+
     public void testGettersSetters() {
         DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
         provider.setPasswordEncoder(new ShaPasswordEncoder());
@@ -264,6 +303,41 @@ public class DaoAuthenticationProviderTests extends TestCase {
         assertFalse(provider.isForcePrincipalAsString());
         provider.setForcePrincipalAsString(true);
         assertTrue(provider.isForcePrincipalAsString());
+
+        provider.setApplicationContext(new ClassPathXmlApplicationContext(
+                "net/sf/acegisecurity/util/filtertest-valid.xml"));
+        assertEquals(ClassPathXmlApplicationContext.class.getName(),
+            provider.getContext().getClass().getName());
+    }
+
+    public void testGoesBackToAuthenticationDaoToObtainLatestPasswordIfCachedPasswordSeemsIncorrect() {
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa",
+                "koala");
+
+        MockAuthenticationDaoUserMarissa authenticationDao = new MockAuthenticationDaoUserMarissa();
+        MockUserCache cache = new MockUserCache();
+        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
+        provider.setAuthenticationDao(authenticationDao);
+        provider.setUserCache(cache);
+
+        // This will work, as password still "koala"
+        provider.authenticate(token);
+
+        // Check "marissa = koala" ended up in the cache
+        assertEquals("koala", cache.getUserFromCache("marissa").getPassword());
+
+        // Now change the password the AuthenticationDao will return
+        authenticationDao.setPassword("easternLongNeckTurtle");
+
+        // Now try authentication again, with the new password
+        token = new UsernamePasswordAuthenticationToken("marissa",
+                "easternLongNeckTurtle");
+        provider.authenticate(token);
+
+        // To get this far, the new password was accepted
+        // Check the cache was updated
+        assertEquals("easternLongNeckTurtle",
+            cache.getUserFromCache("marissa").getPassword());
     }
 
     public void testStartupFailsIfNoAuthenticationDao()
@@ -320,10 +394,16 @@ public class DaoAuthenticationProviderTests extends TestCase {
     }
 
     private class MockAuthenticationDaoUserMarissa implements AuthenticationDao {
+        private String password = "koala";
+
+        public void setPassword(String password) {
+            this.password = password;
+        }
+
         public UserDetails loadUserByUsername(String username)
             throws UsernameNotFoundException, DataAccessException {
             if ("marissa".equals(username)) {
-                return new User("marissa", "koala", true,
+                return new User("marissa", password, true,
                     new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
                             "ROLE_TWO")});
             } else {

+ 21 - 0
core/src/test/java/org/acegisecurity/providers/dao/PasswordDaoAuthenticationProviderTests.java

@@ -198,6 +198,27 @@ public class PasswordDaoAuthenticationProviderTests extends TestCase {
         assertEquals(result.getCredentials(), result2.getCredentials());
     }
 
+    public void testAuthenticatesWithForcePrincipalAsString() {
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("marissa",
+                "koala");
+
+        PasswordDaoAuthenticationProvider provider = new PasswordDaoAuthenticationProvider();
+        provider.setPasswordAuthenticationDao(new MockAuthenticationDaoUserMarissa());
+        provider.setUserCache(new MockUserCache());
+        provider.setForcePrincipalAsString(true);
+
+        Authentication result = provider.authenticate(token);
+
+        if (!(result instanceof UsernamePasswordAuthenticationToken)) {
+            fail(
+                "Should have returned instance of UsernamePasswordAuthenticationToken");
+        }
+
+        UsernamePasswordAuthenticationToken castResult = (UsernamePasswordAuthenticationToken) result;
+        assertEquals(String.class, castResult.getPrincipal().getClass());
+        assertEquals("marissa", castResult.getPrincipal());
+    }
+
     public void testGettersSetters() {
         PasswordDaoAuthenticationProvider provider = new PasswordDaoAuthenticationProvider();
         provider.setUserCache(new EhCacheBasedUserCache());

+ 16 - 0
core/src/test/java/org/acegisecurity/providers/dao/event/LoggerListenerTests.java

@@ -65,6 +65,22 @@ public class LoggerListenerTests extends TestCase {
         assertTrue(true);
     }
 
+    public void testLogsUsernameNotFoundEvents() {
+        AuthenticationFailureUsernameNotFoundEvent event = new AuthenticationFailureUsernameNotFoundEvent(getAuthentication(),
+                getUser());
+        LoggerListener listener = new LoggerListener();
+        listener.onApplicationEvent(event);
+        assertTrue(true);
+    }
+
+    public void testLogsUsernameOfPasswordEvent() {
+        AuthenticationFailureUsernameOrPasswordEvent event = new AuthenticationFailureUsernameOrPasswordEvent(getAuthentication(),
+                getUser());
+        LoggerListener listener = new LoggerListener();
+        listener.onApplicationEvent(event);
+        assertTrue(true);
+    }
+
     private Authentication getAuthentication() {
         UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken("Principal",
                 "Credentials");

+ 226 - 0
core/src/test/java/org/acegisecurity/taglibs/authz/AclTagTests.java

@@ -0,0 +1,226 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.taglibs.authz;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.MockAclManager;
+import net.sf.acegisecurity.MockApplicationContext;
+import net.sf.acegisecurity.acl.AclEntry;
+import net.sf.acegisecurity.acl.AclManager;
+import net.sf.acegisecurity.acl.basic.MockAclObjectIdentity;
+import net.sf.acegisecurity.acl.basic.SimpleAclEntry;
+import net.sf.acegisecurity.context.ContextHolder;
+import net.sf.acegisecurity.context.SecureContext;
+import net.sf.acegisecurity.context.SecureContextImpl;
+import net.sf.acegisecurity.providers.TestingAuthenticationToken;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.Tag;
+
+
+/**
+ * Tests {@link AclTag}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AclTagTests extends TestCase {
+    //~ Instance fields ========================================================
+
+    private final MyAclTag aclTag = new MyAclTag();
+
+    //~ Methods ================================================================
+
+    public void testInclusionDeniedWhenAclManagerUnawareOfObject()
+        throws JspException {
+        Authentication auth = new TestingAuthenticationToken("marissa",
+                "koala", new GrantedAuthority[] {});
+        SecureContext sc = new SecureContextImpl();
+        sc.setAuthentication(auth);
+        ContextHolder.setContext(sc);
+
+        aclTag.setHasPermission(new Long(SimpleAclEntry.ADMINISTRATION)
+            .toString());
+        aclTag.setDomainObject(new Integer(54));
+        assertEquals(Tag.SKIP_BODY, aclTag.doStartTag());
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testInclusionDeniedWhenAuthenticationEmpty()
+        throws JspException {
+        ContextHolder.setContext(new SecureContextImpl());
+
+        aclTag.setHasPermission(new Long(SimpleAclEntry.ADMINISTRATION)
+            .toString());
+        aclTag.setDomainObject("object1");
+        assertEquals(Tag.SKIP_BODY, aclTag.doStartTag());
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testInclusionDeniedWhenContextHolderEmpty()
+        throws JspException {
+        ContextHolder.setContext(null);
+
+        aclTag.setHasPermission(new Long(SimpleAclEntry.ADMINISTRATION)
+            .toString());
+        aclTag.setDomainObject("object1");
+        assertEquals(Tag.SKIP_BODY, aclTag.doStartTag());
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testInclusionDeniedWhenNoListOfPermissionsGiven()
+        throws JspException {
+        Authentication auth = new TestingAuthenticationToken("marissa",
+                "koala", new GrantedAuthority[] {});
+        SecureContext sc = new SecureContextImpl();
+        sc.setAuthentication(auth);
+        ContextHolder.setContext(sc);
+
+        aclTag.setHasPermission(null);
+        aclTag.setDomainObject("object1");
+        assertEquals(Tag.SKIP_BODY, aclTag.doStartTag());
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testInclusionDeniedWhenPrincipalDoesNotHoldAnyPermissions()
+        throws JspException {
+        Authentication auth = new TestingAuthenticationToken("john", "crow",
+                new GrantedAuthority[] {});
+        SecureContext sc = new SecureContextImpl();
+        sc.setAuthentication(auth);
+        ContextHolder.setContext(sc);
+
+        aclTag.setHasPermission(new Integer(SimpleAclEntry.ADMINISTRATION)
+            + "," + new Integer(SimpleAclEntry.READ));
+        assertEquals(new Integer(SimpleAclEntry.ADMINISTRATION) + ","
+            + new Integer(SimpleAclEntry.READ), aclTag.getHasPermission());
+        aclTag.setDomainObject("object1");
+        assertEquals("object1", aclTag.getDomainObject());
+        assertEquals(Tag.SKIP_BODY, aclTag.doStartTag());
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testInclusionDeniedWhenPrincipalDoesNotHoldRequiredPermissions()
+        throws JspException {
+        Authentication auth = new TestingAuthenticationToken("marissa",
+                "koala", new GrantedAuthority[] {});
+        SecureContext sc = new SecureContextImpl();
+        sc.setAuthentication(auth);
+        ContextHolder.setContext(sc);
+
+        aclTag.setHasPermission(new Integer(SimpleAclEntry.DELETE).toString());
+        aclTag.setDomainObject("object1");
+        assertEquals(Tag.SKIP_BODY, aclTag.doStartTag());
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testInclusionPermittedWhenDomainObjectIsNull()
+        throws JspException {
+        aclTag.setHasPermission(new Integer(SimpleAclEntry.READ).toString());
+        aclTag.setDomainObject(null);
+        assertEquals(Tag.EVAL_BODY_INCLUDE, aclTag.doStartTag());
+    }
+
+    public void testJspExceptionThrownIfHasPermissionNotValidFormat()
+        throws JspException {
+        Authentication auth = new TestingAuthenticationToken("john", "crow",
+                new GrantedAuthority[] {});
+        SecureContext sc = new SecureContextImpl();
+        sc.setAuthentication(auth);
+        ContextHolder.setContext(sc);
+
+        aclTag.setHasPermission("0,5, 6"); // shouldn't be any space
+
+        try {
+            aclTag.doStartTag();
+            fail("Should have thrown JspException");
+        } catch (JspException expected) {
+            assertTrue(true);
+        }
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testOperationWhenPrincipalHoldsPermissionOfMultipleList()
+        throws JspException {
+        Authentication auth = new TestingAuthenticationToken("marissa",
+                "koala", new GrantedAuthority[] {});
+        SecureContext sc = new SecureContextImpl();
+        sc.setAuthentication(auth);
+        ContextHolder.setContext(sc);
+
+        aclTag.setHasPermission(new Integer(SimpleAclEntry.ADMINISTRATION)
+            + "," + new Integer(SimpleAclEntry.READ));
+        aclTag.setDomainObject("object1");
+        assertEquals(Tag.EVAL_BODY_INCLUDE, aclTag.doStartTag());
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testOperationWhenPrincipalHoldsPermissionOfSingleList()
+        throws JspException {
+        Authentication auth = new TestingAuthenticationToken("marissa",
+                "koala", new GrantedAuthority[] {});
+        SecureContext sc = new SecureContextImpl();
+        sc.setAuthentication(auth);
+        ContextHolder.setContext(sc);
+
+        aclTag.setHasPermission(new Integer(SimpleAclEntry.READ).toString());
+        aclTag.setDomainObject("object1");
+        assertEquals(Tag.EVAL_BODY_INCLUDE, aclTag.doStartTag());
+
+        ContextHolder.setContext(null);
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockAclEntry implements AclEntry {
+        // just so AclTag iterates some different types of AclEntrys
+    }
+
+    private class MyAclTag extends AclTag {
+        protected ApplicationContext getContext(PageContext pageContext) {
+            ConfigurableApplicationContext context = MockApplicationContext
+                .getContext();
+
+            // Create an AclManager
+            AclManager aclManager = new MockAclManager("object1", "marissa",
+                    new AclEntry[] {new MockAclEntry(), new SimpleAclEntry(
+                            "marissa", new MockAclObjectIdentity(), null,
+                            SimpleAclEntry.ADMINISTRATION), new SimpleAclEntry(
+                            "marissa", new MockAclObjectIdentity(), null,
+                            SimpleAclEntry.READ)});
+
+            // Register the AclManager into our ApplicationContext
+            context.getBeanFactory().registerSingleton("aclManager", aclManager);
+
+            return context;
+        }
+    }
+}

+ 133 - 0
core/src/test/java/org/acegisecurity/taglibs/authz/AuthenticationTagTests.java

@@ -0,0 +1,133 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.taglibs.authz;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.context.ContextHolder;
+import net.sf.acegisecurity.context.SecureContext;
+import net.sf.acegisecurity.context.SecureContextImpl;
+import net.sf.acegisecurity.providers.TestingAuthenticationToken;
+import net.sf.acegisecurity.providers.dao.User;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.Tag;
+
+
+/**
+ * Tests {@link AuthenticationTag}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthenticationTagTests extends TestCase {
+    //~ Instance fields ========================================================
+
+    private final MyAuthenticationTag authenticationTag = new MyAuthenticationTag();
+
+    //~ Methods ================================================================
+
+    public void testOperationWhenAuthenticationIsNull()
+        throws JspException {
+        ContextHolder.setContext(new SecureContextImpl());
+
+        authenticationTag.setOperation("principal");
+        assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
+        assertEquals(null, authenticationTag.getLastMessage());
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testOperationWhenContextHolderIsNull()
+        throws JspException {
+        ContextHolder.setContext(null);
+
+        authenticationTag.setOperation("principal");
+        assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
+        assertEquals(null, authenticationTag.getLastMessage());
+    }
+
+    public void testOperationWhenPrincipalIsAString() throws JspException {
+        Authentication auth = new TestingAuthenticationToken("marissaAsString",
+                "koala", new GrantedAuthority[] {});
+        SecureContext sc = new SecureContextImpl();
+        sc.setAuthentication(auth);
+        ContextHolder.setContext(sc);
+
+        authenticationTag.setOperation("principal");
+        assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
+        assertEquals("marissaAsString", authenticationTag.getLastMessage());
+    }
+
+    public void testOperationWhenPrincipalIsAUserDetailsInstance()
+        throws JspException {
+        Authentication auth = new TestingAuthenticationToken(new User(
+                    "marissaUserDetails", "koala", true,
+                    new GrantedAuthority[] {}), "koala",
+                new GrantedAuthority[] {});
+        SecureContext sc = new SecureContextImpl();
+        sc.setAuthentication(auth);
+        ContextHolder.setContext(sc);
+
+        authenticationTag.setOperation("principal");
+        assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
+        assertEquals("marissaUserDetails", authenticationTag.getLastMessage());
+    }
+
+    public void testOperationWhenPrincipalIsNull() throws JspException {
+        Authentication auth = new TestingAuthenticationToken(null, "koala",
+                new GrantedAuthority[] {});
+        SecureContext sc = new SecureContextImpl();
+        sc.setAuthentication(auth);
+        ContextHolder.setContext(sc);
+
+        authenticationTag.setOperation("principal");
+        assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
+    }
+
+    public void testSkipsBodyIfNullOrEmptyOperation() throws Exception {
+        authenticationTag.setOperation("");
+        assertEquals("", authenticationTag.getOperation());
+        assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
+    }
+
+    public void testThrowsExceptionForUnrecognisedOperation() {
+        authenticationTag.setOperation("qsq");
+
+        try {
+            authenticationTag.doStartTag();
+            fail("Should have throwns JspException");
+        } catch (JspException expected) {
+            assertTrue(true);
+        }
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MyAuthenticationTag extends AuthenticationTag {
+        String lastMessage = null;
+
+        public String getLastMessage() {
+            return lastMessage;
+        }
+
+        protected void writeMessage(String msg) throws JspException {
+            lastMessage = msg;
+        }
+    }
+}

+ 6 - 0
core/src/test/java/org/acegisecurity/ui/AbstractProcessingFilterTests.java

@@ -179,6 +179,10 @@ public class AbstractProcessingFilterTests extends TestCase {
         filter.setAuthenticationProxyUntrustedFailureUrl("/proxy");
         assertEquals("/proxy",
             filter.getAuthenticationProxyUntrustedFailureUrl());
+
+        filter.setAuthenticationServiceFailureUrl("/serviceFailure");
+        assertEquals("/serviceFailure",
+            filter.getAuthenticationServiceFailureUrl());
     }
 
     public void testIgnoresAnyServletPathOtherThanFilterProcessesUrl()
@@ -365,7 +369,9 @@ public class AbstractProcessingFilterTests extends TestCase {
         MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
         filter.setFilterProcessesUrl("/j_mock_post");
         filter.setDefaultTargetUrl("/foobar");
+        assertFalse(filter.isAlwaysUseDefaultTargetUrl()); // check default
         filter.setAlwaysUseDefaultTargetUrl(true);
+        assertTrue(filter.isAlwaysUseDefaultTargetUrl()); // check changed
 
         // Test
         executeFilterInContainerSimulator(config, filter, request, response,

+ 143 - 0
core/src/test/java/org/acegisecurity/ui/httpinvoker/AuthenticationSimpleHttpInvokerRequestExecutorTests.java

@@ -0,0 +1,143 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.ui.httpinvoker;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.context.ContextHolder;
+import net.sf.acegisecurity.context.SecureContext;
+import net.sf.acegisecurity.context.SecureContextImpl;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import java.io.IOException;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Tests {@link AuthenticationSimpleHttpInvokerRequestExecutor}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthenticationSimpleHttpInvokerRequestExecutorTests
+    extends TestCase {
+    //~ Constructors ===========================================================
+
+    public AuthenticationSimpleHttpInvokerRequestExecutorTests() {
+        super();
+    }
+
+    public AuthenticationSimpleHttpInvokerRequestExecutorTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AuthenticationSimpleHttpInvokerRequestExecutorTests.class);
+    }
+
+    public void testNormalOperation() throws Exception {
+        // Setup client-side context
+        SecureContext clientSideContext = new SecureContextImpl();
+        Authentication clientSideAuthentication = new UsernamePasswordAuthenticationToken("Aladdin",
+                "open sesame");
+        clientSideContext.setAuthentication(clientSideAuthentication);
+        ContextHolder.setContext(clientSideContext);
+
+        // Create a connection and ensure our executor sets its
+        // properties correctly
+        AuthenticationSimpleHttpInvokerRequestExecutor executor = new AuthenticationSimpleHttpInvokerRequestExecutor();
+        HttpURLConnection conn = new MockHttpURLConnection(new URL(
+                    "http://localhost/"));
+        executor.prepareConnection(conn, 10);
+
+        // Check connection properties
+        // See http://www.faqs.org/rfcs/rfc1945.html section 11.1 for example
+        // we are comparing against
+        assertEquals("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
+            conn.getRequestProperty("Authorization"));
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testNullAuthenticationIsNull() throws Exception {
+        // Setup client-side context
+        SecureContext clientSideContext = new SecureContextImpl();
+        clientSideContext.setAuthentication(null);
+        ContextHolder.setContext(clientSideContext);
+
+        // Create a connection and ensure our executor sets its
+        // properties correctly
+        AuthenticationSimpleHttpInvokerRequestExecutor executor = new AuthenticationSimpleHttpInvokerRequestExecutor();
+        HttpURLConnection conn = new MockHttpURLConnection(new URL(
+                    "http://localhost/"));
+        executor.prepareConnection(conn, 10);
+
+        // Check connection properties (shouldn't be an Authorization header)
+        assertNull(conn.getRequestProperty("Authorization"));
+    }
+
+    public void testNullContextHolderIsNull() throws Exception {
+        ContextHolder.setContext(null); // just to be explicit
+
+        // Create a connection and ensure our executor sets its
+        // properties correctly
+        AuthenticationSimpleHttpInvokerRequestExecutor executor = new AuthenticationSimpleHttpInvokerRequestExecutor();
+        HttpURLConnection conn = new MockHttpURLConnection(new URL(
+                    "http://localhost/"));
+        executor.prepareConnection(conn, 10);
+
+        // Check connection properties (shouldn't be an Authorization header)
+        assertNull(conn.getRequestProperty("Authorization"));
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockHttpURLConnection extends HttpURLConnection {
+        private Map requestProperties = new HashMap();
+
+        public MockHttpURLConnection(URL u) {
+            super(u);
+        }
+
+        public void setRequestProperty(String key, String value) {
+            requestProperties.put(key, value);
+        }
+
+        public String getRequestProperty(String key) {
+            return (String) requestProperties.get(key);
+        }
+
+        public void connect() throws IOException {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+
+        public void disconnect() {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+
+        public boolean usingProxy() {
+            throw new UnsupportedOperationException("mock not implemented");
+        }
+    }
+}

+ 102 - 0
core/src/test/java/org/acegisecurity/ui/rmi/ContextPropagatingRemoteInvocationTests.java

@@ -0,0 +1,102 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.ui.rmi;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.MockMethodInvocation;
+import net.sf.acegisecurity.TargetObject;
+import net.sf.acegisecurity.context.ContextHolder;
+import net.sf.acegisecurity.context.SecureContext;
+import net.sf.acegisecurity.context.SecureContextImpl;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import java.lang.reflect.Method;
+
+
+/**
+ * Tests {@link ContextPropagatingRemoteInvocation} and {@link
+ * ContextPropagatingRemoteInvocationFactory}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class ContextPropagatingRemoteInvocationTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public ContextPropagatingRemoteInvocationTests() {
+        super();
+    }
+
+    public ContextPropagatingRemoteInvocationTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(ContextPropagatingRemoteInvocationTests.class);
+    }
+
+    public void testNormalOperation() throws Exception {
+        // Setup client-side context
+        SecureContext clientSideContext = new SecureContextImpl();
+        Authentication clientSideAuthentication = new UsernamePasswordAuthenticationToken("marissa",
+                "koala");
+        clientSideContext.setAuthentication(clientSideAuthentication);
+        ContextHolder.setContext(clientSideContext);
+
+        ContextPropagatingRemoteInvocation remoteInvocation = getRemoteInvocation();
+
+        // Set to null, as ContextPropagatingRemoteInvocation already obtained
+        // a copy and nulling is necessary to ensure the Context delivered by
+        // ContextPropagatingRemoteInvocation is used on server-side
+        ContextHolder.setContext(null);
+
+        // The result from invoking the TargetObject should contain the
+        // Authentication class delivered via the ContextHolder
+        assertEquals("some_string net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken false",
+            remoteInvocation.invoke(new TargetObject()));
+    }
+
+    public void testNullContextHolderDoesNotCauseInvocationProblems()
+        throws Exception {
+        ContextHolder.setContext(null); // just to be explicit
+
+        ContextPropagatingRemoteInvocation remoteInvocation = getRemoteInvocation();
+        ContextHolder.setContext(null); // unnecessary, but for explicitness
+
+        assertEquals("some_string ContextHolder Not Security Aware",
+            remoteInvocation.invoke(new TargetObject()));
+    }
+
+    private ContextPropagatingRemoteInvocation getRemoteInvocation()
+        throws Exception {
+        Class clazz = TargetObject.class;
+        Method method = clazz.getMethod("makeLowerCase",
+                new Class[] {String.class});
+        MethodInvocation mi = new MockMethodInvocation(method,
+                new Object[] {"SOME_STRING"});
+
+        ContextPropagatingRemoteInvocationFactory factory = new ContextPropagatingRemoteInvocationFactory();
+
+        return (ContextPropagatingRemoteInvocation) factory
+        .createRemoteInvocation(mi);
+    }
+}

+ 32 - 1
core/src/test/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilterEntryPointTests.java

@@ -98,7 +98,8 @@ public class AuthenticationProcessingFilterEntryPointTests extends TestCase {
         assertTrue(ep.getForceHttps());
     }
 
-    public void testHttpsOperation() throws Exception {
+    public void testHttpsOperationFromOriginalHttpUrl()
+        throws Exception {
         MockHttpServletRequest request = new MockHttpServletRequest(
                 "/some_path");
         request.setScheme("http");
@@ -150,6 +151,36 @@ public class AuthenticationProcessingFilterEntryPointTests extends TestCase {
             response.getRedirect());
     }
 
+    public void testHttpsOperationFromOriginalHttpsUrl()
+        throws Exception {
+        MockHttpServletRequest request = new MockHttpServletRequest(
+                "/some_path");
+        request.setScheme("https");
+        request.setServerName("www.example.com");
+        request.setContextPath("/bigWebApp");
+        request.setServerPort(443);
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        AuthenticationProcessingFilterEntryPoint ep = new AuthenticationProcessingFilterEntryPoint();
+        ep.setLoginFormUrl("/hello");
+        ep.setPortMapper(new PortMapperImpl());
+        ep.setForceHttps(true);
+        ep.setPortMapper(new PortMapperImpl());
+        ep.setPortResolver(new MockPortResolver(80, 443));
+        ep.afterPropertiesSet();
+
+        ep.commence(request, response);
+        assertEquals("https://www.example.com/bigWebApp/hello",
+            response.getRedirect());
+
+        request.setServerPort(8443);
+        ep.setPortResolver(new MockPortResolver(8080, 8443));
+        ep.commence(request, response);
+        assertEquals("https://www.example.com:8443/bigWebApp/hello",
+            response.getRedirect());
+    }
+
     public void testNormalOperation() throws Exception {
         AuthenticationProcessingFilterEntryPoint ep = new AuthenticationProcessingFilterEntryPoint();
         ep.setLoginFormUrl("/hello");

+ 20 - 0
core/src/test/java/org/acegisecurity/util/FilterToBeanProxyTests.java

@@ -226,6 +226,26 @@ public class FilterToBeanProxyTests extends TestCase {
             chain);
     }
 
+    public void testNullDelegateDoesNotCauseNullPointerException()
+        throws Exception {
+        // Setup our filter
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("targetBean", "aFilterThatDoesntExist");
+        config.setInitParmeter("init", "lazy");
+
+        // Setup our expectation that the filter chain will be invoked
+        MockFilterChain chain = new MockFilterChain(true);
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockHttpServletRequest request = new MockHttpServletRequest("/go");
+
+        FilterToBeanProxy filter = new MockFilterToBeanProxy(
+                "net/sf/acegisecurity/util/filtertest-valid.xml");
+
+        // do not init (which would hapen if called .doFilter)
+        filter.destroy();
+    }
+
     private void executeFilterInContainerSimulator(FilterConfig filterConfig,
         Filter filter, ServletRequest request, ServletResponse response,
         FilterChain filterChain) throws ServletException, IOException {

+ 12 - 0
core/src/test/java/org/acegisecurity/util/PortResolverImplTests.java

@@ -67,6 +67,18 @@ public class PortResolverImplTests extends TestCase {
         assertEquals(8443, pr.getServerPort(request));
     }
 
+    public void testDetectsEmptyPortMapper() throws Exception {
+        PortResolverImpl pr = new PortResolverImpl();
+        pr.setPortMapper(null);
+
+        try {
+            pr.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
     public void testGettersSetters() throws Exception {
         PortResolverImpl pr = new PortResolverImpl();
         assertTrue(pr.getPortMapper() != null);

+ 485 - 0
core/src/test/java/org/acegisecurity/vote/BasicAclEntryVoterTests.java

@@ -0,0 +1,485 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.vote;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.AuthorizationServiceException;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockAclManager;
+import net.sf.acegisecurity.MockMethodInvocation;
+import net.sf.acegisecurity.SecurityConfig;
+import net.sf.acegisecurity.acl.AclEntry;
+import net.sf.acegisecurity.acl.AclManager;
+import net.sf.acegisecurity.acl.basic.MockAclObjectIdentity;
+import net.sf.acegisecurity.acl.basic.SimpleAclEntry;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.aspectj.lang.JoinPoint;
+
+import java.lang.reflect.Method;
+
+
+/**
+ * Tests {@link BasicAclEntryVoter}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class BasicAclEntryVoterTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public BasicAclEntryVoterTests() {
+        super();
+    }
+
+    public BasicAclEntryVoterTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(BasicAclEntryVoterTests.class);
+    }
+
+    public void testNormalOperation() throws Exception {
+        // Setup a domain object subject of this test
+        SomeDomainObject domainObject = new SomeDomainObject("foo");
+
+        // Setup an AclManager
+        AclManager aclManager = new MockAclManager(domainObject, "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)});
+
+        // Wire up a voter
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        voter.setAclManager(aclManager);
+        assertEquals(aclManager, voter.getAclManager());
+        voter.setProcessConfigAttribute("FOO_ADMIN_OR_WRITE_ACCESS");
+        assertEquals("FOO_ADMIN_OR_WRITE_ACCESS",
+            voter.getProcessConfigAttribute());
+        voter.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION, SimpleAclEntry.WRITE});
+        assertEquals(2, voter.getRequirePermission().length);
+        voter.setProcessDomainObjectClass(SomeDomainObject.class);
+        assertEquals(SomeDomainObject.class, voter.getProcessDomainObjectClass());
+        voter.afterPropertiesSet();
+
+        // Wire up an invocation to be voted on
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("FOO_ADMIN_OR_WRITE_ACCESS"));
+
+        // Setup a MockMethodInvocation, so voter can retrieve domainObject
+        MethodInvocation mi = getMethodInvocation(domainObject);
+
+        assertEquals(AccessDecisionVoter.ACCESS_GRANTED,
+            voter.vote(
+                new UsernamePasswordAuthenticationToken("marissa", null), mi,
+                attr));
+    }
+
+    public void testOnlySupportsMethodInvocation() {
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        assertTrue(voter.supports(MethodInvocation.class));
+        assertFalse(voter.supports(JoinPoint.class));
+    }
+
+    public void testStartupRejectsMissingAclManager() throws Exception {
+        AclManager aclManager = new MockAclManager("domain1", "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)});
+
+        // Wire up a voter
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        voter.setProcessConfigAttribute("FOO_ADMIN_OR_WRITE_ACCESS");
+        voter.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION, SimpleAclEntry.WRITE});
+        voter.setProcessDomainObjectClass(SomeDomainObject.class);
+
+        try {
+            voter.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testStartupRejectsMissingProcessConfigAttribute()
+        throws Exception {
+        AclManager aclManager = new MockAclManager("domain1", "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)});
+
+        // Wire up a voter
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        voter.setAclManager(aclManager);
+        voter.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION, SimpleAclEntry.WRITE});
+        voter.setProcessDomainObjectClass(SomeDomainObject.class);
+
+        try {
+            voter.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testStartupRejectsMissingProcessDomainObjectClass()
+        throws Exception {
+        AclManager aclManager = new MockAclManager("domain1", "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)});
+
+        // Wire up a voter
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        voter.setAclManager(aclManager);
+        voter.setProcessConfigAttribute("FOO_ADMIN_OR_WRITE_ACCESS");
+        voter.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION, SimpleAclEntry.WRITE});
+
+        try {
+            voter.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testStartupRejectsMissingRequirePermission()
+        throws Exception {
+        AclManager aclManager = new MockAclManager("domain1", "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)});
+
+        // Wire up a voter
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        voter.setAclManager(aclManager);
+        voter.setProcessConfigAttribute("FOO_ADMIN_OR_WRITE_ACCESS");
+        voter.setProcessDomainObjectClass(SomeDomainObject.class);
+
+        try {
+            voter.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testSupportsConfigAttribute() {
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        voter.setProcessConfigAttribute("foobar");
+        assertTrue(voter.supports(new SecurityConfig("foobar")));
+    }
+
+    public void testVoterAbstainsIfDomainObjectIsNull()
+        throws Exception {
+        // Setup a domain object subject of this test
+        SomeDomainObject domainObject = new SomeDomainObject("foo");
+
+        // Setup an AclManager
+        AclManager aclManager = new MockAclManager(domainObject, "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)});
+
+        // Wire up a voter
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        voter.setAclManager(aclManager);
+        voter.setProcessConfigAttribute("FOO_ADMIN_OR_WRITE_ACCESS");
+        voter.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION, SimpleAclEntry.WRITE});
+        voter.setProcessDomainObjectClass(SomeDomainObject.class);
+        voter.afterPropertiesSet();
+
+        // Wire up an invocation to be voted on
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("A_DIFFERENT_ATTRIBUTE"));
+
+        // Setup a MockMethodInvocation, so voter can retrieve domainObject
+        MethodInvocation mi = getMethodInvocation(domainObject);
+
+        assertEquals(AccessDecisionVoter.ACCESS_ABSTAIN,
+            voter.vote(
+                new UsernamePasswordAuthenticationToken("marissa", null), mi,
+                attr));
+    }
+
+    public void testVoterAbstainsIfNotMatchingConfigAttribute()
+        throws Exception {
+        // Setup a domain object subject of this test
+        SomeDomainObject domainObject = null;
+
+        // Setup an AclManager
+        AclManager aclManager = new MockAclManager(domainObject, "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)});
+
+        // Wire up a voter
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        voter.setAclManager(aclManager);
+        voter.setProcessConfigAttribute("FOO_ADMIN_OR_WRITE_ACCESS");
+        voter.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION, SimpleAclEntry.WRITE});
+        voter.setProcessDomainObjectClass(SomeDomainObject.class);
+        voter.afterPropertiesSet();
+
+        // Wire up an invocation to be voted on
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("FOO_ADMIN_OR_WRITE_ACCESS"));
+
+        // Setup a MockMethodInvocation, so voter can retrieve domainObject
+        MethodInvocation mi = getMethodInvocation(domainObject);
+
+        assertEquals(AccessDecisionVoter.ACCESS_ABSTAIN,
+            voter.vote(
+                new UsernamePasswordAuthenticationToken("marissa", null), mi,
+                attr));
+    }
+
+    public void testVoterCanDenyAccessBasedOnInternalMethodOfDomainObject()
+        throws Exception {
+        // Setup a domain object subject of this test
+        SomeDomainObject domainObject = new SomeDomainObject("foo");
+
+        // Setup an AclManager
+        AclManager aclManager = new MockAclManager(domainObject.getParent(),
+                "marissa",
+                new AclEntry[] {new MockAclEntry(), new SimpleAclEntry(
+                        "marissa", new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.DELETE)});
+
+        // Wire up a voter
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        voter.setAclManager(aclManager);
+        voter.setProcessConfigAttribute("FOO_ADMIN_OR_WRITE_ACCESS");
+        voter.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION, SimpleAclEntry.WRITE});
+        voter.setProcessDomainObjectClass(SomeDomainObject.class);
+        voter.setInternalMethod("getParent");
+        voter.afterPropertiesSet();
+
+        // Wire up an invocation to be voted on
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("FOO_ADMIN_OR_WRITE_ACCESS"));
+
+        // Setup a MockMethodInvocation, so voter can retrieve domainObject
+        MethodInvocation mi = getMethodInvocation(domainObject);
+
+        assertEquals(AccessDecisionVoter.ACCESS_DENIED,
+            voter.vote(
+                new UsernamePasswordAuthenticationToken("marissa", null), mi,
+                attr));
+    }
+
+    public void testVoterCanDenyAccessIfPrincipalHasNoPermissionsAtAllToDomainObject()
+        throws Exception {
+        // Setup a domain object subject of this test
+        SomeDomainObject domainObject = new SomeDomainObject("foo");
+
+        // Setup an AclManager
+        AclManager aclManager = new MockAclManager(domainObject, "marissa",
+                new AclEntry[] {new MockAclEntry(), new SimpleAclEntry(
+                        "marissa", new MockAclObjectIdentity(), null,
+                        SimpleAclEntry.DELETE)});
+
+        // Wire up a voter
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        voter.setAclManager(aclManager);
+        voter.setProcessConfigAttribute("FOO_ADMIN_OR_WRITE_ACCESS");
+        voter.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION, SimpleAclEntry.WRITE});
+        voter.setProcessDomainObjectClass(SomeDomainObject.class);
+        voter.setInternalMethod("getParent");
+        voter.afterPropertiesSet();
+
+        // Wire up an invocation to be voted on
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("FOO_ADMIN_OR_WRITE_ACCESS"));
+
+        // Setup a MockMethodInvocation, so voter can retrieve domainObject
+        MethodInvocation mi = getMethodInvocation(domainObject);
+
+        // NB: scott is the principal, not marissa
+        assertEquals(AccessDecisionVoter.ACCESS_DENIED,
+            voter.vote(new UsernamePasswordAuthenticationToken("scott", null),
+                mi, attr));
+    }
+
+    public void testVoterCanGrantAccessBasedOnInternalMethodOfDomainObject()
+        throws Exception {
+        // Setup a domain object subject of this test
+        SomeDomainObject domainObject = new SomeDomainObject("foo");
+
+        // Setup an AclManager
+        AclManager aclManager = new MockAclManager(domainObject.getParent(),
+                "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)});
+
+        // Wire up a voter
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        voter.setAclManager(aclManager);
+        voter.setProcessConfigAttribute("FOO_ADMIN_OR_WRITE_ACCESS");
+        voter.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION, SimpleAclEntry.WRITE});
+        voter.setProcessDomainObjectClass(SomeDomainObject.class);
+        voter.setInternalMethod("getParent");
+        assertEquals("getParent", voter.getInternalMethod());
+        voter.afterPropertiesSet();
+
+        // Wire up an invocation to be voted on
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("FOO_ADMIN_OR_WRITE_ACCESS"));
+
+        // Setup a MockMethodInvocation, so voter can retrieve domainObject
+        // (well actually it will access domainObject.getParent())
+        MethodInvocation mi = getMethodInvocation(domainObject);
+
+        assertEquals(AccessDecisionVoter.ACCESS_GRANTED,
+            voter.vote(
+                new UsernamePasswordAuthenticationToken("marissa", null), mi,
+                attr));
+    }
+
+    public void testVoterThrowsExceptionIfInvalidInternalMethodOfDomainObject()
+        throws Exception {
+        // Setup a domain object subject of this test
+        SomeDomainObject domainObject = new SomeDomainObject("foo");
+
+        // Setup an AclManager
+        AclManager aclManager = new MockAclManager(domainObject.getParent(),
+                "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)});
+
+        // Wire up a voter
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        voter.setAclManager(aclManager);
+        voter.setProcessConfigAttribute("FOO_ADMIN_OR_WRITE_ACCESS");
+        voter.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION, SimpleAclEntry.WRITE});
+        voter.setProcessDomainObjectClass(SomeDomainObject.class);
+        voter.setInternalMethod("getNonExistentParentName");
+        voter.afterPropertiesSet();
+
+        // Wire up an invocation to be voted on
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("FOO_ADMIN_OR_WRITE_ACCESS"));
+
+        // Setup a MockMethodInvocation, so voter can retrieve domainObject
+        // (well actually it will access domainObject.getParent())
+        MethodInvocation mi = getMethodInvocation(domainObject);
+
+        try {
+            voter.vote(new UsernamePasswordAuthenticationToken("marissa", null),
+                mi, attr);
+            fail("Should have thrown AuthorizationServiceException");
+        } catch (AuthorizationServiceException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testVoterThrowsExceptionIfProcessDomainObjectNotFound()
+        throws Exception {
+        // Setup a domain object subject of this test
+        SomeDomainObject domainObject = new SomeDomainObject("foo");
+
+        // Setup an AclManager
+        AclManager aclManager = new MockAclManager(domainObject.getParent(),
+                "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)});
+
+        // Wire up a voter
+        BasicAclEntryVoter voter = new BasicAclEntryVoter();
+        voter.setAclManager(aclManager);
+        voter.setProcessConfigAttribute("FOO_ADMIN_OR_WRITE_ACCESS");
+        voter.setRequirePermission(new int[] {SimpleAclEntry.ADMINISTRATION, SimpleAclEntry.WRITE});
+        voter.setProcessDomainObjectClass(SomeDomainObject.class);
+        voter.afterPropertiesSet();
+
+        // Wire up an invocation to be voted on
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("FOO_ADMIN_OR_WRITE_ACCESS"));
+
+        // Setup a MockMethodInvocation that doesn't provide SomeDomainObject arg
+        Class clazz = String.class;
+        Method method = clazz.getMethod("toString", new Class[] {});
+
+        MethodInvocation mi = new MockMethodInvocation(method,
+                new Object[] {domainObject});
+
+        try {
+            voter.vote(new UsernamePasswordAuthenticationToken("marissa", null),
+                mi, attr);
+            fail("Should have thrown AuthorizationServiceException");
+        } catch (AuthorizationServiceException expected) {
+            assertTrue(true);
+        }
+    }
+
+    private MethodInvocation getMethodInvocation(SomeDomainObject domainObject)
+        throws Exception {
+        Class clazz = SomeDomainObjectManager.class;
+        Method method = clazz.getMethod("someServiceMethod",
+                new Class[] {SomeDomainObject.class});
+
+        return new MockMethodInvocation(method, new Object[] {domainObject});
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockAclEntry implements AclEntry {
+        // just so AclTag iterates some different types of AclEntrys
+    }
+}

+ 42 - 0
core/src/test/java/org/acegisecurity/vote/SomeDomainObject.java

@@ -0,0 +1,42 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.vote;
+
+/**
+ * Simple domain object, used by {@link BasicAclEntryVoterTests}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class SomeDomainObject {
+    //~ Instance fields ========================================================
+
+    private String identity;
+
+    //~ Constructors ===========================================================
+
+    public SomeDomainObject(String identity) {
+        this.identity = identity;
+    }
+
+    private SomeDomainObject() {}
+
+    //~ Methods ================================================================
+
+    public String getParent() {
+        return "parentOf" + identity;
+    }
+}

+ 29 - 0
core/src/test/java/org/acegisecurity/vote/SomeDomainObjectManager.java

@@ -0,0 +1,29 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.vote;
+
+/**
+ * Used by {@link BasicAclEntryVoterTests} so it can create a
+ * <code>MethodInvocation</code> contining <code>SomeDomainObject</code>.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class SomeDomainObjectManager {
+    //~ Methods ================================================================
+
+    public void someServiceMethod(SomeDomainObject someDomainObject) {}
+}

+ 29 - 0
core/src/test/resources/org/acegisecurity/applicationContext.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+<!--
+ * Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Id$
+-->
+
+<beans>
+
+   <!-- Automatically receives AuthenticationEvent messages from DaoAuthenticationProvider -->
+   <bean id="authenticationLoggerListener" class="net.sf.acegisecurity.providers.dao.event.LoggerListener"/>
+
+   <!-- Automatically receives AuthenticationEvent messages from AbstractSecurityInterceptor -->
+   <bean id="secureObjectLoggerListener" class="net.sf.acegisecurity.intercept.event.LoggerListener"/>
+
+</beans>

+ 13 - 10
core/src/test/resources/org/acegisecurity/intercept/method/aopalliance/applicationContext.xml

@@ -20,6 +20,7 @@
 
 <beans>
 
+	<bean id="afterInvocation" class="net.sf.acegisecurity.MockAfterInvocationManager"/>
 	<bean id="authentication" class="net.sf.acegisecurity.MockAuthenticationManager"/>
 	<bean id="accessDecision" class="net.sf.acegisecurity.MockAccessDecisionManager"/>
 	<bean id="runAs" class="net.sf.acegisecurity.MockRunAsManager"/>
@@ -29,25 +30,27 @@
 	<bean id="securityInterceptor" class="net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
 		<property name="authenticationManager"><ref local="authentication"/></property>
 		<property name="accessDecisionManager"><ref local="accessDecision"/></property>
+		<property name="afterInvocationManager"><ref local="afterInvocation"/></property>
 		<property name="runAsManager"><ref local="runAs"/></property>
 		<property name="objectDefinitionSource">
 			<value>
 				net.sf.acegisecurity.ITargetObject.makeLower*=MOCK_LOWER
 				net.sf.acegisecurity.ITargetObject.makeUpper*=MOCK_UPPER,RUN_AS
+				net.sf.acegisecurity.ITargetObject.computeHashCode*=MOCK_HASH,AFTER_INVOCATION_MOCK
 			</value>
 		</property>
 	</bean>
 	
-	<bean id="target" class="net.sf.acegisecurity.TargetObject"/>
+	<bean id="realTarget" class="net.sf.acegisecurity.TargetObject"/>
 
-	<!-- Autowire is fine as advisor requires MethodSecurityInterceptor -->
-	<bean id="methodSecurityAdvisor"
-		class="net.sf.acegisecurity.intercept.method.aopalliance.MethodDefinitionSourceAdvisor"
-		autowire="constructor" >
-	</bean>
-	
-	<bean id="autoproxy" 
-		class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
-	</bean>
+    <bean id="target" class="org.springframework.aop.framework.ProxyFactoryBean">
+      <property name="proxyInterfaces"><value>net.sf.acegisecurity.ITargetObject</value></property>
+      <property name="interceptorNames">
+         <list>
+            <idref local="securityInterceptor"/>
+            <idref local="realTarget"/>
+         </list>
+      </property>
+    </bean>
 
 </beans>

+ 55 - 0
core/src/test/resources/org/acegisecurity/intercept/method/applicationContext.xml

@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+<!--
+ * Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Id$
+-->
+
+<beans>
+
+	<bean id="authentication" class="net.sf.acegisecurity.MockAuthenticationManager"/>
+	<bean id="accessDecision" class="net.sf.acegisecurity.MockAccessDecisionManager"/>
+	<bean id="runAs" class="net.sf.acegisecurity.MockRunAsManager"/>
+	<bean id="attributes" class="net.sf.acegisecurity.intercept.method.MockAttributes"/>
+
+	<bean id="objectDefinitionSource" class="net.sf.acegisecurity.intercept.method.MethodDefinitionAttributes">
+		<property name="attributes"><ref local="attributes"/></property>
+	</bean>
+	
+	<bean id="securityInterceptor" class="net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
+		<property name="authenticationManager"><ref local="authentication"/></property>
+		<property name="accessDecisionManager"><ref local="accessDecision"/></property>
+		<property name="runAsManager"><ref local="runAs"/></property>
+		<property name="objectDefinitionSource">
+			<ref local="objectDefinitionSource"/>
+		</property>
+	</bean>
+	
+	<bean id="realTarget" class="net.sf.acegisecurity.TargetObject"/>
+
+    <bean id="target" class="org.springframework.aop.framework.ProxyFactoryBean">
+      <property name="proxyInterfaces"><value>net.sf.acegisecurity.ITargetObject</value></property>
+      <property name="interceptorNames">
+         <list>
+            <idref local="securityInterceptor"/>
+            <idref local="realTarget"/>
+         </list>
+      </property>
+    </bean>
+
+	<bean id="loggerListener" class="net.sf.acegisecurity.intercept.event.LoggerListener"/>
+
+</beans>