Add config prop for endpoints' CORS allowed origin patterns
See gh-24680
This commit is contained in:
parent
743343cf18
commit
d7f891be39
|
@ -38,10 +38,17 @@ public class CorsEndpointProperties {
|
|||
|
||||
/**
|
||||
* Comma-separated list of origins to allow. '*' allows all origins. When not set,
|
||||
* CORS support is disabled.
|
||||
* CORS support is disabled. When credentials are supported only explicit urls are
|
||||
* allowed.
|
||||
*/
|
||||
private List<String> allowedOrigins = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Comma-separated list of origins patterns to allow. Must be used when credentials
|
||||
* are supported and do you want to use wildcard urls.
|
||||
*/
|
||||
private List<String> allowedOriginPatterns = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Comma-separated list of methods to allow. '*' allows all methods. When not set,
|
||||
* defaults to GET.
|
||||
|
@ -78,6 +85,14 @@ public class CorsEndpointProperties {
|
|||
this.allowedOrigins = allowedOrigins;
|
||||
}
|
||||
|
||||
public List<String> getAllowedOriginPatterns() {
|
||||
return this.allowedOriginPatterns;
|
||||
}
|
||||
|
||||
public void setAllowedOriginPatterns(List<String> allowedOriginPatterns) {
|
||||
this.allowedOriginPatterns = allowedOriginPatterns;
|
||||
}
|
||||
|
||||
public List<String> getAllowedMethods() {
|
||||
return this.allowedMethods;
|
||||
}
|
||||
|
@ -119,12 +134,13 @@ public class CorsEndpointProperties {
|
|||
}
|
||||
|
||||
public CorsConfiguration toCorsConfiguration() {
|
||||
if (CollectionUtils.isEmpty(this.allowedOrigins)) {
|
||||
if (CollectionUtils.isEmpty(this.allowedOrigins) && CollectionUtils.isEmpty(this.allowedOriginPatterns)) {
|
||||
return null;
|
||||
}
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
map.from(this::getAllowedOrigins).to(configuration::setAllowedOrigins);
|
||||
map.from(this::getAllowedOriginPatterns).to(configuration::setAllowedOriginPatterns);
|
||||
map.from(this::getAllowedHeaders).whenNot(CollectionUtils::isEmpty).to(configuration::setAllowedHeaders);
|
||||
map.from(this::getAllowedMethods).whenNot(CollectionUtils::isEmpty).to(configuration::setAllowedMethods);
|
||||
map.from(this::getExposedHeaders).whenNot(CollectionUtils::isEmpty).to(configuration::setExposedHeaders);
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.integrationtest;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -145,6 +148,29 @@ class WebFluxEndpointCorsIntegrationTests {
|
|||
.expectHeader().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void settingAllowedOriginsPattern() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.endpoints.web.cors.allowed-origin-patterns:*.example.com",
|
||||
"management.endpoints.web.cors.allow-credentials:true")
|
||||
.run(withWebTestClient((webTestClient) -> webTestClient.options().uri("/actuator/beans")
|
||||
.header("Origin", "spring.example.com")
|
||||
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "HEAD").exchange().expectStatus().isOk()
|
||||
.expectHeader().valueEquals(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET,HEAD")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void requestsWithDisallowedOriginPatternsAreRejected() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("management.endpoints.web.cors.allowed-origin-patterns:*.example.com",
|
||||
"management.endpoints.web.cors.allow-credentials:true")
|
||||
.run(withWebTestClient((webTestClient) -> webTestClient.options().uri("/actuator/beans")
|
||||
.header("Origin", "spring.example.org")
|
||||
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "HEAD").exchange().expectStatus()
|
||||
.isForbidden()));
|
||||
|
||||
}
|
||||
|
||||
private ContextConsumer<ReactiveWebApplicationContext> withWebTestClient(Consumer<WebTestClient> webTestClient) {
|
||||
return (context) -> webTestClient.accept(WebTestClient.bindToApplicationContext(context).configureClient()
|
||||
.baseUrl("https://spring.example.org").build());
|
||||
|
|
|
@ -156,6 +156,27 @@ class WebMvcEndpointCorsIntegrationTests {
|
|||
.andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void settingAllowedOriginsPattern() {
|
||||
this.contextRunner.withPropertyValues("management.endpoints.web.cors.allowed-origin-patterns:*.example.com",
|
||||
"management.endpoints.web.cors.allow-credentials:true").run(withMockMvc((mockMvc) -> {
|
||||
mockMvc.perform(options("/actuator/beans").header("Origin", "bar.example.com")
|
||||
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET")).andExpect(status().isOk());
|
||||
performAcceptedCorsRequest(mockMvc);
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void requestsWithDisallowedOriginPatternsAreRejected() {
|
||||
this.contextRunner.withPropertyValues("management.endpoints.web.cors.allowed-origin-patterns:*.example.com",
|
||||
"management.endpoints.web.cors.allow-credentials:true").run(withMockMvc((mockMvc) -> {
|
||||
mockMvc.perform(options("/actuator/beans").header("Origin", "bar.domain.com")
|
||||
.header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"))
|
||||
.andExpect(status().isForbidden());
|
||||
performAcceptedCorsRequest(mockMvc);
|
||||
}));
|
||||
}
|
||||
|
||||
private ContextConsumer<WebApplicationContext> withMockMvc(MockMvcConsumer mockMvc) {
|
||||
return (context) -> mockMvc.accept(MockMvcBuilders.webAppContextSetup(context).build());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue