Add Cross Origin Policies headers

Add DSL support for Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy and Cross-Origin-Resource-Policy headers

Closes gh-9385, gh-10118
This commit is contained in:
Marcus Da Coregio 2021-12-03 16:47:21 -03:00 committed by Eleftheria Stein-Kousathana
parent 7ec3b55ab3
commit 65426a40ec
38 changed files with 2513 additions and 9 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -31,6 +31,9 @@ import org.springframework.security.web.header.HeaderWriter;
import org.springframework.security.web.header.HeaderWriterFilter; import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.security.web.header.writers.CacheControlHeadersWriter; import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter; import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;
import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter; import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;
import org.springframework.security.web.header.writers.HpkpHeaderWriter; import org.springframework.security.web.header.writers.HpkpHeaderWriter;
import org.springframework.security.web.header.writers.HstsHeaderWriter; import org.springframework.security.web.header.writers.HstsHeaderWriter;
@ -97,6 +100,12 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
private final PermissionsPolicyConfig permissionsPolicy = new PermissionsPolicyConfig(); private final PermissionsPolicyConfig permissionsPolicy = new PermissionsPolicyConfig();
private final CrossOriginOpenerPolicyConfig crossOriginOpenerPolicy = new CrossOriginOpenerPolicyConfig();
private final CrossOriginEmbedderPolicyConfig crossOriginEmbedderPolicy = new CrossOriginEmbedderPolicyConfig();
private final CrossOriginResourcePolicyConfig crossOriginResourcePolicy = new CrossOriginResourcePolicyConfig();
/** /**
* Creates a new instance * Creates a new instance
* *
@ -392,6 +401,9 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
addIfNotNull(writers, this.referrerPolicy.writer); addIfNotNull(writers, this.referrerPolicy.writer);
addIfNotNull(writers, this.featurePolicy.writer); addIfNotNull(writers, this.featurePolicy.writer);
addIfNotNull(writers, this.permissionsPolicy.writer); addIfNotNull(writers, this.permissionsPolicy.writer);
addIfNotNull(writers, this.crossOriginOpenerPolicy.writer);
addIfNotNull(writers, this.crossOriginEmbedderPolicy.writer);
addIfNotNull(writers, this.crossOriginResourcePolicy.writer);
writers.addAll(this.headerWriters); writers.addAll(this.headerWriters);
return writers; return writers;
} }
@ -544,6 +556,129 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this.permissionsPolicy; return this.permissionsPolicy;
} }
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a> header.
* <p>
* Configuration is provided to the {@link CrossOriginOpenerPolicyHeaderWriter} which
* responsible for writing the header.
* </p>
* @return the {@link CrossOriginOpenerPolicyConfig} for additional confniguration
* @since 5.7
* @see CrossOriginOpenerPolicyHeaderWriter
*/
public CrossOriginOpenerPolicyConfig crossOriginOpenerPolicy() {
this.crossOriginOpenerPolicy.writer = new CrossOriginOpenerPolicyHeaderWriter();
return this.crossOriginOpenerPolicy;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a> header.
* <p>
* Calling this method automatically enables (includes) the
* {@code Cross-Origin-Opener-Policy} header in the response using the supplied
* policy.
* <p>
* <p>
* Configuration is provided to the {@link CrossOriginOpenerPolicyHeaderWriter} which
* responsible for writing the header.
* </p>
* @return the {@link HeadersConfigurer} for additional customizations
* @since 5.7
* @see CrossOriginOpenerPolicyHeaderWriter
*/
public HeadersConfigurer<H> crossOriginOpenerPolicy(
Customizer<CrossOriginOpenerPolicyConfig> crossOriginOpenerPolicyCustomizer) {
this.crossOriginOpenerPolicy.writer = new CrossOriginOpenerPolicyHeaderWriter();
crossOriginOpenerPolicyCustomizer.customize(this.crossOriginOpenerPolicy);
return HeadersConfigurer.this;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a> header.
* <p>
* Configuration is provided to the {@link CrossOriginEmbedderPolicyHeaderWriter}
* which is responsible for writing the header.
* </p>
* @return the {@link CrossOriginEmbedderPolicyConfig} for additional customizations
* @since 5.7
* @see CrossOriginEmbedderPolicyHeaderWriter
*/
public CrossOriginEmbedderPolicyConfig crossOriginEmbedderPolicy() {
this.crossOriginEmbedderPolicy.writer = new CrossOriginEmbedderPolicyHeaderWriter();
return this.crossOriginEmbedderPolicy;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a> header.
* <p>
* Calling this method automatically enables (includes) the
* {@code Cross-Origin-Embedder-Policy} header in the response using the supplied
* policy.
* <p>
* <p>
* Configuration is provided to the {@link CrossOriginEmbedderPolicyHeaderWriter}
* which is responsible for writing the header.
* </p>
* @return the {@link HeadersConfigurer} for additional customizations
* @since 5.7
* @see CrossOriginEmbedderPolicyHeaderWriter
*/
public HeadersConfigurer<H> crossOriginEmbedderPolicy(
Customizer<CrossOriginEmbedderPolicyConfig> crossOriginEmbedderPolicyCustomizer) {
this.crossOriginEmbedderPolicy.writer = new CrossOriginEmbedderPolicyHeaderWriter();
crossOriginEmbedderPolicyCustomizer.customize(this.crossOriginEmbedderPolicy);
return HeadersConfigurer.this;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a> header.
* <p>
* Configuration is provided to the {@link CrossOriginResourcePolicyHeaderWriter}
* which is responsible for writing the header:
* </p>
* @return the {@link HeadersConfigurer} for additional customizations
* @since 5.7
* @see CrossOriginResourcePolicyHeaderWriter
*/
public CrossOriginResourcePolicyConfig crossOriginResourcePolicy() {
this.crossOriginResourcePolicy.writer = new CrossOriginResourcePolicyHeaderWriter();
return this.crossOriginResourcePolicy;
}
/**
* Allows configuration for <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a> header.
* <p>
* Calling this method automatically enables (includes) the
* {@code Cross-Origin-Resource-Policy} header in the response using the supplied
* policy.
* <p>
* <p>
* Configuration is provided to the {@link CrossOriginResourcePolicyHeaderWriter}
* which is responsible for writing the header:
* </p>
* @return the {@link HeadersConfigurer} for additional customizations
* @since 5.7
* @see CrossOriginResourcePolicyHeaderWriter
*/
public HeadersConfigurer<H> crossOriginResourcePolicy(
Customizer<CrossOriginResourcePolicyConfig> crossOriginResourcePolicyCustomizer) {
this.crossOriginResourcePolicy.writer = new CrossOriginResourcePolicyHeaderWriter();
crossOriginResourcePolicyCustomizer.customize(this.crossOriginResourcePolicy);
return HeadersConfigurer.this;
}
public final class ContentTypeOptionsConfig { public final class ContentTypeOptionsConfig {
private XContentTypeOptionsHeaderWriter writer; private XContentTypeOptionsHeaderWriter writer;
@ -1142,4 +1277,96 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
} }
public final class CrossOriginOpenerPolicyConfig {
private CrossOriginOpenerPolicyHeaderWriter writer;
public CrossOriginOpenerPolicyConfig() {
}
/**
* Sets the policy to be used in the {@code Cross-Origin-Opener-Policy} header
* @param openerPolicy a {@code Cross-Origin-Opener-Policy}
* @return the {@link CrossOriginOpenerPolicyConfig} for additional configuration
* @throws IllegalArgumentException if openerPolicy is null
*/
public CrossOriginOpenerPolicyConfig policy(
CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy openerPolicy) {
this.writer.setPolicy(openerPolicy);
return this;
}
/**
* Allows completing configuration of Cross Origin Opener Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
public final class CrossOriginEmbedderPolicyConfig {
private CrossOriginEmbedderPolicyHeaderWriter writer;
public CrossOriginEmbedderPolicyConfig() {
}
/**
* Sets the policy to be used in the {@code Cross-Origin-Embedder-Policy} header
* @param embedderPolicy a {@code Cross-Origin-Embedder-Policy}
* @return the {@link CrossOriginEmbedderPolicyConfig} for additional
* configuration
* @throws IllegalArgumentException if embedderPolicy is null
*/
public CrossOriginEmbedderPolicyConfig policy(
CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy embedderPolicy) {
this.writer.setPolicy(embedderPolicy);
return this;
}
/**
* Allows completing configuration of Cross-Origin-Embedder-Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
public final class CrossOriginResourcePolicyConfig {
private CrossOriginResourcePolicyHeaderWriter writer;
public CrossOriginResourcePolicyConfig() {
}
/**
* Sets the policy to be used in the {@code Cross-Origin-Resource-Policy} header
* @param resourcePolicy a {@code Cross-Origin-Resource-Policy}
* @return the {@link CrossOriginResourcePolicyConfig} for additional
* configuration
* @throws IllegalArgumentException if resourcePolicy is null
*/
public CrossOriginResourcePolicyConfig policy(
CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy resourcePolicy) {
this.writer.setPolicy(resourcePolicy);
return this;
}
/**
* Allows completing configuration of Cross-Origin-Resource-Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
*/
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -36,6 +36,9 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.web.header.HeaderWriterFilter; import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.security.web.header.writers.CacheControlHeadersWriter; import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter; import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;
import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter; import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;
import org.springframework.security.web.header.writers.HpkpHeaderWriter; import org.springframework.security.web.header.writers.HpkpHeaderWriter;
import org.springframework.security.web.header.writers.HstsHeaderWriter; import org.springframework.security.web.header.writers.HstsHeaderWriter;
@ -122,6 +125,12 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
private static final String PERMISSIONS_POLICY_ELEMENT = "permissions-policy"; private static final String PERMISSIONS_POLICY_ELEMENT = "permissions-policy";
private static final String CROSS_ORIGIN_OPENER_POLICY_ELEMENT = "cross-origin-opener-policy";
private static final String CROSS_ORIGIN_EMBEDDER_POLICY_ELEMENT = "cross-origin-embedder-policy";
private static final String CROSS_ORIGIN_RESOURCE_POLICY_ELEMENT = "cross-origin-resource-policy";
private static final String ALLOW_FROM = "ALLOW-FROM"; private static final String ALLOW_FROM = "ALLOW-FROM";
private ManagedList<BeanMetadataElement> headerWriters; private ManagedList<BeanMetadataElement> headerWriters;
@ -144,6 +153,9 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
parseReferrerPolicyElement(element, parserContext); parseReferrerPolicyElement(element, parserContext);
parseFeaturePolicyElement(element, parserContext); parseFeaturePolicyElement(element, parserContext);
parsePermissionsPolicyElement(element, parserContext); parsePermissionsPolicyElement(element, parserContext);
parseCrossOriginOpenerPolicy(disabled, element);
parseCrossOriginEmbedderPolicy(disabled, element);
parseCrossOriginResourcePolicy(disabled, element);
parseHeaderElements(element); parseHeaderElements(element);
boolean noWriters = this.headerWriters.isEmpty(); boolean noWriters = this.headerWriters.isEmpty();
if (disabled && !noWriters) { if (disabled && !noWriters) {
@ -376,6 +388,75 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
this.headerWriters.add(headersWriter.getBeanDefinition()); this.headerWriters.add(headersWriter.getBeanDefinition());
} }
private void parseCrossOriginOpenerPolicy(boolean elementDisabled, Element element) {
if (elementDisabled || element == null) {
return;
}
CrossOriginOpenerPolicyHeaderWriter writer = new CrossOriginOpenerPolicyHeaderWriter();
Element crossOriginOpenerPolicyElement = DomUtils.getChildElementByTagName(element,
CROSS_ORIGIN_OPENER_POLICY_ELEMENT);
if (crossOriginOpenerPolicyElement != null) {
addCrossOriginOpenerPolicy(crossOriginOpenerPolicyElement, writer);
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(CrossOriginOpenerPolicyHeaderWriter.class, () -> writer);
this.headerWriters.add(builder.getBeanDefinition());
}
private void parseCrossOriginEmbedderPolicy(boolean elementDisabled, Element element) {
if (elementDisabled || element == null) {
return;
}
CrossOriginEmbedderPolicyHeaderWriter writer = new CrossOriginEmbedderPolicyHeaderWriter();
Element crossOriginEmbedderPolicyElement = DomUtils.getChildElementByTagName(element,
CROSS_ORIGIN_EMBEDDER_POLICY_ELEMENT);
if (crossOriginEmbedderPolicyElement != null) {
addCrossOriginEmbedderPolicy(crossOriginEmbedderPolicyElement, writer);
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(CrossOriginEmbedderPolicyHeaderWriter.class, () -> writer);
this.headerWriters.add(builder.getBeanDefinition());
}
private void parseCrossOriginResourcePolicy(boolean elementDisabled, Element element) {
if (elementDisabled || element == null) {
return;
}
CrossOriginResourcePolicyHeaderWriter writer = new CrossOriginResourcePolicyHeaderWriter();
Element crossOriginResourcePolicyElement = DomUtils.getChildElementByTagName(element,
CROSS_ORIGIN_RESOURCE_POLICY_ELEMENT);
if (crossOriginResourcePolicyElement != null) {
addCrossOriginResourcePolicy(crossOriginResourcePolicyElement, writer);
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(CrossOriginResourcePolicyHeaderWriter.class, () -> writer);
this.headerWriters.add(builder.getBeanDefinition());
}
private void addCrossOriginResourcePolicy(Element crossOriginResourcePolicyElement,
CrossOriginResourcePolicyHeaderWriter writer) {
String policy = crossOriginResourcePolicyElement.getAttribute(ATT_POLICY);
if (StringUtils.hasText(policy)) {
writer.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.from(policy));
}
}
private void addCrossOriginEmbedderPolicy(Element crossOriginEmbedderPolicyElement,
CrossOriginEmbedderPolicyHeaderWriter writer) {
String policy = crossOriginEmbedderPolicyElement.getAttribute(ATT_POLICY);
if (StringUtils.hasText(policy)) {
writer.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.from(policy));
}
}
private void addCrossOriginOpenerPolicy(Element crossOriginOpenerPolicyElement,
CrossOriginOpenerPolicyHeaderWriter writer) {
String policy = crossOriginOpenerPolicyElement.getAttribute(ATT_POLICY);
if (StringUtils.hasText(policy)) {
writer.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.from(policy));
}
}
private void attrNotAllowed(ParserContext context, String attrName, String otherAttrName, Element element) { private void attrNotAllowed(ParserContext context, String attrName, String otherAttrName, Element element) {
context.getReaderContext().error("Only one of '" + attrName + "' or '" + otherAttrName + "' can be set.", context.getReaderContext().error("Only one of '" + attrName + "' or '" + otherAttrName + "' can be set.",
element); element);

View File

@ -149,6 +149,12 @@ import org.springframework.security.web.server.header.CacheControlServerHttpHead
import org.springframework.security.web.server.header.CompositeServerHttpHeadersWriter; import org.springframework.security.web.server.header.CompositeServerHttpHeadersWriter;
import org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter; import org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter; import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy;
import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy;
import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy;
import org.springframework.security.web.server.header.FeaturePolicyServerHttpHeadersWriter; import org.springframework.security.web.server.header.FeaturePolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.HttpHeaderWriterWebFilter; import org.springframework.security.web.server.header.HttpHeaderWriterWebFilter;
import org.springframework.security.web.server.header.PermissionsPolicyServerHttpHeadersWriter; import org.springframework.security.web.server.header.PermissionsPolicyServerHttpHeadersWriter;
@ -2380,10 +2386,17 @@ public class ServerHttpSecurity {
private ReferrerPolicyServerHttpHeadersWriter referrerPolicy = new ReferrerPolicyServerHttpHeadersWriter(); private ReferrerPolicyServerHttpHeadersWriter referrerPolicy = new ReferrerPolicyServerHttpHeadersWriter();
private CrossOriginOpenerPolicyServerHttpHeadersWriter crossOriginOpenerPolicy = new CrossOriginOpenerPolicyServerHttpHeadersWriter();
private CrossOriginEmbedderPolicyServerHttpHeadersWriter crossOriginEmbedderPolicy = new CrossOriginEmbedderPolicyServerHttpHeadersWriter();
private CrossOriginResourcePolicyServerHttpHeadersWriter crossOriginResourcePolicy = new CrossOriginResourcePolicyServerHttpHeadersWriter();
private HeaderSpec() { private HeaderSpec() {
this.writers = new ArrayList<>(Arrays.asList(this.cacheControl, this.contentTypeOptions, this.hsts, this.writers = new ArrayList<>(Arrays.asList(this.cacheControl, this.contentTypeOptions, this.hsts,
this.frameOptions, this.xss, this.featurePolicy, this.permissionsPolicy, this.contentSecurityPolicy, this.frameOptions, this.xss, this.featurePolicy, this.permissionsPolicy, this.contentSecurityPolicy,
this.referrerPolicy)); this.referrerPolicy, this.crossOriginOpenerPolicy, this.crossOriginEmbedderPolicy,
this.crossOriginResourcePolicy));
} }
/** /**
@ -2595,6 +2608,84 @@ public class ServerHttpSecurity {
return this; return this;
} }
/**
* Configures the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a> header.
* @return the {@link CrossOriginOpenerPolicySpec} to configure
* @since 5.7
* @see CrossOriginOpenerPolicyServerHttpHeadersWriter
*/
public CrossOriginOpenerPolicySpec crossOriginOpenerPolicy() {
return new CrossOriginOpenerPolicySpec();
}
/**
* Configures the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a> header.
* @return the {@link HeaderSpec} to customize
* @since 5.7
* @see CrossOriginOpenerPolicyServerHttpHeadersWriter
*/
public HeaderSpec crossOriginOpenerPolicy(
Customizer<CrossOriginOpenerPolicySpec> crossOriginOpenerPolicyCustomizer) {
crossOriginOpenerPolicyCustomizer.customize(new CrossOriginOpenerPolicySpec());
return this;
}
/**
* Configures the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a> header.
* @return the {@link CrossOriginEmbedderPolicySpec} to configure
* @since 5.7
* @see CrossOriginEmbedderPolicyServerHttpHeadersWriter
*/
public CrossOriginEmbedderPolicySpec crossOriginEmbedderPolicy() {
return new CrossOriginEmbedderPolicySpec();
}
/**
* Configures the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a> header.
* @return the {@link HeaderSpec} to customize
* @since 5.7
* @see CrossOriginEmbedderPolicyServerHttpHeadersWriter
*/
public HeaderSpec crossOriginEmbedderPolicy(
Customizer<CrossOriginEmbedderPolicySpec> crossOriginEmbedderPolicyCustomizer) {
crossOriginEmbedderPolicyCustomizer.customize(new CrossOriginEmbedderPolicySpec());
return this;
}
/**
* Configures the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a> header.
* @return the {@link CrossOriginResourcePolicySpec} to configure
* @since 5.7
* @see CrossOriginResourcePolicyServerHttpHeadersWriter
*/
public CrossOriginResourcePolicySpec crossOriginResourcePolicy() {
return new CrossOriginResourcePolicySpec();
}
/**
* Configures the <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a> header.
* @return the {@link HeaderSpec} to customize
* @since 5.7
* @see CrossOriginResourcePolicyServerHttpHeadersWriter
*/
public HeaderSpec crossOriginResourcePolicy(
Customizer<CrossOriginResourcePolicySpec> crossOriginResourcePolicyCustomizer) {
crossOriginResourcePolicyCustomizer.customize(new CrossOriginResourcePolicySpec());
return this;
}
/** /**
* Configures cache control headers * Configures cache control headers
* *
@ -2910,6 +3001,99 @@ public class ServerHttpSecurity {
} }
/**
* Configures the Cross-Origin-Opener-Policy header
*
* @since 5.7
*/
public final class CrossOriginOpenerPolicySpec {
private CrossOriginOpenerPolicySpec() {
}
/**
* Sets the value to be used in the `Cross-Origin-Opener-Policy` header
* @param openerPolicy a opener policy
* @return the {@link CrossOriginOpenerPolicySpec} to continue configuring
*/
public CrossOriginOpenerPolicySpec policy(CrossOriginOpenerPolicy openerPolicy) {
HeaderSpec.this.crossOriginOpenerPolicy.setPolicy(openerPolicy);
return this;
}
/**
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
* @return the {@link HeaderSpec} to continue configuring
*/
public HeaderSpec and() {
return HeaderSpec.this;
}
}
/**
* Configures the Cross-Origin-Embedder-Policy header
*
* @since 5.7
*/
public final class CrossOriginEmbedderPolicySpec {
private CrossOriginEmbedderPolicySpec() {
}
/**
* Sets the value to be used in the `Cross-Origin-Embedder-Policy` header
* @param embedderPolicy a opener policy
* @return the {@link CrossOriginEmbedderPolicySpec} to continue configuring
*/
public CrossOriginEmbedderPolicySpec policy(CrossOriginEmbedderPolicy embedderPolicy) {
HeaderSpec.this.crossOriginEmbedderPolicy.setPolicy(embedderPolicy);
return this;
}
/**
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
* @return the {@link HeaderSpec} to continue configuring
*/
public HeaderSpec and() {
return HeaderSpec.this;
}
}
/**
* Configures the Cross-Origin-Resource-Policy header
*
* @since 5.7
*/
public final class CrossOriginResourcePolicySpec {
private CrossOriginResourcePolicySpec() {
}
/**
* Sets the value to be used in the `Cross-Origin-Resource-Policy` header
* @param resourcePolicy a opener policy
* @return the {@link CrossOriginResourcePolicySpec} to continue configuring
*/
public CrossOriginResourcePolicySpec policy(CrossOriginResourcePolicy resourcePolicy) {
HeaderSpec.this.crossOriginResourcePolicy.setPolicy(resourcePolicy);
return this;
}
/**
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
* @return the {@link HeaderSpec} to continue configuring
*/
public HeaderSpec and() {
return HeaderSpec.this;
}
}
} }
/** /**

View File

@ -0,0 +1,42 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.config.web.server
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter
/**
* A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Embedder-Policy header using
* idiomatic Kotlin code.
*
* @author Marcus Da Coregio
* @since 5.7
* @property policy the policy to be used in the response header.
*/
@ServerSecurityMarker
class ServerCrossOriginEmbedderPolicyDsl {
var policy: CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy? = null
internal fun get(): (ServerHttpSecurity.HeaderSpec.CrossOriginEmbedderPolicySpec) -> Unit {
return { crossOriginEmbedderPolicy ->
policy?.also {
crossOriginEmbedderPolicy.policy(policy)
}
}
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.config.web.server
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter
/**
* A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Opener-Policy header using
* idiomatic Kotlin code.
*
* @author Marcus Da Coregio
* @since 5.7
* @property policy the policy to be used in the response header.
*/
@ServerSecurityMarker
class ServerCrossOriginOpenerPolicyDsl {
var policy: CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy? = null
internal fun get(): (ServerHttpSecurity.HeaderSpec.CrossOriginOpenerPolicySpec) -> Unit {
return { crossOriginOpenerPolicy ->
policy?.also {
crossOriginOpenerPolicy.policy(policy)
}
}
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.config.web.server
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter
/**
* A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Resource-Policy header using
* idiomatic Kotlin code.
*
* @author Marcus Da Coregio
* @since 5.7
* @property policy the policy to be used in the response header.
*/
@ServerSecurityMarker
class ServerCrossOriginResourcePolicyDsl {
var policy: CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy? = null
internal fun get(): (ServerHttpSecurity.HeaderSpec.CrossOriginResourcePolicySpec) -> Unit {
return { crossOriginResourcePolicy ->
policy?.also {
crossOriginResourcePolicy.policy(policy)
}
}
}
}

View File

@ -16,7 +16,12 @@
package org.springframework.security.config.web.server package org.springframework.security.config.web.server
import org.springframework.security.web.server.header.* import org.springframework.security.web.server.header.CacheControlServerHttpHeadersWriter
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter
import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter
import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter
import org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter
import org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter
/** /**
* A Kotlin DSL to configure [ServerHttpSecurity] headers using idiomatic Kotlin code. * A Kotlin DSL to configure [ServerHttpSecurity] headers using idiomatic Kotlin code.
@ -35,6 +40,9 @@ class ServerHeadersDsl {
private var referrerPolicy: ((ServerHttpSecurity.HeaderSpec.ReferrerPolicySpec) -> Unit)? = null private var referrerPolicy: ((ServerHttpSecurity.HeaderSpec.ReferrerPolicySpec) -> Unit)? = null
private var featurePolicyDirectives: String? = null private var featurePolicyDirectives: String? = null
private var permissionsPolicy: ((ServerHttpSecurity.HeaderSpec.PermissionsPolicySpec) -> Unit)? = null private var permissionsPolicy: ((ServerHttpSecurity.HeaderSpec.PermissionsPolicySpec) -> Unit)? = null
private var crossOriginOpenerPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginOpenerPolicySpec) -> Unit)? = null
private var crossOriginEmbedderPolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginEmbedderPolicySpec) -> Unit)? = null
private var crossOriginResourcePolicy: ((ServerHttpSecurity.HeaderSpec.CrossOriginResourcePolicySpec) -> Unit)? = null
private var disabled = false private var disabled = false
@ -157,6 +165,39 @@ class ServerHeadersDsl {
this.permissionsPolicy = ServerPermissionsPolicyDsl().apply(permissionsPolicyConfig).get() this.permissionsPolicy = ServerPermissionsPolicyDsl().apply(permissionsPolicyConfig).get()
} }
/**
* Allows configuration for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a> header.
*
* @since 5.7
* @param crossOriginOpenerPolicyConfig the customization to apply to the header
*/
fun crossOriginOpenerPolicy(crossOriginOpenerPolicyConfig: ServerCrossOriginOpenerPolicyDsl.() -> Unit) {
this.crossOriginOpenerPolicy = ServerCrossOriginOpenerPolicyDsl().apply(crossOriginOpenerPolicyConfig).get()
}
/**
* Allows configuration for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a> header.
*
* @since 5.7
* @param crossOriginEmbedderPolicyConfig the customization to apply to the header
*/
fun crossOriginEmbedderPolicy(crossOriginEmbedderPolicyConfig: ServerCrossOriginEmbedderPolicyDsl.() -> Unit) {
this.crossOriginEmbedderPolicy = ServerCrossOriginEmbedderPolicyDsl().apply(crossOriginEmbedderPolicyConfig).get()
}
/**
* Allows configuration for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a> header.
*
* @since 5.7
* @param crossOriginResourcePolicyConfig the customization to apply to the header
*/
fun crossOriginResourcePolicy(crossOriginResourcePolicyConfig: ServerCrossOriginResourcePolicyDsl.() -> Unit) {
this.crossOriginResourcePolicy = ServerCrossOriginResourcePolicyDsl().apply(crossOriginResourcePolicyConfig).get()
}
/** /**
* Disables HTTP response headers. * Disables HTTP response headers.
*/ */
@ -194,6 +235,15 @@ class ServerHeadersDsl {
referrerPolicy?.also { referrerPolicy?.also {
headers.referrerPolicy(referrerPolicy) headers.referrerPolicy(referrerPolicy)
} }
crossOriginOpenerPolicy?.also {
headers.crossOriginOpenerPolicy(crossOriginOpenerPolicy)
}
crossOriginEmbedderPolicy?.also {
headers.crossOriginEmbedderPolicy(crossOriginEmbedderPolicy)
}
crossOriginResourcePolicy?.also {
headers.crossOriginResourcePolicy(crossOriginResourcePolicy)
}
if (disabled) { if (disabled) {
headers.disable() headers.disable()
} }

View File

@ -42,6 +42,9 @@ class HeadersDsl {
private var referrerPolicy: ((HeadersConfigurer<HttpSecurity>.ReferrerPolicyConfig) -> Unit)? = null private var referrerPolicy: ((HeadersConfigurer<HttpSecurity>.ReferrerPolicyConfig) -> Unit)? = null
private var featurePolicyDirectives: String? = null private var featurePolicyDirectives: String? = null
private var permissionsPolicy: ((HeadersConfigurer<HttpSecurity>.PermissionsPolicyConfig) -> Unit)? = null private var permissionsPolicy: ((HeadersConfigurer<HttpSecurity>.PermissionsPolicyConfig) -> Unit)? = null
private var crossOriginOpenerPolicy: ((HeadersConfigurer<HttpSecurity>.CrossOriginOpenerPolicyConfig) -> Unit)? = null
private var crossOriginEmbedderPolicy: ((HeadersConfigurer<HttpSecurity>.CrossOriginEmbedderPolicyConfig) -> Unit)? = null
private var crossOriginResourcePolicy: ((HeadersConfigurer<HttpSecurity>.CrossOriginResourcePolicyConfig) -> Unit)? = null
private var disabled = false private var disabled = false
private var headerWriters = mutableListOf<HeaderWriter>() private var headerWriters = mutableListOf<HeaderWriter>()
@ -181,6 +184,54 @@ class HeadersDsl {
this.permissionsPolicy = PermissionsPolicyDsl().apply(permissionsPolicyConfig).get() this.permissionsPolicy = PermissionsPolicyDsl().apply(permissionsPolicyConfig).get()
} }
/**
* Allows configuration for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a> header.
*
* <p>
* Calling this method automatically enables (includes) the Cross-Origin-Opener-Policy
* header in the response using the supplied policy.
* <p>
*
* @since 5.7
* @param crossOriginOpenerPolicyConfig the customization to apply to the header
*/
fun crossOriginOpenerPolicy(crossOriginOpenerPolicyConfig: CrossOriginOpenerPolicyDsl.() -> Unit) {
this.crossOriginOpenerPolicy = CrossOriginOpenerPolicyDsl().apply(crossOriginOpenerPolicyConfig).get()
}
/**
* Allows configuration for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a> header.
*
* <p>
* Calling this method automatically enables (includes) the Cross-Origin-Embedder-Policy
* header in the response using the supplied policy.
* <p>
*
* @since 5.7
* @param crossOriginEmbedderPolicyConfig the customization to apply to the header
*/
fun crossOriginEmbedderPolicy(crossOriginEmbedderPolicyConfig: CrossOriginEmbedderPolicyDsl.() -> Unit) {
this.crossOriginEmbedderPolicy = CrossOriginEmbedderPolicyDsl().apply(crossOriginEmbedderPolicyConfig).get()
}
/**
* Configures the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a> header.
*
* <p>
* Calling this method automatically enables (includes) the Cross-Origin-Resource-Policy
* header in the response using the supplied policy.
* <p>
*
* @since 5.7
* @param crossOriginResourcePolicyConfig the customization to apply to the header
*/
fun crossOriginResourcePolicy(crossOriginResourcePolicyConfig: CrossOriginResourcePolicyDsl.() -> Unit) {
this.crossOriginResourcePolicy = CrossOriginResourcePolicyDsl().apply(crossOriginResourcePolicyConfig).get()
}
/** /**
* Adds a [HeaderWriter] instance. * Adds a [HeaderWriter] instance.
* *
@ -238,6 +289,15 @@ class HeadersDsl {
permissionsPolicy?.also { permissionsPolicy?.also {
headers.permissionsPolicy(permissionsPolicy) headers.permissionsPolicy(permissionsPolicy)
} }
crossOriginOpenerPolicy?.also {
headers.crossOriginOpenerPolicy(crossOriginOpenerPolicy)
}
crossOriginEmbedderPolicy?.also {
headers.crossOriginEmbedderPolicy(crossOriginEmbedderPolicy)
}
crossOriginResourcePolicy?.also {
headers.crossOriginResourcePolicy(crossOriginResourcePolicy)
}
headerWriters.forEach { headerWriter -> headerWriters.forEach { headerWriter ->
headers.addHeaderWriter(headerWriter) headers.addHeaderWriter(headerWriter)
} }

View File

@ -0,0 +1,43 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.config.web.servlet.headers
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter
/**
* A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Embedder-Policy header using
* idiomatic Kotlin code.
*
* @author Marcus Da Coregio
* @since 5.7
* @property policy the policy to be used in the response header.
*/
@HeadersSecurityMarker
class CrossOriginEmbedderPolicyDsl {
var policy: CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy? = null
internal fun get(): (HeadersConfigurer<HttpSecurity>.CrossOriginEmbedderPolicyConfig) -> Unit {
return { crossOriginEmbedderPolicy ->
policy?.also {
crossOriginEmbedderPolicy.policy(policy)
}
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.config.web.servlet.headers
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter
/**
* A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Opener-Policy header using
* idiomatic Kotlin code.
*
* @author Marcus Da Coregio
* @since 5.7
* @property policy the policy to be used in the response header.
*/
@HeadersSecurityMarker
class CrossOriginOpenerPolicyDsl {
var policy: CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy? = null
internal fun get(): (HeadersConfigurer<HttpSecurity>.CrossOriginOpenerPolicyConfig) -> Unit {
return { crossOriginOpenerPolicy ->
policy?.also {
crossOriginOpenerPolicy.policy(policy)
}
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.config.web.servlet.headers
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer
import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter
/**
* A Kotlin DSL to configure the [HttpSecurity] Cross-Origin-Resource-Policy header using
* idiomatic Kotlin code.
*
* @author Marcus Da Coregio
* @since 5.7
* @property policy the policy to be used in the response header.
*/
@HeadersSecurityMarker
class CrossOriginResourcePolicyDsl {
var policy: CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy? = null
internal fun get(): (HeadersConfigurer<HttpSecurity>.CrossOriginResourcePolicyConfig) -> Unit {
return { crossOriginResourcePolicy ->
policy?.also {
crossOriginResourcePolicy.policy(policy)
}
}
}
}

View File

@ -943,7 +943,7 @@ csrf-options.attlist &=
headers = headers =
## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers. ## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.
element headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & header*)} element headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & cross-origin-opener-policy? & cross-origin-embedder-policy? & cross-origin-resource-policy? & header*)}
headers-options.attlist &= headers-options.attlist &=
## Specifies if the default headers should be disabled. Default false. ## Specifies if the default headers should be disabled. Default false.
attribute defaults-disabled {xsd:token}? attribute defaults-disabled {xsd:token}?
@ -1092,6 +1092,27 @@ content-type-options.attlist &=
## If disabled, the X-Content-Type-Options header will not be included. Default false. ## If disabled, the X-Content-Type-Options header will not be included. Default false.
attribute disabled {xsd:boolean}? attribute disabled {xsd:boolean}?
cross-origin-opener-policy =
## Adds support for Cross-Origin-Opener-Policy header
element cross-origin-opener-policy {cross-origin-opener-policy-options.attlist,empty}
cross-origin-opener-policy-options.attlist &=
## The policies for the Cross-Origin-Opener-Policy header.
attribute policy {"unsafe-none","same-origin","same-origin-allow-popups"}?
cross-origin-embedder-policy =
## Adds support for Cross-Origin-Embedder-Policy header
element cross-origin-embedder-policy {cross-origin-embedder-policy-options.attlist,empty}
cross-origin-embedder-policy-options.attlist &=
## The policies for the Cross-Origin-Embedder-Policy header.
attribute policy {"unsafe-none","require-corp"}?
cross-origin-resource-policy =
## Adds support for Cross-Origin-Resource-Policy header
element cross-origin-resource-policy {cross-origin-resource-policy-options.attlist,empty}
cross-origin-resource-policy-options.attlist &=
## The policies for the Cross-Origin-Resource-Policy header.
attribute policy {"cross-origin","same-origin","same-site"}?
header= header=
## Add additional headers to the response. ## Add additional headers to the response.
element header {header.attlist} element header {header.attlist}

View File

@ -2768,6 +2768,9 @@
<xs:element ref="security:referrer-policy"/> <xs:element ref="security:referrer-policy"/>
<xs:element ref="security:feature-policy"/> <xs:element ref="security:feature-policy"/>
<xs:element ref="security:permissions-policy"/> <xs:element ref="security:permissions-policy"/>
<xs:element ref="security:cross-origin-opener-policy"/>
<xs:element ref="security:cross-origin-embedder-policy"/>
<xs:element ref="security:cross-origin-resource-policy"/>
<xs:element ref="security:header"/> <xs:element ref="security:header"/>
</xs:choice> </xs:choice>
<xs:attributeGroup ref="security:headers-options.attlist"/> <xs:attributeGroup ref="security:headers-options.attlist"/>
@ -3151,6 +3154,77 @@
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
</xs:attributeGroup> </xs:attributeGroup>
<xs:element name="cross-origin-opener-policy">
<xs:annotation>
<xs:documentation>Adds support for Cross-Origin-Opener-Policy header
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="security:cross-origin-opener-policy-options.attlist"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="cross-origin-opener-policy-options.attlist">
<xs:attribute name="policy">
<xs:annotation>
<xs:documentation>The policies for the Cross-Origin-Opener-Policy header.
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="unsafe-none"/>
<xs:enumeration value="same-origin"/>
<xs:enumeration value="same-origin-allow-popups"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="cross-origin-embedder-policy">
<xs:annotation>
<xs:documentation>Adds support for Cross-Origin-Embedder-Policy header
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="security:cross-origin-embedder-policy-options.attlist"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="cross-origin-embedder-policy-options.attlist">
<xs:attribute name="policy">
<xs:annotation>
<xs:documentation>The policies for the Cross-Origin-Embedder-Policy header.
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="unsafe-none"/>
<xs:enumeration value="require-corp"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="cross-origin-resource-policy">
<xs:annotation>
<xs:documentation>Adds support for Cross-Origin-Resource-Policy header
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="security:cross-origin-resource-policy-options.attlist"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="cross-origin-resource-policy-options.attlist">
<xs:attribute name="policy">
<xs:annotation>
<xs:documentation>The policies for the Cross-Origin-Resource-Policy header.
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="cross-origin"/>
<xs:enumeration value="same-origin"/>
<xs:enumeration value="same-site"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="header"> <xs:element name="header">
<xs:annotation> <xs:annotation>
<xs:documentation>Add additional headers to the response. <xs:documentation>Add additional headers to the response.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,11 +26,16 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;
import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy; import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode; import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
@ -52,6 +57,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Eddú Meléndez * @author Eddú Meléndez
* @author Vedran Pavic * @author Vedran Pavic
* @author Eleftheria Stein * @author Eleftheria Stein
* @author Marcus Da Coregio
*/ */
@ExtendWith(SpringTestContextExtension.class) @ExtendWith(SpringTestContextExtension.class)
public class HeadersConfigurerTests { public class HeadersConfigurerTests {
@ -514,6 +520,30 @@ public class HeadersConfigurerTests {
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.STRICT_TRANSPORT_SECURITY); assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.STRICT_TRANSPORT_SECURITY);
} }
@Test
public void getWhenCustomCrossOriginPoliciesInLambdaThenCrossOriginPolicyHeadersWithCustomValuesInResponse()
throws Exception {
this.spring.register(CrossOriginCustomPoliciesInLambdaConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/"))
.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY, "same-origin"))
.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, "require-corp"))
.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY, "same-origin")).andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY,
HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY);
}
@Test
public void getWhenCustomCrossOriginPoliciesThenCrossOriginPolicyHeadersWithCustomValuesInResponse()
throws Exception {
this.spring.register(CrossOriginCustomPoliciesConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/"))
.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY, "same-origin"))
.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, "require-corp"))
.andExpect(header().string(HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY, "same-origin")).andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactly(HttpHeaders.CROSS_ORIGIN_OPENER_POLICY,
HttpHeaders.CROSS_ORIGIN_EMBEDDER_POLICY, HttpHeaders.CROSS_ORIGIN_RESOURCE_POLICY);
}
@EnableWebSecurity @EnableWebSecurity
static class HeadersConfig extends WebSecurityConfigurerAdapter { static class HeadersConfig extends WebSecurityConfigurerAdapter {
@ -1146,4 +1176,50 @@ public class HeadersConfigurerTests {
} }
@EnableWebSecurity
static class CrossOriginCustomPoliciesInLambdaConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http.headers((headers) -> headers
.defaultsDisabled()
.crossOriginOpenerPolicy((policy) -> policy
.policy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN)
)
.crossOriginEmbedderPolicy((policy) -> policy
.policy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)
)
.crossOriginResourcePolicy((policy) -> policy
.policy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_ORIGIN)
)
);
// @formatter:on
return http.build();
}
}
@EnableWebSecurity
static class CrossOriginCustomPoliciesConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http.headers()
.defaultsDisabled()
.crossOriginOpenerPolicy()
.policy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN)
.and()
.crossOriginEmbedderPolicy()
.policy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)
.and()
.crossOriginResourcePolicy()
.policy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_ORIGIN);
// @formatter:on
return http.build();
}
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -48,6 +48,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Tim Ysewyn * @author Tim Ysewyn
* @author Josh Cummings * @author Josh Cummings
* @author Rafiullah Hamedy * @author Rafiullah Hamedy
* @author Marcus Da Coregio
*/ */
@ExtendWith(SpringTestContextExtension.class) @ExtendWith(SpringTestContextExtension.class)
public class HttpHeadersConfigTests { public class HttpHeadersConfigTests {
@ -733,6 +734,53 @@ public class HttpHeadersConfigTests {
// @formatter:on // @formatter:on
} }
@Test
public void requestWhenCrossOriginOpenerPolicyWithSameOriginAllowPopupsThenRespondsWithSameOriginAllowPopups()
throws Exception {
this.spring.configLocations(this.xml("DefaultsDisabledWithCrossOriginOpenerPolicy")).autowire();
// @formatter:off
this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(excludesDefaults())
.andExpect(header().string("Cross-Origin-Opener-Policy", "same-origin-allow-popups"));
// @formatter:on
}
@Test
public void requestWhenCrossOriginEmbedderPolicyWithRequireCorpThenRespondsWithRequireCorp() throws Exception {
this.spring.configLocations(this.xml("DefaultsDisabledWithCrossOriginEmbedderPolicy")).autowire();
// @formatter:off
this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(excludesDefaults())
.andExpect(header().string("Cross-Origin-Embedder-Policy", "require-corp"));
// @formatter:on
}
@Test
public void requestWhenCrossOriginResourcePolicyWithSameOriginThenRespondsWithSameOrigin() throws Exception {
this.spring.configLocations(this.xml("DefaultsDisabledWithCrossOriginResourcePolicy")).autowire();
// @formatter:off
this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(excludesDefaults())
.andExpect(header().string("Cross-Origin-Resource-Policy", "same-origin"));
// @formatter:on
}
@Test
public void requestWhenCrossOriginPoliciesRespondsCrossOriginPolicies() throws Exception {
this.spring.configLocations(this.xml("DefaultsDisabledWithCrossOriginPolicies")).autowire();
// @formatter:off
this.mvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(excludesDefaults())
.andExpect(header().string("Cross-Origin-Opener-Policy", "same-origin"))
.andExpect(header().string("Cross-Origin-Embedder-Policy", "require-corp"))
.andExpect(header().string("Cross-Origin-Resource-Policy", "same-origin"));
// @formatter:on
}
private static ResultMatcher includesDefaults() { private static ResultMatcher includesDefaults() {
return includes(defaultHeaders); return includes(defaultHeaders);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -30,6 +30,9 @@ import org.springframework.http.HttpHeaders;
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder; import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
import org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter; import org.springframework.security.web.server.header.ContentSecurityPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter; import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.FeaturePolicyServerHttpHeadersWriter; import org.springframework.security.web.server.header.FeaturePolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter; import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter;
import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy; import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy;
@ -48,6 +51,7 @@ import static org.springframework.security.config.Customizer.withDefaults;
* @author Rob Winch * @author Rob Winch
* @author Vedran Pavic * @author Vedran Pavic
* @author Ankur Pathak * @author Ankur Pathak
* @author Marcus Da Coregio
* @since 5.0 * @since 5.0
*/ */
public class HeaderSpecTests { public class HeaderSpecTests {
@ -406,6 +410,53 @@ public class HeaderSpecTests {
assertHeaders(); assertHeaders();
} }
@Test
public void headersWhenCrossOriginPoliciesCustomEnabledThenCustomCrossOriginPoliciesWritten() {
this.expectedHeaders.add(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY,
CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS
.getPolicy());
this.expectedHeaders.add(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY,
CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP.getPolicy());
this.expectedHeaders.add(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY,
CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN.getPolicy());
// @formatter:off
this.http.headers()
.crossOriginOpenerPolicy()
.policy(CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS)
.and()
.crossOriginEmbedderPolicy()
.policy(CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)
.and()
.crossOriginResourcePolicy()
.policy(CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN);
// @formatter:on
assertHeaders();
}
@Test
public void headersWhenCrossOriginPoliciesCustomEnabledInLambdaThenCustomCrossOriginPoliciesWritten() {
this.expectedHeaders.add(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY,
CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS
.getPolicy());
this.expectedHeaders.add(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY,
CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP.getPolicy());
this.expectedHeaders.add(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY,
CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN.getPolicy());
// @formatter:off
this.http.headers()
.crossOriginOpenerPolicy((policy) -> policy
.policy(CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS)
)
.crossOriginEmbedderPolicy((policy) -> policy
.policy(CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP)
)
.crossOriginResourcePolicy((policy) -> policy
.policy(CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN)
);
// @formatter:on
assertHeaders();
}
private void expectHeaderNamesNotPresent(String... headerNames) { private void expectHeaderNamesNotPresent(String... headerNames) {
for (String headerName : headerNames) { for (String headerName : headerNames) {
this.expectedHeaders.remove(headerName); this.expectedHeaders.remove(headerName);

View File

@ -28,6 +28,9 @@ import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter
import org.springframework.security.web.server.SecurityWebFilterChain import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter
import org.springframework.security.web.server.header.CrossOriginEmbedderPolicyServerHttpHeadersWriter
import org.springframework.security.web.server.header.CrossOriginOpenerPolicyServerHttpHeadersWriter
import org.springframework.security.web.server.header.CrossOriginResourcePolicyServerHttpHeadersWriter
import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter import org.springframework.security.web.server.header.StrictTransportSecurityServerHttpHeadersWriter
import org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter import org.springframework.security.web.server.header.XFrameOptionsServerHttpHeadersWriter
import org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter import org.springframework.security.web.server.header.XXssProtectionServerHttpHeadersWriter
@ -133,4 +136,60 @@ class ServerHeadersDslTests {
} }
} }
} }
@Test
fun `request when no cross-origin policies configured then does not write cross-origin policies headers in response`() {
this.spring.register(CrossOriginPoliciesConfig::class.java).autowire()
this.client.get()
.uri("/")
.exchange()
.expectHeader().doesNotExist("Cross-Origin-Opener-Policy")
.expectHeader().doesNotExist("Cross-Origin-Embedder-Policy")
.expectHeader().doesNotExist("Cross-Origin-Resource-Policy")
}
@EnableWebFluxSecurity
@EnableWebFlux
open class CrossOriginPoliciesConfig {
@Bean
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
headers { }
}
}
}
@Test
fun `request when cross-origin custom policies configured then cross-origin custom policies headers in response`() {
this.spring.register(CrossOriginPoliciesCustomConfig::class.java).autowire()
this.client.get()
.uri("/")
.exchange()
.expectHeader().valueEquals("Cross-Origin-Opener-Policy", "same-origin")
.expectHeader().valueEquals("Cross-Origin-Embedder-Policy", "require-corp")
.expectHeader().valueEquals("Cross-Origin-Resource-Policy", "same-origin")
}
@EnableWebFluxSecurity
@EnableWebFlux
open class CrossOriginPoliciesCustomConfig {
@Bean
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
headers {
crossOriginOpenerPolicy {
policy = CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN
}
crossOriginEmbedderPolicy {
policy = CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP
}
crossOriginResourcePolicy {
policy = CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN
}
}
}
}
}
} }

View File

@ -19,13 +19,13 @@ package org.springframework.security.config.web.servlet
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.http.HttpHeaders import org.springframework.http.HttpHeaders
import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.config.test.SpringTestContext import org.springframework.security.config.test.SpringTestContext
import org.springframework.security.config.test.SpringTestContextExtension import org.springframework.security.config.test.SpringTestContextExtension
import org.springframework.security.config.web.servlet.headers.PermissionsPolicyDsl
import org.springframework.security.web.header.writers.StaticHeadersWriter import org.springframework.security.web.header.writers.StaticHeadersWriter
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2021 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
~
~ https://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
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true">
<headers defaults-disabled="true">
<cross-origin-embedder-policy policy="require-corp"/>
</headers>
</http>
<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-2021 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
~
~ https://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
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true">
<headers defaults-disabled="true">
<cross-origin-opener-policy policy="same-origin-allow-popups"/>
</headers>
</http>
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
<b:import resource="userservice.xml"/>
</b:beans>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2021 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
~
~ https://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
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true">
<headers defaults-disabled="true">
<cross-origin-opener-policy policy="same-origin"/>
<cross-origin-embedder-policy policy="require-corp"/>
<cross-origin-resource-policy policy="same-origin"/>
</headers>
</http>
<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-2021 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
~
~ https://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
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true">
<headers defaults-disabled="true">
<cross-origin-resource-policy policy="same-origin"/>
</headers>
</http>
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
<b:import resource="userservice.xml"/>
</b:beans>

View File

@ -378,6 +378,26 @@ Clear-Site-Data: "cache", "cookies", "storage", "executionContexts"
This is a nice clean-up action to perform on logout. This is a nice clean-up action to perform on logout.
[[headers-cross-origin-policies]]
== Cross-Origin Policies
[NOTE]
====
Refer to the relevant sections to see how to configure for both <<servlet-headers-cross-origin-policies,servlet>> and <<webflux-headers-cross-origin-policies,webflux>> based applications.
====
Spring Security provides support for some important Cross-Origin Policies headers.
Those headers are:
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy[`Cross-Origin-Opener-Policy`]
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy[`Cross-Origin-Embedder-Policy`]
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy[`Cross-Origin-Resource-Policy`]
`Cross-Origin-Opener-Policy` (COOP) allows a top-level document to break the association between its window and any others in the browsing context group (e.g., between a popup and its opener), preventing any direct DOM access between them.
Enabling `Cross-Origin-Embedder-Policy` (COEP) prevents a document from loading any non-same-origin resources which don't explicitly grant the document permission to be loaded.
The `Cross-Origin-Resource-Policy` (CORP) header allows you to control the set of origins that are empowered to include a resource. It is a robust defense against attacks like https://meltdownattack.com[Spectre], as it allows browsers to block a given response before it enters an attacker's process.
[[headers-custom]] [[headers-custom]]
== Custom Headers == Custom Headers

