Skip to content

Commit f832fa7

Browse files
committed
Add permissionsPolicy http header
1 parent 48ef27b commit f832fa7

File tree

21 files changed

+1015
-3
lines changed

21 files changed

+1015
-3
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurer.java

+79
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;
3535
import org.springframework.security.web.header.writers.HpkpHeaderWriter;
3636
import org.springframework.security.web.header.writers.HstsHeaderWriter;
37+
import org.springframework.security.web.header.writers.PermissionsPolicyHeaderWriter;
3738
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
3839
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;
3940
import org.springframework.security.web.header.writers.XContentTypeOptionsHeaderWriter;
@@ -93,6 +94,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
9394

9495
private final FeaturePolicyConfig featurePolicy = new FeaturePolicyConfig();
9596

97+
private final PermissionsPolicyConfig permissionsPolicy = new PermissionsPolicyConfig();
98+
9699
/**
97100
* Creates a new instance
98101
*
@@ -387,6 +390,7 @@ private List<HeaderWriter> getHeaderWriters() {
387390
addIfNotNull(writers, this.contentSecurityPolicy.writer);
388391
addIfNotNull(writers, this.referrerPolicy.writer);
389392
addIfNotNull(writers, this.featurePolicy.writer);
393+
addIfNotNull(writers, this.permissionsPolicy.writer);
390394
writers.addAll(this.headerWriters);
391395
return writers;
392396
}
@@ -487,12 +491,58 @@ public HeadersConfigurer<H> referrerPolicy(Customizer<ReferrerPolicyConfig> refe
487491
* @throws IllegalArgumentException if policyDirectives is {@code null} or empty
488492
* @since 5.1
489493
* @see FeaturePolicyHeaderWriter
494+
* @deprecated Use {@link #permissionsPolicy(Customizer)} instead.
490495
*/
496+
@Deprecated
491497
public FeaturePolicyConfig featurePolicy(String policyDirectives) {
492498
this.featurePolicy.writer = new FeaturePolicyHeaderWriter(policyDirectives);
493499
return this.featurePolicy;
494500
}
495501

502+
/**
503+
* <p>
504+
* Allows configuration for
505+
* <a href="https://w3c.github.io/webappsec-permissions-policy/">Permissions
506+
* Policy</a>.
507+
* </p>
508+
*
509+
* <p>
510+
* Configuration is provided to the {@link PermissionsPolicyHeaderWriter} which
511+
* support the writing of the header as detailed in the W3C Technical Report:
512+
* </p>
513+
* <ul>
514+
* <li>Permissions-Policy</li>
515+
* </ul>
516+
* @return the {@link PermissionsPolicyConfig} for additional configuration
517+
* @since 5.5
518+
* @see PermissionsPolicyHeaderWriter
519+
*/
520+
public PermissionsPolicyConfig permissionsPolicy() {
521+
this.permissionsPolicy.writer = new PermissionsPolicyHeaderWriter();
522+
return this.permissionsPolicy;
523+
}
524+
525+
/**
526+
* Allows configuration for
527+
* <a href="https://w3c.github.io/webappsec-permissions-policy/"> Permissions
528+
* Policy</a>.
529+
* <p>
530+
* Calling this method automatically enables (includes) the {@code Permissions-Policy}
531+
* header in the response using the supplied policy directive(s).
532+
* <p>
533+
* Configuration is provided to the {@link PermissionsPolicyHeaderWriter} which is
534+
* responsible for writing the header.
535+
* @return the {@link PermissionsPolicyConfig} for additional configuration
536+
* @throws IllegalArgumentException if policyDirectives is {@code null} or empty
537+
* @since 5.5
538+
* @see PermissionsPolicyHeaderWriter
539+
*/
540+
public PermissionsPolicyConfig permissionsPolicy(Customizer<PermissionsPolicyConfig> permissionsPolicyCustomizer) {
541+
this.permissionsPolicy.writer = new PermissionsPolicyHeaderWriter();
542+
permissionsPolicyCustomizer.customize(this.permissionsPolicy);
543+
return this.permissionsPolicy;
544+
}
545+
496546
public final class ContentTypeOptionsConfig {
497547

498548
private XContentTypeOptionsHeaderWriter writer;
@@ -1063,4 +1113,33 @@ public HeadersConfigurer<H> and() {
10631113

10641114
}
10651115

1116+
public final class PermissionsPolicyConfig {
1117+
1118+
private PermissionsPolicyHeaderWriter writer;
1119+
1120+
private PermissionsPolicyConfig() {
1121+
}
1122+
1123+
/**
1124+
* Sets the policy to be used in the response header.
1125+
* @param policy a permissions policy
1126+
* @return the {@link PermissionsPolicyConfig} for additional configuration
1127+
* @throws IllegalArgumentException if policy is null
1128+
*/
1129+
public PermissionsPolicyConfig policy(String policy) {
1130+
this.writer.setPolicy(policy);
1131+
return this;
1132+
}
1133+
1134+
/**
1135+
* Allows completing configuration of Permissions Policy and continuing
1136+
* configuration of headers.
1137+
* @return the {@link HeadersConfigurer} for additional configuration
1138+
*/
1139+
public HeadersConfigurer<H> and() {
1140+
return HeadersConfigurer.this;
1141+
}
1142+
1143+
}
1144+
10661145
}

