SEC-1100: Added support for <access-denied-handler> element which can take a ref or an error-page attribute.

This commit is contained in:
Luke Taylor 2009-04-30 05:46:55 +00:00
parent 6db9a3facc
commit 90b849c271
9 changed files with 1691 additions and 1589 deletions

View File

@ -8,6 +8,7 @@ package org.springframework.security.config;
*/
public abstract class Elements {
public static final String ACCESS_DENIED_HANDLER = "access-denied-handler";
public static final String AUTHENTICATION_MANAGER = "authentication-manager";
public static final String USER_SERVICE = "user-service";
public static final String JDBC_USER_SERVICE = "jdbc-user-service";

View File

@ -8,6 +8,7 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@ -99,6 +100,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
private static final String ATT_ENTRY_POINT_REF = "entry-point-ref";
private static final String ATT_ONCE_PER_REQUEST = "once-per-request";
private static final String ATT_ACCESS_DENIED_PAGE = "access-denied-page";
private static final String ATT_ACCESS_DENIED_ERROR_PAGE = "error-page";
private static final String ATT_USE_EXPRESSIONS = "use-expressions";
@ -336,22 +338,51 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
}
private void registerExceptionTranslationFilter(Element element, ParserContext pc, boolean allowSessionCreation) {
String accessDeniedPage = element.getAttribute(ATT_ACCESS_DENIED_PAGE);
ConfigUtils.validateHttpRedirect(accessDeniedPage, pc, pc.extractSource(element));
BeanDefinitionBuilder exceptionTranslationFilterBuilder
= BeanDefinitionBuilder.rootBeanDefinition(ExceptionTranslationFilter.class);
exceptionTranslationFilterBuilder.addPropertyValue("createSessionAllowed", new Boolean(allowSessionCreation));
if (StringUtils.hasText(accessDeniedPage)) {
BeanDefinition accessDeniedHandler = new RootBeanDefinition(AccessDeniedHandlerImpl.class);
accessDeniedHandler.getPropertyValues().addPropertyValue("errorPage", accessDeniedPage);
exceptionTranslationFilterBuilder.addPropertyValue("accessDeniedHandler", accessDeniedHandler);
}
exceptionTranslationFilterBuilder.addPropertyValue("createSessionAllowed", Boolean.valueOf(allowSessionCreation));
exceptionTranslationFilterBuilder.addPropertyValue("accessDeniedHandler", createAccessDeniedHandler(element, pc));
pc.getRegistry().registerBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER, exceptionTranslationFilterBuilder.getBeanDefinition());
ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.EXCEPTION_TRANSLATION_FILTER));
}
private BeanMetadataElement createAccessDeniedHandler(Element element, ParserContext pc) {
String accessDeniedPage = element.getAttribute(ATT_ACCESS_DENIED_PAGE);
ConfigUtils.validateHttpRedirect(accessDeniedPage, pc, pc.extractSource(element));
Element accessDeniedElt = DomUtils.getChildElementByTagName(element, Elements.ACCESS_DENIED_HANDLER);
BeanDefinitionBuilder accessDeniedHandler = BeanDefinitionBuilder.rootBeanDefinition(AccessDeniedHandlerImpl.class);
if (StringUtils.hasText(accessDeniedPage)) {
if (accessDeniedElt != null) {
pc.getReaderContext().error("The attribute " + ATT_ACCESS_DENIED_PAGE +
" cannot be used with <" + Elements.ACCESS_DENIED_HANDLER + ">", pc.extractSource(accessDeniedElt));
}
accessDeniedHandler.addPropertyValue("errorPage", accessDeniedPage);
}
if (accessDeniedElt != null) {
String errorPage = accessDeniedElt.getAttribute("error-page");
String ref = accessDeniedElt.getAttribute("ref");
if (StringUtils.hasText(errorPage)) {
if (StringUtils.hasText(ref)) {
pc.getReaderContext().error("The attribute " + ATT_ACCESS_DENIED_ERROR_PAGE +
" cannot be used together with the 'ref' attribute within <" +
Elements.ACCESS_DENIED_HANDLER + ">", pc.extractSource(accessDeniedElt));
}
accessDeniedHandler.addPropertyValue("errorPage", errorPage);
} else if (StringUtils.hasText(ref)) {
return new RuntimeBeanReference(ref);
}
}
return accessDeniedHandler.getBeanDefinition();
}
private void registerFilterSecurityInterceptor(Element element, ParserContext pc, String accessManagerId,
BeanDefinition fids) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);