View File

@ -578,3 +578,65 @@ fun webFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
} }
---- ----
==== ====
[[webflux-headers-cross-origin-policies]]
== Cross-Origin Policies
Spring Security provides built-in support for adding some Cross-Origin policies headers, those headers are:
[source]
----
Cross-Origin-Opener-Policy
Cross-Origin-Embedder-Policy
Cross-Origin-Resource-Policy
----
Spring Security does not add <<headers-cross-origin-policies,Cross-Origin Policies>> headers by default.
The headers can be added with the following configuration:
.Cross-Origin Policies
====
.Java
[source,java,role="primary"]
----
@EnableWebFluxSecurity
@EnableWebFlux
public class WebSecurityConfig {
@Bean
SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) {
http.headers((headers) -> headers
.crossOriginOpenerPolicy(CrossOriginOpenerPolicy.SAME_ORIGIN)
.crossOriginEmbedderPolicy(CrossOriginEmbedderPolicy.REQUIRE_CORP)
.crossOriginResourcePolicy(CrossOriginResourcePolicy.SAME_ORIGIN));
return http.build();
}
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableWebFluxSecurity
@EnableWebFlux
open class CrossOriginPoliciesCustomConfig {
@Bean
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
headers {
crossOriginOpenerPolicy(CrossOriginOpenerPolicy.SAME_ORIGIN)
crossOriginEmbedderPolicy(CrossOriginEmbedderPolicy.REQUIRE_CORP)
crossOriginResourcePolicy(CrossOriginResourcePolicy.SAME_ORIGIN)
}
}
}
}
----
====
This configuration will write the headers with the values provided:
[source]
----
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-origin
----

View File

@ -238,6 +238,9 @@ This allows HTTPS websites to resist impersonation by attackers using mis-issued
https://www.w3.org/TR/CSP2/[Content Security Policy (CSP)] is a mechanism that web applications can leverage to mitigate content injection vulnerabilities, such as cross-site scripting (XSS). https://www.w3.org/TR/CSP2/[Content Security Policy (CSP)] is a mechanism that web applications can leverage to mitigate content injection vulnerabilities, such as cross-site scripting (XSS).
** `Referrer-Policy` - Can be set using the <<nsa-referrer-policy,referrer-policy>> element, https://www.w3.org/TR/referrer-policy/[Referrer-Policy] is a mechanism that web applications can leverage to manage the referrer field, which contains the last page the user was on. ** `Referrer-Policy` - Can be set using the <<nsa-referrer-policy,referrer-policy>> element, https://www.w3.org/TR/referrer-policy/[Referrer-Policy] is a mechanism that web applications can leverage to manage the referrer field, which contains the last page the user was on.
** `Feature-Policy` - Can be set using the <<nsa-feature-policy,feature-policy>> element, https://wicg.github.io/feature-policy/[Feature-Policy] is a mechanism that allows web developers to selectively enable, disable, and modify the behavior of certain APIs and web features in the browser. ** `Feature-Policy` - Can be set using the <<nsa-feature-policy,feature-policy>> element, https://wicg.github.io/feature-policy/[Feature-Policy] is a mechanism that allows web developers to selectively enable, disable, and modify the behavior of certain APIs and web features in the browser.
** `Cross-Origin-Opener-Policy` - Can be set using the <<nsa-cross-origin-opener-policy,cross-origin-opener-policy>> element, https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy[Cross-Origin-Opener-Policy] is a mechanism that allows you to ensure a top-level document does not share a browsing context group with cross-origin documents.
** `Cross-Origin-Embedder-Policy` - Can be set using the <<nsa-cross-origin-embedder-policy,cross-origin-embedder-policy>> element, https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy[Cross-Origin-Embedder-Policy] is a mechanism that prevents a document from loading any cross-origin resources that don't explicitly grant the document permission.
** `Cross-Origin-Resource-Policy` - Can be set using the <<nsa-cross-origin-resource-policy,cross-origin-resource-policy>> element, https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy[Cross-Origin-Resource-Policy] is a mechanism that conveys a desire that the browser blocks no-cors cross-origin/cross-site requests to the given resource.
[[nsa-headers-attributes]] [[nsa-headers-attributes]]
=== <headers> Attributes === <headers> Attributes
@ -269,6 +272,9 @@ The default is false (the headers are enabled).
* <<nsa-cache-control,cache-control>> * <<nsa-cache-control,cache-control>>
* <<nsa-content-security-policy,content-security-policy>> * <<nsa-content-security-policy,content-security-policy>>
* <<nsa-content-type-options,content-type-options>> * <<nsa-content-type-options,content-type-options>>
* <<nsa-cross-origin-embedder-policy,cross-origin-embedder-policy>>
* <<nsa-cross-origin-opener-policy,cross-origin-opener-policy>>
* <<nsa-cross-origin-resource-policy,cross-origin-resource-policy>>
* <<nsa-feature-policy,feature-policy>> * <<nsa-feature-policy,feature-policy>>
* <<nsa-frame-options,frame-options>> * <<nsa-frame-options,frame-options>>
* <<nsa-header,header>> * <<nsa-header,header>>
@ -584,6 +590,66 @@ Default false.
[[nsa-cross-origin-embedder-policy]]
==== <cross-origin-embedder-policy>
When enabled adds the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy[Cross-Origin-Embedder-Policy] header to the response.
[[nsa-cross-origin-embedder-policy-attributes]]
===== <cross-origin-embedder-policy> Attributes
[[nsa-cross-origin-embedder-policy-policy]]
* **policy**
The policy for the `Cross-Origin-Embedder-Policy` header.
[[nsa-cross-origin-embedder-policy-parents]]
===== Parent Elements of <cross-origin-embedder-policy>
* <<nsa-headers,headers>>
[[nsa-cross-origin-opener-policy]]
==== <cross-origin-opener-policy>
When enabled adds the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy[Cross-Origin-Opener-Policy] header to the response.
[[nsa-cross-origin-opener-policy-attributes]]
===== <cross-origin-opener-policy> Attributes
[[nsa-cross-origin-opener-policy-policy]]
* **policy**
The policy for the `Cross-Origin-Opener-Policy` header.
[[nsa-cross-origin-opener-policy-parents]]
===== Parent Elements of <cross-origin-opener-policy>
* <<nsa-headers,headers>>
[[nsa-cross-origin-resource-policy]]
==== <cross-origin-resource-policy>
When enabled adds the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy[Cross-Origin-Resource-Policy] header to the response.
[[nsa-cross-origin-resource-policy-attributes]]
===== <cross-origin-resource-policy> Attributes
[[nsa-cross-origin-resource-policy-policy]]
* **policy**
The policy for the `Cross-Origin-Resource-Policy` header.
[[nsa-cross-origin-resource-policy-parents]]
===== Parent Elements of <cross-origin-resource-policy>
* <<nsa-headers,headers>>
[[nsa-header]] [[nsa-header]]
== <header> == <header>
Add additional headers to the response, both the name and value need to be specified. Add additional headers to the response, both the name and value need to be specified.

