Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduced placeholder support for headers tag attributes #6623

Merged
merged 1 commit into from
Mar 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -49,6 +49,7 @@
* @author Tim Ysewyn
* @author Eddú Meléndez
* @author Vedran Pavic
* @author Rafiullah Hamedy
* @since 3.2
*/
public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -136,6 +138,19 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
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);
Expand Down
3 changes: 2 additions & 1 deletion config/src/main/resources/META-INF/spring.schemas
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -45,6 +45,7 @@
* @author Rob Winch
* @author Tim Ysewyn
* @author Josh Cummings
* @author Rafiullah Hamedy
*/
public class HttpHeadersConfigTests {

Expand Down Expand Up @@ -79,6 +80,45 @@ public void requestWhenHeadersDisabledThenResponseExcludesAllSecureHeaders()
.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(() ->
Expand Down Expand Up @@ -139,6 +179,45 @@ public void requestWhenDefaultsDisabledWithNoOverrideThenExcludesAllSecureHeader
.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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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>