View File

@ -1,6 +1,6 @@
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-2.5.xsd
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-3.0.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.xsd=org/springframework/security/config/spring-security-2.0.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.1.xsd=org/springframework/security/config/spring-security-2.0.1.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.2.xsd=org/springframework/security/config/spring-security-2.0.2.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.4.xsd=org/springframework/security/config/spring-security-2.0.4.xsd
http\://www.springframework.org/schema/security/spring-security-2.5.xsd=org/springframework/security/config/spring-security-2.5.xsd
http\://www.springframework.org/schema/security/spring-security-3.0.xsd=org/springframework/security/config/spring-security-3.0.xsd

View File

@ -219,7 +219,7 @@ protect-pointcut.attlist &=
http =
## Container element for HTTP security configuration
element http {http.attlist, (intercept-url+ & form-login? & openid-login & x509? & http-basic? & logout? & concurrent-session-control? & remember-me? & anonymous? & port-mappings) }
element http {http.attlist, (intercept-url+ & access-denied-handler? & form-login? & openid-login? & x509? & http-basic? & logout? & concurrent-session-control? & remember-me? & anonymous? & port-mappings) }
http.attlist &=
## Automatically registers a login form, BASIC authentication, anonymous authentication, logout services, remember-me and servlet-api-integration. If set to "true", all of these capabilities are added (although you can still customize the configuration of each by providing the respective element). If unspecified, defaults to "false".
attribute auto-config {boolean}?
@ -229,7 +229,7 @@ http.attlist &=
## Controls the eagerness with which an HTTP session is created. If not set, defaults to "ifRequired". Note that if a custom SecurityContextRepository is set using security-context-repository-ref, then the only value which can be set is "always". Otherwise the session creation behaviour will be determined by the repository bean implementation.
attribute create-session {"ifRequired" | "always" | "never" }?
http.attlist &=
## A reference to a SecurityContextRepository bean. This can be used to customize the way the SecurityContext is stored between requests.
## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests.
attribute security-context-repository-ref {xsd:string}?
http.attlist &=
## The path format used to define the paths in child elements.
@ -256,12 +256,20 @@ http.attlist &=
## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to "true"
attribute once-per-request {boolean}?
http.attlist &=
## Allows the access denied page to be set (the user will be redirected here if an AccessDeniedException is raised).
## Deprecated in favour of the access-denied-handler element.
attribute access-denied-page {xsd:string}?
http.attlist &=
##
attribute disable-url-rewriting {boolean}?
attribute disable-url-rewriting {boolean}?
access-denied-handler =
## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.
element access-denied-handler {access-denied-handler.attlist, empty}
access-denied-handler.attlist &= (ref | access-denied-handler-page)
access-denied-handler-page =
## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access.
attribute error-page {xsd:string}
intercept-url =
## Specifies the access attributes and/or filter list for a particular set of URLs.

View File

@ -10,7 +10,7 @@
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="elts-to-inline">
<xsl:text>,anonymous,concurrent-session-control,filter-chain,form-login,http-basic,intercept-url,logout,password-encoder,port-mappings,port-mapper,password-compare,protect,protect-pointcut,remember-me,salt-source,x509,</xsl:text>
<xsl:text>,access-denied-handler,anonymous,concurrent-session-control,filter-chain,form-login,http-basic,intercept-url,logout,password-encoder,port-mappings,port-mapper,password-compare,protect,protect-pointcut,remember-me,salt-source,x509,</xsl:text>
</xsl:variable>
<xsl:template match="xs:element">