View File

@ -938,6 +938,67 @@ class SecurityConfig : WebSecurityConfigurerAdapter() {
---- ----
==== ====
[[servlet-headers-cross-origin-policies]]
== Cross-Origin Policies
Spring Security provides built-in support for adding some Cross-Origin policies headers, those headers are:
[source]
----
Cross-Origin-Opener-Policy
Cross-Origin-Embedder-Policy
Cross-Origin-Resource-Policy
----
Spring Security does not add <<headers-cross-origin-policies,Cross-Origin Policies>> headers by default.
The headers can be added with the following configuration:
.Cross-Origin Policies
====
.Java
[source,java,role="primary"]
----
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) {
http.headers((headers) -> headers
.crossOriginOpenerPolicy(CrossOriginOpenerPolicy.SAME_ORIGIN)
.crossOriginEmbedderPolicy(CrossOriginEmbedderPolicy.REQUIRE_CORP)
.crossOriginResourcePolicy(CrossOriginResourcePolicy.SAME_ORIGIN)));
return http.build();
}
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableWebSecurity
open class CrossOriginPoliciesConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
headers {
crossOriginOpenerPolicy(CrossOriginOpenerPolicy.SAME_ORIGIN)
crossOriginEmbedderPolicy(CrossOriginEmbedderPolicy.REQUIRE_CORP)
crossOriginResourcePolicy(CrossOriginResourcePolicy.SAME_ORIGIN)
}
}
return http.build()
}
}
----
====
This configuration will write the headers with the values provided:
[source]
----
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-origin
----
[[servlet-headers-custom]] [[servlet-headers-custom]]
== Custom Headers == Custom Headers
Spring Security has mechanisms to make it convenient to add the more common security headers to your application. Spring Security has mechanisms to make it convenient to add the more common security headers to your application.

