Przeglądaj źródła

Initial commit.

Ben Alex 21 lat temu
rodzic
commit
eaffc00fc4
27 zmienionych plików z 4874 dodań i 0 usunięć
  1. 39 0
      core/src/test/java/org/acegisecurity/MockFilterChain.java
  2. 64 0
      core/src/test/java/org/acegisecurity/MockFilterConfig.java
  3. 291 0
      core/src/test/java/org/acegisecurity/MockHttpServletRequest.java
  4. 170 0
      core/src/test/java/org/acegisecurity/MockHttpServletResponse.java
  5. 108 0
      core/src/test/java/org/acegisecurity/MockHttpSession.java
  6. 52 0
      core/src/test/java/org/acegisecurity/MockMethodInvocation.java
  7. 100 0
      core/src/test/java/org/acegisecurity/intercept/method/AbstractMethodDefinitionSourceTests.java
  8. 283 0
      core/src/test/java/org/acegisecurity/intercept/method/MethodDefinitionAttributesTests.java
  9. 220 0
      core/src/test/java/org/acegisecurity/intercept/method/MethodDefinitionSourceEditorTests.java
  10. 521 0
      core/src/test/java/org/acegisecurity/intercept/method/MethodSecurityInterceptorTests.java
  11. 160 0
      core/src/test/java/org/acegisecurity/intercept/method/MockAttributes.java
  12. 88 0
      core/src/test/java/org/acegisecurity/intercept/method/MockMethodDefinitionSource.java
  13. 117 0
      core/src/test/java/org/acegisecurity/intercept/web/AbstractFilterInvocationDefinitionSourceTests.java
  14. 123 0
      core/src/test/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionMapTests.java
  15. 240 0
      core/src/test/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionSourceEditorTests.java
  16. 374 0
      core/src/test/java/org/acegisecurity/intercept/web/FilterInvocationTests.java
  17. 258 0
      core/src/test/java/org/acegisecurity/intercept/web/FilterSecurityInterceptorTests.java
  18. 88 0
      core/src/test/java/org/acegisecurity/intercept/web/MockFilterInvocationDefinitionSource.java
  19. 317 0
      core/src/test/java/org/acegisecurity/intercept/web/SecurityEnforcementFilterTests.java
  20. 37 0
      core/src/test/java/org/acegisecurity/intercept/web/securityfiltertest-invalid.xml
  21. 76 0
      core/src/test/java/org/acegisecurity/intercept/web/securityfiltertest-valid.xml
  22. 262 0
      core/src/test/java/org/acegisecurity/ui/AbstractIntegrationFilterTests.java
  23. 208 0
      core/src/test/java/org/acegisecurity/ui/AutoIntegrationFilterTests.java
  24. 495 0
      core/src/test/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilterTests.java
  25. 93 0
      core/src/test/java/org/acegisecurity/ui/webapp/HttpSessionIntegrationFilterTests.java
  26. 38 0
      core/src/test/java/org/acegisecurity/ui/webapp/filtertest-invalid.xml
  27. 52 0
      core/src/test/java/org/acegisecurity/ui/webapp/filtertest-valid.xml

+ 39 - 0
core/src/test/java/org/acegisecurity/MockFilterChain.java

@@ -0,0 +1,39 @@
+/* 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.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+/**
+ * Mocks a <code>FilterChain</code> but with no behaviour.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MockFilterChain implements FilterChain {
+    //~ Methods ================================================================
+
+    public void doFilter(ServletRequest arg0, ServletResponse arg1)
+        throws IOException, ServletException {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+}

+ 64 - 0
core/src/test/java/org/acegisecurity/MockFilterConfig.java

@@ -0,0 +1,64 @@
+/* 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.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MockFilterConfig implements FilterConfig {
+    //~ Instance fields ========================================================
+
+    private Map map = new HashMap();
+
+    //~ Methods ================================================================
+
+    public String getFilterName() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getInitParameter(String arg0) {
+        Object result = map.get(arg0);
+
+        if (result != null) {
+            return (String) result;
+        } else {
+            return null;
+        }
+    }
+
+    public Enumeration getInitParameterNames() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void setInitParmeter(String parameter, String value) {
+        map.put(parameter, value);
+    }
+
+    public ServletContext getServletContext() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+}

+ 291 - 0
core/src/test/java/org/acegisecurity/MockHttpServletRequest.java

@@ -0,0 +1,291 @@
+/* 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.io.BufferedReader;
+import java.io.IOException;
+
+import java.security.Principal;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+
+/**
+ * Mocks a <code>HttpServletRequest</code> and provides the
+ * <code>getUserPrincipal()</code>, <code>getContextPath()</code>,
+ * <code>getServletPath()</code> and <code>getSession()</code> methods.
+ * 
+ * <P>
+ * Also provides a convenience <code>Map</code> for storing request parameters.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MockHttpServletRequest implements HttpServletRequest {
+    //~ Instance fields ========================================================
+
+    private HttpSession session;
+    private Map map = new HashMap();
+    private Principal principal;
+    private String contextPath = "";
+    private String queryString = null;
+    private String servletPath;
+
+    //~ Constructors ===========================================================
+
+    public MockHttpServletRequest(Principal principal, HttpSession session) {
+        this.principal = principal;
+        this.session = session;
+    }
+
+    public MockHttpServletRequest(String queryString) {
+        this.queryString = queryString;
+    }
+
+    private MockHttpServletRequest() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    public void setAttribute(String arg0, Object arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Object getAttribute(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Enumeration getAttributeNames() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getAuthType() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void setCharacterEncoding(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getCharacterEncoding() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public int getContentLength() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getContentType() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void setContextPath(String contextPath) {
+        this.contextPath = contextPath;
+    }
+
+    public String getContextPath() {
+        return contextPath;
+    }
+
+    public Cookie[] getCookies() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public long getDateHeader(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getHeader(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Enumeration getHeaderNames() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Enumeration getHeaders(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public ServletInputStream getInputStream() throws IOException {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public int getIntHeader(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Locale getLocale() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Enumeration getLocales() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getMethod() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void setParameter(String arg0, String value) {
+        map.put(arg0, value);
+    }
+
+    public String getParameter(String arg0) {
+        Object result = map.get(arg0);
+
+        if (result != null) {
+            return (String) map.get(arg0);
+        } else {
+            return null;
+        }
+    }
+
+    public Map getParameterMap() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Enumeration getParameterNames() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String[] getParameterValues(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getPathInfo() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getPathTranslated() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getProtocol() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getQueryString() {
+        return this.queryString;
+    }
+
+    public BufferedReader getReader() throws IOException {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getRealPath(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getRemoteAddr() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getRemoteHost() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getRemoteUser() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public RequestDispatcher getRequestDispatcher(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getRequestURI() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public StringBuffer getRequestURL() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getRequestedSessionId() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public boolean isRequestedSessionIdFromCookie() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public boolean isRequestedSessionIdFromURL() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public boolean isRequestedSessionIdFromUrl() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public boolean isRequestedSessionIdValid() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getScheme() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public boolean isSecure() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getServerName() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public int getServerPort() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void setServletPath(String servletPath) {
+        this.servletPath = servletPath;
+    }
+
+    public String getServletPath() {
+        return this.servletPath;
+    }
+
+    public HttpSession getSession(boolean arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public HttpSession getSession() {
+        return this.session;
+    }
+
+    public boolean isUserInRole(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Principal getUserPrincipal() {
+        return this.principal;
+    }
+
+    public void removeAttribute(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+}

+ 170 - 0
core/src/test/java/org/acegisecurity/MockHttpServletResponse.java

@@ -0,0 +1,170 @@
+/* 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.io.IOException;
+import java.io.PrintWriter;
+
+import java.util.Locale;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Mocks a <code>HttpServletResponse</code>, recording the
+ * <code>sendRedirect</code> URL and <code>sendError</code> code.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MockHttpServletResponse implements HttpServletResponse {
+    //~ Instance fields ========================================================
+
+    private String redirect;
+    private int error;
+
+    //~ Methods ================================================================
+
+    public void setBufferSize(int arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public int getBufferSize() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getCharacterEncoding() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public boolean isCommitted() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void setContentLength(int arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void setContentType(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void setDateHeader(String arg0, long arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public int getError() {
+        return this.error;
+    }
+
+    public void setHeader(String arg0, String arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void setIntHeader(String arg0, int arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void setLocale(Locale arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Locale getLocale() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public ServletOutputStream getOutputStream() throws IOException {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getRedirect() {
+        return redirect;
+    }
+
+    public void setStatus(int arg0, String arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void setStatus(int arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public PrintWriter getWriter() throws IOException {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void addCookie(Cookie arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void addDateHeader(String arg0, long arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void addHeader(String arg0, String arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void addIntHeader(String arg0, int arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public boolean containsHeader(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String encodeRedirectURL(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String encodeRedirectUrl(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String encodeURL(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String encodeUrl(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void flushBuffer() throws IOException {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void reset() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void resetBuffer() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void sendError(int arg0, String arg1) throws IOException {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void sendError(int arg0) throws IOException {
+        this.error = arg0;
+    }
+
+    public void sendRedirect(String arg0) throws IOException {
+        this.redirect = arg0;
+    }
+}

+ 108 - 0
core/src/test/java/org/acegisecurity/MockHttpSession.java

@@ -0,0 +1,108 @@
+/* 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.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionContext;
+
+
+/**
+ * Mocks a <code>HttpSession</code> and provides the
+ * <code>getAttribute()</code> and <code>setAttribute()</code> methods.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MockHttpSession implements HttpSession {
+    //~ Instance fields ========================================================
+
+    private Map map = new HashMap();
+
+    //~ Methods ================================================================
+
+    public void setAttribute(String arg0, Object arg1) {
+        map.put(arg0, arg1);
+    }
+
+    public Object getAttribute(String arg0) {
+        return map.get(arg0);
+    }
+
+    public Enumeration getAttributeNames() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public long getCreationTime() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String getId() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public long getLastAccessedTime() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void setMaxInactiveInterval(int arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public int getMaxInactiveInterval() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public boolean isNew() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public ServletContext getServletContext() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public HttpSessionContext getSessionContext() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Object getValue(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public String[] getValueNames() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void invalidate() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void putValue(String arg0, Object arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void removeAttribute(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public void removeValue(String arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+}

+ 52 - 0
core/src/test/java/org/acegisecurity/MockMethodInvocation.java

@@ -0,0 +1,52 @@
+/* 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.aopalliance.intercept.MethodInvocation;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Method;
+
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MockMethodInvocation implements MethodInvocation {
+    //~ Methods ================================================================
+
+    public Object[] getArguments() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Method getMethod() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public AccessibleObject getStaticPart() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Object getThis() {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Object proceed() throws Throwable {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+}

+ 100 - 0
core/src/test/java/org/acegisecurity/intercept/method/AbstractMethodDefinitionSourceTests.java

@@ -0,0 +1,100 @@
+/* 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;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.MockMethodInvocation;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+
+/**
+ * Tests {@link AbstractMethodDefinitionSource} and associated {@link
+ * ConfigAttributeDefinition}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AbstractMethodDefinitionSourceTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public AbstractMethodDefinitionSourceTests() {
+        super();
+    }
+
+    public AbstractMethodDefinitionSourceTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AbstractMethodDefinitionSourceTests.class);
+    }
+
+    public void testDoesNotSupportAnotherObject() {
+        MockMethodDefinitionSource mds = new MockMethodDefinitionSource(false,
+                true);
+        assertFalse(mds.supports(String.class));
+    }
+
+    public void testGetAttributesForANonMethodInvocation() {
+        MockMethodDefinitionSource mds = new MockMethodDefinitionSource(false,
+                true);
+
+        try {
+            mds.getAttributes(new String());
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testGetAttributesForANullObject() {
+        MockMethodDefinitionSource mds = new MockMethodDefinitionSource(false,
+                true);
+
+        try {
+            mds.getAttributes(null);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testGetAttributesForMethodInvocation() {
+        MockMethodDefinitionSource mds = new MockMethodDefinitionSource(false,
+                true);
+
+        try {
+            mds.getAttributes(new MockMethodInvocation());
+            fail("Should have thrown UnsupportedOperationException");
+        } catch (UnsupportedOperationException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testSupportsMethodInvocation() {
+        MockMethodDefinitionSource mds = new MockMethodDefinitionSource(false,
+                true);
+        assertTrue(mds.supports(MethodInvocation.class));
+    }
+}

+ 283 - 0
core/src/test/java/org/acegisecurity/intercept/method/MethodDefinitionAttributesTests.java

@@ -0,0 +1,283 @@
+/* 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;
+
+import junit.framework.TestCase;
+
+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.MockMethodInvocation;
+import net.sf.acegisecurity.OtherTargetObject;
+import net.sf.acegisecurity.SecurityConfig;
+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.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;
+
+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;
+
+
+/**
+ * Tests {@link MethodDefinitionAttributes}.
+ *
+ * @author Cameron Braid
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MethodDefinitionAttributesTests extends TestCase {
+    //~ Instance fields ========================================================
+
+    ClassPathXmlApplicationContext applicationContext;
+
+    //~ Constructors ===========================================================
+
+    public MethodDefinitionAttributesTests(String a) {
+        super(a);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(MethodDefinitionAttributesTests.class);
+    }
+
+    public void testAttributesForInterfaceTargetObject()
+        throws Exception {
+        ConfigAttributeDefinition def1 = getConfigAttributeDefinition(ITargetObject.class,
+                "countLength", new Class[] {String.class});
+        Set set1 = toSet(def1);
+        assertTrue(set1.contains(new SecurityConfig("MOCK_INTERFACE")));
+        assertTrue(set1.contains(
+                new SecurityConfig("MOCK_INTERFACE_METHOD_COUNT_LENGTH")));
+
+        ConfigAttributeDefinition def2 = getConfigAttributeDefinition(ITargetObject.class,
+                "makeLowerCase", new Class[] {String.class});
+        Set set2 = toSet(def2);
+        assertTrue(set2.contains(new SecurityConfig("MOCK_INTERFACE")));
+        assertTrue(set2.contains(
+                new SecurityConfig("MOCK_INTERFACE_METHOD_MAKE_LOWER_CASE")));
+
+        ConfigAttributeDefinition def3 = getConfigAttributeDefinition(ITargetObject.class,
+                "makeUpperCase", new Class[] {String.class});
+        Set set3 = toSet(def3);
+        assertTrue(set3.contains(new SecurityConfig("MOCK_INTERFACE")));
+        assertTrue(set3.contains(
+                new SecurityConfig("MOCK_INTERFACE_METHOD_MAKE_UPPER_CASE")));
+    }
+
+    public void testAttributesForOtherTargetObject() throws Exception {
+        ConfigAttributeDefinition def1 = getConfigAttributeDefinition(OtherTargetObject.class,
+                "countLength", new Class[] {String.class});
+        Set set1 = toSet(def1);
+        assertTrue(set1.contains(new SecurityConfig("MOCK_INTERFACE")));
+        assertTrue(set1.contains(
+                new SecurityConfig("MOCK_INTERFACE_METHOD_COUNT_LENGTH")));
+
+        // Confirm MOCK_CLASS_METHOD_COUNT_LENGTH not added, as it's a String (not a ConfigAttribute)
+        // Confirm also MOCK_CLASS not added, as we return null for class
+        assertEquals(2, set1.size());
+
+        ConfigAttributeDefinition def2 = getConfigAttributeDefinition(OtherTargetObject.class,
+                "makeLowerCase", new Class[] {String.class});
+        Set set2 = toSet(def2);
+        assertTrue(set2.contains(new SecurityConfig("MOCK_INTERFACE")));
+        assertTrue(set2.contains(
+                new SecurityConfig("MOCK_INTERFACE_METHOD_MAKE_LOWER_CASE")));
+        assertTrue(set2.contains(
+                new SecurityConfig("MOCK_CLASS_METHOD_MAKE_LOWER_CASE")));
+
+        // Confirm MOCK_CLASS not added, as we return null for class
+        assertEquals(3, set2.size());
+
+        ConfigAttributeDefinition def3 = getConfigAttributeDefinition(OtherTargetObject.class,
+                "makeUpperCase", new Class[] {String.class});
+        Set set3 = toSet(def3);
+        assertTrue(set3.contains(new SecurityConfig("MOCK_INTERFACE")));
+        assertTrue(set3.contains(
+                new SecurityConfig("MOCK_INTERFACE_METHOD_MAKE_UPPER_CASE")));
+        assertTrue(set3.contains(new SecurityConfig("RUN_AS"))); // defined against interface
+
+        assertEquals(3, set3.size());
+    }
+
+    public void testAttributesForTargetObject() throws Exception {
+        ConfigAttributeDefinition def1 = getConfigAttributeDefinition(TargetObject.class,
+                "countLength", new Class[] {String.class});
+        Set set1 = toSet(def1);
+        assertTrue(set1.contains(new SecurityConfig("MOCK_INTERFACE")));
+        assertTrue(set1.contains(
+                new SecurityConfig("MOCK_INTERFACE_METHOD_COUNT_LENGTH")));
+
+        assertTrue(set1.contains(new SecurityConfig("MOCK_CLASS")));
+
+        // Confirm the MOCK_CLASS_METHOD_COUNT_LENGTH was not added, as it's not a ConfigAttribute
+        assertEquals(3, set1.size());
+
+        ConfigAttributeDefinition def2 = getConfigAttributeDefinition(TargetObject.class,
+                "makeLowerCase", new Class[] {String.class});
+        Set set2 = toSet(def2);
+        assertTrue(set2.contains(new SecurityConfig("MOCK_INTERFACE")));
+        assertTrue(set2.contains(
+                new SecurityConfig("MOCK_INTERFACE_METHOD_MAKE_LOWER_CASE")));
+        assertTrue(set2.contains(new SecurityConfig("MOCK_CLASS")));
+        assertTrue(set2.contains(
+                new SecurityConfig("MOCK_CLASS_METHOD_MAKE_LOWER_CASE")));
+        assertEquals(4, set2.size());
+
+        ConfigAttributeDefinition def3 = getConfigAttributeDefinition(TargetObject.class,
+                "makeUpperCase", new Class[] {String.class});
+        Set set3 = toSet(def3);
+        assertTrue(set3.contains(new SecurityConfig("MOCK_INTERFACE")));
+        assertTrue(set3.contains(
+                new SecurityConfig("MOCK_INTERFACE_METHOD_MAKE_UPPER_CASE")));
+        assertTrue(set3.contains(new SecurityConfig("MOCK_CLASS")));
+        assertTrue(set3.contains(
+                new SecurityConfig("MOCK_CLASS_METHOD_MAKE_UPPER_CASE")));
+        assertTrue(set3.contains(new SecurityConfig("RUN_AS")));
+        assertEquals(5, set3.size());
+    }
+
+    public void testMethodCallWithRunAsReplacement() throws Exception {
+        SecureContext context = new SecureContextImpl();
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
+                "Password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_INTERFACE_METHOD_MAKE_UPPER_CASE")});
+        context.setAuthentication(token);
+        ContextHolder.setContext(context);
+
+        ITargetObject target = makeInterceptedTarget();
+        String result = target.makeUpperCase("hello");
+        assertEquals("HELLO net.sf.acegisecurity.MockRunAsAuthenticationToken true",
+            result);
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testMethodCallWithoutRunAsReplacement()
+        throws Exception {
+        SecureContext context = new SecureContextImpl();
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
+                "Password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_INTERFACE_METHOD_MAKE_LOWER_CASE")});
+        context.setAuthentication(token);
+        ContextHolder.setContext(context);
+
+        ITargetObject target = makeInterceptedTarget();
+        String result = target.makeLowerCase("HELLO");
+
+        assertEquals("hello net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken true",
+            result);
+
+        ContextHolder.setContext(null);
+    }
+
+    private ConfigAttributeDefinition getConfigAttributeDefinition(
+        Class clazz, String methodName, Class[] args) throws Exception {
+        final Method method = clazz.getMethod(methodName, args);
+        MethodDefinitionAttributes source = new MethodDefinitionAttributes();
+        source.setAttributes(new MockAttributes());
+
+        ConfigAttributeDefinition config = source.getAttributes(new MockMethodInvocation() {
+                    public Method getMethod() {
+                        return method;
+                    }
+                });
+
+        return config;
+    }
+
+    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.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");
+    }
+
+    /**
+     * convert a <code>ConfigAttributeDefinition</code> into a set of
+     * <code>ConfigAttribute</code>(s)
+     *
+     * @param def the <code>ConfigAttributeDefinition</code> to cover
+     *
+     * @return a Set of <code>ConfigAttributes</code>
+     */
+    private Set toSet(ConfigAttributeDefinition def) {
+        Set set = new HashSet();
+        Iterator i = def.getConfigAttributes();
+
+        while (i.hasNext()) {
+            ConfigAttribute a = (ConfigAttribute) i.next();
+            set.add(a);
+        }
+
+        return set;
+    }
+}