View File

@ -33,6 +33,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.openid.OpenIDAuthenticationProcessingFilter;
import org.springframework.security.openid.OpenIDAuthenticationProvider;
import org.springframework.security.util.FieldUtils;
import org.springframework.security.web.AccessDeniedHandlerImpl;
import org.springframework.security.web.ExceptionTranslationFilter;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
@ -351,6 +352,49 @@ public class HttpSecurityBeanDefinitionParserTests {
assertEquals("/go-away", FieldUtils.getFieldValue(filter, "accessDeniedHandler.errorPage"));
}
@Test
public void accessDeniedHandlerPageIsSetCorectly() throws Exception {
setContext(
" <http auto-config='true'>" +
" <access-denied-handler error-page='/go-away'/>" +
" </http>" + AUTH_PROVIDER_XML);
ExceptionTranslationFilter filter = (ExceptionTranslationFilter) appContext.getBean(BeanIds.EXCEPTION_TRANSLATION_FILTER);
assertEquals("/go-away", FieldUtils.getFieldValue(filter, "accessDeniedHandler.errorPage"));
}
@Test
public void accessDeniedHandlerIsSetCorectly() throws Exception {
setContext(
" <b:bean id='adh' class='" + AccessDeniedHandlerImpl.class.getName() + "'/>" +
" <http auto-config='true'>" +
" <access-denied-handler ref='adh'/>" +
" </http>" + AUTH_PROVIDER_XML);
ExceptionTranslationFilter filter = (ExceptionTranslationFilter) appContext.getBean(BeanIds.EXCEPTION_TRANSLATION_FILTER);
AccessDeniedHandlerImpl adh = (AccessDeniedHandlerImpl) appContext.getBean("adh");
assertSame(adh, FieldUtils.getFieldValue(filter, "accessDeniedHandler"));
}
@Test(expected=BeanDefinitionParsingException.class)
public void accessDeniedHandlerAndAccessDeniedHandlerAreMutuallyExclusive() throws Exception {
setContext(
" <http auto-config='true' access-denied-page='/go-away'>" +
" <access-denied-handler error-page='/go-away'/>" +
" </http>" + AUTH_PROVIDER_XML);
ExceptionTranslationFilter filter = (ExceptionTranslationFilter) appContext.getBean(BeanIds.EXCEPTION_TRANSLATION_FILTER);
assertEquals("/go-away", FieldUtils.getFieldValue(filter, "accessDeniedHandler.errorPage"));
}
@Test(expected=BeanDefinitionParsingException.class)
public void accessDeniedHandlerPageAndRefAreMutuallyExclusive() throws Exception {
setContext(
" <b:bean id='adh' class='" + AccessDeniedHandlerImpl.class.getName() + "'/>" +
" <http auto-config='true'>" +
" <access-denied-handler error-page='/go-away' ref='adh'/>" +
" </http>" + AUTH_PROVIDER_XML);
ExceptionTranslationFilter filter = (ExceptionTranslationFilter) appContext.getBean(BeanIds.EXCEPTION_TRANSLATION_FILTER);
assertEquals("/go-away", FieldUtils.getFieldValue(filter, "accessDeniedHandler.errorPage"));
}
@Test
public void externalFiltersAreTreatedCorrectly() throws Exception {
// Decorated user-filters should be added to stack. The others should be ignored.

View File

@ -16,7 +16,7 @@ public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext
" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'\n" +
" xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\n" +
"http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd\n" +
"http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.5.xsd'>\n";
"http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd'>\n";
private static final String BEANS_CLOSE = "</b:beans>\n";
Resource inMemoryXml;