diff --git a/config/src/main/java/org/springframework/security/config/http/HeadersBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/HeadersBeanDefinitionParser.java index 84044c55dea..873537cfbb8 100644 --- a/config/src/main/java/org/springframework/security/config/http/HeadersBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/http/HeadersBeanDefinitionParser.java @@ -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 @@ * @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 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 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); diff --git a/config/src/main/resources/META-INF/spring.schemas b/config/src/main/resources/META-INF/spring.schemas index fe5518e1347..e6197602a6c 100644 --- a/config/src/main/resources/META-INF/spring.schemas +++ b/config/src/main/resources/META-INF/spring.schemas @@ -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 diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-5.2.rnc b/config/src/main/resources/org/springframework/security/config/spring-security-5.2.rnc index e4d637f062f..63a05b7abdb 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-5.2.rnc +++ b/config/src/main/resources/org/springframework/security/config/spring-security-5.2.rnc @@ -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} diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-5.2.xsd b/config/src/main/resources/org/springframework/security/config/spring-security-5.2.xsd index 362a4142b93..a4970309fbd 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-5.2.xsd +++ b/config/src/main/resources/org/springframework/security/config/spring-security-5.2.xsd @@ -2261,13 +2261,13 @@ - + Specifies if the default headers should be disabled. Default false. - + Specifies if headers should be disabled. Default false. diff --git a/config/src/test/java/org/springframework/security/config/http/HttpHeadersConfigTests.java b/config/src/test/java/org/springframework/security/config/http/HttpHeadersConfigTests.java index 6e34b5f5bc6..f1db6bf4bf7 100644 --- a/config/src/test/java/org/springframework/security/config/http/HttpHeadersConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/HttpHeadersConfigTests.java @@ -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 @@ * @author Rob Winch * @author Tim Ysewyn * @author Josh Cummings + * @author Rafiullah Hamedy */ public class HttpHeadersConfigTests { @@ -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(() -> @@ -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 { diff --git a/config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithPlaceholder.xml b/config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithPlaceholder.xml new file mode 100644 index 00000000000..f4739ba31a1 --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DefaultsDisabledWithPlaceholder.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DisabledWithPlaceholder.xml b/config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DisabledWithPlaceholder.xml new file mode 100644 index 00000000000..86dcc5dcb4e --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/http/HttpHeadersConfigTests-DisabledWithPlaceholder.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + +