Allow "*" for Access-Control-Expose-Headers
Closes gh-26113
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -112,6 +112,8 @@ public @interface CrossOrigin {
|
||||
* {@code Expires}, {@code Last-Modified}, or {@code Pragma},
|
||||
* <p>Exposed headers are listed in the {@code Access-Control-Expose-Headers}
|
||||
* response header of actual CORS requests.
|
||||
* <p>The special value {@code "*"} allows all headers to be exposed for
|
||||
* non-credentialed requests.
|
||||
* <p>By default no headers are listed as exposed.
|
||||
*/
|
||||
String[] exposedHeaders() default {};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -254,13 +254,11 @@ public class CorsConfiguration {
|
||||
* {@code Cache-Control}, {@code Content-Language}, {@code Content-Type},
|
||||
* {@code Expires}, {@code Last-Modified}, or {@code Pragma}) that an
|
||||
* actual response might have and can be exposed.
|
||||
* <p>Note that {@code "*"} is not a valid exposed header value.
|
||||
* <p>The special value {@code "*"} allows all headers to be exposed for
|
||||
* non-credentialed requests.
|
||||
* <p>By default this is not set.
|
||||
*/
|
||||
public void setExposedHeaders(@Nullable List<String> exposedHeaders) {
|
||||
if (exposedHeaders != null && exposedHeaders.contains(ALL)) {
|
||||
throw new IllegalArgumentException("'*' is not a valid exposed header value");
|
||||
}
|
||||
this.exposedHeaders = (exposedHeaders != null ? new ArrayList<>(exposedHeaders) : null);
|
||||
}
|
||||
|
||||
@@ -276,12 +274,10 @@ public class CorsConfiguration {
|
||||
|
||||
/**
|
||||
* Add a response header to expose.
|
||||
* <p>Note that {@code "*"} is not a valid exposed header value.
|
||||
* <p>The special value {@code "*"} allows all headers to be exposed for
|
||||
* non-credentialed requests.
|
||||
*/
|
||||
public void addExposedHeader(String exposedHeader) {
|
||||
if (ALL.equals(exposedHeader)) {
|
||||
throw new IllegalArgumentException("'*' is not a valid exposed header value");
|
||||
}
|
||||
if (this.exposedHeaders == null) {
|
||||
this.exposedHeaders = new ArrayList<>(4);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -25,7 +25,6 @@ import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link CorsConfiguration}.
|
||||
@@ -61,29 +60,13 @@ public class CorsConfigurationTests {
|
||||
assertThat(config.getAllowedHeaders()).isEqualTo(Arrays.asList("*"));
|
||||
config.addAllowedMethod("*");
|
||||
assertThat(config.getAllowedMethods()).isEqualTo(Arrays.asList("*"));
|
||||
config.addExposedHeader("header1");
|
||||
config.addExposedHeader("header2");
|
||||
assertThat(config.getExposedHeaders()).isEqualTo(Arrays.asList("header1", "header2"));
|
||||
config.addExposedHeader("*");
|
||||
config.setAllowCredentials(true);
|
||||
assertThat((boolean) config.getAllowCredentials()).isTrue();
|
||||
config.setMaxAge(123L);
|
||||
assertThat(config.getMaxAge()).isEqualTo(new Long(123));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asteriskWildCardOnAddExposedHeader() {
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
config.addExposedHeader("*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asteriskWildCardOnSetExposedHeaders() {
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
config.setExposedHeaders(Arrays.asList("*")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void combineWithNull() {
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
@@ -120,26 +103,34 @@ public class CorsConfigurationTests {
|
||||
other.addAllowedMethod(HttpMethod.PUT.name());
|
||||
|
||||
CorsConfiguration combinedConfig = config.combine(other);
|
||||
assertThat(combinedConfig.getAllowedOrigins()).isEqualTo(Arrays.asList("https://domain.com"));
|
||||
assertThat(combinedConfig.getAllowedHeaders()).isEqualTo(Arrays.asList("header1"));
|
||||
assertThat(combinedConfig.getAllowedMethods()).isEqualTo(Arrays.asList(HttpMethod.PUT.name()));
|
||||
assertThat(combinedConfig).isNotNull();
|
||||
assertThat(combinedConfig.getAllowedOrigins()).containsExactly("https://domain.com");
|
||||
assertThat(combinedConfig.getAllowedHeaders()).containsExactly("header1");
|
||||
assertThat(combinedConfig.getAllowedMethods()).containsExactly(HttpMethod.PUT.name());
|
||||
assertThat(combinedConfig.getExposedHeaders()).isEmpty();
|
||||
|
||||
combinedConfig = other.combine(config);
|
||||
assertThat(combinedConfig.getAllowedOrigins()).isEqualTo(Arrays.asList("https://domain.com"));
|
||||
assertThat(combinedConfig.getAllowedHeaders()).isEqualTo(Arrays.asList("header1"));
|
||||
assertThat(combinedConfig.getAllowedMethods()).isEqualTo(Arrays.asList(HttpMethod.PUT.name()));
|
||||
assertThat(combinedConfig).isNotNull();
|
||||
assertThat(combinedConfig.getAllowedOrigins()).containsExactly("https://domain.com");
|
||||
assertThat(combinedConfig.getAllowedHeaders()).containsExactly("header1");
|
||||
assertThat(combinedConfig.getAllowedMethods()).containsExactly(HttpMethod.PUT.name());
|
||||
assertThat(combinedConfig.getExposedHeaders()).isEmpty();
|
||||
|
||||
combinedConfig = config.combine(new CorsConfiguration());
|
||||
assertThat(config.getAllowedOrigins()).isEqualTo(Arrays.asList("*"));
|
||||
assertThat(config.getAllowedHeaders()).isEqualTo(Arrays.asList("*"));
|
||||
assertThat(combinedConfig.getAllowedMethods()).isEqualTo(Arrays.asList(HttpMethod.GET.name(), HttpMethod.HEAD.name(),
|
||||
HttpMethod.POST.name()));
|
||||
assertThat(config.getAllowedOrigins()).containsExactly("*");
|
||||
assertThat(config.getAllowedHeaders()).containsExactly("*");
|
||||
assertThat(combinedConfig).isNotNull();
|
||||
assertThat(combinedConfig.getAllowedMethods())
|
||||
.containsExactly(HttpMethod.GET.name(), HttpMethod.HEAD.name(), HttpMethod.POST.name());
|
||||
assertThat(combinedConfig.getExposedHeaders()).isEmpty();
|
||||
|
||||
combinedConfig = new CorsConfiguration().combine(config);
|
||||
assertThat(config.getAllowedOrigins()).isEqualTo(Arrays.asList("*"));
|
||||
assertThat(config.getAllowedHeaders()).isEqualTo(Arrays.asList("*"));
|
||||
assertThat(combinedConfig.getAllowedMethods()).isEqualTo(Arrays.asList(HttpMethod.GET.name(), HttpMethod.HEAD.name(),
|
||||
HttpMethod.POST.name()));
|
||||
assertThat(config.getAllowedOrigins()).containsExactly("*");
|
||||
assertThat(config.getAllowedHeaders()).containsExactly("*");
|
||||
assertThat(combinedConfig).isNotNull();
|
||||
assertThat(combinedConfig.getAllowedMethods())
|
||||
.containsExactly(HttpMethod.GET.name(), HttpMethod.HEAD.name(), HttpMethod.POST.name());
|
||||
assertThat(combinedConfig.getExposedHeaders()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -147,20 +138,29 @@ public class CorsConfigurationTests {
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
config.addAllowedOrigin("*");
|
||||
config.addAllowedHeader("*");
|
||||
config.addExposedHeader("*");
|
||||
config.addAllowedMethod("*");
|
||||
CorsConfiguration other = new CorsConfiguration();
|
||||
other.addAllowedOrigin("https://domain.com");
|
||||
other.addAllowedHeader("header1");
|
||||
other.addExposedHeader("header2");
|
||||
other.addAllowedHeader("anotherHeader1");
|
||||
other.addExposedHeader("anotherHeader2");
|
||||
other.addAllowedMethod(HttpMethod.PUT.name());
|
||||
CorsConfiguration combinedConfig = config.combine(other);
|
||||
assertThat(combinedConfig.getAllowedOrigins()).isEqualTo(Arrays.asList("*"));
|
||||
assertThat(combinedConfig.getAllowedHeaders()).isEqualTo(Arrays.asList("*"));
|
||||
assertThat(combinedConfig.getAllowedMethods()).isEqualTo(Arrays.asList("*"));
|
||||
assertThat(combinedConfig).isNotNull();
|
||||
assertThat(combinedConfig.getAllowedOrigins()).containsExactly("*");
|
||||
assertThat(combinedConfig.getAllowedHeaders()).containsExactly("*");
|
||||
assertThat(combinedConfig.getExposedHeaders()).containsExactly("*");
|
||||
assertThat(combinedConfig.getAllowedMethods()).containsExactly("*");
|
||||
|
||||
combinedConfig = other.combine(config);
|
||||
assertThat(combinedConfig.getAllowedOrigins()).isEqualTo(Arrays.asList("*"));
|
||||
assertThat(combinedConfig.getAllowedHeaders()).isEqualTo(Arrays.asList("*"));
|
||||
assertThat(combinedConfig.getAllowedMethods()).isEqualTo(Arrays.asList("*"));
|
||||
assertThat(combinedConfig).isNotNull();
|
||||
assertThat(combinedConfig.getAllowedOrigins()).containsExactly("*");
|
||||
assertThat(combinedConfig.getAllowedHeaders()).containsExactly("*");
|
||||
assertThat(combinedConfig.getExposedHeaders()).containsExactly("*");
|
||||
assertThat(combinedConfig.getAllowedMethods()).containsExactly("*");
|
||||
assertThat(combinedConfig.getAllowedHeaders()).containsExactly("*");
|
||||
}
|
||||
|
||||
@Test // SPR-14792
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -93,7 +93,8 @@ public class CorsRegistration {
|
||||
* {@code Cache-Control}, {@code Content-Language}, {@code Content-Type},
|
||||
* {@code Expires}, {@code Last-Modified}, or {@code Pragma}, that an
|
||||
* actual response might have and can be exposed.
|
||||
* <p>Note that {@code "*"} is not supported on this property.
|
||||
* <p>The special value {@code "*"} allows all headers to be exposed for
|
||||
* non-credentialed requests.
|
||||
* <p>By default this is not set.
|
||||
*/
|
||||
public CorsRegistration exposedHeaders(String... headers) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -95,7 +95,8 @@ public class CorsRegistration {
|
||||
* {@code Cache-Control}, {@code Content-Language}, {@code Content-Type},
|
||||
* {@code Expires}, {@code Last-Modified}, or {@code Pragma}, that an
|
||||
* actual response might have and can be exposed.
|
||||
* <p>Note that {@code "*"} is not supported on this property.
|
||||
* <p>The special value {@code "*"} allows all headers to be exposed for
|
||||
* non-credentialed requests.
|
||||
* <p>By default this is not set.
|
||||
*/
|
||||
public CorsRegistration exposedHeaders(String... headers) {
|
||||
|
||||
@@ -1377,6 +1377,7 @@
|
||||
Comma-separated list of response headers other than simple headers (i.e.
|
||||
Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma) that an
|
||||
actual response might have and can be exposed.
|
||||
The special value "*" allows all headers to be exposed for non-credentialed requests.
|
||||
Empty by default.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
Reference in New Issue
Block a user