Permit write and delete operations on Cloud Foundry with Spring Security
Update `CloudFoundryActuatorAutoConfiguration` so that CSRF protection is ignored for Cloud Foundry endpoint requests. This aligns with the behavior of Spring Boot 3.4 where `ignoring()` was used for security configuration. With the migration to `permitAll()` we now need additional CSRF configuration. Fixes gh-45848
This commit is contained in:
@@ -174,6 +174,7 @@ public class CloudFoundryActuatorAutoConfiguration {
|
||||
SecurityFilterChain cloudFoundrySecurityFilterChain(HttpSecurity http,
|
||||
CloudFoundryWebEndpointServletHandlerMapping handlerMapping) throws Exception {
|
||||
RequestMatcher cloudFoundryRequest = getRequestMatcher(handlerMapping);
|
||||
http.csrf((csrf) -> csrf.ignoringRequestMatchers(cloudFoundryRequest));
|
||||
http.securityMatchers((matches) -> matches.requestMatchers(cloudFoundryRequest))
|
||||
.authorizeHttpRequests((authorize) -> authorize.anyRequest().permitAll());
|
||||
return http.build();
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.boot.actuate.endpoint.ApiVersion;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperation;
|
||||
@@ -49,17 +50,24 @@ import org.springframework.boot.test.context.assertj.AssertableWebApplicationCon
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
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.client.RestTemplate;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
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.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for {@link CloudFoundryActuatorAutoConfiguration}.
|
||||
@@ -170,7 +178,7 @@ class CloudFoundryActuatorAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void cloudFoundryPathsIgnoredBySpringSecurity() {
|
||||
void cloudFoundryPathsPermittedBySpringSecurity() {
|
||||
this.contextRunner.withBean(TestEndpoint.class, TestEndpoint::new)
|
||||
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id")
|
||||
.run((context) -> {
|
||||
@@ -189,6 +197,19 @@ class CloudFoundryActuatorAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void cloudFoundryPathsPermittedWithCsrfBySpringSecurity() {
|
||||
this.contextRunner.withBean(TestEndpoint.class, TestEndpoint::new)
|
||||
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id")
|
||||
.run((context) -> {
|
||||
MockMvc mvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
|
||||
mvc.perform(post(BASE_PATH + "/test?name=test").contentType(MediaType.APPLICATION_JSON)
|
||||
.with(csrf().useInvalidToken())).andExpect(status().isServiceUnavailable());
|
||||
// If CSRF 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);
|
||||
@@ -258,7 +279,7 @@ class CloudFoundryActuatorAutoConfigurationTests {
|
||||
.findFirst()
|
||||
.get();
|
||||
Collection<WebOperation> operations = endpoint.getOperations();
|
||||
assertThat(operations).hasSize(1);
|
||||
assertThat(operations).hasSize(2);
|
||||
assertThat(operations.iterator().next().getRequestPredicate().getPath()).isEqualTo("test");
|
||||
});
|
||||
}
|
||||
@@ -307,6 +328,10 @@ class CloudFoundryActuatorAutoConfigurationTests {
|
||||
return "hello world";
|
||||
}
|
||||
|
||||
@WriteOperation
|
||||
void update(String name) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user