+ 220 - 0
core/src/test/java/org/acegisecurity/intercept/method/MethodDefinitionSourceEditorTests.java

@@ -0,0 +1,220 @@
+/* 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;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.SecurityConfig;
+import net.sf.acegisecurity.TargetObject;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Method;
+
+import java.util.Iterator;
+
+
+/**
+ * Tests {@link MethodDefinitionSourceEditor} and its asociated {@link
+ * MethodDefinitionMap}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MethodDefinitionSourceEditorTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public MethodDefinitionSourceEditorTests() {
+        super();
+    }
+
+    public MethodDefinitionSourceEditorTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(MethodDefinitionSourceEditorTests.class);
+    }
+
+    public void testClassNameNotFoundResultsInException() {
+        MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
+
+        try {
+            editor.setAsText("net.sf.acegisecurity.DOES_NOT_EXIST_NAME=FOO,BAR");
+            fail("Should have given IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testClassNameNotInProperFormatResultsInException() {
+        MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
+
+        try {
+            editor.setAsText("DOES_NOT_EXIST_NAME=FOO,BAR");
+            fail("Should have given IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testClassNameValidButMethodNameInvalidResultsInException() {
+        MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
+
+        try {
+            editor.setAsText(
+                "net.sf.acegisecurity.TargetObject.INVALID_METHOD=FOO,BAR");
+            fail("Should have given IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testEmptyStringReturnsEmptyMap() {
+        MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
+        editor.setAsText("");
+
+        MethodDefinitionMap map = (MethodDefinitionMap) editor.getValue();
+        assertEquals(0, map.getMethodMapSize());
+    }
+
+    public void testIterator() {
+        MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
+        editor.setAsText(
+            "net.sf.acegisecurity.TargetObject.countLength=ROLE_ONE,ROLE_TWO,RUN_AS_ENTRY\r\nnet.sf.acegisecurity.TargetObject.make*=ROLE_NINE,ROLE_SUPERVISOR");
+
+        MethodDefinitionMap map = (MethodDefinitionMap) editor.getValue();
+        Iterator iter = map.getConfigAttributeDefinitions();
+        int counter = 0;
+
+        while (iter.hasNext()) {
+            iter.next();
+            counter++;
+        }
+
+        assertEquals(3, counter);
+    }
+
+    public void testMultiMethodParsing() {
+        MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
+        editor.setAsText(
+            "net.sf.acegisecurity.TargetObject.countLength=ROLE_ONE,ROLE_TWO,RUN_AS_ENTRY\r\nnet.sf.acegisecurity.TargetObject.make*=ROLE_NINE,ROLE_SUPERVISOR");
+
+        MethodDefinitionMap map = (MethodDefinitionMap) editor.getValue();
+        assertEquals(3, map.getMethodMapSize());
+    }
+
+    public void testMultiMethodParsingWhereLaterMethodsOverrideEarlierMethods()
+        throws Exception {
+        MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
+        editor.setAsText(
+            "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());
+
+        ConfigAttributeDefinition returnedMakeLower = map.getAttributes(new MockMethodInvocation(
+                    TargetObject.class, "makeLowerCase",
+                    new Class[] {String.class}));
+        ConfigAttributeDefinition expectedMakeLower = new ConfigAttributeDefinition();
+        expectedMakeLower.addConfigAttribute(new SecurityConfig("ROLE_LOWER"));
+        assertEquals(expectedMakeLower, returnedMakeLower);
+
+        ConfigAttributeDefinition returnedMakeUpper = map.getAttributes(new MockMethodInvocation(
+                    TargetObject.class, "makeUpperCase",
+                    new Class[] {String.class}));
+        ConfigAttributeDefinition expectedMakeUpper = new ConfigAttributeDefinition();
+        expectedMakeUpper.addConfigAttribute(new SecurityConfig("ROLE_UPPER"));
+        assertEquals(expectedMakeUpper, returnedMakeUpper);
+
+        ConfigAttributeDefinition returnedCountLength = map.getAttributes(new MockMethodInvocation(
+                    TargetObject.class, "countLength",
+                    new Class[] {String.class}));
+        ConfigAttributeDefinition expectedCountLength = new ConfigAttributeDefinition();
+        expectedCountLength.addConfigAttribute(new SecurityConfig(
+                "ROLE_GENERAL"));
+        assertEquals(expectedCountLength, returnedCountLength);
+    }
+
+    public void testNullReturnsEmptyMap() {
+        MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor();
+        editor.setAsText(null);
+
+        MethodDefinitionMap map = (MethodDefinitionMap) editor.getValue();
+        assertEquals(0, map.getMethodMapSize());
+    }
+
+    public void testSingleMethodParsing() 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();
+
+        ConfigAttributeDefinition returnedCountLength = map.getAttributes(new MockMethodInvocation(
+                    TargetObject.class, "countLength",
+                    new Class[] {String.class}));
+        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);
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockMethodInvocation implements MethodInvocation {
+        Method method;
+
+        public MockMethodInvocation(Class clazz, String methodName,
+            Class[] parameterTypes) throws NoSuchMethodException {
+            method = clazz.getMethod(methodName, parameterTypes);
+        }
+
+        private MockMethodInvocation() {
+            super();
+        }
+
+        public Object[] getArguments() {
+            return null;
+        }
+
+        public Method getMethod() {
+            return method;
+        }
+
+        public AccessibleObject getStaticPart() {
+            return null;
+        }
+
+        public Object getThis() {
+            return null;
+        }
+
+        public Object proceed() throws Throwable {
+            return null;
+        }
+    }
+}

+ 521 - 0
core/src/test/java/org/acegisecurity/intercept/method/MethodSecurityInterceptorTests.java

@@ -0,0 +1,521 @@
+/* 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;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.AccessDecisionManager;
+import net.sf.acegisecurity.AccessDeniedException;
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.AuthenticationCredentialsNotFoundException;
+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.MockAuthenticationManager;
+import net.sf.acegisecurity.MockRunAsManager;
+import net.sf.acegisecurity.RunAsManager;
+import net.sf.acegisecurity.context.ContextHolder;
+import net.sf.acegisecurity.context.ContextImpl;
+import net.sf.acegisecurity.context.SecureContext;
+import net.sf.acegisecurity.context.SecureContextImpl;
+import net.sf.acegisecurity.intercept.SecurityInterceptorCallback;
+import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;
+
+import java.util.Iterator;
+import java.util.Properties;
+
+
+/**
+ * Tests {@link MethodSecurityInterceptor}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MethodSecurityInterceptorTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public MethodSecurityInterceptorTests() {
+        super();
+    }
+
+    public MethodSecurityInterceptorTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(MethodSecurityInterceptorTests.class);
+    }
+
+    public void testCallingAPublicMethodFacadeWillNotRepeatSecurityChecksWhenPassedToTheSecuredMethodItFronts()
+        throws Exception {
+        ITargetObject target = makeInterceptedTarget();
+        String result = target.publicMakeLowerCase("HELLO");
+        assertEquals("hello ContextHolder Not Security Aware", result);
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testCallingAPublicMethodWhenPresentingASecureContextButWithoutAnyAuthenticationObject()
+        throws Exception {
+        SecureContext context = new SecureContextImpl();
+        ContextHolder.setContext(context);
+
+        ITargetObject target = makeInterceptedTarget();
+        String result = target.publicMakeLowerCase("HELLO");
+        assertEquals("hello Authentication empty", result);
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testCallingAPublicMethodWhenPresentingAnAuthenticationObjectWillProperlySetItsIsAuthenticatedProperty()
+        throws Exception {
+        SecureContext context = new SecureContextImpl();
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
+                "Password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_THIS_IS_NOT_REQUIRED_AS_IT_IS_PUBLIC")});
+        assertTrue(!token.isAuthenticated());
+        context.setAuthentication(token);
+        ContextHolder.setContext(context);
+
+        ITargetObject target = makeInterceptedTarget();
+        String result = target.publicMakeLowerCase("HELLO");
+        assertEquals("hello net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken false",
+            result);
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testDeniesWhenAppropriate() throws Exception {
+        SecureContext context = new SecureContextImpl();
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
+                "Password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_NO_BENEFIT_TO_THIS_GRANTED_AUTHORITY")});
+        context.setAuthentication(token);
+        ContextHolder.setContext(context);
+
+        ITargetObject target = makeInterceptedTarget();
+
+        try {
+            target.makeUpperCase("HELLO");
+            fail("Should have thrown AccessDeniedException");
+        } catch (AccessDeniedException expected) {
+            assertTrue(true);
+        }
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testGetters() {
+        MockAccessDecisionManager accessDecision = new MockAccessDecisionManager();
+        MockRunAsManager runAs = new MockRunAsManager();
+        MockAuthenticationManager authManager = new MockAuthenticationManager();
+        MockMethodDefinitionSource methodSource = new MockMethodDefinitionSource(false,
+                true);
+
+        MethodSecurityInterceptor si = new MethodSecurityInterceptor();
+        si.setAccessDecisionManager(accessDecision);
+        si.setRunAsManager(runAs);
+        si.setAuthenticationManager(authManager);
+        si.setObjectDefinitionSource(methodSource);
+
+        assertEquals(accessDecision, si.getAccessDecisionManager());
+        assertEquals(runAs, si.getRunAsManager());
+        assertEquals(authManager, si.getAuthenticationManager());
+        assertEquals(methodSource, si.getObjectDefinitionSource());
+    }
+
+    public void testMethodCallWithRunAsReplacement() throws Exception {
+        SecureContext context = new SecureContextImpl();
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
+                "Password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_UPPER")});
+        context.setAuthentication(token);
+        ContextHolder.setContext(context);
+
+        ITargetObject target = makeInterceptedTarget();
+        String result = target.makeUpperCase("hello");
+        assertEquals("HELLO net.sf.acegisecurity.MockRunAsAuthenticationToken true",
+            result);
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testMethodCallWithoutRunAsReplacement()
+        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 = makeInterceptedTarget();
+        String result = target.makeLowerCase("HELLO");
+
+        // Note we check the isAuthenticated becomes true in following line
+        assertEquals("hello net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken true",
+            result);
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testRejectionOfEmptyContextHolder() throws Exception {
+        ITargetObject target = makeInterceptedTarget();
+
+        try {
+            target.makeUpperCase("hello");
+            fail(
+                "Should have thrown AuthenticationCredentialsNotFoundException");
+        } catch (AuthenticationCredentialsNotFoundException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testRejectionOfNonSecureContextOnContextHolder()
+        throws Exception {
+        ContextHolder.setContext(new ContextImpl());
+
+        ITargetObject target = makeInterceptedTarget();
+
+        try {
+            target.makeUpperCase("hello");
+            fail(
+                "Should have thrown AuthenticationCredentialsNotFoundException");
+        } catch (AuthenticationCredentialsNotFoundException expected) {
+            assertTrue(true);
+        }
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testRejectionOfSecureContextThatContainsNoAuthenticationObject()
+        throws Exception {
+        ContextHolder.setContext(new SecureContextImpl());
+
+        ITargetObject target = makeInterceptedTarget();
+
+        try {
+            target.makeUpperCase("hello");
+            fail(
+                "Should have thrown AuthenticationCredentialsNotFoundException");
+        } catch (AuthenticationCredentialsNotFoundException expected) {
+            assertTrue(true);
+        }
+
+        ContextHolder.setContext(null);
+    }
+
+    public void testRejectsAccessDecisionManagersThatDoNotSupportMethodInvocation() {
+        MethodSecurityInterceptor si = new MethodSecurityInterceptor();
+        si.setAccessDecisionManager(new MockAccessDecisionManagerWhichOnlySupportsStrings());
+        si.setAuthenticationManager(new MockAuthenticationManager());
+        si.setObjectDefinitionSource(new MockMethodDefinitionSource(false, true));
+        si.setRunAsManager(new MockRunAsManager());
+
+        try {
+            si.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("AccessDecisionManager does not support MethodInvocation",
+                expected.getMessage());
+        }
+    }
+
+    public void testRejectsCallsWhenCallbackIsNull() throws Throwable {
+        MethodSecurityInterceptor interceptor = new MethodSecurityInterceptor();
+
+        try {
+            interceptor.interceptor(new Object(), null);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("Callback was null", expected.getMessage());
+        }
+    }
+
+    public void testRejectsCallsWhenObjectDefinitionSourceDoesNotSupportObject()
+        throws Throwable {
+        MethodSecurityInterceptor interceptor = new MethodSecurityInterceptor();
+        interceptor.setObjectDefinitionSource(new MockObjectDefinitionSourceWhichOnlySupportsStrings());
+
+        try {
+            interceptor.interceptor(new Integer(1),
+                new MockSecurityInterceptorCallback());
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(expected.getMessage().startsWith("ObjectDefinitionSource does not support objects of type"));
+        }
+    }
+
+    public void testRejectsCallsWhenObjectIsNull() throws Throwable {
+        MethodSecurityInterceptor interceptor = new MethodSecurityInterceptor();
+
+        try {
+            interceptor.interceptor(null, new MockSecurityInterceptorCallback());
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("Object was null", expected.getMessage());
+        }
+    }
+
+    public void testRejectsRunAsManagersThatDoNotSupportMethodInvocation() {
+        MethodSecurityInterceptor si = new MethodSecurityInterceptor();
+        si.setAccessDecisionManager(new MockAccessDecisionManager());
+        si.setAuthenticationManager(new MockAuthenticationManager());
+        si.setObjectDefinitionSource(new MockMethodDefinitionSource(false, true));
+        si.setRunAsManager(new MockRunAsManagerWhichOnlySupportsStrings());
+
+        try {
+            si.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("RunAsManager does not support MethodInvocation",
+                expected.getMessage());
+        }
+    }
+
+    public void testStartupCheckForAccessDecisionManager() {
+        MethodSecurityInterceptor si = new MethodSecurityInterceptor();
+        si.setRunAsManager(new MockRunAsManager());
+        si.setAuthenticationManager(new MockAuthenticationManager());
+
+        si.setObjectDefinitionSource(new MockMethodDefinitionSource(false, true));
+
+        try {
+            si.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("An AccessDecisionManager is required",
+                expected.getMessage());
+        }
+    }
+
+    public void testStartupCheckForAuthenticationManager() {
+        MethodSecurityInterceptor si = new MethodSecurityInterceptor();
+        si.setAccessDecisionManager(new MockAccessDecisionManager());
+        si.setRunAsManager(new MockRunAsManager());
+
+        si.setObjectDefinitionSource(new MockMethodDefinitionSource(false, true));
+
+        try {
+            si.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("An AuthenticationManager is required",
+                expected.getMessage());
+        }
+    }
+
+    public void testStartupCheckForMethodDefinitionSource() {
+        MethodSecurityInterceptor si = new MethodSecurityInterceptor();
+        si.setAccessDecisionManager(new MockAccessDecisionManager());
+        si.setRunAsManager(new MockRunAsManager());
+        si.setAuthenticationManager(new MockAuthenticationManager());
+
+        try {
+            si.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("An ObjectDefinitionSource is required",
+                expected.getMessage());
+        }
+    }
+
+    public void testStartupCheckForRunAsManager() {
+        MethodSecurityInterceptor si = new MethodSecurityInterceptor();
+        si.setAccessDecisionManager(new MockAccessDecisionManager());
+        si.setAuthenticationManager(new MockAuthenticationManager());
+
+        si.setObjectDefinitionSource(new MockMethodDefinitionSource(false, true));
+
+        try {
+            si.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("A RunAsManager is required", expected.getMessage());
+        }
+    }
+
+    public void testValidationFailsIfInvalidAttributePresented() {
+        MethodSecurityInterceptor si = new MethodSecurityInterceptor();
+        si.setAccessDecisionManager(new MockAccessDecisionManager());
+        si.setRunAsManager(new MockRunAsManager());
+        si.setAuthenticationManager(new MockAuthenticationManager());
+
+        assertTrue(si.isValidateConfigAttributes()); // check default
+        si.setObjectDefinitionSource(new MockMethodDefinitionSource(true, true));
+
+        try {
+            si.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("Unsupported configuration attributes: [ANOTHER_INVALID, INVALID_ATTRIBUTE]",
+                expected.getMessage());
+        }
+    }
+
+    public void testValidationNotAttemptedIfIsValidateConfigAttributesSetToFalse() {
+        MethodSecurityInterceptor si = new MethodSecurityInterceptor();
+        si.setAccessDecisionManager(new MockAccessDecisionManager());
+        si.setRunAsManager(new MockRunAsManager());
+        si.setAuthenticationManager(new MockAuthenticationManager());
+
+        assertTrue(si.isValidateConfigAttributes()); // check default
+        si.setValidateConfigAttributes(false);
+        assertTrue(!si.isValidateConfigAttributes()); // check changed
+
+        si.setObjectDefinitionSource(new MockMethodDefinitionSource(true, true));
+        si.afterPropertiesSet();
+        assertTrue(true);
+    }
+
+    public void testValidationNotAttemptedIfMethodDefinitionSourceCannotReturnIterator() {
+        MethodSecurityInterceptor si = new MethodSecurityInterceptor();
+        si.setAccessDecisionManager(new MockAccessDecisionManager());
+        si.setRunAsManager(new MockRunAsManager());
+        si.setAuthenticationManager(new MockAuthenticationManager());
+
+        assertTrue(si.isValidateConfigAttributes()); // check default
+        si.setObjectDefinitionSource(new MockMethodDefinitionSource(true, false));
+        si.afterPropertiesSet();
+        assertTrue(true);
+    }
+
+    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 + "securityInterceptor.class",
+            "net.sf.acegisecurity.intercept.method.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",
+            "net.sf.acegisecurity.ITargetObject.makeLower*=MOCK_LOWER\r\nnet.sf.acegisecurity.ITargetObject.makeUpper*=MOCK_UPPER,RUN_AS");
+
+        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");
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockAccessDecisionManagerWhichOnlySupportsStrings
+        implements AccessDecisionManager {
+        public void decide(Authentication authentication, Object object,
+            ConfigAttributeDefinition config) 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() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public boolean supports(Class clazz) {
+            if (String.class.isAssignableFrom(clazz)) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        protected ConfigAttributeDefinition lookupAttributes(
+            MethodInvocation mi) {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+    }
+
+    private class MockRunAsManagerWhichOnlySupportsStrings
+        implements RunAsManager {
+        public Authentication buildRunAs(Authentication authentication,
+            Object object, ConfigAttributeDefinition config) {
+            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 MockSecurityInterceptorCallback
+        implements SecurityInterceptorCallback {
+        public Object proceedWithObject(Object object)
+            throws Throwable {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+    }
+}

+ 160 - 0
core/src/test/java/org/acegisecurity/intercept/method/MockAttributes.java

@@ -0,0 +1,160 @@
+/* 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;
+
+import net.sf.acegisecurity.ITargetObject;
+import net.sf.acegisecurity.OtherTargetObject;
+import net.sf.acegisecurity.SecurityConfig;
+import net.sf.acegisecurity.TargetObject;
+
+import org.springframework.metadata.Attributes;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+
+/**
+ * Used by the {@link MethodDefinitionAttributesTests}.
+ *
+ * @author Cameron Braid
+ * @author Ben Alex
+ */
+public class MockAttributes implements Attributes {
+    //~ Instance fields ========================================================
+
+    List classAttributes = Arrays.asList(new SecurityConfig[] {new SecurityConfig(
+                    "MOCK_CLASS")});
+    List classMethodAttributesCountLength = Arrays.asList(new String[] {new String(
+                    "MOCK_CLASS_METHOD_COUNT_LENGTH")});
+    List classMethodAttributesMakeLowerCase = Arrays.asList(new SecurityConfig[] {new SecurityConfig(
+                    "MOCK_CLASS_METHOD_MAKE_LOWER_CASE")});
+    List classMethodAttributesMakeUpperCase = Arrays.asList(new SecurityConfig[] {new SecurityConfig(
+                    "MOCK_CLASS_METHOD_MAKE_UPPER_CASE")});
+    List interfaceAttributes = Arrays.asList(new SecurityConfig[] {new SecurityConfig(
+                    "MOCK_INTERFACE")});
+    List interfaceMethodAttributesCountLength = Arrays.asList(new SecurityConfig[] {new SecurityConfig(
+                    "MOCK_INTERFACE_METHOD_COUNT_LENGTH")});
+    List interfaceMethodAttributesMakeLowerCase = Arrays.asList(new SecurityConfig[] {new SecurityConfig(
+                    "MOCK_INTERFACE_METHOD_MAKE_LOWER_CASE")});
+    List interfaceMethodAttributesMakeUpperCase = Arrays.asList(new SecurityConfig[] {new SecurityConfig(
+                    "MOCK_INTERFACE_METHOD_MAKE_UPPER_CASE"), new SecurityConfig(
+                    "RUN_AS")});
+
+    //~ Methods ================================================================
+
+    public Collection getAttributes(Class clazz) {
+        // Emphasise we return null for OtherTargetObject
+        if (clazz.equals(OtherTargetObject.class)) {
+            return null;
+        }
+
+        // interface
+        if (clazz.equals(ITargetObject.class)) {
+            return interfaceAttributes;
+        }
+
+        // class
+        if (clazz.equals(TargetObject.class)) {
+            return classAttributes;
+        }
+
+        return null;
+    }
+
+    public Collection getAttributes(Method method) {
+        // interface
+        if (method.getDeclaringClass().equals(ITargetObject.class)) {
+            if (method.getName().equals("countLength")) {
+                return interfaceMethodAttributesCountLength;
+            }
+
+            if (method.getName().equals("makeLowerCase")) {
+                return interfaceMethodAttributesMakeLowerCase;
+            }
+
+            if (method.getName().equals("makeUpperCase")) {
+                return interfaceMethodAttributesMakeUpperCase;
+            }
+
+            if (method.getName().equals("publicMakeLowerCase")) {
+                throw new UnsupportedOperationException(
+                    "mock support not implemented");
+            }
+        }
+
+        // class
+        if (method.getDeclaringClass().equals(TargetObject.class)) {
+            if (method.getName().equals("countLength")) {
+                return classMethodAttributesCountLength;
+            }
+
+            if (method.getName().equals("makeLowerCase")) {
+                return classMethodAttributesMakeLowerCase;
+            }
+
+            if (method.getName().equals("makeUpperCase")) {
+                return classMethodAttributesMakeUpperCase;
+            }
+
+            if (method.getName().equals("publicMakeLowerCase")) {
+                throw new UnsupportedOperationException(
+                    "mock support not implemented");
+            }
+        }
+
+        // other target object
+        if (method.getDeclaringClass().equals(OtherTargetObject.class)) {
+            if (method.getName().equals("countLength")) {
+                return classMethodAttributesCountLength;
+            }
+
+            if (method.getName().equals("makeLowerCase")) {
+                return classMethodAttributesMakeLowerCase;
+            }
+
+            if (method.getName().equals("makeUpperCase")) {
+                return null; // NB
+            }
+
+            if (method.getName().equals("publicMakeLowerCase")) {
+                throw new UnsupportedOperationException(
+                    "mock support not implemented");
+            }
+        }
+
+        return null;
+    }
+
+    public Collection getAttributes(Class arg0, Class arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Collection getAttributes(Field arg0, Class arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Collection getAttributes(Field arg0) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+
+    public Collection getAttributes(Method arg0, Class arg1) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+}

+ 88 - 0
core/src/test/java/org/acegisecurity/intercept/method/MockMethodDefinitionSource.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.method;
+
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.SecurityConfig;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MockMethodDefinitionSource extends AbstractMethodDefinitionSource {
+    //~ Instance fields ========================================================
+
+    private List list;
+    private boolean returnAnIterator;
+
+    //~ Constructors ===========================================================
+
+    public MockMethodDefinitionSource(boolean includeInvalidAttributes,
+        boolean returnAnIteratorWhenRequested) {
+        returnAnIterator = returnAnIteratorWhenRequested;
+        list = new Vector();
+
+        ConfigAttributeDefinition def1 = new ConfigAttributeDefinition();
+        def1.addConfigAttribute(new SecurityConfig("MOCK_LOWER"));
+        list.add(def1);
+
+        if (includeInvalidAttributes) {
+            ConfigAttributeDefinition def2 = new ConfigAttributeDefinition();
+            def2.addConfigAttribute(new SecurityConfig("MOCK_LOWER"));
+            def2.addConfigAttribute(new SecurityConfig("INVALID_ATTRIBUTE"));
+            list.add(def2);
+        }
+
+        ConfigAttributeDefinition def3 = new ConfigAttributeDefinition();
+        def3.addConfigAttribute(new SecurityConfig("MOCK_UPPER"));
+        def3.addConfigAttribute(new SecurityConfig("RUN_AS"));
+        list.add(def3);
+
+        if (includeInvalidAttributes) {
+            ConfigAttributeDefinition def4 = new ConfigAttributeDefinition();
+            def4.addConfigAttribute(new SecurityConfig("MOCK_SOMETHING"));
+            def4.addConfigAttribute(new SecurityConfig("ANOTHER_INVALID"));
+            list.add(def4);
+        }
+    }
+
+    private MockMethodDefinitionSource() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    public Iterator getConfigAttributeDefinitions() {
+        if (returnAnIterator) {
+            return list.iterator();
+        } else {
+            return null;
+        }
+    }
+
+    protected ConfigAttributeDefinition lookupAttributes(MethodInvocation mi) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+}

+ 117 - 0
core/src/test/java/org/acegisecurity/intercept/web/AbstractFilterInvocationDefinitionSourceTests.java

@@ -0,0 +1,117 @@
+/* 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.web;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpServletResponse;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+/**
+ * Tests {@link AbstractFilterInvocationDefinitionSource}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AbstractFilterInvocationDefinitionSourceTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public AbstractFilterInvocationDefinitionSourceTests() {
+        super();
+    }
+
+    public AbstractFilterInvocationDefinitionSourceTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AbstractFilterInvocationDefinitionSourceTests.class);
+    }
+
+    public void testDoesNotSupportAnotherObject() {
+        MockFilterInvocationDefinitionSource mfis = new MockFilterInvocationDefinitionSource(false,
+                true);
+        assertFalse(mfis.supports(String.class));
+    }
+
+    public void testGetAttributesForANonFilterInvocation() {
+        MockFilterInvocationDefinitionSource mfis = new MockFilterInvocationDefinitionSource(false,
+                true);
+
+        try {
+            mfis.getAttributes(new String());
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testGetAttributesForANullObject() {
+        MockFilterInvocationDefinitionSource mfis = new MockFilterInvocationDefinitionSource(false,
+                true);
+
+        try {
+            mfis.getAttributes(null);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testGetAttributesForFilterInvocationSuccess() {
+        MockFilterInvocationDefinitionSource mfis = new MockFilterInvocationDefinitionSource(false,
+                true);
+
+        try {
+            mfis.getAttributes(new FilterInvocation(
+                    new MockHttpServletRequest(null, null),
+                    new MockHttpServletResponse(), new MockFilterChain()));
+            fail("Should have thrown UnsupportedOperationException");
+        } catch (UnsupportedOperationException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testSupportsFilterInvocation() {
+        MockFilterInvocationDefinitionSource mfis = new MockFilterInvocationDefinitionSource(false,
+                true);
+        assertTrue(mfis.supports(FilterInvocation.class));
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockFilterChain implements FilterChain {
+        public void doFilter(ServletRequest arg0, ServletResponse arg1)
+            throws IOException, ServletException {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+    }
+}

+ 123 - 0
core/src/test/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionMapTests.java

@@ -0,0 +1,123 @@
+/* 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.web;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockFilterChain;
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpServletResponse;
+import net.sf.acegisecurity.SecurityConfig;
+
+
+/**
+ * Tests parts of {@link FilterInvocationDefinitionMap} not tested by {@link
+ * FilterInvocationDefinitionSourceEditorTests}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class FilterInvocationDefinitionMapTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public FilterInvocationDefinitionMapTests() {
+        super();
+    }
+
+    public FilterInvocationDefinitionMapTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(FilterInvocationDefinitionMapTests.class);
+    }
+
+    public void testConvertUrlToLowercaseIsFalseByDefault() {
+        FilterInvocationDefinitionMap map = new FilterInvocationDefinitionMap();
+        assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
+    }
+
+    public void testConvertUrlToLowercaseSetterRespected() {
+        FilterInvocationDefinitionMap map = new FilterInvocationDefinitionMap();
+        map.setConvertUrlToLowercaseBeforeComparison(true);
+        assertTrue(map.isConvertUrlToLowercaseBeforeComparison());
+    }
+
+    public void testLookupNotRequiringExactMatchSuccessIfNotMatching() {
+        FilterInvocationDefinitionMap map = new FilterInvocationDefinitionMap();
+        map.setConvertUrlToLowercaseBeforeComparison(true);
+        assertTrue(map.isConvertUrlToLowercaseBeforeComparison());
+
+        ConfigAttributeDefinition def = new ConfigAttributeDefinition();
+        def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
+        map.addSecureUrl("\\A/secure/super.*\\Z", def);
+
+        // Build a HTTP request
+        MockHttpServletRequest req = new MockHttpServletRequest(null);
+        req.setServletPath("/SeCuRE/super/somefile.html");
+
+        FilterInvocation fi = new FilterInvocation(req,
+                new MockHttpServletResponse(), new MockFilterChain());
+
+        ConfigAttributeDefinition response = map.lookupAttributes(fi);
+        assertEquals(def, response);
+    }
+
+    public void testLookupRequiringExactMatchFailsIfNotMatching() {
+        FilterInvocationDefinitionMap map = new FilterInvocationDefinitionMap();
+        assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
+
+        ConfigAttributeDefinition def = new ConfigAttributeDefinition();
+        def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
+        map.addSecureUrl("\\A/secure/super.*\\Z", def);
+
+        // Build a HTTP request
+        MockHttpServletRequest req = new MockHttpServletRequest(null);
+        req.setServletPath("/SeCuRE/super/somefile.html");
+
+        FilterInvocation fi = new FilterInvocation(req,
+                new MockHttpServletResponse(), new MockFilterChain());
+
+        ConfigAttributeDefinition response = map.lookupAttributes(fi);
+        assertEquals(null, response);
+    }
+
+    public void testLookupRequiringExactMatchIsSuccessful() {
+        FilterInvocationDefinitionMap map = new FilterInvocationDefinitionMap();
+        assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
+
+        ConfigAttributeDefinition def = new ConfigAttributeDefinition();
+        def.addConfigAttribute(new SecurityConfig("ROLE_ONE"));
+        map.addSecureUrl("\\A/secure/super.*\\Z", def);
+
+        // Build a HTTP request
+        MockHttpServletRequest req = new MockHttpServletRequest(null);
+        req.setServletPath("/secure/super/somefile.html");
+
+        FilterInvocation fi = new FilterInvocation(req,
+                new MockHttpServletResponse(), new MockFilterChain());
+
+        ConfigAttributeDefinition response = map.lookupAttributes(fi);
+        assertEquals(def, response);
+    }
+}

+ 240 - 0
core/src/test/java/org/acegisecurity/intercept/web/FilterInvocationDefinitionSourceEditorTests.java

@@ -0,0 +1,240 @@
+/* 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.web;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.MockFilterChain;
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpServletResponse;
+import net.sf.acegisecurity.SecurityConfig;
+
+import java.util.Iterator;
+
+
+/**
+ * Tests {@link FilterInvocationDefinitionSourceEditor} and its associated
+ * {@link FilterInvocationDefinitionMap}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class FilterInvocationDefinitionSourceEditorTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public FilterInvocationDefinitionSourceEditorTests() {
+        super();
+    }
+
+    public FilterInvocationDefinitionSourceEditorTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(FilterInvocationDefinitionSourceEditorTests.class);
+    }
+
+    public void testConvertUrlToLowercaseDefaultSettingUnchangedByEditor() {
+        FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+        editor.setAsText(
+            "\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE\r\n\\A/secure/.*\\Z=ROLE_SUPERVISOR,ROLE_TELLER");
+
+        FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+            .getValue();
+        assertFalse(map.isConvertUrlToLowercaseBeforeComparison());
+    }
+
+    public void testConvertUrlToLowercaseSettingApplied() {
+        FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+        editor.setAsText(
+            "CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON\r\n\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE\r\n\\A/secure/.*\\Z=ROLE_SUPERVISOR,ROLE_TELLER");
+
+        FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+            .getValue();
+        assertTrue(map.isConvertUrlToLowercaseBeforeComparison());
+    }
+
+    public void testEmptyStringReturnsEmptyMap() {
+        FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+        editor.setAsText("");
+
+        FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+            .getValue();
+        assertEquals(0, map.getMapSize());
+    }
+
+    public void testInvalidRegularExpressionsDetected()
+        throws Exception {
+        FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+
+        try {
+            editor.setAsText("*=SOME_ROLE");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("Malformed regular expression: *",
+                expected.getMessage());
+        }
+    }
+
+    public void testIterator() {
+        FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+        editor.setAsText(
+            "\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE\r\n\\A/secure/.*\\Z=ROLE_SUPERVISOR,ROLE_TELLER");
+
+        FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+            .getValue();
+        Iterator iter = map.getConfigAttributeDefinitions();
+        int counter = 0;
+
+        while (iter.hasNext()) {
+            iter.next();
+            counter++;
+        }
+
+        assertEquals(2, counter);
+    }
+
+    public void testMapReturnsNullWhenNoMatchFound() throws Exception {
+        FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+        editor.setAsText("\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE,ANOTHER_ROLE");
+
+        FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+            .getValue();
+
+        MockHttpServletRequest httpRequest = new MockHttpServletRequest(null,
+                null);
+        httpRequest.setServletPath("/totally/different/path/index.html");
+
+        ConfigAttributeDefinition returned = map.getAttributes(new FilterInvocation(
+                    httpRequest, new MockHttpServletResponse(),
+                    new MockFilterChain()));
+
+        assertEquals(null, returned);
+    }
+
+    public void testMultiUrlParsing() {
+        FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+        editor.setAsText(
+            "\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE\r\n\\A/secure/.*\\Z=ROLE_SUPERVISOR,ROLE_TELLER");
+
+        FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+            .getValue();
+        assertEquals(2, map.getMapSize());
+    }
+
+    public void testNoArgsConstructor() {
+        try {
+            new FilterInvocationDefinitionMap().new EntryHolder();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testNullReturnsEmptyMap() {
+        FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+        editor.setAsText(null);
+
+        FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+            .getValue();
+        assertEquals(0, map.getMapSize());
+    }
+
+    public void testOrderOfEntriesIsPreservedOrderA() {
+        FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+        editor.setAsText(
+            "\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE,ANOTHER_ROLE\r\n\\A/secure/.*\\Z=ROLE_SUPERVISOR,ROLE_TELLER");
+
+        FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+            .getValue();
+
+        // Test ensures we match the first entry, not the second
+        MockHttpServletRequest httpRequest = new MockHttpServletRequest(null,
+                null);
+        httpRequest.setServletPath("/secure/super/very_secret.html");
+
+        ConfigAttributeDefinition returned = map.getAttributes(new FilterInvocation(
+                    httpRequest, new MockHttpServletResponse(),
+                    new MockFilterChain()));
+
+        ConfigAttributeDefinition expected = new ConfigAttributeDefinition();
+        expected.addConfigAttribute(new SecurityConfig("ROLE_WE_DONT_HAVE"));
+        expected.addConfigAttribute(new SecurityConfig("ANOTHER_ROLE"));
+
+        assertEquals(expected, returned);
+    }
+
+    public void testOrderOfEntriesIsPreservedOrderB() {
+        FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+        editor.setAsText(
+            "\\A/secure/.*\\Z=ROLE_SUPERVISOR,ROLE_TELLER\r\n\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE,ANOTHER_ROLE");
+
+        FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+            .getValue();
+
+        MockHttpServletRequest httpRequest = new MockHttpServletRequest(null,
+                null);
+        httpRequest.setServletPath("/secure/super/very_secret.html");
+
+        ConfigAttributeDefinition returned = map.getAttributes(new FilterInvocation(
+                    httpRequest, new MockHttpServletResponse(),
+                    new MockFilterChain()));
+
+        ConfigAttributeDefinition expected = new ConfigAttributeDefinition();
+        expected.addConfigAttribute(new SecurityConfig("ROLE_SUPERVISOR"));
+        expected.addConfigAttribute(new SecurityConfig("ROLE_TELLER"));
+
+        assertEquals(expected, returned);
+    }
+
+    public void testSingleUrlParsing() throws Exception {
+        FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+        editor.setAsText("\\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE,ANOTHER_ROLE");
+
+        FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+            .getValue();
+
+        MockHttpServletRequest httpRequest = new MockHttpServletRequest(null,
+                null);
+        httpRequest.setServletPath("/secure/super/very_secret.html");
+
+        ConfigAttributeDefinition returned = map.getAttributes(new FilterInvocation(
+                    httpRequest, new MockHttpServletResponse(),
+                    new MockFilterChain()));
+
+        ConfigAttributeDefinition expected = new ConfigAttributeDefinition();
+        expected.addConfigAttribute(new SecurityConfig("ROLE_WE_DONT_HAVE"));
+        expected.addConfigAttribute(new SecurityConfig("ANOTHER_ROLE"));
+
+        assertEquals(expected, returned);
+    }
+
+    public void testWhitespaceAndCommentsAndLinesWithoutEqualsSignsAreIgnored() {
+        FilterInvocationDefinitionSourceEditor editor = new FilterInvocationDefinitionSourceEditor();
+        editor.setAsText(
+            "         \\A/secure/super.*\\Z=ROLE_WE_DONT_HAVE,ANOTHER_ROLE      \r\n   \r\n     \r\n   // comment line  \r\n   \\A/testing.*\\Z=ROLE_TEST   \r\n");
+
+        FilterInvocationDefinitionMap map = (FilterInvocationDefinitionMap) editor
+            .getValue();
+        assertEquals(2, map.getMapSize());
+    }
+}

+ 374 - 0
core/src/test/java/org/acegisecurity/intercept/web/FilterInvocationTests.java

@@ -0,0 +1,374 @@
+/* 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.web;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.MockFilterChain;
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpServletResponse;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+/**
+ * Tests {@link FilterInvocation}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class FilterInvocationTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public FilterInvocationTests() {
+        super();
+    }
+
+    public FilterInvocationTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(FilterInvocationTests.class);
+    }
+
+    public void testGettersAndStringMethods() {
+        MockHttpServletRequest request = new MockHttpServletRequest(null, null);
+        request.setServletPath("/HelloWorld");
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+        assertEquals(request, fi.getRequest());
+        assertEquals(request, fi.getHttpRequest());
+        assertEquals(response, fi.getResponse());
+        assertEquals(response, fi.getHttpResponse());
+        assertEquals(chain, fi.getChain());
+        assertEquals("/HelloWorld", fi.getRequestUrl());
+        assertEquals("FilterInvocation: URL: /HelloWorld", fi.toString());
+    }
+
+    public void testNoArgsConstructor() {
+        try {
+            new FilterInvocation();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testRejectsNullFilterChain() {
+        MockHttpServletRequest request = new MockHttpServletRequest(null, null);
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        try {
+            new FilterInvocation(request, response, null);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testRejectsNullServletRequest() {
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+
+        try {
+            new FilterInvocation(null, response, chain);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testRejectsNullServletResponse() {
+        MockHttpServletRequest request = new MockHttpServletRequest(null, null);
+        MockFilterChain chain = new MockFilterChain();
+
+        try {
+            new FilterInvocation(request, null, chain);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testRejectsServletRequestWhichIsNotHttpServletRequest() {
+        MockServletRequest request = new MockServletRequest();
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+
+        try {
+            new FilterInvocation(request, response, chain);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("Can only process HttpServletRequest",
+                expected.getMessage());
+        }
+    }
+
+    public void testRejectsServletResponseWhichIsNotHttpServletResponse() {
+        MockHttpServletRequest request = new MockHttpServletRequest(null, null);
+        MockServletResponse response = new MockServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+
+        try {
+            new FilterInvocation(request, response, chain);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("Can only process HttpServletResponse",
+                expected.getMessage());
+        }
+    }
+
+    public void testStringMethodsWithAQueryString() {
+        MockHttpServletRequest request = new MockHttpServletRequest("foo=bar");
+        request.setServletPath("/HelloWorld");
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+        assertEquals("/HelloWorld?foo=bar", fi.getRequestUrl());
+        assertEquals("FilterInvocation: URL: /HelloWorld?foo=bar", fi.toString());
+    }
+
+    public void testStringMethodsWithoutAnyQueryString() {
+        MockHttpServletRequest request = new MockHttpServletRequest(null, null);
+        request.setServletPath("/HelloWorld");
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+        assertEquals("/HelloWorld", fi.getRequestUrl());
+        assertEquals("FilterInvocation: URL: /HelloWorld", fi.toString());
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockServletRequest implements ServletRequest {
+        public void setAttribute(String arg0, Object arg1) {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public Object getAttribute(String arg0) {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public Enumeration getAttributeNames() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public void setCharacterEncoding(String arg0)
+            throws UnsupportedEncodingException {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public String getCharacterEncoding() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public int getContentLength() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public String getContentType() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public ServletInputStream getInputStream() throws IOException {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public Locale getLocale() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public Enumeration getLocales() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public String getParameter(String arg0) {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public Map getParameterMap() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public Enumeration getParameterNames() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public String[] getParameterValues(String arg0) {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public String getProtocol() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public BufferedReader getReader() throws IOException {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public String getRealPath(String arg0) {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public String getRemoteAddr() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public String getRemoteHost() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public RequestDispatcher getRequestDispatcher(String arg0) {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public String getScheme() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public boolean isSecure() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public String getServerName() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public int getServerPort() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public void removeAttribute(String arg0) {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+    }
+
+    private class MockServletResponse implements ServletResponse {
+        public void setBufferSize(int arg0) {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public int getBufferSize() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public String getCharacterEncoding() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public boolean isCommitted() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public void setContentLength(int arg0) {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public void setContentType(String arg0) {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public void setLocale(Locale arg0) {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public Locale getLocale() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public ServletOutputStream getOutputStream() throws IOException {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public PrintWriter getWriter() throws IOException {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public void flushBuffer() throws IOException {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public void reset() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+
+        public void resetBuffer() {
+            throw new UnsupportedOperationException(
+                "mock method not implemented");
+        }
+    }
+}

+ 258 - 0
core/src/test/java/org/acegisecurity/intercept/web/FilterSecurityInterceptorTests.java

@@ -0,0 +1,258 @@
+/* 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.web;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.AccessDecisionManager;
+import net.sf.acegisecurity.AccessDeniedException;
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.ConfigAttribute;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+import net.sf.acegisecurity.MockAccessDecisionManager;
+import net.sf.acegisecurity.MockAuthenticationManager;
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpServletResponse;
+import net.sf.acegisecurity.MockHttpSession;
+import net.sf.acegisecurity.MockRunAsManager;
+import net.sf.acegisecurity.RunAsManager;
+import net.sf.acegisecurity.SecurityConfig;
+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.util.Iterator;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+/**
+ * Tests {@link FiltSecurityInterceptor}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class FilterSecurityInterceptorTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public FilterSecurityInterceptorTests() {
+        super();
+    }
+
+    public FilterSecurityInterceptorTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(FilterSecurityInterceptorTests.class);
+    }
+
+    public void testEnsuresAccessDecisionManagerSupportsFilterInvocationClass() {
+        FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
+        interceptor.setAuthenticationManager(new MockAuthenticationManager());
+        interceptor.setObjectDefinitionSource(new FilterInvocationDefinitionMap());
+        interceptor.setRunAsManager(new MockRunAsManager());
+
+        interceptor.setAccessDecisionManager(new AccessDecisionManager() {
+                public boolean supports(Class clazz) {
+                    return false;
+                }
+
+                public boolean supports(ConfigAttribute attribute) {
+                    return true;
+                }
+
+                public void decide(Authentication authentication,
+                    Object object, ConfigAttributeDefinition config)
+                    throws AccessDeniedException {
+                    throw new UnsupportedOperationException(
+                        "mock method not implemented");
+                }
+            });
+
+        try {
+            interceptor.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("AccessDecisionManager does not support FilterInvocation",
+                expected.getMessage());
+        }
+    }
+
+    public void testEnsuresRunAsManagerSupportsFilterInvocationClass() {
+        FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
+        interceptor.setAccessDecisionManager(new MockAccessDecisionManager());
+        interceptor.setAuthenticationManager(new MockAuthenticationManager());
+        interceptor.setObjectDefinitionSource(new FilterInvocationDefinitionMap());
+
+        interceptor.setRunAsManager(new RunAsManager() {
+                public boolean supports(Class clazz) {
+                    return false;
+                }
+
+                public boolean supports(ConfigAttribute attribute) {
+                    return true;
+                }
+
+                public Authentication buildRunAs(
+                    Authentication authentication, Object object,
+                    ConfigAttributeDefinition config) {
+                    throw new UnsupportedOperationException(
+                        "mock method not implemented");
+                }
+            });
+
+        try {
+            interceptor.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("RunAsManager does not support FilterInvocation",
+                expected.getMessage());
+        }
+    }
+
+    public void testNormalStartupAndGetter() {
+        FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
+        interceptor.setAccessDecisionManager(new MockAccessDecisionManager());
+        interceptor.setAuthenticationManager(new MockAuthenticationManager());
+
+        FilterInvocationDefinitionMap fidp = new FilterInvocationDefinitionMap();
+        interceptor.setObjectDefinitionSource(fidp);
+        interceptor.setRunAsManager(new MockRunAsManager());
+        interceptor.afterPropertiesSet();
+        assertTrue(true);
+        assertEquals(fidp, interceptor.getObjectDefinitionSource());
+    }
+
+    /**
+     * We just test invocation works in a success event. There is no need to
+     * test  access denied events as the abstract parent enforces that logic,
+     * which is extensively tested separately.
+     *
+     * @throws Throwable DOCUMENT ME!
+     */
+    public void testSuccessfulInvocation() throws Throwable {
+        // Setup the FilterSecurityInterceptor
+        FilterSecurityInterceptor interceptor = new FilterSecurityInterceptor();
+        interceptor.setAccessDecisionManager(new MockAccessDecisionManager());
+        interceptor.setAuthenticationManager(new MockAuthenticationManager());
+        interceptor.setRunAsManager(new MockRunAsManager());
+
+        // 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 HTTP request and response
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockHttpServletRequest request = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request.setServletPath("/secure/page.html");
+
+        // 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);
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockFilterChain implements FilterChain {
+        private boolean expectToProceed;
+
+        public MockFilterChain(boolean expectToProceed) {
+            this.expectToProceed = expectToProceed;
+        }
+
+        private MockFilterChain() {
+            super();
+        }
+
+        public void doFilter(ServletRequest request, ServletResponse response)
+            throws IOException, ServletException {
+            if (expectToProceed) {
+                assertTrue(true);
+            } else {
+                fail("Did not expect filter chain to proceed");
+            }
+        }
+    }
+
+    private class MockFilterInvocationDefinitionMap
+        implements FilterInvocationDefinitionSource {
+        private ConfigAttributeDefinition toReturn;
+        private String servletPath;
+
+        public MockFilterInvocationDefinitionMap(String servletPath,
+            ConfigAttributeDefinition toReturn) {
+            this.servletPath = servletPath;
+            this.toReturn = toReturn;
+        }
+
+        private MockFilterInvocationDefinitionMap() {
+            super();
+        }
+
+        public ConfigAttributeDefinition getAttributes(Object object)
+            throws IllegalArgumentException {
+            FilterInvocation fi = (FilterInvocation) object;
+
+            if (servletPath.equals(fi.getHttpRequest().getServletPath())) {
+                return toReturn;
+            } else {
+                return null;
+            }
+        }
+
+        public Iterator getConfigAttributeDefinitions() {
+            return null;
+        }
+
+        public boolean supports(Class clazz) {
+            return true;
+        }
+    }
+}

+ 88 - 0
core/src/test/java/org/acegisecurity/intercept/web/MockFilterInvocationDefinitionSource.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.web;
+
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.SecurityConfig;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+
+/**
+ * DOCUMENT ME!
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class MockFilterInvocationDefinitionSource
+    extends AbstractFilterInvocationDefinitionSource {
+    //~ Instance fields ========================================================
+
+    private List list;
+    private boolean returnAnIterator;
+
+    //~ Constructors ===========================================================
+
+    public MockFilterInvocationDefinitionSource(
+        boolean includeInvalidAttributes, boolean returnAnIteratorWhenRequested) {
+        returnAnIterator = returnAnIteratorWhenRequested;
+        list = new Vector();
+
+        ConfigAttributeDefinition def1 = new ConfigAttributeDefinition();
+        def1.addConfigAttribute(new SecurityConfig("MOCK_LOWER"));
+        list.add(def1);
+
+        if (includeInvalidAttributes) {
+            ConfigAttributeDefinition def2 = new ConfigAttributeDefinition();
+            def2.addConfigAttribute(new SecurityConfig("MOCK_LOWER"));
+            def2.addConfigAttribute(new SecurityConfig("INVALID_ATTRIBUTE"));
+            list.add(def2);
+        }
+
+        ConfigAttributeDefinition def3 = new ConfigAttributeDefinition();
+        def3.addConfigAttribute(new SecurityConfig("MOCK_UPPER"));
+        def3.addConfigAttribute(new SecurityConfig("RUN_AS"));
+        list.add(def3);
+
+        if (includeInvalidAttributes) {
+            ConfigAttributeDefinition def4 = new ConfigAttributeDefinition();
+            def4.addConfigAttribute(new SecurityConfig("MOCK_SOMETHING"));
+            def4.addConfigAttribute(new SecurityConfig("ANOTHER_INVALID"));
+            list.add(def4);
+        }
+    }
+
+    private MockFilterInvocationDefinitionSource() {
+        super();
+    }
+
+    //~ Methods ================================================================
+
+    public Iterator getConfigAttributeDefinitions() {
+        if (returnAnIterator) {
+            return list.iterator();
+        } else {
+            return null;
+        }
+    }
+
+    protected ConfigAttributeDefinition lookupAttributes(
+        FilterInvocation filterInvocation) {
+        throw new UnsupportedOperationException("mock method not implemented");
+    }
+}

+ 317 - 0
core/src/test/java/org/acegisecurity/intercept/web/SecurityEnforcementFilterTests.java

@@ -0,0 +1,317 @@
+/* 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.web;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.AccessDeniedException;
+import net.sf.acegisecurity.BadCredentialsException;
+import net.sf.acegisecurity.MockFilterConfig;
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpServletResponse;
+import net.sf.acegisecurity.MockHttpSession;
+import net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+/**
+ * Tests {@link SecurityEnforcementFilter}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class SecurityEnforcementFilterTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public SecurityEnforcementFilterTests() {
+        super();
+    }
+
+    public SecurityEnforcementFilterTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(SecurityEnforcementFilterTests.class);
+    }
+
+    public void testAccessDeniedWhenAccessDeniedException()
+        throws Exception {
+        // Setup our HTTP request
+        MockHttpServletRequest request = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request.setServletPath("/secure/page.html");
+
+        // Setup our expectation that the filter chain will not be invoked, as access is denied
+        MockFilterChain chain = new MockFilterChain(false);
+
+        // Setup the FilterSecurityInterceptor thrown an access denied exception
+        MockFilterSecurityInterceptor interceptor = new MockFilterSecurityInterceptor(true,
+                false);
+
+        // Test
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        SecurityEnforcementFilter filter = new MockSecurityEnforcementFilter(interceptor,
+                "/login.jsp");
+        filter.doFilter(request, response, chain);
+        assertEquals(403, response.getError());
+    }
+
+    public void testDoFilterWithNonHttpServletRequestDetected()
+        throws Exception {
+        SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
+
+        try {
+            filter.doFilter(null, new MockHttpServletResponse(),
+                new MockFilterChain());
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("HttpServletRequest required", expected.getMessage());
+        }
+    }
+
+    public void testDoFilterWithNonHttpServletResponseDetected()
+        throws Exception {
+        SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
+
+        try {
+            filter.doFilter(new MockHttpServletRequest(null, null), null,
+                new MockFilterChain());
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("HttpServletResponse required", expected.getMessage());
+        }
+    }
+
+    public void testRedirectedToLoginFormAndSessionShowsOriginalTargetWhenAuthenticationException()
+        throws Exception {
+        // Setup our HTTP request
+        MockHttpServletRequest request = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request.setServletPath("/secure/page.html");
+
+        // Setup our expectation that the filter chain will not be invoked, as access is denied
+        MockFilterChain chain = new MockFilterChain(false);
+
+        // Setup the FilterSecurityInterceptor thrown an authentication failure exceptions
+        MockFilterSecurityInterceptor interceptor = new MockFilterSecurityInterceptor(false,
+                true);
+
+        // Test
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        SecurityEnforcementFilter filter = new MockSecurityEnforcementFilter(interceptor,
+                "/login.jsp");
+        filter.doFilter(request, response, chain);
+        assertEquals("/login.jsp", response.getRedirect());
+        assertEquals("/secure/page.html",
+            request.getSession().getAttribute(AuthenticationProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY));
+    }
+
+    public void testStartupDetectsInvalidAppContextLocation()
+        throws Exception {
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("loginFormUrl", "/login.jsp");
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/intercept/web/securityfiltertest-invalid.xml");
+
+        SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("Bean context must contain at least one bean of type FilterSecurityInterceptor",
+                expected.getMessage());
+        }
+    }
+
+    public void testStartupDetectsMissingAppContextLocation()
+        throws Exception {
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("loginFormUrl", "/login.jsp");
+
+        SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("appContextLocation must be specified",
+                expected.getMessage());
+        }
+
+        config.setInitParmeter("appContextLocation", "");
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("appContextLocation must be specified",
+                expected.getMessage());
+        }
+    }
+
+    public void testStartupDetectsMissingInvalidAppContextLocation()
+        throws Exception {
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("loginFormUrl", "/login.jsp");
+        config.setInitParmeter("appContextLocation", "DOES_NOT_EXIST");
+
+        SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertTrue(expected.getMessage().startsWith("Cannot locate"));
+        }
+    }
+
+    public void testStartupDetectsMissingLoginFormUrl()
+        throws Exception {
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/intercept/web/securityfiltertest-valid.xml");
+
+        SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("loginFormUrl must be specified", expected.getMessage());
+        }
+
+        config.setInitParmeter("loginFormUrl", "");
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("loginFormUrl must be specified", expected.getMessage());
+        }
+    }
+
+    public void testSuccessfulAccessGrant() throws Exception {
+        // Setup our HTTP request
+        MockHttpServletRequest request = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request.setServletPath("/secure/page.html");
+
+        // Setup our expectation that the filter chain will be invoked, as access is granted
+        MockFilterChain chain = new MockFilterChain(true);
+
+        // Setup the FilterSecurityInterceptor to not thrown any exceptions
+        MockFilterSecurityInterceptor interceptor = new MockFilterSecurityInterceptor(false,
+                false);
+
+        // Test
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        SecurityEnforcementFilter filter = new MockSecurityEnforcementFilter(interceptor,
+                "/login.jsp");
+        filter.doFilter(request, response, chain);
+    }
+
+    public void testSuccessfulStartupAndShutdownDown()
+        throws Exception {
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/intercept/web/securityfiltertest-valid.xml");
+        config.setInitParmeter("loginFormUrl", "/login.jsp");
+
+        SecurityEnforcementFilter filter = new SecurityEnforcementFilter();
+
+        filter.init(config);
+        filter.destroy();
+        assertTrue(true);
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockFilterChain implements FilterChain {
+        private boolean expectToProceed;
+
+        public MockFilterChain(boolean expectToProceed) {
+            this.expectToProceed = expectToProceed;
+        }
+
+        private MockFilterChain() {
+            super();
+        }
+
+        public void doFilter(ServletRequest request, ServletResponse response)
+            throws IOException, ServletException {
+            if (expectToProceed) {
+                assertTrue(true);
+            } else {
+                fail("Did not expect filter chain to proceed");
+            }
+        }
+    }
+
+    private class MockFilterSecurityInterceptor
+        extends FilterSecurityInterceptor {
+        private boolean throwAccessDenied;
+        private boolean throwAuthenticationFailure;
+
+        public MockFilterSecurityInterceptor(boolean throwAccessDenied,
+            boolean throwAuthenticationFailure) {
+            this.throwAccessDenied = throwAccessDenied;
+            this.throwAuthenticationFailure = throwAuthenticationFailure;
+        }
+
+        private MockFilterSecurityInterceptor() {
+            super();
+        }
+
+        public void invoke(FilterInvocation fi) throws Throwable {
+            if (throwAccessDenied) {
+                throw new AccessDeniedException("As requested");
+            }
+
+            if (throwAuthenticationFailure) {
+                throw new BadCredentialsException("As requested");
+            }
+
+            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
+        }
+    }
+
+    private class MockSecurityEnforcementFilter
+        extends SecurityEnforcementFilter {
+        public MockSecurityEnforcementFilter(
+            FilterSecurityInterceptor securityInterceptor, String loginFormUrl) {
+            super.securityInterceptor = securityInterceptor;
+            super.loginFormUrl = loginFormUrl;
+        }
+
+        private MockSecurityEnforcementFilter() {
+            super();
+        }
+    }
+}

+ 37 - 0
core/src/test/java/org/acegisecurity/intercept/web/securityfiltertest-invalid.xml

@@ -0,0 +1,37 @@
+<?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="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
+  		<property name="userMap">
+			<value>
+				marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR
+				dianne=emu,ROLE_TELLER
+				scott=wombat,ROLE_TELLER
+				peter=opal,disabled,ROLE_TELLER
+			</value>
+		</property>
+	</bean>
+	
+	<!-- FilterSecurityInterceptor deliberately missing to cause error -->
+
+</beans>

+ 76 - 0
core/src/test/java/org/acegisecurity/intercept/web/securityfiltertest-valid.xml

@@ -0,0 +1,76 @@
+<?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="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
+  		<property name="userMap">
+			<value>
+				marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR
+				dianne=emu,ROLE_TELLER
+				scott=wombat,ROLE_TELLER
+				peter=opal,disabled,ROLE_TELLER
+			</value>
+		</property>
+	</bean>
+	
+	<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
+     	<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
+ 		<property name="ignorePasswordCase"><value>false</value></property>
+ 		<property name="ignoreUsernameCase"><value>true</value></property>
+	</bean>
+
+	<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
+		<property name="providers">
+		  <list>
+		    <ref bean="daoAuthenticationProvider"/>
+		  </list>
+		</property>
+	</bean>
+	
+	<bean id="runAsManager" class="net.sf.acegisecurity.runas.RunAsManagerImpl">
+     	<property name="key"><value>my_run_as_password</value></property>
+ 	</bean>
+	
+	<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
+
+	<bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
+   		<property name="allowIfAllAbstainDecisions"><value>false</value></property>
+		<property name="decisionVoters">
+		  <list>
+		    <ref bean="roleVoter"/>
+		  </list>
+		</property>
+	</bean>
+
+	<bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor">
+	<property name="authenticationManager"><ref bean="authenticationManager"/></property>
+	<property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
+	<property name="runAsManager"><ref bean="runAsManager"/></property>
+		<property name="objectDefinitionSource">
+		<value>
+			\A/secure/super.*\Z=ROLE_WE_DONT_HAVE
+			\A/secure/.*\Z=ROLE_SUPERVISOR,ROLE_TELLER
+		</value>
+	</property>
+	</bean>
+
+</beans>

+ 262 - 0
core/src/test/java/org/acegisecurity/ui/AbstractIntegrationFilterTests.java

@@ -0,0 +1,262 @@
+/* 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;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+import net.sf.acegisecurity.adapters.MockPrincipal;
+import net.sf.acegisecurity.adapters.PrincipalAcegiUserToken;
+import net.sf.acegisecurity.context.Context;
+import net.sf.acegisecurity.context.ContextHolder;
+import net.sf.acegisecurity.context.SecureContext;
+import net.sf.acegisecurity.context.SecureContextImpl;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+/**
+ * Tests {@link AbstractIntegrationFilter}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AbstractIntegrationFilterTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public AbstractIntegrationFilterTests() {
+        super();
+    }
+
+    public AbstractIntegrationFilterTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AbstractIntegrationFilterTests.class);
+    }
+
+    public void testContextHolderContentsPreserved() throws Exception {
+        PrincipalAcegiUserToken principal = new PrincipalAcegiUserToken("key",
+                "someone", "password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_ROLE")});
+        MockAbstractIntegrationFilterImpl filter = new MockAbstractIntegrationFilterImpl(principal);
+        MockFilterChain chain = new MockFilterChain(true, principal);
+
+        MockSecureContextImpl secureContext = new MockSecureContextImpl(
+                "FOO_BAR");
+        ContextHolder.setContext(secureContext);
+        assertEquals(secureContext, ContextHolder.getContext());
+
+        executeFilterInContainerSimulator(filter, null, null, chain);
+
+        MockSecureContextImpl after = (MockSecureContextImpl) ContextHolder
+            .getContext();
+        assertEquals(secureContext.getInfo(), after.getInfo());
+        ContextHolder.setContext(null);
+    }
+
+    public void testContextHolderHasAuthenticationRemoved()
+        throws Exception {
+        PrincipalAcegiUserToken principal = new PrincipalAcegiUserToken("key",
+                "someone", "password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_ROLE")});
+        MockAbstractIntegrationFilterImpl filter = new MockAbstractIntegrationFilterImpl(principal);
+        MockFilterChain chain = new MockFilterChain(true, principal);
+
+        SecureContext secureContext = new SecureContextImpl();
+        secureContext.setAuthentication(principal);
+        ContextHolder.setContext(secureContext);
+        assertEquals(secureContext, ContextHolder.getContext());
+
+        executeFilterInContainerSimulator(filter, null, null, chain);
+
+        SecureContext after = (SecureContext) ContextHolder.getContext();
+        assertEquals(null, after.getAuthentication());
+        ContextHolder.setContext(null);
+    }
+
+    public void testIgnoredWhenConcreteClassReturnsANonAuthenticationObject()
+        throws Exception {
+        MockPrincipal principal = new MockPrincipal();
+        MockAbstractIntegrationFilterImpl filter = new MockAbstractIntegrationFilterImpl(principal);
+        MockFilterChain chain = new MockFilterChain(false, null);
+
+        Context before = ContextHolder.getContext();
+
+        if (before != null) {
+            if (before instanceof SecureContext) {
+                assertEquals(null, ((SecureContext) before).getAuthentication());
+            }
+        }
+
+        executeFilterInContainerSimulator(filter, null, null, chain);
+
+        Context after = ContextHolder.getContext();
+
+        if (after != null) {
+            if (after instanceof SecureContext) {
+                assertEquals(null, ((SecureContext) after).getAuthentication());
+            }
+        }
+    }
+
+    public void testIgnoredWhenConcreteClassReturnsNullAuthenticationObject()
+        throws Exception {
+        MockAbstractIntegrationFilterImpl filter = new MockAbstractIntegrationFilterImpl(null);
+        MockFilterChain chain = new MockFilterChain(false, null);
+
+        Context before = ContextHolder.getContext();
+
+        if (before != null) {
+            if (before instanceof SecureContext) {
+                assertEquals(null, ((SecureContext) before).getAuthentication());
+            }
+        }
+
+        executeFilterInContainerSimulator(filter, null, null, chain);
+
+        Context after = ContextHolder.getContext();
+
+        if (after != null) {
+            if (after instanceof SecureContext) {
+                assertEquals(null, ((SecureContext) after).getAuthentication());
+            }
+        }
+    }
+
+    public void testSuccessWhenConcreteClassReturnsValidAuthenticationObject()
+        throws Exception {
+        PrincipalAcegiUserToken principal = new PrincipalAcegiUserToken("key",
+                "someone", "password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_ROLE")});
+        MockAbstractIntegrationFilterImpl filter = new MockAbstractIntegrationFilterImpl(principal);
+        MockFilterChain chain = new MockFilterChain(true, principal);
+
+        Context before = ContextHolder.getContext();
+
+        if (before != null) {
+            if (before instanceof SecureContext) {
+                assertEquals(null, ((SecureContext) before).getAuthentication());
+            }
+        }
+
+        executeFilterInContainerSimulator(filter, null, null, chain);
+
+        Context after = ContextHolder.getContext();
+
+        if (after != null) {
+            if (after instanceof SecureContext) {
+                assertEquals(null, ((SecureContext) after).getAuthentication());
+            }
+        }
+    }
+
+    private void executeFilterInContainerSimulator(Filter filter,
+        ServletRequest request, ServletResponse response,
+        FilterChain filterChain) throws ServletException, IOException {
+        filter.init(null);
+        filter.doFilter(request, response, filterChain);
+        filter.destroy();
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockAbstractIntegrationFilterImpl
+        extends AbstractIntegrationFilter {
+        private Object extractFromContainerResult;
+
+        public MockAbstractIntegrationFilterImpl(
+            Object extractFromContainerResult) {
+            this.extractFromContainerResult = extractFromContainerResult;
+        }
+
+        private MockAbstractIntegrationFilterImpl() {
+            super();
+        }
+
+        public Object extractFromContainer(ServletRequest request) {
+            return this.extractFromContainerResult;
+        }
+    }
+
+    private class MockFilterChain implements FilterChain {
+        private Authentication expectedAuthenticationObjectInContextHolder;
+        private boolean expectContextHolderContainSecureContext = false;
+
+        public MockFilterChain(
+            boolean expectContextHolderContainSecureContext,
+            Authentication expectedAuthenticationObjectInContextHolder) {
+            if ((expectedAuthenticationObjectInContextHolder != null)
+                && !expectContextHolderContainSecureContext) {
+                throw new IllegalArgumentException(
+                    "If an Authentication object is expected, the ContextHolder should contain a SecureContext");
+            }
+
+            this.expectContextHolderContainSecureContext = expectContextHolderContainSecureContext;
+            this.expectedAuthenticationObjectInContextHolder = expectedAuthenticationObjectInContextHolder;
+        }
+
+        private MockFilterChain() {
+            super();
+        }
+
+        public void doFilter(ServletRequest request, ServletResponse response)
+            throws IOException, ServletException {
+            if (expectContextHolderContainSecureContext) {
+                Context context = ContextHolder.getContext();
+
+                if (!(context instanceof SecureContext)) {
+                    fail("ContextHolder should have contained SecureContext");
+                }
+            } else {
+                if (ContextHolder.getContext() != null) {
+                    fail("ContextHolder should have been null but wasn't");
+                }
+            }
+        }
+    }
+
+    private class MockSecureContextImpl extends SecureContextImpl {
+        private String info;
+
+        public MockSecureContextImpl(String info) {
+            this.info = info;
+        }
+
+        private MockSecureContextImpl() {
+            super();
+        }
+
+        public String getInfo() {
+            return this.info;
+        }
+    }
+}

+ 208 - 0
core/src/test/java/org/acegisecurity/ui/AutoIntegrationFilterTests.java

@@ -0,0 +1,208 @@
+/* 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;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.adapters.MockPrincipal;
+import net.sf.acegisecurity.adapters.PrincipalAcegiUserToken;
+import net.sf.acegisecurity.adapters.jboss.JbossIntegrationFilter;
+import net.sf.acegisecurity.adapters.jboss.MockInitialContext;
+import net.sf.acegisecurity.adapters.jboss.MockJbossIntegrationFilter;
+import net.sf.acegisecurity.ui.webapp.HttpSessionIntegrationFilter;
+
+import org.jboss.security.SimplePrincipal;
+
+import java.security.Principal;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.naming.Context;
+
+import javax.security.auth.Subject;
+
+import javax.servlet.ServletRequest;
+
+
+/**
+ * Tests {@link AutoIntegrationFilter}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AutoIntegrationFilterTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public AutoIntegrationFilterTests() {
+        super();
+    }
+
+    public AutoIntegrationFilterTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AutoIntegrationFilterTests.class);
+    }
+
+    public void testDetectsAuthenticationObjectInHttpRequest() {
+        AutoIntegrationFilter filter = new AutoIntegrationFilter();
+        PrincipalAcegiUserToken principal = new PrincipalAcegiUserToken("key",
+                "someone", "password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_ROLE")});
+        Object result = filter.extractFromContainer(new MockHttpServletRequest(
+                    principal, null));
+
+        if (!(result instanceof PrincipalAcegiUserToken)) {
+            fail("Should have returned PrincipalAcegiUserToken");
+        }
+
+        PrincipalAcegiUserToken castResult = (PrincipalAcegiUserToken) result;
+        assertEquals(principal, result);
+    }
+
+    public void testDetectsAuthenticationObjectInHttpSession() {
+        PrincipalAcegiUserToken principal = new PrincipalAcegiUserToken("key",
+                "someone", "password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_ROLE")});
+
+        AutoIntegrationFilter filter = new MockAutoIntegrationFilterHttpSession(new MockHttpSessionIntegrationFilter(
+                    principal));
+        Object result = filter.extractFromContainer(new MockHttpServletRequest(
+                    null, null));
+
+        if (!(result instanceof PrincipalAcegiUserToken)) {
+            fail("Should have returned PrincipalAcegiUserToken");
+        }
+
+        PrincipalAcegiUserToken castResult = (PrincipalAcegiUserToken) result;
+        assertEquals(principal, result);
+    }
+
+    public void testDetectsAuthenticationObjectInJboss() {
+        // Prepare a mock Jboss environment reflecting completed authentication
+        PrincipalAcegiUserToken principal = new PrincipalAcegiUserToken("key",
+                "someone", "password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_ROLE")});
+        Context context = new MockInitialContext(makeIntoSubject(principal));
+        JbossIntegrationFilter jbossFilter = new MockJbossIntegrationFilter(context);
+
+        // Create a SimplePrincipal, which is what JBoss places into HttpRequest
+        SimplePrincipal simplePrincipal = new SimplePrincipal("TEST");
+
+        // Now try to extract authentication information via our mock AutoIntegrationFilter
+        AutoIntegrationFilter filter = new MockAutoIntegrationFilterJboss(jbossFilter);
+        Object result = filter.extractFromContainer(new MockHttpServletRequest(
+                    simplePrincipal, null));
+
+        System.out.println(result);
+
+        if (!(result instanceof PrincipalAcegiUserToken)) {
+            fail("Should have returned PrincipalAcegiUserToken");
+        }
+
+        PrincipalAcegiUserToken castResult = (PrincipalAcegiUserToken) result;
+        assertEquals(principal, result);
+    }
+
+    public void testHandlesIfHttpRequestIsNullForSomeReason() {
+        AutoIntegrationFilter filter = new AutoIntegrationFilter();
+        assertEquals(null, filter.extractFromContainer(null));
+    }
+
+    public void testHandlesIfThereIsNoPrincipal() {
+        AutoIntegrationFilter filter = new AutoIntegrationFilter();
+        assertEquals(null,
+            filter.extractFromContainer(new MockHttpServletRequest(null, null)));
+    }
+
+    public void testReturnsNullIfNonAuthenticationObjectInHttpRequest() {
+        AutoIntegrationFilter filter = new AutoIntegrationFilter();
+        assertEquals(null,
+            filter.extractFromContainer(
+                new MockHttpServletRequest(new MockPrincipal(), null)));
+    }
+
+    private Subject makeIntoSubject(Principal principal) {
+        Set principals = new HashSet();
+        principals.add(principal);
+
+        return new Subject(false, principals, new HashSet(), new HashSet());
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockAutoIntegrationFilterHttpSession
+        extends AutoIntegrationFilter {
+        private HttpSessionIntegrationFilter filter;
+
+        public MockAutoIntegrationFilterHttpSession(
+            HttpSessionIntegrationFilter filter) {
+            this.filter = filter;
+        }
+
+        private MockAutoIntegrationFilterHttpSession() {
+            super();
+        }
+
+        protected HttpSessionIntegrationFilter getHttpSessionIntegrationFilter() {
+            return this.filter;
+        }
+    }
+
+    private class MockAutoIntegrationFilterJboss extends AutoIntegrationFilter {
+        private JbossIntegrationFilter filter;
+
+        public MockAutoIntegrationFilterJboss(JbossIntegrationFilter filter) {
+            this.filter = filter;
+        }
+
+        private MockAutoIntegrationFilterJboss() {
+            super();
+        }
+
+        protected JbossIntegrationFilter getJbossIntegrationFilter() {
+            return this.filter;
+        }
+    }
+
+    private class MockHttpSessionIntegrationFilter
+        extends HttpSessionIntegrationFilter {
+        private Object toReturn;
+
+        public MockHttpSessionIntegrationFilter(Object toReturn) {
+            this.toReturn = toReturn;
+        }
+
+        private MockHttpSessionIntegrationFilter() {
+            super();
+        }
+
+        public Object extractFromContainer(ServletRequest request) {
+            return this.toReturn;
+        }
+    }
+}

+ 495 - 0
core/src/test/java/org/acegisecurity/ui/webapp/AuthenticationProcessingFilterTests.java

@@ -0,0 +1,495 @@
+/* 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.webapp;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.Authentication;
+import net.sf.acegisecurity.MockFilterConfig;
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpServletResponse;
+import net.sf.acegisecurity.MockHttpSession;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+/**
+ * Tests {@link AuthenticationProcessingFilter}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class AuthenticationProcessingFilterTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public AuthenticationProcessingFilterTests() {
+        super();
+    }
+
+    public AuthenticationProcessingFilterTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(AuthenticationProcessingFilterTests.class);
+    }
+
+    public void testDoFilterWithNonHttpServletRequestDetected()
+        throws Exception {
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+
+        try {
+            filter.doFilter(null, new MockHttpServletResponse(),
+                new MockFilterChain());
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("Can only process HttpServletRequest",
+                expected.getMessage());
+        }
+    }
+
+    public void testDoFilterWithNonHttpServletResponseDetected()
+        throws Exception {
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+
+        try {
+            filter.doFilter(new MockHttpServletRequest(null, null), null,
+                new MockFilterChain());
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("Can only process HttpServletResponse",
+                expected.getMessage());
+        }
+    }
+
+    public void testFailedAuthenticationRedirectsAppropriately()
+        throws Exception {
+        // Setup our HTTP request
+        MockHttpServletRequest request = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request.setContextPath("/myApp");
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_USERNAME_KEY,
+            "marissa");
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_PASSWORD_KEY,
+            "WRONG_PASSWORD");
+        request.setServletPath("/j_acegi_security_check");
+
+        // Setup our filter configuration
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/ui/webapp/filtertest-valid.xml");
+        config.setInitParmeter("defaultTargetUrl", "/");
+        config.setInitParmeter("authenticationFailureUrl", "/failed.jsp");
+
+        // Setup our expectation that the filter chain will not be invoked, as we redirect to authenticationFailureUrl
+        MockFilterChain chain = new MockFilterChain(false);
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        // Test
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+        executeFilterInContainerSimulator(config, filter, request, response,
+            chain);
+        System.out.println(response.getRedirect());
+        assertEquals("/myApp/failed.jsp", response.getRedirect());
+        assertTrue(request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY) == null);
+    }
+
+    public void testFilterProcessesUrlVariationsRespected()
+        throws Exception {
+        // Setup our HTTP request
+        MockHttpServletRequest request = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_USERNAME_KEY,
+            "marissa");
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_PASSWORD_KEY,
+            "koala");
+        request.setServletPath("/j_my_security_check");
+
+        // Setup our filter configuration
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/ui/webapp/filtertest-valid.xml");
+        config.setInitParmeter("defaultTargetUrl", "/");
+        config.setInitParmeter("authenticationFailureUrl", "/failed.jsp");
+        config.setInitParmeter("filterProcessesUrl", "/j_my_security_check");
+
+        // Setup our expectation that the filter chain will not be invoked, as we redirect to defaultTargetUrl
+        MockFilterChain chain = new MockFilterChain(false);
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        // Test
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+        executeFilterInContainerSimulator(config, filter, request, response,
+            chain);
+        assertEquals("/", response.getRedirect());
+        assertTrue(request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY) != null);
+        assertEquals("marissa",
+            ((Authentication) request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY)).getPrincipal()
+             .toString());
+    }
+
+    public void testIgnoresAnyServletPathOtherThanFilterProcessesUrl()
+        throws Exception {
+        // Setup our HTTP request
+        MockHttpServletRequest request = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request.setServletPath("/j_some_other_url");
+
+        // Setup our filter configuration
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/ui/webapp/filtertest-valid.xml");
+        config.setInitParmeter("defaultTargetUrl", "/");
+        config.setInitParmeter("authenticationFailureUrl", "/failed.jsp");
+
+        // Setup our expectation that the filter chain will be invoked, as should just proceed with chain
+        MockFilterChain chain = new MockFilterChain(true);
+
+        // Test
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+        executeFilterInContainerSimulator(config, filter, request,
+            new MockHttpServletResponse(), chain);
+    }
+
+    public void testNormalOperationWithDefaultFilterProcessesUrl()
+        throws Exception {
+        // Setup our HTTP request
+        MockHttpServletRequest request = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_USERNAME_KEY,
+            "marissa");
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_PASSWORD_KEY,
+            "koala");
+        request.setServletPath("/j_acegi_security_check");
+
+        // Setup our filter configuration
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/ui/webapp/filtertest-valid.xml");
+        config.setInitParmeter("defaultTargetUrl", "/");
+        config.setInitParmeter("authenticationFailureUrl", "/failed.jsp");
+
+        // Setup our expectation that the filter chain will not be invoked, as we redirect to defaultTargetUrl
+        MockFilterChain chain = new MockFilterChain(false);
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        // Test
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+        executeFilterInContainerSimulator(config, filter, request, response,
+            chain);
+        assertEquals("/", response.getRedirect());
+        assertTrue(request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY) != null);
+        assertEquals("marissa",
+            ((Authentication) request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY)).getPrincipal()
+             .toString());
+    }
+
+    public void testNullPasswordHandledGracefully() throws Exception {
+        // Setup our HTTP request
+        MockHttpServletRequest request = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_USERNAME_KEY,
+            "marissa");
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_PASSWORD_KEY,
+            null);
+        request.setServletPath("/j_acegi_security_check");
+
+        // Setup our filter configuration
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/ui/webapp/filtertest-valid.xml");
+        config.setInitParmeter("defaultTargetUrl", "/");
+        config.setInitParmeter("authenticationFailureUrl", "/failed.jsp");
+
+        // Setup our expectation that the filter chain will not be invoked, as we redirect to defaultTargetUrl
+        MockFilterChain chain = new MockFilterChain(false);
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        // Test
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+        executeFilterInContainerSimulator(config, filter, request, response,
+            chain);
+        assertEquals("/failed.jsp", response.getRedirect());
+        assertTrue(request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY) == null);
+    }
+
+    public void testNullUsernameHandledGracefully() throws Exception {
+        // Setup our HTTP request
+        MockHttpServletRequest request = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_USERNAME_KEY,
+            null);
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_PASSWORD_KEY,
+            "koala");
+        request.setServletPath("/j_acegi_security_check");
+
+        // Setup our filter configuration
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/ui/webapp/filtertest-valid.xml");
+        config.setInitParmeter("defaultTargetUrl", "/");
+        config.setInitParmeter("authenticationFailureUrl", "/failed.jsp");
+
+        // Setup our expectation that the filter chain will not be invoked, as we redirect to defaultTargetUrl
+        MockFilterChain chain = new MockFilterChain(false);
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        // Test
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+        executeFilterInContainerSimulator(config, filter, request, response,
+            chain);
+        assertEquals("/failed.jsp", response.getRedirect());
+        assertTrue(request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY) == null);
+    }
+
+    public void testStartupDetectsInvalidAppContextLocation()
+        throws Exception {
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("defaultTargetUrl", "/");
+        config.setInitParmeter("authenticationFailureUrl", "/failed.jsp");
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/ui/webapp/filtertest-invalid.xml");
+
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("Bean context must contain at least one bean of type AuthenticationManager",
+                expected.getMessage());
+        }
+    }
+
+    public void testStartupDetectsMissingAppContextLocation()
+        throws Exception {
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("defaultTargetUrl", "/");
+        config.setInitParmeter("authenticationFailureUrl", "/failed.jsp");
+
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("appContextLocation must be specified",
+                expected.getMessage());
+        }
+
+        config.setInitParmeter("appContextLocation", "");
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("appContextLocation must be specified",
+                expected.getMessage());
+        }
+    }
+
+    public void testStartupDetectsMissingAuthenticationFailureUrl()
+        throws Exception {
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/ui/webapp/filtertest-valid.xml");
+        config.setInitParmeter("defaultTargetUrl", "/");
+
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("authenticationFailureUrl must be specified",
+                expected.getMessage());
+        }
+
+        config.setInitParmeter("authenticationFailureUrl", "");
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("authenticationFailureUrl must be specified",
+                expected.getMessage());
+        }
+    }
+
+    public void testStartupDetectsMissingDefaultTargetUrl()
+        throws Exception {
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("authenticationFailureUrl", "/failed.jsp");
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/ui/webapp/filtertest-valid.xml");
+
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("defaultTargetUrl must be specified",
+                expected.getMessage());
+        }
+
+        config.setInitParmeter("defaultTargetUrl", "");
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("defaultTargetUrl must be specified",
+                expected.getMessage());
+        }
+    }
+
+    public void testStartupDetectsMissingInvalidAppContextLocation()
+        throws Exception {
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("defaultTargetUrl", "/");
+        config.setInitParmeter("authenticationFailureUrl", "/failed.jsp");
+        config.setInitParmeter("appContextLocation", "DOES_NOT_EXIST");
+
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+
+        try {
+            filter.init(config);
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertTrue(expected.getMessage().startsWith("Cannot locate"));
+        }
+    }
+
+    public void testSuccessLoginThenFailureLoginResultsInSessionLoosingToken()
+        throws Exception {
+        // Setup our HTTP request
+        MockHttpServletRequest request = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_USERNAME_KEY,
+            "marissa");
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_PASSWORD_KEY,
+            "koala");
+        request.setServletPath("/j_acegi_security_check");
+
+        // Setup our filter configuration
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/ui/webapp/filtertest-valid.xml");
+        config.setInitParmeter("defaultTargetUrl", "/");
+        config.setInitParmeter("authenticationFailureUrl", "/failed.jsp");
+
+        // Setup our expectation that the filter chain will not be invoked, as we redirect to authenticationFailureUrl
+        MockFilterChain chain = new MockFilterChain(false);
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        // Test
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+        executeFilterInContainerSimulator(config, filter, request, response,
+            chain);
+        assertEquals("/", response.getRedirect());
+        assertTrue(request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY) != null);
+
+        // Now try again with a wrong password
+        MockHttpServletRequest request2 = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request2.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_USERNAME_KEY,
+            "marissa");
+        request2.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_PASSWORD_KEY,
+            "WRONG_PASSWORD");
+        request2.setServletPath("/j_acegi_security_check");
+
+        executeFilterInContainerSimulator(config, filter, request2, response,
+            chain);
+        assertTrue(request2.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY) == null);
+    }
+
+    public void testSuccessfulAuthenticationCausesRedirectToSessionSpecifiedUrl()
+        throws Exception {
+        // Setup our HTTP request
+        MockHttpServletRequest request = new MockHttpServletRequest(null,
+                new MockHttpSession());
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_USERNAME_KEY,
+            "marissa");
+        request.setParameter(AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_PASSWORD_KEY,
+            "koala");
+        request.setServletPath("/j_acegi_security_check");
+        request.getSession().setAttribute(AuthenticationProcessingFilter.ACEGI_SECURITY_TARGET_URL_KEY,
+            "/my-destination");
+
+        // Setup our filter configuration
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter("appContextLocation",
+            "net/sf/acegisecurity/ui/webapp/filtertest-valid.xml");
+        config.setInitParmeter("defaultTargetUrl", "/");
+        config.setInitParmeter("authenticationFailureUrl", "/failed.jsp");
+
+        // Setup our expectation that the filter chain will not be invoked, as we redirect to defaultTargetUrl
+        MockFilterChain chain = new MockFilterChain(false);
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        // Test
+        AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
+        executeFilterInContainerSimulator(config, filter, request, response,
+            chain);
+        assertEquals("/my-destination", response.getRedirect());
+        assertTrue(request.getSession().getAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY) != null);
+    }
+
+    private void executeFilterInContainerSimulator(FilterConfig filterConfig,
+        Filter filter, ServletRequest request, ServletResponse response,
+        FilterChain filterChain) throws ServletException, IOException {
+        filter.init(filterConfig);
+        filter.doFilter(request, response, filterChain);
+        filter.destroy();
+    }
+
+    //~ Inner Classes ==========================================================
+
+    private class MockFilterChain implements FilterChain {
+        private boolean expectToProceed;
+
+        public MockFilterChain(boolean expectToProceed) {
+            this.expectToProceed = expectToProceed;
+        }
+
+        private MockFilterChain() {
+            super();
+        }
+
+        public void doFilter(ServletRequest request, ServletResponse response)
+            throws IOException, ServletException {
+            if (expectToProceed) {
+                assertTrue(true);
+            } else {
+                fail("Did not expect filter chain to proceed");
+            }
+        }
+    }
+}

+ 93 - 0
core/src/test/java/org/acegisecurity/ui/webapp/HttpSessionIntegrationFilterTests.java

@@ -0,0 +1,93 @@
+/* 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.webapp;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.GrantedAuthority;
+import net.sf.acegisecurity.GrantedAuthorityImpl;
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpSession;
+import net.sf.acegisecurity.adapters.PrincipalAcegiUserToken;
+
+
+/**
+ * Tests {@link HttpSessionIntegrationFilter}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class HttpSessionIntegrationFilterTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public HttpSessionIntegrationFilterTests() {
+        super();
+    }
+
+    public HttpSessionIntegrationFilterTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(HttpSessionIntegrationFilterTests.class);
+    }
+
+    public void testCorrectOperation() {
+        // Build a mock session containing the authenticated user
+        PrincipalAcegiUserToken principal = new PrincipalAcegiUserToken("key",
+                "someone", "password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("SOME_ROLE")});
+        MockHttpSession session = new MockHttpSession();
+        session.setAttribute(HttpSessionIntegrationFilter.ACEGI_SECURITY_AUTHENTICATION_KEY,
+            principal);
+
+        // Confirm filter can extract required credentials from session
+        HttpSessionIntegrationFilter filter = new HttpSessionIntegrationFilter();
+        Object result = filter.extractFromContainer(new MockHttpServletRequest(
+                    null, session));
+
+        if (!(result instanceof PrincipalAcegiUserToken)) {
+            fail("Should have returned PrincipalAcegiUserToken");
+        }
+
+        PrincipalAcegiUserToken castResult = (PrincipalAcegiUserToken) result;
+        assertEquals(principal, result);
+    }
+
+    public void testHandlesIfHttpRequestIsNullForSomeReason() {
+        HttpSessionIntegrationFilter filter = new HttpSessionIntegrationFilter();
+        assertEquals(null, filter.extractFromContainer(null));
+    }
+
+    public void testHandlesIfHttpSessionIsNullForSomeReason() {
+        HttpSessionIntegrationFilter filter = new HttpSessionIntegrationFilter();
+        assertEquals(null,
+            filter.extractFromContainer(new MockHttpServletRequest(null, null)));
+    }
+
+    public void testHandlesIfThereIsNoPrincipalInTheHttpSession() {
+        HttpSessionIntegrationFilter filter = new HttpSessionIntegrationFilter();
+        assertEquals(null,
+            filter.extractFromContainer(
+                new MockHttpServletRequest(null, new MockHttpSession())));
+    }
+}

+ 38 - 0
core/src/test/java/org/acegisecurity/ui/webapp/filtertest-invalid.xml

@@ -0,0 +1,38 @@
+<?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>
+
+	<!-- Data access object which stores authentication information -->
+	<bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
+  		<property name="userMap">
+			<value>
+				marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR
+				dianne=emu,ROLE_TELLER
+				scott=wombat,ROLE_TELLER
+				peter=opal,disabled,ROLE_TELLER
+			</value>
+		</property>
+	</bean>
+	
+	<!-- The authentication manager is deliberately missing in order to test error detection -->
+
+</beans>

+ 52 - 0
core/src/test/java/org/acegisecurity/ui/webapp/filtertest-valid.xml

@@ -0,0 +1,52 @@
+<?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>
+
+	<!-- Data access object which stores authentication information -->
+	<bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
+  		<property name="userMap">
+			<value>
+				marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR
+				dianne=emu,ROLE_TELLER
+				scott=wombat,ROLE_TELLER
+				peter=opal,disabled,ROLE_TELLER
+			</value>
+		</property>
+	</bean>
+	
+	<!-- Authentication provider that queries our data access object  -->
+	<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
+     	<property name="authenticationDao"><ref bean="inMemoryDaoImpl"/></property>
+ 		<property name="ignorePasswordCase"><value>false</value></property>
+ 		<property name="ignoreUsernameCase"><value>true</value></property>
+	</bean>
+
+	<!-- The authentication manager that iterates through our only authentication provider -->
+	<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
+		<property name="providers">
+		  <list>
+		    <ref bean="daoAuthenticationProvider"/>
+		  </list>
+		</property>
+	</bean>
+
+</beans>