From cc07f620df5d703cf07b80ebcdc7da255a6278c0 Mon Sep 17 00:00:00 2001
From: Ben Alex
+ * This implementation sends a 403 (SC_FORBIDDEN) HTTP error code. In addition,
+ * if a {@link #errorPage} is defined, the implementation will perform a
+ * request dispatcher "forward" to the specified error page view. Being a
+ * "forward", the AccessDeniedException.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface AccessDeniedHandler {
+ //~ Methods ================================================================
+
+ /**
+ * Handles an access denied failure.
+ *
+ * @param request that resulted in an AccessDeniedException
+ * @param response so that the user agent can be advised of the failure
+ * @param accessDeniedException that caused the invocation
+ *
+ * @throws IOException in the event of an IOException
+ * @throws ServletException in the event of a ServletException
+ */
+ public void handle(ServletRequest request, ServletResponse response,
+ AccessDeniedException accessDeniedException)
+ throws IOException, ServletException;
+}
diff --git a/core/src/main/java/org/acegisecurity/ui/AccessDeniedHandlerImpl.java b/core/src/main/java/org/acegisecurity/ui/AccessDeniedHandlerImpl.java
new file mode 100644
index 0000000000..5ba873556e
--- /dev/null
+++ b/core/src/main/java/org/acegisecurity/ui/AccessDeniedHandlerImpl.java
@@ -0,0 +1,106 @@
+/* Copyright 2004, 2005, 2006 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 org.acegisecurity.ui;
+
+import org.acegisecurity.AccessDeniedException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Base implementation of {@link AccessDeniedHandler}.
+ *
+ * SecurityContextHolder will remain populated.
+ * This is of benefit if the view (or a tag library or macro) wishes to access
+ * the SecurityContextHolder. The request scope will also be
+ * populated with the exception itself, available from the key {@link
+ * #ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY}.
+ * authenticationEntryPoint will be launched. If they
- * are not an anonymous user, the filter will respond with a
- * HttpServletResponse.SC_FORBIDDEN (403 error). In addition,
- * the AccessDeniedException itself will be placed in the
- * HttpSession attribute keyed against {@link
- * #ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY} (to allow access to the stack
- * trace etc). Again, this allows common access denied handling irrespective
- * of the originating security interceptor.
+ * are not an anonymous user, the filter will delegate to the {@link
+ * org.acegisecurity.ui.AccessDeniedHandler}. By default the filter will use
+ * {@link org.acegisecurity.ui.AccessDeniedHandlerImpl}.
*
@@ -109,10 +105,10 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean { //~ Static fields/initializers ============================================= private static final Log logger = LogFactory.getLog(ExceptionTranslationFilter.class); - public static final String ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY = "ACEGI_SECURITY_403_EXCEPTION"; //~ Instance fields ======================================================== + private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl(); private AuthenticationEntryPoint authenticationEntryPoint; private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl(); private PortResolver portResolver = new PortResolverImpl(); @@ -199,11 +195,11 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean { "Full authentication is required to access this resource")); } else { if (logger.isDebugEnabled()) { - logger.debug("Access is denied (user is not anonymous); sending back forbidden response", + logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler", exception); } - sendAccessDeniedError(request, response, chain, + accessDeniedHandler.handle(request, response, (AccessDeniedException) exception); } } @@ -231,20 +227,6 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean { return createSessionAllowed; } - protected void sendAccessDeniedError(ServletRequest request, - ServletResponse response, FilterChain chain, - AccessDeniedException accessDenied) - throws ServletException, IOException { - if (createSessionAllowed) { - ((HttpServletRequest) request).getSession() - .setAttribute(ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY, - accessDenied); - } - - ((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN, - accessDenied.getMessage()); // 403 - } - protected void sendStartAuthentication(ServletRequest request, ServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException { @@ -274,6 +256,11 @@ public class ExceptionTranslationFilter implements Filter, InitializingBean { (HttpServletResponse) response, reason); } + public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) { + Assert.notNull(accessDeniedHandler, "AccessDeniedHandler required"); + this.accessDeniedHandler = accessDeniedHandler; + } + public void setAuthenticationEntryPoint( AuthenticationEntryPoint authenticationEntryPoint) { this.authenticationEntryPoint = authenticationEntryPoint; diff --git a/core/src/test/java/org/acegisecurity/ui/ExceptionTranslationFilterTests.java b/core/src/test/java/org/acegisecurity/ui/ExceptionTranslationFilterTests.java index ca5a6adada..ab60766b9b 100644 --- a/core/src/test/java/org/acegisecurity/ui/ExceptionTranslationFilterTests.java +++ b/core/src/test/java/org/acegisecurity/ui/ExceptionTranslationFilterTests.java @@ -113,17 +113,22 @@ public class ExceptionTranslationFilterTests extends TestCase { // Setup SecurityContextHolder, as filter needs to check if user is anonymous SecurityContextHolder.getContext().setAuthentication(null); + // Setup a new AccessDeniedHandlerImpl that will do a "forward" + AccessDeniedHandlerImpl adh = new AccessDeniedHandlerImpl(); + adh.setErrorPage("/error.jsp"); + // Test ExceptionTranslationFilter filter = new ExceptionTranslationFilter(); filter.setAuthenticationEntryPoint(new MockAuthenticationEntryPoint( "/login.jsp")); + filter.setAccessDeniedHandler(adh); MockHttpServletResponse response = new MockHttpServletResponse(); filter.doFilter(request, response, chain); assertEquals(403, response.getStatus()); assertEquals(AccessDeniedException.class, - request.getSession() - .getAttribute(ExceptionTranslationFilter.ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY) + request.getAttribute( + AccessDeniedHandlerImpl.ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY) .getClass()); } diff --git a/doc/xdocs/upgrade/upgrade-090-100.html b/doc/xdocs/upgrade/upgrade-090-100.html index 012a04e802..99af9438ac 100644 --- a/doc/xdocs/upgrade/upgrade-090-100.html +++ b/doc/xdocs/upgrade/upgrade-090-100.html @@ -81,7 +81,11 @@ applications: AbstractProcessingFilter.onUnsuccessfulAuthentication(HttpServletRequest, HttpServletResponse) has changed it signature (SEC-238). If subclassing, please override the new signature. - + +
+<%= request.getAttribute(AccessDeniedHandlerImpl.ACEGI_SECURITY_ACCESS_DENIED_EXCEPTION_KEY)%> + +
+
+<% Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+ if (auth != null) { %>
+ Authentication object as a String: <%= auth.toString() %>
+<% } %>
diff --git a/samples/contacts/src/main/webapp/filter/error.html b/samples/contacts/src/main/webapp/filter/error.html
deleted file mode 100644
index 5d461b5a25..0000000000
--- a/samples/contacts/src/main/webapp/filter/error.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
-