Introduced placeholder support for Headers tag attributes

Added the functionality to allow the disabled and defaults-disabled
attribute of <header> tag to accept a placeholder and resolve it during
parsing.

- Updated the spring-security .rnc files starting from 4.2 up to 5.2
with xsd:token type instead of boolean
- Added unit tests for headers.disabled and headers.defaults-disabled
attributes with placeholder
- Modified the HeadersBeanDefinitionParser to support resolving
placeholders
- Updated spring.schemas to point to latest spring-security-5.2.xsd

Fixes gh-6547
This commit is contained in:
Rafiullah Hamedy 2019-03-17 11:38:21 -04:00 committed by Josh Cummings
parent bfe1e6a154
commit 3617fd257e
7 changed files with 176 additions and 9 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -49,6 +49,7 @@ import org.w3c.dom.Node;
* @author Tim Ysewyn
* @author Eddú Meléndez
* @author Vedran Pavic
* @author Rafiullah Hamedy
* @since 3.2
*/
public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
@ -95,14 +96,15 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
private ManagedList<BeanMetadataElement> headerWriters;
public BeanDefinition parse(Element element, ParserContext parserContext) {
headerWriters = new ManagedList<>();
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.rootBeanDefinition(HeaderWriterFilter.class);
boolean disabled = element != null
&& "true".equals(element.getAttribute("disabled"));
&& "true".equals(resolveAttribute(parserContext, element, "disabled"));
boolean defaultsDisabled = element != null
&& "true".equals(element.getAttribute("defaults-disabled"));
&& "true".equals(resolveAttribute(parserContext, element, "defaults-disabled"));
boolean addIfNotPresent = element == null || !disabled && !defaultsDisabled;
@ -136,6 +138,19 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
return builder.getBeanDefinition();
}
/**
*
* Resolve the placeholder for a given attribute on a element.
*
* @param pc
* @param element
* @param attributeName
* @return Resolved value of the placeholder
*/
private String resolveAttribute(ParserContext pc, Element element, String attributeName) {
return pc.getReaderContext().getEnvironment().resolvePlaceholders(element.getAttribute(attributeName));
}
private void parseCacheControlElement(boolean addIfNotPresent, Element element) {
Element cacheControlElement = element == null ? null : DomUtils
.getChildElementByTagName(element, CACHE_CONTROL_ELEMENT);

View File

@ -1,4 +1,5 @@
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-5.1.xsd
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-5.2.xsd
http\://www.springframework.org/schema/security/spring-security-5.2.xsd=org/springframework/security/config/spring-security-5.2.xsd
http\://www.springframework.org/schema/security/spring-security-5.1.xsd=org/springframework/security/config/spring-security-5.1.xsd
http\://www.springframework.org/schema/security/spring-security-5.0.xsd=org/springframework/security/config/spring-security-5.0.xsd
http\://www.springframework.org/schema/security/spring-security-4.2.xsd=org/springframework/security/config/spring-security-4.2.xsd

View File

@ -746,10 +746,10 @@ headers =
element headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & header*)}
headers-options.attlist &=
## Specifies if the default headers should be disabled. Default false.
attribute defaults-disabled {xsd:boolean}?
attribute defaults-disabled {xsd:token}?
headers-options.attlist &=
## Specifies if headers should be disabled. Default false.
attribute disabled {xsd:boolean}?
attribute disabled {xsd:token}?
hsts =
## Adds support for HTTP Strict Transport Security (HSTS)
element hsts {hsts-options.attlist}

View File

@ -2261,13 +2261,13 @@
</xs:complexType>
</xs:element>
<xs:attributeGroup name="headers-options.attlist">
<xs:attribute name="defaults-disabled" type="xs:boolean">
<xs:attribute name="defaults-disabled" type="xs:token">
<xs:annotation>
<xs:documentation>Specifies if the default headers should be disabled. Default false.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="disabled" type="xs:boolean">
<xs:attribute name="disabled" type="xs:token">
<xs:annotation>
<xs:documentation>Specifies if headers should be disabled. Default false.
</xs:documentation>

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -45,6 +45,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Rob Winch
* @author Tim Ysewyn
* @author Josh Cummings
* @author Rafiullah Hamedy
*/
public class HttpHeadersConfigTests {
@ -79,6 +80,45 @@ public class HttpHeadersConfigTests {
.andExpect(excludesDefaults());
}
@Test
public void requestWhenHeadersDisabledViaPlaceholderThenResponseExcludesAllSecureHeaders()
throws Exception {
System.setProperty("security.headers.disabled", "true");
this.spring.configLocations(this.xml("DisabledWithPlaceholder")).autowire();
this.mvc.perform(get("/").secure(true))
.andExpect(status().isOk())
.andExpect(excludesDefaults());
}
@Test
public void requestWhenHeadersEnabledViaPlaceholderThenResponseIncludesAllSecureHeaders()
throws Exception {
System.setProperty("security.headers.disabled", "false");
this.spring.configLocations(this.xml("DisabledWithPlaceholder")).autowire();
this.mvc.perform(get("/").secure(true))
.andExpect(status().isOk())
.andExpect(includesDefaults());
}
@Test
public void requestWhenHeadersDisabledRefMissingPlaceholderThenResponseIncludesAllSecureHeaders()
throws Exception {
System.clearProperty("security.headers.disabled");
this.spring.configLocations(this.xml("DisabledWithPlaceholder")).autowire();
this.mvc.perform(get("/").secure(true))
.andExpect(status().isOk())
.andExpect(includesDefaults());
}
@Test
public void configureWhenHeadersDisabledHavingChildElementThenAutowireFails() {
assertThatThrownBy(() ->
@ -139,6 +179,45 @@ public class HttpHeadersConfigTests {
.andExpect(excludesDefaults());
}
@Test
public void requestWhenDefaultsDisabledWithPlaceholderTrueThenExcludesAllSecureHeaders()
throws Exception {
System.setProperty("security.headers.defaults.disabled", "true");
this.spring.configLocations(this.xml("DefaultsDisabledWithPlaceholder")).autowire();
this.mvc.perform(get("/").secure(true))
.andExpect(status().isOk())
.andExpect(excludesDefaults());
}
@Test
public void requestWhenDefaultsDisabledWithPlaceholderFalseThenIncludeAllSecureHeaders()
throws Exception {
System.setProperty("security.headers.defaults.disabled", "false");
this.spring.configLocations(this.xml("DefaultsDisabledWithPlaceholder")).autowire();
this.mvc.perform(get("/").secure(true))
.andExpect(status().isOk())
.andExpect(includesDefaults());
}
@Test
public void requestWhenDefaultsDisabledWithPlaceholderMissingThenIncludeAllSecureHeaders()
throws Exception {
System.clearProperty("security.headers.defaults.disabled");
this.spring.configLocations(this.xml("DefaultsDisabledWithPlaceholder")).autowire();
this.mvc.perform(get("/").secure(true))
.andExpect(status().isOk())
.andExpect(includesDefaults());
}
@Test
public void requestWhenUsingContentTypeOptionsThenDefaultsToNoSniff()
throws Exception {

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2019 the original author or authors.
~
~ 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.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true">
<headers defaults-disabled="${security.headers.defaults.disabled}"/>
</http>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
<b:import resource="userservice.xml"/>
</b:beans>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2019 the original author or authors.
~
~ 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.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true">
<headers disabled="${security.headers.disabled}" />
</http>
<b:bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
<b:import resource="userservice.xml"/>
</b:beans>