View File

@ -0,0 +1,84 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.web.header.writers;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.header.HeaderWriter;
import org.springframework.util.Assert;
/**
* Inserts Cross-Origin-Embedder-Policy header.
*
* @author Marcus Da Coregio
* @since 5.7
* @see <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a>
*/
public final class CrossOriginEmbedderPolicyHeaderWriter implements HeaderWriter {
private static final String EMBEDDER_POLICY = "Cross-Origin-Embedder-Policy";
private CrossOriginEmbedderPolicy policy;
/**
* Sets the {@link CrossOriginEmbedderPolicy} value to be used in the
* {@code Cross-Origin-Embedder-Policy} header
* @param embedderPolicy the {@link CrossOriginEmbedderPolicy} to use
*/
public void setPolicy(CrossOriginEmbedderPolicy embedderPolicy) {
Assert.notNull(embedderPolicy, "embedderPolicy cannot be null");
this.policy = embedderPolicy;
}
@Override
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
if (this.policy != null && !response.containsHeader(EMBEDDER_POLICY)) {
response.addHeader(EMBEDDER_POLICY, this.policy.getPolicy());
}
}
public enum CrossOriginEmbedderPolicy {
UNSAFE_NONE("unsafe-none"),
REQUIRE_CORP("require-corp");
private final String policy;
CrossOriginEmbedderPolicy(String policy) {
this.policy = policy;
}
public String getPolicy() {
return this.policy;
}
public static CrossOriginEmbedderPolicy from(String embedderPolicy) {
for (CrossOriginEmbedderPolicy policy : values()) {
if (policy.getPolicy().equals(embedderPolicy)) {
return policy;
}
}
return null;
}
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.web.header.writers;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.header.HeaderWriter;
import org.springframework.util.Assert;
/**
* Inserts the Cross-Origin-Opener-Policy header
*
* @author Marcus Da Coregio
* @since 5.7
* @see <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a>
*/
public final class CrossOriginOpenerPolicyHeaderWriter implements HeaderWriter {
private static final String OPENER_POLICY = "Cross-Origin-Opener-Policy";
private CrossOriginOpenerPolicy policy;
/**
* Sets the {@link CrossOriginOpenerPolicy} value to be used in the
* {@code Cross-Origin-Opener-Policy} header
* @param openerPolicy the {@link CrossOriginOpenerPolicy} to use
*/
public void setPolicy(CrossOriginOpenerPolicy openerPolicy) {
Assert.notNull(openerPolicy, "openerPolicy cannot be null");
this.policy = openerPolicy;
}
@Override
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
if (this.policy != null && !response.containsHeader(OPENER_POLICY)) {
response.addHeader(OPENER_POLICY, this.policy.getPolicy());
}
}
public enum CrossOriginOpenerPolicy {
UNSAFE_NONE("unsafe-none"),
SAME_ORIGIN_ALLOW_POPUPS("same-origin-allow-popups"),
SAME_ORIGIN("same-origin");
private final String policy;
CrossOriginOpenerPolicy(String policy) {
this.policy = policy;
}
public String getPolicy() {
return this.policy;
}
public static CrossOriginOpenerPolicy from(String openerPolicy) {
for (CrossOriginOpenerPolicy policy : values()) {
if (policy.getPolicy().equals(openerPolicy)) {
return policy;
}
}
return null;
}
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.web.header.writers;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.header.HeaderWriter;
import org.springframework.util.Assert;
/**
* Inserts Cross-Origin-Resource-Policy header
*
* @author Marcus Da Coregio
* @since 5.7
* @see <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a>
*/
public final class CrossOriginResourcePolicyHeaderWriter implements HeaderWriter {
private static final String RESOURCE_POLICY = "Cross-Origin-Resource-Policy";
private CrossOriginResourcePolicy policy;
/**
* Sets the {@link CrossOriginResourcePolicy} value to be used in the
* {@code Cross-Origin-Resource-Policy} header
* @param resourcePolicy the {@link CrossOriginResourcePolicy} to use
*/
public void setPolicy(CrossOriginResourcePolicy resourcePolicy) {
Assert.notNull(resourcePolicy, "resourcePolicy cannot be null");
this.policy = resourcePolicy;
}
@Override
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
if (this.policy != null && !response.containsHeader(RESOURCE_POLICY)) {
response.addHeader(RESOURCE_POLICY, this.policy.getPolicy());
}
}
public enum CrossOriginResourcePolicy {
SAME_SITE("same-site"),
SAME_ORIGIN("same-origin"),
CROSS_ORIGIN("cross-origin");
private final String policy;
CrossOriginResourcePolicy(String policy) {
this.policy = policy;
}
public String getPolicy() {
return this.policy;
}
public static CrossOriginResourcePolicy from(String resourcePolicy) {
for (CrossOriginResourcePolicy policy : values()) {
if (policy.getPolicy().equals(resourcePolicy)) {
return policy;
}
}
return null;
}
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.web.server.header;
import reactor.core.publisher.Mono;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
/**
* Inserts Cross-Origin-Embedder-Policy headers.
*
* @author Marcus Da Coregio
* @since 5.7
* @see <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
* Cross-Origin-Embedder-Policy</a>
*/
public final class CrossOriginEmbedderPolicyServerHttpHeadersWriter implements ServerHttpHeadersWriter {
public static final String EMBEDDER_POLICY = "Cross-Origin-Embedder-Policy";
private ServerHttpHeadersWriter delegate;
/**
* Sets the {@link CrossOriginEmbedderPolicy} value to be used in the
* {@code Cross-Origin-Embedder-Policy} header
* @param embedderPolicy the {@link CrossOriginEmbedderPolicy} to use
*/
public void setPolicy(CrossOriginEmbedderPolicy embedderPolicy) {
Assert.notNull(embedderPolicy, "embedderPolicy cannot be null");
this.delegate = createDelegate(embedderPolicy);
}
@Override
public Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {
return (this.delegate != null) ? this.delegate.writeHttpHeaders(exchange) : Mono.empty();
}
private static ServerHttpHeadersWriter createDelegate(CrossOriginEmbedderPolicy embedderPolicy) {
StaticServerHttpHeadersWriter.Builder builder = StaticServerHttpHeadersWriter.builder();
builder.header(EMBEDDER_POLICY, embedderPolicy.getPolicy());
return builder.build();
}
public enum CrossOriginEmbedderPolicy {
UNSAFE_NONE("unsafe-none"),
REQUIRE_CORP("require-corp");
private final String policy;
CrossOriginEmbedderPolicy(String policy) {
this.policy = policy;
}
public String getPolicy() {
return this.policy;
}
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.web.server.header;
import reactor.core.publisher.Mono;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
/**
* Inserts Cross-Origin-Opener-Policy header.
*
* @author Marcus Da Coregio
* @since 5.7
* @see <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
* Cross-Origin-Opener-Policy</a>
*/
public final class CrossOriginOpenerPolicyServerHttpHeadersWriter implements ServerHttpHeadersWriter {
public static final String OPENER_POLICY = "Cross-Origin-Opener-Policy";
private ServerHttpHeadersWriter delegate;
/**
* Sets the {@link CrossOriginOpenerPolicy} value to be used in the
* {@code Cross-Origin-Opener-Policy} header
* @param openerPolicy the {@link CrossOriginOpenerPolicy} to use
*/
public void setPolicy(CrossOriginOpenerPolicy openerPolicy) {
Assert.notNull(openerPolicy, "openerPolicy cannot be null");
this.delegate = createDelegate(openerPolicy);
}
@Override
public Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {
return (this.delegate != null) ? this.delegate.writeHttpHeaders(exchange) : Mono.empty();
}
private static ServerHttpHeadersWriter createDelegate(CrossOriginOpenerPolicy openerPolicy) {
StaticServerHttpHeadersWriter.Builder builder = StaticServerHttpHeadersWriter.builder();
builder.header(OPENER_POLICY, openerPolicy.getPolicy());
return builder.build();
}
public enum CrossOriginOpenerPolicy {
UNSAFE_NONE("unsafe-none"),
SAME_ORIGIN_ALLOW_POPUPS("same-origin-allow-popups"),
SAME_ORIGIN("same-origin");
private final String policy;
CrossOriginOpenerPolicy(String policy) {
this.policy = policy;
}
public String getPolicy() {
return this.policy;
}
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.web.server.header;
import reactor.core.publisher.Mono;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
/**
* Inserts Cross-Origin-Resource-Policy headers.
*
* @author Marcus Da Coregio
* @since 5.7
* @see <a href=
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
* Cross-Origin-Resource-Policy</a>
*/
public final class CrossOriginResourcePolicyServerHttpHeadersWriter implements ServerHttpHeadersWriter {
public static final String RESOURCE_POLICY = "Cross-Origin-Resource-Policy";
private ServerHttpHeadersWriter delegate;
/**
* Sets the {@link CrossOriginResourcePolicy} value to be used in the
* {@code Cross-Origin-Embedder-Policy} header
* @param resourcePolicy the {@link CrossOriginResourcePolicy} to use
*/
public void setPolicy(CrossOriginResourcePolicy resourcePolicy) {
Assert.notNull(resourcePolicy, "resourcePolicy cannot be null");
this.delegate = createDelegate(resourcePolicy);
}
@Override
public Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {
return (this.delegate != null) ? this.delegate.writeHttpHeaders(exchange) : Mono.empty();
}
private static ServerHttpHeadersWriter createDelegate(CrossOriginResourcePolicy resourcePolicy) {
StaticServerHttpHeadersWriter.Builder builder = StaticServerHttpHeadersWriter.builder();
builder.header(RESOURCE_POLICY, resourcePolicy.getPolicy());
return builder.build();
}
public enum CrossOriginResourcePolicy {
SAME_SITE("same-site"),
SAME_ORIGIN("same-origin"),
CROSS_ORIGIN("cross-origin");
private final String policy;
CrossOriginResourcePolicy(String policy) {
this.policy = policy;
}
public String getPolicy() {
return this.policy;
}
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.web.header.writers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
class CrossOriginEmbedderPolicyHeaderWriterTests {
private static final String EMBEDDER_HEADER_NAME = "Cross-Origin-Embedder-Policy";
private CrossOriginEmbedderPolicyHeaderWriter writer;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
@BeforeEach
void setup() {
this.writer = new CrossOriginEmbedderPolicyHeaderWriter();
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
}
@Test
void setEmbedderPolicyWhenNullEmbedderPolicyThenThrowsIllegalArgument() {
assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))
.withMessage("embedderPolicy cannot be null");
}
@Test
void writeHeadersWhenDefaultValuesThenDontWriteHeaders() {
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeaderNames()).hasSize(0);
}
@Test
void writeHeadersWhenResponseHeaderExistsThenDontOverride() {
this.response.addHeader(EMBEDDER_HEADER_NAME, "require-corp");
this.writer.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.UNSAFE_NONE);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeader(EMBEDDER_HEADER_NAME)).isEqualTo("require-corp");
}
@Test
void writeHeadersWhenSetHeaderValuesThenWrites() {
this.writer.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeader(EMBEDDER_HEADER_NAME)).isEqualTo("require-corp");
}
@Test
void writeHeadersWhenSetEmbedderPolicyThenWritesEmbedderPolicy() {
this.writer.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.UNSAFE_NONE);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeaderNames()).hasSize(1);
assertThat(this.response.getHeader(EMBEDDER_HEADER_NAME)).isEqualTo("unsafe-none");
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.web.header.writers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
class CrossOriginOpenerPolicyHeaderWriterTests {
private static final String OPENER_HEADER_NAME = "Cross-Origin-Opener-Policy";
private CrossOriginOpenerPolicyHeaderWriter writer;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
@BeforeEach
void setup() {
this.writer = new CrossOriginOpenerPolicyHeaderWriter();
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
}
@Test
void setOpenerPolicyWhenNullOpenerPolicyThenThrowsIllegalArgument() {
assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))
.withMessage("openerPolicy cannot be null");
}
@Test
void writeHeadersWhenDefaultValuesThenDontWriteHeaders() {
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeaderNames()).hasSize(0);
}
@Test
void writeHeadersWhenResponseHeaderExistsThenDontOverride() {
this.response.addHeader(OPENER_HEADER_NAME, "same-origin");
this.writer.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeader(OPENER_HEADER_NAME)).isEqualTo("same-origin");
}
@Test
void writeHeadersWhenSetHeaderValuesThenWrites() {
this.writer.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeader(OPENER_HEADER_NAME)).isEqualTo("same-origin-allow-popups");
}
@Test
void writeHeadersWhenSetOpenerPolicyThenWritesOpenerPolicy() {
this.writer.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeaderNames()).hasSize(1);
assertThat(this.response.getHeader(OPENER_HEADER_NAME)).isEqualTo("same-origin-allow-popups");
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.web.header.writers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
class CrossOriginResourcePolicyHeaderWriterTests {
private static final String RESOURCE_HEADER_NAME = "Cross-Origin-Resource-Policy";
private CrossOriginResourcePolicyHeaderWriter writer;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
@BeforeEach
void setup() {
this.writer = new CrossOriginResourcePolicyHeaderWriter();
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
}
@Test
void setResourcePolicyWhenNullThenThrowsIllegalArgument() {
assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))
.withMessage("resourcePolicy cannot be null");
}
@Test
void writeHeadersWhenDefaultValuesThenDontWriteHeaders() {
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeaderNames()).hasSize(0);
}
@Test
void writeHeadersWhenResponseHeaderExistsThenDontOverride() {
this.response.addHeader(RESOURCE_HEADER_NAME, "same-site");
this.writer.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.CROSS_ORIGIN);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeader(RESOURCE_HEADER_NAME)).isEqualTo("same-site");
}
@Test
void writeHeadersWhenSetHeaderValuesThenWrites() {
this.writer.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_ORIGIN);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeader(RESOURCE_HEADER_NAME)).isEqualTo("same-origin");
}
@Test
void writeHeadersWhenSetResourcePolicyThenWritesResourcePolicy() {
this.writer.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.SAME_SITE);
this.writer.writeHeaders(this.request, this.response);
assertThat(this.response.getHeaderNames()).hasSize(1);
assertThat(this.response.getHeader(RESOURCE_HEADER_NAME)).isEqualTo("same-site");
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.web.server.header;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
class CrossOriginEmbedderPolicyServerHttpHeadersWriterTests {
private ServerWebExchange exchange;
private CrossOriginEmbedderPolicyServerHttpHeadersWriter writer;
@BeforeEach
void setup() {
this.exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/"));
this.writer = new CrossOriginEmbedderPolicyServerHttpHeadersWriter();
}
@Test
void setEmbedderPolicyWhenNullEmbedderPolicyThenThrowsIllegalArgument() {
assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))
.withMessage("embedderPolicy cannot be null");
}
@Test
void writeHeadersWhenNoValuesThenDoesNotWriteHeaders() {
this.writer.writeHttpHeaders(this.exchange);
HttpHeaders headers = this.exchange.getResponse().getHeaders();
assertThat(headers).isEmpty();
}
@Test
void writeHeadersWhenResponseHeaderExistsThenDontOverride() {
this.exchange.getResponse().getHeaders().add(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY,
"require-corp");
this.writer.writeHttpHeaders(this.exchange);
HttpHeaders headers = this.exchange.getResponse().getHeaders();
assertThat(headers).hasSize(1);
assertThat(headers.get(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY))
.containsOnly("require-corp");
}
@Test
void writeHeadersWhenSetHeaderValuesThenWrites() {
this.writer.setPolicy(CrossOriginEmbedderPolicyServerHttpHeadersWriter.CrossOriginEmbedderPolicy.REQUIRE_CORP);
this.writer.writeHttpHeaders(this.exchange);
HttpHeaders headers = this.exchange.getResponse().getHeaders();
assertThat(headers).hasSize(1);
assertThat(headers.get(CrossOriginEmbedderPolicyServerHttpHeadersWriter.EMBEDDER_POLICY))
.containsOnly("require-corp");
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.web.server.header;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
class CrossOriginOpenerPolicyServerHttpHeadersWriterTests {
private ServerWebExchange exchange;
private CrossOriginOpenerPolicyServerHttpHeadersWriter writer;
@BeforeEach
void setup() {
this.exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/"));
this.writer = new CrossOriginOpenerPolicyServerHttpHeadersWriter();
}
@Test
void setOpenerPolicyWhenNullOpenerPolicyThenThrowsIllegalArgument() {
assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))
.withMessage("openerPolicy cannot be null");
}
@Test
void writeHeadersWhenNoValuesThenDoesNotWriteHeaders() {
this.writer.writeHttpHeaders(this.exchange);
HttpHeaders headers = this.exchange.getResponse().getHeaders();
assertThat(headers).isEmpty();
}
@Test
void writeHeadersWhenResponseHeaderExistsThenDontOverride() {
this.exchange.getResponse().getHeaders().add(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY,
"same-origin");
this.writer.writeHttpHeaders(this.exchange);
HttpHeaders headers = this.exchange.getResponse().getHeaders();
assertThat(headers).hasSize(1);
assertThat(headers.get(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY))
.containsOnly("same-origin");
}
@Test
void writeHeadersWhenSetHeaderValuesThenWrites() {
this.writer.setPolicy(
CrossOriginOpenerPolicyServerHttpHeadersWriter.CrossOriginOpenerPolicy.SAME_ORIGIN_ALLOW_POPUPS);
this.writer.writeHttpHeaders(this.exchange);
HttpHeaders headers = this.exchange.getResponse().getHeaders();
assertThat(headers).hasSize(1);
assertThat(headers.get(CrossOriginOpenerPolicyServerHttpHeadersWriter.OPENER_POLICY))
.containsOnly("same-origin-allow-popups");
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2002-2021 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
*
* https://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.
*/
package org.springframework.security.web.server.header;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.ServerWebExchange;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
class CrossOriginResourcePolicyServerHttpHeadersWriterTests {
private ServerWebExchange exchange;
private CrossOriginResourcePolicyServerHttpHeadersWriter writer;
@BeforeEach
void setup() {
this.exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/"));
this.writer = new CrossOriginResourcePolicyServerHttpHeadersWriter();
}
@Test
void setResourcePolicyWhenNullThenThrowsIllegalArgument() {
assertThatIllegalArgumentException().isThrownBy(() -> this.writer.setPolicy(null))
.withMessage("resourcePolicy cannot be null");
}
@Test
void writeHeadersWhenNoValuesThenDoesNotWriteHeaders() {
this.writer.writeHttpHeaders(this.exchange);
HttpHeaders headers = this.exchange.getResponse().getHeaders();
assertThat(headers).isEmpty();
}
@Test
void writeHeadersWhenResponseHeaderExistsThenDontOverride() {
this.exchange.getResponse().getHeaders().add(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY,
"same-origin");
this.writer.writeHttpHeaders(this.exchange);
HttpHeaders headers = this.exchange.getResponse().getHeaders();
assertThat(headers).hasSize(1);
assertThat(headers.get(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY))
.containsOnly("same-origin");
}
@Test
void writeHeadersWhenSetHeaderValuesThenWrites() {
this.writer.setPolicy(CrossOriginResourcePolicyServerHttpHeadersWriter.CrossOriginResourcePolicy.SAME_ORIGIN);
this.writer.writeHttpHeaders(this.exchange);
HttpHeaders headers = this.exchange.getResponse().getHeaders();
assertThat(headers).hasSize(1);
assertThat(headers.get(CrossOriginResourcePolicyServerHttpHeadersWriter.RESOURCE_POLICY))
.containsOnly("same-origin");
}
}