config/src/main/java/org/springframework/security/config/http/HeadersBeanDefinitionParser.java

+25
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;
4040
import org.springframework.security.web.header.writers.HpkpHeaderWriter;
4141
import org.springframework.security.web.header.writers.HstsHeaderWriter;
42+
import org.springframework.security.web.header.writers.PermissionsPolicyHeaderWriter;
4243
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
4344
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy;
4445
import org.springframework.security.web.header.writers.StaticHeadersWriter;
@@ -119,6 +120,8 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
119120

120121
private static final String FEATURE_POLICY_ELEMENT = "feature-policy";
121122

123+
private static final String PERMISSIONS_POLICY_ELEMENT = "permissions-policy";
124+
122125
private static final String ALLOW_FROM = "ALLOW-FROM";
123126

124127
private ManagedList<BeanMetadataElement> headerWriters;
@@ -140,6 +143,7 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
140143
parseContentSecurityPolicyElement(disabled, element, parserContext);
141144
parseReferrerPolicyElement(element, parserContext);
142145
parseFeaturePolicyElement(element, parserContext);
146+
parsePermissionsPolicyElement(element, parserContext);
143147
parseHeaderElements(element);
144148
boolean noWriters = this.headerWriters.isEmpty();
145149
if (disabled && !noWriters) {
@@ -351,6 +355,27 @@ private void addFeaturePolicy(Element featurePolicyElement, ParserContext contex
351355
this.headerWriters.add(headersWriter.getBeanDefinition());
352356
}
353357

358+
private void parsePermissionsPolicyElement(Element element, ParserContext context) {
359+
Element permissionsPolicyElement = (element != null)
360+
? DomUtils.getChildElementByTagName(element, PERMISSIONS_POLICY_ELEMENT) : null;
361+
if (permissionsPolicyElement != null) {
362+
addPermissionsPolicy(permissionsPolicyElement, context);
363+
}
364+
}
365+
366+
private void addPermissionsPolicy(Element permissionsPolicyElement, ParserContext context) {
367+
BeanDefinitionBuilder headersWriter = BeanDefinitionBuilder
368+
.genericBeanDefinition(PermissionsPolicyHeaderWriter.class);
369+
String policyDirectives = permissionsPolicyElement.getAttribute(ATT_POLICY);
370+
if (!StringUtils.hasText(policyDirectives)) {
371+
context.getReaderContext().error(ATT_POLICY + " requires a 'value' to be set.", permissionsPolicyElement);
372+
}
373+
else {
374+
headersWriter.addConstructorArgValue(policyDirectives);
375+
}
376+
this.headerWriters.add(headersWriter.getBeanDefinition());
377+
}
378+
354379
private void attrNotAllowed(ParserContext context, String attrName, String otherAttrName, Element element) {
355380
context.getReaderContext().error("Only one of '" + attrName + "' or '" + otherAttrName + "' can be set.",
356381
element);

config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java

+57-2
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@
149149
import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter;
150150
import org.springframework.security.web.server.header.FeaturePolicyServerHttpHeadersWriter;
151151
import org.springframework.security.web.server.header.HttpHeaderWriterWebFilter;
152+
import org.springframework.security.web.server.header.PermissionsPolicyServerHttpHeadersWriter;
152153
import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter;
153154
import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy;
154155
import org.springframework.security.web.server.header.ServerHttpHeadersWriter;
@@ -2232,13 +2233,16 @@ public final class HeaderSpec {
22322233

22332234
private FeaturePolicyServerHttpHeadersWriter featurePolicy = new FeaturePolicyServerHttpHeadersWriter();
22342235

2236+
private PermissionsPolicyServerHttpHeadersWriter permissionsPolicy = new PermissionsPolicyServerHttpHeadersWriter();
2237+
22352238
private ContentSecurityPolicyServerHttpHeadersWriter contentSecurityPolicy = new ContentSecurityPolicyServerHttpHeadersWriter();
22362239

22372240
private ReferrerPolicyServerHttpHeadersWriter referrerPolicy = new ReferrerPolicyServerHttpHeadersWriter();
22382241

22392242
private HeaderSpec() {
22402243
this.writers = new ArrayList<>(Arrays.asList(this.cacheControl, this.contentTypeOptions, this.hsts,
2241-
this.frameOptions, this.xss, this.featurePolicy, this.contentSecurityPolicy, this.referrerPolicy));
2244+
this.frameOptions, this.xss, this.featurePolicy, this.permissionsPolicy, this.contentSecurityPolicy,
2245+
this.referrerPolicy));
22422246
}
22432247

22442248
/**
@@ -2395,13 +2399,32 @@ public HeaderSpec contentSecurityPolicy(Customizer<ContentSecurityPolicySpec> co
23952399

23962400
/**
23972401
* Configures {@code Feature-Policy} response header.
2398-
* @param policyDirectives the policy directive(s)
2402+
* @param policyDirectives the policy
23992403
* @return the {@link FeaturePolicySpec} to configure
24002404
*/
24012405
public FeaturePolicySpec featurePolicy(String policyDirectives) {
24022406
return new FeaturePolicySpec(policyDirectives);
24032407
}
24042408

2409+
/**
2410+
* Configures {@code Permissions-Policy} response header.
2411+
* @return the {@link PermissionsPolicySpec} to configure
2412+
*/
2413+
public PermissionsPolicySpec permissionsPolicy() {
2414+
return new PermissionsPolicySpec();
2415+
}
2416+
2417+
/**
2418+
* Configures {@code Permissions-Policy} response header.
2419+
* @param permissionsPolicyCustomizer the {@link Customizer} to provide more
2420+
* options for the {@link PermissionsPolicySpec}
2421+
* @return the {@link HeaderSpec} to customize
2422+
*/
2423+
public HeaderSpec permissionsPolicy(Customizer<PermissionsPolicySpec> permissionsPolicyCustomizer) {
2424+
permissionsPolicyCustomizer.customize(new PermissionsPolicySpec());
2425+
return this;
2426+
}
2427+
24052428
/**
24062429
* Configures {@code Referrer-Policy} response header.
24072430
* @param referrerPolicy the policy to use
@@ -2677,6 +2700,38 @@ public HeaderSpec and() {
26772700

26782701
}
26792702

2703+
/**
2704+
* Configures {@code Permissions-Policy} response header.
2705+
*
2706+
* @since 5.5
2707+
* @see #permissionsPolicy()
2708+
*/
2709+
public final class PermissionsPolicySpec {
2710+
2711+
private PermissionsPolicySpec() {
2712+
}
2713+
2714+
/**
2715+
* Sets the policy to be used in the response header.
2716+
* @param policy a permissions policy
2717+
* @return the {@link PermissionsPolicySpec} to continue configuring
2718+
*/
2719+
public PermissionsPolicySpec policy(String policy) {
2720+
HeaderSpec.this.permissionsPolicy.setPolicy(policy);
2721+
return this;
2722+
}
2723+
2724+
/**
2725+
* Allows method chaining to continue configuring the
2726+
* {@link ServerHttpSecurity}.
2727+
* @return the {@link HeaderSpec} to continue configuring
2728+
*/
2729+
public HeaderSpec and() {
2730+
return HeaderSpec.this;
2731+
}
2732+
2733+
}
2734+
26802735
/**
26812736
* Configures {@code Referrer-Policy} response header.
26822737
*

config/src/main/kotlin/org/springframework/security/config/web/server/ServerHeadersDsl.kt

+19
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class ServerHeadersDsl {
3434
private var contentSecurityPolicy: ((ServerHttpSecurity.HeaderSpec.ContentSecurityPolicySpec) -> Unit)? = null
3535
private var referrerPolicy: ((ServerHttpSecurity.HeaderSpec.ReferrerPolicySpec) -> Unit)? = null
3636
private var featurePolicyDirectives: String? = null
37+
private var permissionsPolicy: ((ServerHttpSecurity.HeaderSpec.PermissionsPolicySpec) -> Unit)? = null
3738

3839
private var disabled = false
3940

@@ -140,6 +141,21 @@ class ServerHeadersDsl {
140141
this.featurePolicyDirectives = policyDirectives
141142
}
142143

144+
/**
145+
* Allows configuration for <a href="https://w3c.github.io/webappsec-permissions-policy/">Permissions
146+
* Policy</a>.
147+
*
148+
* <p>
149+
* Calling this method automatically enables (includes) the Permissions-Policy
150+
* header in the response using the supplied policy directive(s).
151+
* <p>
152+
*
153+
* @param permissionsPolicyConfig the customization to apply to the header
154+
*/
155+
fun permissionsPolicy(permissionsPolicyConfig: ServerPermissionsPolicyDsl.() -> Unit) {
156+
this.permissionsPolicy = ServerPermissionsPolicyDsl().apply(permissionsPolicyConfig).get()
157+
}
158+
143159
/**
144160
* Disables HTTP response headers.
145161
*/
@@ -170,6 +186,9 @@ class ServerHeadersDsl {
170186
featurePolicyDirectives?.also {
171187
headers.featurePolicy(featurePolicyDirectives)
172188
}
189+
permissionsPolicy?.also {
190+
headers.permissionsPolicy(permissionsPolicy)
191+
}
173192
referrerPolicy?.also {
174193
headers.referrerPolicy(referrerPolicy)
175194
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2002-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.web.server
18+
19+
import org.springframework.security.web.server.header.ReferrerPolicyServerHttpHeadersWriter
20+
21+
/**
22+
* A Kotlin DSL to configure the [ServerHttpSecurity] permissions policy header using
23+
* idiomatic Kotlin code.
24+
*
25+
* @author Christophe Gilles
26+
* @since 5.5
27+
* @property policy the policy to be used in the response header.
28+
*/
29+
@ServerSecurityMarker
30+
class ServerPermissionsPolicyDsl {
31+
var policy: String? = null
32+
33+
internal fun get(): (ServerHttpSecurity.HeaderSpec.PermissionsPolicySpec) -> Unit {
34+
return { permissionsPolicy ->
35+
policy?.also {
36+
permissionsPolicy.policy(policy)
37+
}
38+
}
39+
}
40+
}

config/src/main/kotlin/org/springframework/security/config/web/servlet/HeadersDsl.kt

+19
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class HeadersDsl {
4141
private var contentSecurityPolicy: ((HeadersConfigurer<HttpSecurity>.ContentSecurityPolicyConfig) -> Unit)? = null
4242
private var referrerPolicy: ((HeadersConfigurer<HttpSecurity>.ReferrerPolicyConfig) -> Unit)? = null
4343
private var featurePolicyDirectives: String? = null
44+
private var permissionsPolicy: ((HeadersConfigurer<HttpSecurity>.PermissionsPolicyConfig) -> Unit)? = null
4445
private var disabled = false
4546
private var headerWriters = mutableListOf<HeaderWriter>()
4647

@@ -164,6 +165,21 @@ class HeadersDsl {
164165
this.featurePolicyDirectives = policyDirectives
165166
}
166167

168+
/**
169+
* Allows configuration for <a href="https://w3c.github.io/webappsec-permissions-policy/">Permissions
170+
* Policy</a>.
171+
*
172+
* <p>
173+
* Calling this method automatically enables (includes) the Permissions-Policy
174+
* header in the response using the supplied policy directive(s).
175+
* <p>
176+
*
177+
* @param policyDirectives policyDirectives the security policy directive(s)
178+
*/
179+
fun permissionsPolicy(permissionsPolicyConfig: PermissionsPolicyDsl.() -> Unit) {
180+
this.permissionsPolicy = PermissionsPolicyDsl().apply(permissionsPolicyConfig).get()
181+
}
182+
167183
/**
168184
* Adds a [HeaderWriter] instance.
169185
*
@@ -217,6 +233,9 @@ class HeadersDsl {
217233
featurePolicyDirectives?.also {
218234
headers.featurePolicy(featurePolicyDirectives)
219235
}
236+
permissionsPolicy?.also {
237+
headers.permissionsPolicy(permissionsPolicy)
238+
}
220239
headerWriters.forEach { headerWriter ->
221240
headers.addHeaderWriter(headerWriter)
222241
}

0 commit comments

Comments
 (0)