From 173537f4f20e28b11368d51270cb55a0a3afeb00 Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Wed, 13 Oct 2010 19:34:04 +0100 Subject: [PATCH] SEC-1584: Added namespace support for injecting custom HttpFirewall instance into FilterChainProxy. --- .../security/config/Elements.java | 1 + .../config/SecurityNamespaceHandler.java | 4 +- .../HttpFirewallBeanDefinitionParser.java | 38 +++++++++++++++++++ .../HttpSecurityBeanDefinitionParser.java | 3 +- .../security/config/spring-security-3.1.rnc | 3 ++ .../security/config/spring-security-3.1.xsd | 5 +++ .../config/http/MiscHttpConfigTests.groovy | 16 +++++++- .../manual/src/docbook/appendix-namespace.xml | 9 ++++- 8 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 config/src/main/java/org/springframework/security/config/http/HttpFirewallBeanDefinitionParser.java diff --git a/config/src/main/java/org/springframework/security/config/Elements.java b/config/src/main/java/org/springframework/security/config/Elements.java index 132ea5808e..3efc60f7d3 100644 --- a/config/src/main/java/org/springframework/security/config/Elements.java +++ b/config/src/main/java/org/springframework/security/config/Elements.java @@ -53,4 +53,5 @@ public abstract class Elements { public static final String FILTER_INVOCATION_DEFINITION_SOURCE = "filter-invocation-definition-source"; public static final String LDAP_PASSWORD_COMPARE = "password-compare"; public static final String DEBUG = "debug"; + public static final String HTTP_FIREWALL = "http-firewall"; } diff --git a/config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java b/config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java index 1633816d70..17aabc60cf 100644 --- a/config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java +++ b/config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java @@ -15,6 +15,7 @@ import org.springframework.security.config.authentication.JdbcUserServiceBeanDef import org.springframework.security.config.authentication.UserServiceBeanDefinitionParser; import org.springframework.security.config.http.FilterChainMapBeanDefinitionDecorator; import org.springframework.security.config.http.FilterInvocationSecurityMetadataSourceParser; +import org.springframework.security.config.http.HttpFirewallBeanDefinitionParser; import org.springframework.security.config.http.HttpSecurityBeanDefinitionParser; import org.springframework.security.config.ldap.LdapProviderBeanDefinitionParser; import org.springframework.security.config.ldap.LdapServerBeanDefinitionParser; @@ -116,11 +117,12 @@ public final class SecurityNamespaceHandler implements NamespaceHandler { parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser()); parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser()); parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE, new MethodSecurityMetadataSourceBeanDefinitionParser()); - parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser()); // Only load the web-namespace parsers if the web classes are available if (ClassUtils.isPresent("org.springframework.security.web.FilterChainProxy", getClass().getClassLoader())) { + parsers.put(Elements.DEBUG, new DebugBeanDefinitionParser()); parsers.put(Elements.HTTP, new HttpSecurityBeanDefinitionParser()); + parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser()); parsers.put(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser()); parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser()); filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator(); diff --git a/config/src/main/java/org/springframework/security/config/http/HttpFirewallBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/HttpFirewallBeanDefinitionParser.java new file mode 100644 index 0000000000..1c2785ea0f --- /dev/null +++ b/config/src/main/java/org/springframework/security/config/http/HttpFirewallBeanDefinitionParser.java @@ -0,0 +1,38 @@ +package org.springframework.security.config.http; + +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.ManagedMap; +import org.springframework.beans.factory.xml.BeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.security.config.BeanIds; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +import java.util.*; + +/** + * Injects the supplied {@code HttpFirewall} bean reference into the {@code FilterChainProxy}. + * + * @author Luke Taylor + */ +public class HttpFirewallBeanDefinitionParser implements BeanDefinitionParser { + + @Override + public BeanDefinition parse(Element element, ParserContext pc) { + String ref = element.getAttribute("ref"); + + if (!StringUtils.hasText(ref)) { + pc.getReaderContext().error("ref attribute is required", pc.extractSource(element)); + } + + HttpSecurityBeanDefinitionParser.registerFilterChainProxy(pc, + new ManagedMap>(), + pc.extractSource(element)); + BeanDefinition filterChainProxy = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAIN_PROXY); + filterChainProxy.getPropertyValues().addPropertyValue("firewall", new RuntimeBeanReference(ref)); + + return null; + } +} diff --git a/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java index f332e08716..02169337ae 100644 --- a/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java @@ -247,10 +247,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { } @SuppressWarnings("unchecked") - private void registerFilterChainProxy(ParserContext pc, Map> filterChainMap, Object source) { + static void registerFilterChainProxy(ParserContext pc, Map> filterChainMap, Object source) { if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) { // Already registered. Obtain the filter chain map and add the new entries to it - // pc.getReaderContext().error("Duplicate element detected", source); BeanDefinition fcp = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAIN_PROXY); Map existingFilterChainMap = (Map) fcp.getPropertyValues().getPropertyValue("filterChainMap").getValue(); diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc index bb7e34c1a4..e622d4b391 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc +++ b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc @@ -257,6 +257,9 @@ protect-pointcut.attlist &= ## Access configuration attributes list that applies to all methods matching the pointcut, e.g. "ROLE_A,ROLE_B" attribute access {xsd:token} +http-firewall = + ## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace. + element http-firewall {ref} http = ## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the "secured" attribute to "false". diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd index 5be13b9d65..c8b1434f13 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd +++ b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd @@ -589,6 +589,11 @@ + + Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace. + + + Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the "secured" attribute to "false". diff --git a/config/src/test/groovy/org/springframework/security/config/http/MiscHttpConfigTests.groovy b/config/src/test/groovy/org/springframework/security/config/http/MiscHttpConfigTests.groovy index 74be8df651..94140e4cab 100644 --- a/config/src/test/groovy/org/springframework/security/config/http/MiscHttpConfigTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/http/MiscHttpConfigTests.groovy @@ -44,6 +44,7 @@ import org.springframework.security.web.savedrequest.RequestCacheAwareFilter import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter import org.springframework.security.web.session.SessionManagementFilter import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler +import org.springframework.security.web.firewall.DefaultHttpFirewall class MiscHttpConfigTests extends AbstractHttpConfigTests { def 'Minimal configuration parses'() { @@ -582,13 +583,24 @@ class MiscHttpConfigTests extends AbstractHttpConfigTests { getFilter(BasicAuthenticationFilter).authenticationDetailsSource == adsr getFilter(X509AuthenticationFilter).authenticationDetailsSource == adsr } - + def includeJaasApiIntegrationFilter() { xml.http(['auto-config':'true','jaas-api-provision':'true']) createAppContext() expect: getFilter(JaasApiIntegrationFilter.class) != null - + } + + def httpFirewallInjectionIsSupported() { + xml.'http-firewall'(ref: 'fw') + xml.http() { + 'form-login'() + } + bean('fw', DefaultHttpFirewall) + createAppContext() + FilterChainProxy fcp = appContext.getBean(BeanIds.FILTER_CHAIN_PROXY) + expect: + fcp.firewall == appContext.getBean('fw') } } diff --git a/docs/manual/src/docbook/appendix-namespace.xml b/docs/manual/src/docbook/appendix-namespace.xml index 533ddb1cf4..449a8892b0 100644 --- a/docs/manual/src/docbook/appendix-namespace.xml +++ b/docs/manual/src/docbook/appendix-namespace.xml @@ -524,12 +524,19 @@ chapter.
- The <literal>request-cache</literal> Element + The <literal><request-cache></literal> Element Sets the RequestCache instance which will be used by the ExceptionTranslationFilter to store request information before invoking an AuthenticationEntryPoint.
+
+ The <literal><http-firewall></literal> Element + This is a top-level element which can be used to inject a custom implementation of + HttpFirewall into the + FilterChainProxy created by the namespace. The default + implementation should be suitable for most applications. +
Authentication Services