Add CORS support for Private Network Access
This commit adds CORS support for Private Network Access
by adding an Access-Control-Allow-Private-Network response
header when the preflight request is sent with an
Access-Control-Request-Private-Network header and that
Private Network Access has been enabled in the CORS
configuration.
See https://developer.chrome.com/blog/private-network-access-preflight/
for more details.
Closes gh-31974
(cherry picked from commit 318d460256)
This commit is contained in:
@@ -133,6 +133,14 @@ public @interface CrossOrigin {
|
|||||||
*/
|
*/
|
||||||
String allowCredentials() default "";
|
String allowCredentials() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether private network access is supported. Please, see
|
||||||
|
* {@link CorsConfiguration#setAllowPrivateNetwork(Boolean)} for details.
|
||||||
|
* <p>By default this is not set (i.e. private network access is not supported).
|
||||||
|
* @since 6.1.3
|
||||||
|
*/
|
||||||
|
String allowPrivateNetwork() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum age (in seconds) of the cache duration for preflight responses.
|
* The maximum age (in seconds) of the cache duration for preflight responses.
|
||||||
* <p>This property controls the value of the {@code Access-Control-Max-Age}
|
* <p>This property controls the value of the {@code Access-Control-Max-Age}
|
||||||
|
|||||||
@@ -93,6 +93,9 @@ public class CorsConfiguration {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private Boolean allowCredentials;
|
private Boolean allowCredentials;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Boolean allowPrivateNetwork;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Long maxAge;
|
private Long maxAge;
|
||||||
|
|
||||||
@@ -117,6 +120,7 @@ public class CorsConfiguration {
|
|||||||
this.allowedHeaders = other.allowedHeaders;
|
this.allowedHeaders = other.allowedHeaders;
|
||||||
this.exposedHeaders = other.exposedHeaders;
|
this.exposedHeaders = other.exposedHeaders;
|
||||||
this.allowCredentials = other.allowCredentials;
|
this.allowCredentials = other.allowCredentials;
|
||||||
|
this.allowPrivateNetwork = other.allowPrivateNetwork;
|
||||||
this.maxAge = other.maxAge;
|
this.maxAge = other.maxAge;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,9 +133,10 @@ public class CorsConfiguration {
|
|||||||
* {@code Access-Control-Allow-Origin} response header is set either to the
|
* {@code Access-Control-Allow-Origin} response header is set either to the
|
||||||
* matched domain value or to {@code "*"}. Keep in mind however that the
|
* matched domain value or to {@code "*"}. Keep in mind however that the
|
||||||
* CORS spec does not allow {@code "*"} when {@link #setAllowCredentials
|
* CORS spec does not allow {@code "*"} when {@link #setAllowCredentials
|
||||||
* allowCredentials} is set to {@code true} and as of 5.3 that combination
|
* allowCredentials} is set to {@code true}, and does not recommend {@code "*"}
|
||||||
* is rejected in favor of using {@link #setAllowedOriginPatterns
|
* when {@link #setAllowPrivateNetwork allowPrivateNetwork} is set to {@code true}.
|
||||||
* allowedOriginPatterns} instead.
|
* As a consequence, those combinations are rejected in favor of using
|
||||||
|
* {@link #setAllowedOriginPatterns allowedOriginPatterns} instead.
|
||||||
* <p>By default this is not set which means that no origins are allowed.
|
* <p>By default this is not set which means that no origins are allowed.
|
||||||
* However, an instance of this class is often initialized further, e.g. for
|
* However, an instance of this class is often initialized further, e.g. for
|
||||||
* {@code @CrossOrigin}, via {@link #applyPermitDefaultValues()}.
|
* {@code @CrossOrigin}, via {@link #applyPermitDefaultValues()}.
|
||||||
@@ -182,11 +187,13 @@ public class CorsConfiguration {
|
|||||||
* domain1.com on any port, including the default port
|
* domain1.com on any port, including the default port
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>In contrast to {@link #setAllowedOrigins(List) allowedOrigins} which
|
* <p>In contrast to {@link #setAllowedOrigins(List) allowedOrigins} which
|
||||||
* only supports "*" and cannot be used with {@code allowCredentials}, when
|
* only supports "*" and cannot be used with {@code allowCredentials} or
|
||||||
* an allowedOriginPattern is matched, the {@code Access-Control-Allow-Origin}
|
* {@code allowPrivateNetwork}, when an {@code allowedOriginPattern} is matched,
|
||||||
* response header is set to the matched origin and not to {@code "*"} nor
|
* the {@code Access-Control-Allow-Origin} response header is set to the
|
||||||
* to the pattern. Therefore allowedOriginPatterns can be used in combination
|
* matched origin and not to {@code "*"} nor to the pattern.
|
||||||
* with {@link #setAllowCredentials} set to {@code true}.
|
* Therefore, {@code allowedOriginPatterns} can be used in combination with
|
||||||
|
* {@link #setAllowCredentials} and {@link #setAllowPrivateNetwork} set to
|
||||||
|
* {@code true}
|
||||||
* <p>By default this is not set.
|
* <p>By default this is not set.
|
||||||
* @since 5.3
|
* @since 5.3
|
||||||
*/
|
*/
|
||||||
@@ -418,6 +425,33 @@ public class CorsConfiguration {
|
|||||||
return this.allowCredentials;
|
return this.allowCredentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether private network access is supported for user-agents restricting such access by default.
|
||||||
|
* <p>Private network requests are requests whose target server's IP address is more private than
|
||||||
|
* that from which the request initiator was fetched. For example, a request from a public website
|
||||||
|
* (https://example.com) to a private website (https://router.local), or a request from a private
|
||||||
|
* website to localhost.
|
||||||
|
* <p>Setting this property has an impact on how {@link #setAllowedOrigins(List)
|
||||||
|
* origins} and {@link #setAllowedOriginPatterns(List) originPatterns} are processed,
|
||||||
|
* see related API documentation for more details.
|
||||||
|
* <p>By default this is not set (i.e. private network access is not supported).
|
||||||
|
* @since 6.1.3
|
||||||
|
* @see <a href="https://wicg.github.io/private-network-access/">Private network access specifications</a>
|
||||||
|
*/
|
||||||
|
public void setAllowPrivateNetwork(@Nullable Boolean allowPrivateNetwork) {
|
||||||
|
this.allowPrivateNetwork = allowPrivateNetwork;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the configured {@code allowPrivateNetwork} flag, or {@code null} if none.
|
||||||
|
* @since 6.1.3
|
||||||
|
* @see #setAllowPrivateNetwork(Boolean)
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Boolean getAllowPrivateNetwork() {
|
||||||
|
return this.allowPrivateNetwork;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure how long, as a duration, the response from a pre-flight request
|
* Configure how long, as a duration, the response from a pre-flight request
|
||||||
* can be cached by clients.
|
* can be cached by clients.
|
||||||
@@ -500,6 +534,25 @@ public class CorsConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate that when {@link #setAllowPrivateNetwork allowPrivateNetwork} is {@code true},
|
||||||
|
* {@link #setAllowedOrigins allowedOrigins} does not contain the special
|
||||||
|
* value {@code "*"} since this is insecure.
|
||||||
|
* @throws IllegalArgumentException if the validation fails
|
||||||
|
* @since 6.1.3
|
||||||
|
*/
|
||||||
|
public void validateAllowPrivateNetwork() {
|
||||||
|
if (this.allowPrivateNetwork == Boolean.TRUE &&
|
||||||
|
this.allowedOrigins != null && this.allowedOrigins.contains(ALL)) {
|
||||||
|
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"When allowPrivateNetwork is true, allowedOrigins cannot contain the special value \"*\" " +
|
||||||
|
"as it is not recommended from a security perspective. " +
|
||||||
|
"To allow private network access to a set of origins, list them explicitly " +
|
||||||
|
"or consider using \"allowedOriginPatterns\" instead.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combine the non-null properties of the supplied
|
* Combine the non-null properties of the supplied
|
||||||
* {@code CorsConfiguration} with this one.
|
* {@code CorsConfiguration} with this one.
|
||||||
@@ -534,6 +587,10 @@ public class CorsConfiguration {
|
|||||||
if (allowCredentials != null) {
|
if (allowCredentials != null) {
|
||||||
config.setAllowCredentials(allowCredentials);
|
config.setAllowCredentials(allowCredentials);
|
||||||
}
|
}
|
||||||
|
Boolean allowPrivateNetwork = other.getAllowPrivateNetwork();
|
||||||
|
if (allowPrivateNetwork != null) {
|
||||||
|
config.setAllowPrivateNetwork(allowPrivateNetwork);
|
||||||
|
}
|
||||||
Long maxAge = other.getMaxAge();
|
Long maxAge = other.getMaxAge();
|
||||||
if (maxAge != null) {
|
if (maxAge != null) {
|
||||||
config.setMaxAge(maxAge);
|
config.setMaxAge(maxAge);
|
||||||
@@ -597,6 +654,7 @@ public class CorsConfiguration {
|
|||||||
if (!ObjectUtils.isEmpty(this.allowedOrigins)) {
|
if (!ObjectUtils.isEmpty(this.allowedOrigins)) {
|
||||||
if (this.allowedOrigins.contains(ALL)) {
|
if (this.allowedOrigins.contains(ALL)) {
|
||||||
validateAllowCredentials();
|
validateAllowCredentials();
|
||||||
|
validateAllowPrivateNetwork();
|
||||||
return ALL;
|
return ALL;
|
||||||
}
|
}
|
||||||
for (String allowedOrigin : this.allowedOrigins) {
|
for (String allowedOrigin : this.allowedOrigins) {
|
||||||
|
|||||||
@@ -55,6 +55,18 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
|||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(DefaultCorsProcessor.class);
|
private static final Log logger = LogFactory.getLog(DefaultCorsProcessor.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code Access-Control-Request-Private-Network} request header field name.
|
||||||
|
* @see <a href="https://wicg.github.io/private-network-access/">Private Network Access specification</a>
|
||||||
|
*/
|
||||||
|
static final String ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK = "Access-Control-Request-Private-Network";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code Access-Control-Allow-Private-Network} response header field name.
|
||||||
|
* @see <a href="https://wicg.github.io/private-network-access/">Private Network Access specification</a>
|
||||||
|
*/
|
||||||
|
static final String ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK = "Access-Control-Allow-Private-Network";
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
@@ -156,6 +168,11 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
|||||||
responseHeaders.setAccessControlAllowCredentials(true);
|
responseHeaders.setAccessControlAllowCredentials(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Boolean.TRUE.equals(config.getAllowPrivateNetwork()) &&
|
||||||
|
Boolean.parseBoolean(request.getHeaders().getFirst(ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK))) {
|
||||||
|
responseHeaders.set(ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK, Boolean.toString(true));
|
||||||
|
}
|
||||||
|
|
||||||
if (preFlightRequest && config.getMaxAge() != null) {
|
if (preFlightRequest && config.getMaxAge() != null) {
|
||||||
responseHeaders.setAccessControlMaxAge(config.getMaxAge());
|
responseHeaders.setAccessControlMaxAge(config.getMaxAge());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,18 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
|||||||
private static final List<String> VARY_HEADERS = Arrays.asList(
|
private static final List<String> VARY_HEADERS = Arrays.asList(
|
||||||
HttpHeaders.ORIGIN, HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
|
HttpHeaders.ORIGIN, HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code Access-Control-Request-Private-Network} request header field name.
|
||||||
|
* @see <a href="https://wicg.github.io/private-network-access/">Private Network Access specification</a>
|
||||||
|
*/
|
||||||
|
static final String ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK = "Access-Control-Request-Private-Network";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code Access-Control-Allow-Private-Network} response header field name.
|
||||||
|
* @see <a href="https://wicg.github.io/private-network-access/">Private Network Access specification</a>
|
||||||
|
*/
|
||||||
|
static final String ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK = "Access-Control-Allow-Private-Network";
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean process(@Nullable CorsConfiguration config, ServerWebExchange exchange) {
|
public boolean process(@Nullable CorsConfiguration config, ServerWebExchange exchange) {
|
||||||
@@ -155,6 +167,11 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
|||||||
responseHeaders.setAccessControlAllowCredentials(true);
|
responseHeaders.setAccessControlAllowCredentials(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Boolean.TRUE.equals(config.getAllowPrivateNetwork()) &&
|
||||||
|
Boolean.parseBoolean(request.getHeaders().getFirst(ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK))) {
|
||||||
|
responseHeaders.set(ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK, Boolean.toString(true));
|
||||||
|
}
|
||||||
|
|
||||||
if (preFlightRequest && config.getMaxAge() != null) {
|
if (preFlightRequest && config.getMaxAge() != null) {
|
||||||
responseHeaders.setAccessControlMaxAge(config.getMaxAge());
|
responseHeaders.setAccessControlMaxAge(config.getMaxAge());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2023 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.
|
||||||
@@ -50,6 +50,8 @@ class CorsConfigurationTests {
|
|||||||
assertThat(config.getExposedHeaders()).isNull();
|
assertThat(config.getExposedHeaders()).isNull();
|
||||||
config.setAllowCredentials(null);
|
config.setAllowCredentials(null);
|
||||||
assertThat(config.getAllowCredentials()).isNull();
|
assertThat(config.getAllowCredentials()).isNull();
|
||||||
|
config.setAllowPrivateNetwork(null);
|
||||||
|
assertThat(config.getAllowPrivateNetwork()).isNull();
|
||||||
config.setMaxAge((Long) null);
|
config.setMaxAge((Long) null);
|
||||||
assertThat(config.getMaxAge()).isNull();
|
assertThat(config.getMaxAge()).isNull();
|
||||||
}
|
}
|
||||||
@@ -63,6 +65,7 @@ class CorsConfigurationTests {
|
|||||||
config.addAllowedMethod("*");
|
config.addAllowedMethod("*");
|
||||||
config.addExposedHeader("*");
|
config.addExposedHeader("*");
|
||||||
config.setAllowCredentials(true);
|
config.setAllowCredentials(true);
|
||||||
|
config.setAllowPrivateNetwork(true);
|
||||||
config.setMaxAge(123L);
|
config.setMaxAge(123L);
|
||||||
|
|
||||||
assertThat(config.getAllowedOrigins()).containsExactly("*");
|
assertThat(config.getAllowedOrigins()).containsExactly("*");
|
||||||
@@ -71,6 +74,7 @@ class CorsConfigurationTests {
|
|||||||
assertThat(config.getAllowedMethods()).containsExactly("*");
|
assertThat(config.getAllowedMethods()).containsExactly("*");
|
||||||
assertThat(config.getExposedHeaders()).containsExactly("*");
|
assertThat(config.getExposedHeaders()).containsExactly("*");
|
||||||
assertThat(config.getAllowCredentials()).isTrue();
|
assertThat(config.getAllowCredentials()).isTrue();
|
||||||
|
assertThat(config.getAllowPrivateNetwork()).isTrue();
|
||||||
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(123));
|
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(123));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +97,7 @@ class CorsConfigurationTests {
|
|||||||
config.addAllowedMethod(HttpMethod.GET.name());
|
config.addAllowedMethod(HttpMethod.GET.name());
|
||||||
config.setMaxAge(123L);
|
config.setMaxAge(123L);
|
||||||
config.setAllowCredentials(true);
|
config.setAllowCredentials(true);
|
||||||
|
config.setAllowPrivateNetwork(true);
|
||||||
|
|
||||||
CorsConfiguration other = new CorsConfiguration();
|
CorsConfiguration other = new CorsConfiguration();
|
||||||
config = config.combine(other);
|
config = config.combine(other);
|
||||||
@@ -105,6 +110,7 @@ class CorsConfigurationTests {
|
|||||||
assertThat(config.getAllowedMethods()).containsExactly(HttpMethod.GET.name());
|
assertThat(config.getAllowedMethods()).containsExactly(HttpMethod.GET.name());
|
||||||
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(123));
|
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(123));
|
||||||
assertThat(config.getAllowCredentials()).isTrue();
|
assertThat(config.getAllowCredentials()).isTrue();
|
||||||
|
assertThat(config.getAllowPrivateNetwork()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // SPR-15772
|
@Test // SPR-15772
|
||||||
@@ -258,6 +264,7 @@ class CorsConfigurationTests {
|
|||||||
config.addAllowedMethod(HttpMethod.GET.name());
|
config.addAllowedMethod(HttpMethod.GET.name());
|
||||||
config.setMaxAge(123L);
|
config.setMaxAge(123L);
|
||||||
config.setAllowCredentials(true);
|
config.setAllowCredentials(true);
|
||||||
|
config.setAllowPrivateNetwork(true);
|
||||||
|
|
||||||
CorsConfiguration other = new CorsConfiguration();
|
CorsConfiguration other = new CorsConfiguration();
|
||||||
other.addAllowedOrigin("https://domain2.com");
|
other.addAllowedOrigin("https://domain2.com");
|
||||||
@@ -267,6 +274,7 @@ class CorsConfigurationTests {
|
|||||||
other.addAllowedMethod(HttpMethod.PUT.name());
|
other.addAllowedMethod(HttpMethod.PUT.name());
|
||||||
other.setMaxAge(456L);
|
other.setMaxAge(456L);
|
||||||
other.setAllowCredentials(false);
|
other.setAllowCredentials(false);
|
||||||
|
other.setAllowPrivateNetwork(false);
|
||||||
|
|
||||||
config = config.combine(other);
|
config = config.combine(other);
|
||||||
assertThat(config).isNotNull();
|
assertThat(config).isNotNull();
|
||||||
@@ -277,6 +285,7 @@ class CorsConfigurationTests {
|
|||||||
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(456));
|
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(456));
|
||||||
assertThat(config).isNotNull();
|
assertThat(config).isNotNull();
|
||||||
assertThat(config.getAllowCredentials()).isFalse();
|
assertThat(config.getAllowCredentials()).isFalse();
|
||||||
|
assertThat(config.getAllowPrivateNetwork()).isFalse();
|
||||||
assertThat(config.getAllowedOriginPatterns()).containsExactly("http://*.domain1.com", "http://*.domain2.com");
|
assertThat(config.getAllowedOriginPatterns()).containsExactly("http://*.domain1.com", "http://*.domain2.com");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -352,6 +352,32 @@ public class DefaultCorsProcessorTests {
|
|||||||
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void preflightRequestPrivateNetworkWithWildcardOrigin() throws Exception {
|
||||||
|
this.request.setMethod(HttpMethod.OPTIONS.name());
|
||||||
|
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
|
||||||
|
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
|
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
|
||||||
|
this.request.addHeader(DefaultCorsProcessor.ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK, "true");
|
||||||
|
this.conf.setAllowedOrigins(Arrays.asList("https://domain1.com", "*", "http://domain3.example"));
|
||||||
|
this.conf.addAllowedHeader("Header1");
|
||||||
|
this.conf.setAllowPrivateNetwork(true);
|
||||||
|
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||||
|
this.processor.processRequest(this.conf, this.request, this.response));
|
||||||
|
|
||||||
|
this.conf.setAllowedOrigins(null);
|
||||||
|
this.conf.addAllowedOriginPattern("*");
|
||||||
|
|
||||||
|
this.processor.processRequest(this.conf, this.request, this.response);
|
||||||
|
assertThat(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||||
|
assertThat(this.response.containsHeader(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isTrue();
|
||||||
|
assertThat(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("https://domain2.com");
|
||||||
|
assertThat(this.response.getHeaders(HttpHeaders.VARY)).contains(HttpHeaders.ORIGIN,
|
||||||
|
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void preflightRequestAllowedHeaders() throws Exception {
|
public void preflightRequestAllowedHeaders() throws Exception {
|
||||||
this.request.setMethod(HttpMethod.OPTIONS.name());
|
this.request.setMethod(HttpMethod.OPTIONS.name());
|
||||||
@@ -435,4 +461,49 @@ public class DefaultCorsProcessorTests {
|
|||||||
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
|
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void preflightRequestWithoutAccessControlRequestPrivateNetwork() throws Exception {
|
||||||
|
this.request.setMethod(HttpMethod.OPTIONS.name());
|
||||||
|
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
|
||||||
|
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
|
this.conf.addAllowedHeader("*");
|
||||||
|
this.conf.addAllowedOrigin("https://domain2.com");
|
||||||
|
|
||||||
|
this.processor.processRequest(this.conf, this.request, this.response);
|
||||||
|
assertThat(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||||
|
assertThat(this.response.containsHeader(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isFalse();
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void preflightRequestWithAccessControlRequestPrivateNetworkNotAllowed() throws Exception {
|
||||||
|
this.request.setMethod(HttpMethod.OPTIONS.name());
|
||||||
|
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
|
||||||
|
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
|
this.request.addHeader(DefaultCorsProcessor.ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK, "true");
|
||||||
|
this.conf.addAllowedHeader("*");
|
||||||
|
this.conf.addAllowedOrigin("https://domain2.com");
|
||||||
|
|
||||||
|
this.processor.processRequest(this.conf, this.request, this.response);
|
||||||
|
assertThat(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||||
|
assertThat(this.response.containsHeader(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isFalse();
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void preflightRequestWithAccessControlRequestPrivateNetworkAllowed() throws Exception {
|
||||||
|
this.request.setMethod(HttpMethod.OPTIONS.name());
|
||||||
|
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
|
||||||
|
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||||
|
this.request.addHeader(DefaultCorsProcessor.ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK, "true");
|
||||||
|
this.conf.addAllowedHeader("*");
|
||||||
|
this.conf.addAllowedOrigin("https://domain2.com");
|
||||||
|
this.conf.setAllowPrivateNetwork(true);
|
||||||
|
|
||||||
|
this.processor.processRequest(this.conf, this.request, this.response);
|
||||||
|
assertThat(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||||
|
assertThat(this.response.containsHeader(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isTrue();
|
||||||
|
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -364,6 +364,33 @@ public class DefaultCorsProcessorTests {
|
|||||||
assertThat((Object) response.getStatusCode()).isNull();
|
assertThat((Object) response.getStatusCode()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void preflightRequestPrivateNetworkWithWildcardOrigin() {
|
||||||
|
ServerWebExchange exchange = MockServerWebExchange.from(preFlightRequest()
|
||||||
|
.header(ACCESS_CONTROL_REQUEST_METHOD, "GET")
|
||||||
|
.header(ACCESS_CONTROL_REQUEST_HEADERS, "Header1")
|
||||||
|
.header(DefaultCorsProcessor.ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK, "true"));
|
||||||
|
|
||||||
|
this.conf.addAllowedOrigin("https://domain1.com");
|
||||||
|
this.conf.addAllowedOrigin("*");
|
||||||
|
this.conf.addAllowedOrigin("http://domain3.example");
|
||||||
|
this.conf.addAllowedHeader("Header1");
|
||||||
|
this.conf.setAllowPrivateNetwork(true);
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> this.processor.process(this.conf, exchange));
|
||||||
|
|
||||||
|
this.conf.setAllowedOrigins(null);
|
||||||
|
this.conf.addAllowedOriginPattern("*");
|
||||||
|
this.processor.process(this.conf, exchange);
|
||||||
|
|
||||||
|
ServerHttpResponse response = exchange.getResponse();
|
||||||
|
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||||
|
assertThat(response.getHeaders().containsKey(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isTrue();
|
||||||
|
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("https://domain2.com");
|
||||||
|
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||||
|
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||||
|
assertThat(response.getStatusCode()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void preflightRequestAllowedHeaders() {
|
public void preflightRequestAllowedHeaders() {
|
||||||
ServerWebExchange exchange = MockServerWebExchange.from(preFlightRequest()
|
ServerWebExchange exchange = MockServerWebExchange.from(preFlightRequest()
|
||||||
@@ -460,6 +487,57 @@ public class DefaultCorsProcessorTests {
|
|||||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void preflightRequestWithoutAccessControlRequestPrivateNetwork() {
|
||||||
|
ServerWebExchange exchange = MockServerWebExchange.from(preFlightRequest()
|
||||||
|
.header(ACCESS_CONTROL_REQUEST_METHOD, "GET"));
|
||||||
|
|
||||||
|
this.conf.addAllowedHeader("*");
|
||||||
|
this.conf.addAllowedOrigin("https://domain2.com");
|
||||||
|
|
||||||
|
this.processor.process(this.conf, exchange);
|
||||||
|
|
||||||
|
ServerHttpResponse response = exchange.getResponse();
|
||||||
|
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||||
|
assertThat(response.getHeaders().containsKey(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isFalse();
|
||||||
|
assertThat(response.getStatusCode()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void preflightRequestWithAccessControlRequestPrivateNetworkNotAllowed() {
|
||||||
|
ServerWebExchange exchange = MockServerWebExchange.from(preFlightRequest()
|
||||||
|
.header(ACCESS_CONTROL_REQUEST_METHOD, "GET")
|
||||||
|
.header(DefaultCorsProcessor.ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK, "true"));
|
||||||
|
|
||||||
|
this.conf.addAllowedHeader("*");
|
||||||
|
this.conf.addAllowedOrigin("https://domain2.com");
|
||||||
|
|
||||||
|
this.processor.process(this.conf, exchange);
|
||||||
|
|
||||||
|
ServerHttpResponse response = exchange.getResponse();
|
||||||
|
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||||
|
assertThat(response.getHeaders().containsKey(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isFalse();
|
||||||
|
assertThat(response.getStatusCode()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void preflightRequestWithAccessControlRequestPrivateNetworkAllowed() {
|
||||||
|
ServerWebExchange exchange = MockServerWebExchange.from(preFlightRequest()
|
||||||
|
.header(ACCESS_CONTROL_REQUEST_METHOD, "GET")
|
||||||
|
.header(DefaultCorsProcessor.ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK, "true"));
|
||||||
|
|
||||||
|
this.conf.addAllowedHeader("*");
|
||||||
|
this.conf.addAllowedOrigin("https://domain2.com");
|
||||||
|
this.conf.setAllowPrivateNetwork(true);
|
||||||
|
|
||||||
|
this.processor.process(this.conf, exchange);
|
||||||
|
|
||||||
|
ServerHttpResponse response = exchange.getResponse();
|
||||||
|
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||||
|
assertThat(response.getHeaders().containsKey(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isTrue();
|
||||||
|
assertThat(response.getStatusCode()).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private ServerWebExchange actualRequest() {
|
private ServerWebExchange actualRequest() {
|
||||||
return MockServerWebExchange.from(corsRequest(HttpMethod.GET));
|
return MockServerWebExchange.from(corsRequest(HttpMethod.GET));
|
||||||
|
|||||||
@@ -131,6 +131,17 @@ public class CorsRegistration {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether private network access is supported.
|
||||||
|
* <p>Please, see {@link CorsConfiguration#setAllowPrivateNetwork(Boolean)} for details.
|
||||||
|
* <p>By default this is not set (i.e. private network access is not supported).
|
||||||
|
* @since 6.1.3
|
||||||
|
*/
|
||||||
|
public CorsRegistration allowPrivateNetwork(boolean allowPrivateNetwork) {
|
||||||
|
this.config.setAllowPrivateNetwork(allowPrivateNetwork);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure how long in seconds the response from a pre-flight request
|
* Configure how long in seconds the response from a pre-flight request
|
||||||
* can be cached by clients.
|
* can be cached by clients.
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
|
|||||||
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
|
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
config.validateAllowCredentials();
|
config.validateAllowCredentials();
|
||||||
|
config.validateAllowPrivateNetwork();
|
||||||
}
|
}
|
||||||
if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {
|
if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {
|
||||||
return NO_OP_HANDLER;
|
return NO_OP_HANDLER;
|
||||||
|
|||||||
@@ -534,6 +534,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
|||||||
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
|
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
|
||||||
if (corsConfig != null) {
|
if (corsConfig != null) {
|
||||||
corsConfig.validateAllowCredentials();
|
corsConfig.validateAllowCredentials();
|
||||||
|
corsConfig.validateAllowPrivateNetwork();
|
||||||
this.corsLookup.put(handlerMethod, corsConfig);
|
this.corsLookup.put(handlerMethod, corsConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -341,6 +341,18 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
|||||||
"or an empty string (\"\"): current value is [" + allowCredentials + "]");
|
"or an empty string (\"\"): current value is [" + allowCredentials + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String allowPrivateNetwork = resolveCorsAnnotationValue(annotation.allowPrivateNetwork());
|
||||||
|
if ("true".equalsIgnoreCase(allowPrivateNetwork)) {
|
||||||
|
config.setAllowPrivateNetwork(true);
|
||||||
|
}
|
||||||
|
else if ("false".equalsIgnoreCase(allowPrivateNetwork)) {
|
||||||
|
config.setAllowPrivateNetwork(false);
|
||||||
|
}
|
||||||
|
else if (!allowPrivateNetwork.isEmpty()) {
|
||||||
|
throw new IllegalStateException("@CrossOrigin's allowPrivateNetwork value must be \"true\", \"false\", " +
|
||||||
|
"or an empty string (\"\"): current value is [" + allowPrivateNetwork + "]");
|
||||||
|
}
|
||||||
|
|
||||||
if (annotation.maxAge() >= 0) {
|
if (annotation.maxAge() >= 0) {
|
||||||
config.setMaxAge(annotation.maxAge());
|
config.setMaxAge(annotation.maxAge());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ public class CorsRegistryTests {
|
|||||||
@Test
|
@Test
|
||||||
public void customizedMapping() {
|
public void customizedMapping() {
|
||||||
this.registry.addMapping("/foo").allowedOrigins("https://domain2.com", "https://domain2.com")
|
this.registry.addMapping("/foo").allowedOrigins("https://domain2.com", "https://domain2.com")
|
||||||
.allowedMethods("DELETE").allowCredentials(false).allowedHeaders("header1", "header2")
|
.allowedMethods("DELETE").allowCredentials(true).allowPrivateNetwork(true)
|
||||||
.exposedHeaders("header3", "header4").maxAge(3600);
|
.allowedHeaders("header1", "header2").exposedHeaders("header3", "header4").maxAge(3600);
|
||||||
Map<String, CorsConfiguration> configs = this.registry.getCorsConfigurations();
|
Map<String, CorsConfiguration> configs = this.registry.getCorsConfigurations();
|
||||||
assertThat(configs.size()).isEqualTo(1);
|
assertThat(configs.size()).isEqualTo(1);
|
||||||
CorsConfiguration config = configs.get("/foo");
|
CorsConfiguration config = configs.get("/foo");
|
||||||
@@ -60,7 +60,8 @@ public class CorsRegistryTests {
|
|||||||
assertThat(config.getAllowedMethods()).isEqualTo(Collections.singletonList("DELETE"));
|
assertThat(config.getAllowedMethods()).isEqualTo(Collections.singletonList("DELETE"));
|
||||||
assertThat(config.getAllowedHeaders()).isEqualTo(Arrays.asList("header1", "header2"));
|
assertThat(config.getAllowedHeaders()).isEqualTo(Arrays.asList("header1", "header2"));
|
||||||
assertThat(config.getExposedHeaders()).isEqualTo(Arrays.asList("header3", "header4"));
|
assertThat(config.getExposedHeaders()).isEqualTo(Arrays.asList("header3", "header4"));
|
||||||
assertThat(config.getAllowCredentials()).isFalse();
|
assertThat(config.getAllowCredentials()).isTrue();
|
||||||
|
assertThat(config.getAllowPrivateNetwork()).isTrue();
|
||||||
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(3600));
|
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(3600));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +91,7 @@ public class CorsRegistryTests {
|
|||||||
assertThat(config.getAllowedHeaders()).isEqualTo(Collections.singletonList("*"));
|
assertThat(config.getAllowedHeaders()).isEqualTo(Collections.singletonList("*"));
|
||||||
assertThat(config.getExposedHeaders()).isEmpty();
|
assertThat(config.getExposedHeaders()).isEmpty();
|
||||||
assertThat(config.getAllowCredentials()).isNull();
|
assertThat(config.getAllowCredentials()).isNull();
|
||||||
|
assertThat(config.getAllowPrivateNetwork()).isNull();
|
||||||
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(1800));
|
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(1800));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ public class CorsBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
}
|
}
|
||||||
config.applyPermitDefaultValues();
|
config.applyPermitDefaultValues();
|
||||||
config.validateAllowCredentials();
|
config.validateAllowCredentials();
|
||||||
|
config.validateAllowPrivateNetwork();
|
||||||
corsConfigurations.put(mapping.getAttribute("path"), config);
|
corsConfigurations.put(mapping.getAttribute("path"), config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,6 +132,17 @@ public class CorsRegistration {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether private network access is supported.
|
||||||
|
* <p>By default this is not set (i.e. private network access is not supported).
|
||||||
|
* @since 6.1.3
|
||||||
|
* @see <a href="https://wicg.github.io/private-network-access/">Private network access specifications</a>
|
||||||
|
*/
|
||||||
|
public CorsRegistration allowPrivateNetwork(boolean allowPrivateNetwork) {
|
||||||
|
this.config.setAllowPrivateNetwork(allowPrivateNetwork);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure how long in seconds the response from a pre-flight request
|
* Configure how long in seconds the response from a pre-flight request
|
||||||
* can be cached by clients.
|
* can be cached by clients.
|
||||||
|
|||||||
@@ -530,6 +530,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
|||||||
}
|
}
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
config.validateAllowCredentials();
|
config.validateAllowCredentials();
|
||||||
|
config.validateAllowPrivateNetwork();
|
||||||
}
|
}
|
||||||
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
|
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -648,6 +648,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
|||||||
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
|
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
|
||||||
if (corsConfig != null) {
|
if (corsConfig != null) {
|
||||||
corsConfig.validateAllowCredentials();
|
corsConfig.validateAllowCredentials();
|
||||||
|
corsConfig.validateAllowPrivateNetwork();
|
||||||
this.corsLookup.put(handlerMethod, corsConfig);
|
this.corsLookup.put(handlerMethod, corsConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -501,6 +501,18 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
|||||||
"or an empty string (\"\"): current value is [" + allowCredentials + "]");
|
"or an empty string (\"\"): current value is [" + allowCredentials + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String allowPrivateNetwork = resolveCorsAnnotationValue(annotation.allowPrivateNetwork());
|
||||||
|
if ("true".equalsIgnoreCase(allowPrivateNetwork)) {
|
||||||
|
config.setAllowPrivateNetwork(true);
|
||||||
|
}
|
||||||
|
else if ("false".equalsIgnoreCase(allowPrivateNetwork)) {
|
||||||
|
config.setAllowPrivateNetwork(false);
|
||||||
|
}
|
||||||
|
else if (!allowPrivateNetwork.isEmpty()) {
|
||||||
|
throw new IllegalStateException("@CrossOrigin's allowPrivateNetwork value must be \"true\", \"false\", " +
|
||||||
|
"or an empty string (\"\"): current value is [" + allowPrivateNetwork + "]");
|
||||||
|
}
|
||||||
|
|
||||||
if (annotation.maxAge() >= 0 ) {
|
if (annotation.maxAge() >= 0 ) {
|
||||||
config.setMaxAge(annotation.maxAge());
|
config.setMaxAge(annotation.maxAge());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ public class CorsRegistryTests {
|
|||||||
@Test
|
@Test
|
||||||
public void customizedMapping() {
|
public void customizedMapping() {
|
||||||
this.registry.addMapping("/foo").allowedOrigins("https://domain2.com", "https://domain2.com")
|
this.registry.addMapping("/foo").allowedOrigins("https://domain2.com", "https://domain2.com")
|
||||||
.allowedMethods("DELETE").allowCredentials(false).allowedHeaders("header1", "header2")
|
.allowedMethods("DELETE").allowCredentials(true).allowPrivateNetwork(true)
|
||||||
.exposedHeaders("header3", "header4").maxAge(3600);
|
.allowedHeaders("header1", "header2").exposedHeaders("header3", "header4").maxAge(3600);
|
||||||
Map<String, CorsConfiguration> configs = this.registry.getCorsConfigurations();
|
Map<String, CorsConfiguration> configs = this.registry.getCorsConfigurations();
|
||||||
assertThat(configs.size()).isEqualTo(1);
|
assertThat(configs.size()).isEqualTo(1);
|
||||||
CorsConfiguration config = configs.get("/foo");
|
CorsConfiguration config = configs.get("/foo");
|
||||||
@@ -65,7 +65,8 @@ public class CorsRegistryTests {
|
|||||||
assertThat(config.getAllowedMethods()).isEqualTo(Collections.singletonList("DELETE"));
|
assertThat(config.getAllowedMethods()).isEqualTo(Collections.singletonList("DELETE"));
|
||||||
assertThat(config.getAllowedHeaders()).isEqualTo(Arrays.asList("header1", "header2"));
|
assertThat(config.getAllowedHeaders()).isEqualTo(Arrays.asList("header1", "header2"));
|
||||||
assertThat(config.getExposedHeaders()).isEqualTo(Arrays.asList("header3", "header4"));
|
assertThat(config.getExposedHeaders()).isEqualTo(Arrays.asList("header3", "header4"));
|
||||||
assertThat(config.getAllowCredentials()).isFalse();
|
assertThat(config.getAllowCredentials()).isTrue();
|
||||||
|
assertThat(config.getAllowPrivateNetwork()).isTrue();
|
||||||
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(3600));
|
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(3600));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +96,7 @@ public class CorsRegistryTests {
|
|||||||
assertThat(config.getAllowedHeaders()).isEqualTo(Collections.singletonList("*"));
|
assertThat(config.getAllowedHeaders()).isEqualTo(Collections.singletonList("*"));
|
||||||
assertThat(config.getExposedHeaders()).isEmpty();
|
assertThat(config.getExposedHeaders()).isEmpty();
|
||||||
assertThat(config.getAllowCredentials()).isNull();
|
assertThat(config.getAllowCredentials()).isNull();
|
||||||
|
assertThat(config.getAllowPrivateNetwork()).isNull();
|
||||||
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(1800));
|
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(1800));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user