diff --git a/module/spring-boot-cloudfoundry/src/main/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfiguration.java b/module/spring-boot-cloudfoundry/src/main/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfiguration.java index 8caffb08bb3..fbf6b272339 100644 --- a/module/spring-boot-cloudfoundry/src/main/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfiguration.java +++ b/module/spring-boot-cloudfoundry/src/main/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfiguration.java @@ -148,7 +148,7 @@ public final class CloudFoundryActuatorAutoConfiguration { ? new SecurityService(restClientBuilder, cloudControllerUrl, skipSslValidation) : null; } - private CorsConfiguration getCorsConfiguration() { + private static CorsConfiguration getCorsConfiguration() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin(CorsConfiguration.ALL); corsConfiguration.setAllowedMethods(Arrays.asList(HttpMethod.GET.name(), HttpMethod.POST.name())); @@ -173,6 +173,8 @@ public final class CloudFoundryActuatorAutoConfiguration { SecurityFilterChain cloudFoundrySecurityFilterChain(HttpSecurity http) throws Exception { RequestMatcher cloudFoundryRequest = getRequestMatcher(); http.csrf((csrf) -> csrf.ignoringRequestMatchers(cloudFoundryRequest)); + CorsConfiguration corsConfiguration = getCorsConfiguration(); + http.cors((cors) -> cors.configurationSource((request) -> corsConfiguration)); http.securityMatchers((matches) -> matches.requestMatchers(cloudFoundryRequest)) .authorizeHttpRequests((authorize) -> authorize.anyRequest().permitAll()); return http.build(); diff --git a/module/spring-boot-cloudfoundry/src/test/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfigurationTests.java b/module/spring-boot-cloudfoundry/src/test/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfigurationTests.java index aa3cbc1bb0a..149aadbf180 100644 --- a/module/spring-boot-cloudfoundry/src/test/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfigurationTests.java +++ b/module/spring-boot-cloudfoundry/src/test/java/org/springframework/boot/cloudfoundry/autoconfigure/actuate/endpoint/servlet/CloudFoundryActuatorAutoConfigurationTests.java @@ -50,6 +50,7 @@ import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration; import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration; import org.springframework.context.ApplicationContext; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; @@ -61,11 +62,13 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.assertj.MockMvcTester; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CompositeFilter; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -212,6 +215,22 @@ class CloudFoundryActuatorAutoConfigurationTests { }); } + @Test + void crossOriginRequestToCloudFoundryPathsPermittedBySpringSecurity() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", new CorsConfiguration()); + this.contextRunner.withBean(TestEndpoint.class, TestEndpoint::new) + .withBean("corsConfigurationSource", UrlBasedCorsConfigurationSource.class, () -> source) + .withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id") + .run((context) -> { + MockMvc mvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build(); + mvc.perform(get(BASE_PATH + "/test").header(HttpHeaders.ORIGIN, "elsewhere.example.com") + .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isServiceUnavailable()); + // If CORS fails we'll get a 403, if it works we get service unavailable + // because of "Cloud controller URL is not available" + }); + } + private SecurityFilterChain getSecurityFilterChain(AssertableWebApplicationContext context) { Filter springSecurityFilterChain = context.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN, Filter.class); FilterChainProxy filterChainProxy = getFilterChainProxy(springSecurityFilterChain);