mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Use endpoint mappings in CloudFoundry integration
Closes gh-35411
This commit is contained in:
parent
585286e472
commit
307f3c3399
@ -28,6 +28,7 @@ import reactor.core.publisher.Mono;
|
|||||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
|
||||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
|
||||||
import org.springframework.boot.actuate.endpoint.EndpointId;
|
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||||
|
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||||
@ -56,12 +57,15 @@ class CloudFoundryWebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointH
|
|||||||
|
|
||||||
private final EndpointLinksResolver linksResolver;
|
private final EndpointLinksResolver linksResolver;
|
||||||
|
|
||||||
|
private final Collection<ExposableEndpoint<?>> allEndpoints;
|
||||||
|
|
||||||
CloudFoundryWebFluxEndpointHandlerMapping(EndpointMapping endpointMapping,
|
CloudFoundryWebFluxEndpointHandlerMapping(EndpointMapping endpointMapping,
|
||||||
Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes,
|
Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes,
|
||||||
CorsConfiguration corsConfiguration, CloudFoundrySecurityInterceptor securityInterceptor,
|
CorsConfiguration corsConfiguration, CloudFoundrySecurityInterceptor securityInterceptor,
|
||||||
EndpointLinksResolver linksResolver) {
|
Collection<ExposableEndpoint<?>> allEndpoints) {
|
||||||
super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, true);
|
super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, true);
|
||||||
this.linksResolver = linksResolver;
|
this.linksResolver = new EndpointLinksResolver(allEndpoints);
|
||||||
|
this.allEndpoints = allEndpoints;
|
||||||
this.securityInterceptor = securityInterceptor;
|
this.securityInterceptor = securityInterceptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +80,10 @@ class CloudFoundryWebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointH
|
|||||||
return new CloudFoundryLinksHandler();
|
return new CloudFoundryLinksHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Collection<ExposableEndpoint<?>> getAllEndpoints() {
|
||||||
|
return this.allEndpoints;
|
||||||
|
}
|
||||||
|
|
||||||
class CloudFoundryLinksHandler implements LinksHandler {
|
class CloudFoundryLinksHandler implements LinksHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -33,10 +33,10 @@ import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoC
|
|||||||
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
|
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
|
||||||
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||||
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
|
||||||
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
|
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
|
||||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||||
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
|
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
|
||||||
@ -82,6 +82,8 @@ import org.springframework.web.server.WebFilter;
|
|||||||
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
|
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
|
||||||
public class ReactiveCloudFoundryActuatorAutoConfiguration {
|
public class ReactiveCloudFoundryActuatorAutoConfiguration {
|
||||||
|
|
||||||
|
private static final String BASE_PATH = "/cloudfoundryapplication";
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@ConditionalOnAvailableEndpoint
|
@ConditionalOnAvailableEndpoint
|
||||||
@ -117,9 +119,8 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration {
|
|||||||
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
|
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
|
||||||
allEndpoints.addAll(webEndpoints);
|
allEndpoints.addAll(webEndpoints);
|
||||||
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
|
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
|
||||||
return new CloudFoundryWebFluxEndpointHandlerMapping(new EndpointMapping("/cloudfoundryapplication"),
|
return new CloudFoundryWebFluxEndpointHandlerMapping(new EndpointMapping(BASE_PATH), webEndpoints,
|
||||||
webEndpoints, endpointMediaTypes, getCorsConfiguration(), securityInterceptor,
|
endpointMediaTypes, getCorsConfiguration(), securityInterceptor, allEndpoints);
|
||||||
new EndpointLinksResolver(allEndpoints));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CloudFoundrySecurityInterceptor getSecurityInterceptor(WebClient.Builder webClientBuilder,
|
private CloudFoundrySecurityInterceptor getSecurityInterceptor(WebClient.Builder webClientBuilder,
|
||||||
@ -155,25 +156,33 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration {
|
|||||||
static class IgnoredPathsSecurityConfiguration {
|
static class IgnoredPathsSecurityConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
WebFilterChainPostProcessor webFilterChainPostProcessor() {
|
WebFilterChainPostProcessor webFilterChainPostProcessor(
|
||||||
return new WebFilterChainPostProcessor();
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping) {
|
||||||
|
return new WebFilterChainPostProcessor(handlerMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class WebFilterChainPostProcessor implements BeanPostProcessor {
|
private static class WebFilterChainPostProcessor implements BeanPostProcessor {
|
||||||
|
|
||||||
|
private final PathMappedEndpoints pathMappedEndpoints;
|
||||||
|
|
||||||
|
WebFilterChainPostProcessor(CloudFoundryWebFluxEndpointHandlerMapping handlerMapping) {
|
||||||
|
this.pathMappedEndpoints = new PathMappedEndpoints(BASE_PATH, handlerMapping::getAllEndpoints);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||||
if (bean instanceof WebFilterChainProxy) {
|
if (bean instanceof WebFilterChainProxy) {
|
||||||
return postProcess((WebFilterChainProxy) bean);
|
return postProcess((WebFilterChainProxy) bean, this.pathMappedEndpoints);
|
||||||
}
|
}
|
||||||
return bean;
|
return bean;
|
||||||
}
|
}
|
||||||
|
|
||||||
private WebFilterChainProxy postProcess(WebFilterChainProxy existing) {
|
private WebFilterChainProxy postProcess(WebFilterChainProxy existing, PathMappedEndpoints pathMappedEndpoints) {
|
||||||
|
List<String> paths = getPaths(pathMappedEndpoints);
|
||||||
ServerWebExchangeMatcher cloudFoundryRequestMatcher = ServerWebExchangeMatchers
|
ServerWebExchangeMatcher cloudFoundryRequestMatcher = ServerWebExchangeMatchers
|
||||||
.pathMatchers("/cloudfoundryapplication/**");
|
.pathMatchers(paths.toArray(new String[] {}));
|
||||||
WebFilter noOpFilter = (exchange, chain) -> chain.filter(exchange);
|
WebFilter noOpFilter = (exchange, chain) -> chain.filter(exchange);
|
||||||
MatcherSecurityWebFilterChain ignoredRequestFilterChain = new MatcherSecurityWebFilterChain(
|
MatcherSecurityWebFilterChain ignoredRequestFilterChain = new MatcherSecurityWebFilterChain(
|
||||||
cloudFoundryRequestMatcher, Collections.singletonList(noOpFilter));
|
cloudFoundryRequestMatcher, Collections.singletonList(noOpFilter));
|
||||||
@ -182,6 +191,14 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration {
|
|||||||
return new WebFilterChainProxy(ignoredRequestFilterChain, allRequestsFilterChain);
|
return new WebFilterChainProxy(ignoredRequestFilterChain, allRequestsFilterChain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<String> getPaths(PathMappedEndpoints pathMappedEndpoints) {
|
||||||
|
List<String> paths = new ArrayList<>();
|
||||||
|
pathMappedEndpoints.getAllPaths().forEach((path) -> paths.add(path + "/**"));
|
||||||
|
paths.add(BASE_PATH);
|
||||||
|
paths.add(BASE_PATH + "/");
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,10 +31,10 @@ import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfi
|
|||||||
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
|
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
|
||||||
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||||
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
|
||||||
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
|
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
|
||||||
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
|
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
|
||||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||||
@ -66,6 +66,9 @@ import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
|||||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.servlet.DispatcherServlet;
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
|
||||||
@ -86,6 +89,8 @@ import org.springframework.web.servlet.DispatcherServlet;
|
|||||||
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
|
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
|
||||||
public class CloudFoundryActuatorAutoConfiguration {
|
public class CloudFoundryActuatorAutoConfiguration {
|
||||||
|
|
||||||
|
private static final String BASE_PATH = "/cloudfoundryapplication";
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@ConditionalOnAvailableEndpoint
|
@ConditionalOnAvailableEndpoint
|
||||||
@ -123,8 +128,7 @@ public class CloudFoundryActuatorAutoConfiguration {
|
|||||||
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
|
allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
|
||||||
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
|
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
|
||||||
return new CloudFoundryWebEndpointServletHandlerMapping(new EndpointMapping("/cloudfoundryapplication"),
|
return new CloudFoundryWebEndpointServletHandlerMapping(new EndpointMapping("/cloudfoundryapplication"),
|
||||||
webEndpoints, endpointMediaTypes, getCorsConfiguration(), securityInterceptor,
|
webEndpoints, endpointMediaTypes, getCorsConfiguration(), securityInterceptor, allEndpoints);
|
||||||
new EndpointLinksResolver(allEndpoints));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CloudFoundrySecurityInterceptor getSecurityInterceptor(RestTemplateBuilder restTemplateBuilder,
|
private CloudFoundrySecurityInterceptor getSecurityInterceptor(RestTemplateBuilder restTemplateBuilder,
|
||||||
@ -164,8 +168,9 @@ public class CloudFoundryActuatorAutoConfiguration {
|
|||||||
public static class IgnoredCloudFoundryPathsWebSecurityConfiguration {
|
public static class IgnoredCloudFoundryPathsWebSecurityConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
IgnoredCloudFoundryPathsWebSecurityCustomizer ignoreCloudFoundryPathsWebSecurityCustomizer() {
|
IgnoredCloudFoundryPathsWebSecurityCustomizer ignoreCloudFoundryPathsWebSecurityCustomizer(
|
||||||
return new IgnoredCloudFoundryPathsWebSecurityCustomizer();
|
CloudFoundryWebEndpointServletHandlerMapping handlerMapping) {
|
||||||
|
return new IgnoredCloudFoundryPathsWebSecurityCustomizer(handlerMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -173,9 +178,22 @@ public class CloudFoundryActuatorAutoConfiguration {
|
|||||||
@Order(SecurityProperties.IGNORED_ORDER)
|
@Order(SecurityProperties.IGNORED_ORDER)
|
||||||
static class IgnoredCloudFoundryPathsWebSecurityCustomizer implements WebSecurityCustomizer {
|
static class IgnoredCloudFoundryPathsWebSecurityCustomizer implements WebSecurityCustomizer {
|
||||||
|
|
||||||
|
private final PathMappedEndpoints pathMappedEndpoints;
|
||||||
|
|
||||||
|
IgnoredCloudFoundryPathsWebSecurityCustomizer(CloudFoundryWebEndpointServletHandlerMapping handlerMapping) {
|
||||||
|
this.pathMappedEndpoints = new PathMappedEndpoints(BASE_PATH, handlerMapping::getAllEndpoints);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void customize(WebSecurity web) {
|
public void customize(WebSecurity web) {
|
||||||
web.ignoring().requestMatchers(new AntPathRequestMatcher("/cloudfoundryapplication/**"));
|
List<RequestMatcher> requestMatchers = new ArrayList<>();
|
||||||
|
this.pathMappedEndpoints.getAllPaths()
|
||||||
|
.forEach((path) -> requestMatchers.add(new AntPathRequestMatcher(path + "/**")));
|
||||||
|
requestMatchers.add(new AntPathRequestMatcher(BASE_PATH));
|
||||||
|
requestMatchers.add(new AntPathRequestMatcher(BASE_PATH + "/"));
|
||||||
|
if (!CollectionUtils.isEmpty(requestMatchers)) {
|
||||||
|
web.ignoring().requestMatchers(new OrRequestMatcher(requestMatchers));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import org.apache.commons.logging.LogFactory;
|
|||||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
|
||||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
|
||||||
import org.springframework.boot.actuate.endpoint.EndpointId;
|
import org.springframework.boot.actuate.endpoint.EndpointId;
|
||||||
|
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||||
@ -60,13 +61,16 @@ class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebMvcEndpoin
|
|||||||
|
|
||||||
private final EndpointLinksResolver linksResolver;
|
private final EndpointLinksResolver linksResolver;
|
||||||
|
|
||||||
|
private final Collection<ExposableEndpoint<?>> allEndpoints;
|
||||||
|
|
||||||
CloudFoundryWebEndpointServletHandlerMapping(EndpointMapping endpointMapping,
|
CloudFoundryWebEndpointServletHandlerMapping(EndpointMapping endpointMapping,
|
||||||
Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes,
|
Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes,
|
||||||
CorsConfiguration corsConfiguration, CloudFoundrySecurityInterceptor securityInterceptor,
|
CorsConfiguration corsConfiguration, CloudFoundrySecurityInterceptor securityInterceptor,
|
||||||
EndpointLinksResolver linksResolver) {
|
Collection<ExposableEndpoint<?>> allEndpoints) {
|
||||||
super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, true);
|
super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, true);
|
||||||
this.securityInterceptor = securityInterceptor;
|
this.securityInterceptor = securityInterceptor;
|
||||||
this.linksResolver = linksResolver;
|
this.linksResolver = new EndpointLinksResolver(allEndpoints);
|
||||||
|
this.allEndpoints = allEndpoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -80,6 +84,10 @@ class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebMvcEndpoin
|
|||||||
return new CloudFoundryLinksHandler();
|
return new CloudFoundryLinksHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Collection<ExposableEndpoint<?>> getAllEndpoints() {
|
||||||
|
return this.allEndpoints;
|
||||||
|
}
|
||||||
|
|
||||||
class CloudFoundryLinksHandler implements LinksHandler {
|
class CloudFoundryLinksHandler implements LinksHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -17,8 +17,11 @@
|
|||||||
package org.springframework.boot.actuate.autoconfigure.cloudfoundry.reactive;
|
package org.springframework.boot.actuate.autoconfigure.cloudfoundry.reactive;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@ -28,15 +31,16 @@ import reactor.core.publisher.Mono;
|
|||||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
|
||||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException;
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException;
|
||||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason;
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason;
|
||||||
|
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
||||||
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
||||||
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
|
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
|
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
|
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
|
||||||
@ -184,9 +188,10 @@ class CloudFoundryWebFluxEndpointIntegrationTests {
|
|||||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||||
corsConfiguration.setAllowedOrigins(Arrays.asList("https://example.com"));
|
corsConfiguration.setAllowedOrigins(Arrays.asList("https://example.com"));
|
||||||
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
|
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
|
||||||
return new CloudFoundryWebFluxEndpointHandlerMapping(new EndpointMapping("/cfApplication"),
|
Collection<ExposableWebEndpoint> webEndpoints = webEndpointDiscoverer.getEndpoints();
|
||||||
webEndpointDiscoverer.getEndpoints(), endpointMediaTypes, corsConfiguration, interceptor,
|
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>(webEndpoints);
|
||||||
new EndpointLinksResolver(webEndpointDiscoverer.getEndpoints()));
|
return new CloudFoundryWebFluxEndpointHandlerMapping(new EndpointMapping("/cfApplication"), webEndpoints,
|
||||||
|
endpointMediaTypes, corsConfiguration, interceptor, allEndpoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -95,6 +95,8 @@ class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
|||||||
InfoContributorAutoConfiguration.class, InfoEndpointAutoConfiguration.class,
|
InfoContributorAutoConfiguration.class, InfoEndpointAutoConfiguration.class,
|
||||||
ProjectInfoAutoConfiguration.class, ReactiveCloudFoundryActuatorAutoConfiguration.class));
|
ProjectInfoAutoConfiguration.class, ReactiveCloudFoundryActuatorAutoConfiguration.class));
|
||||||
|
|
||||||
|
private static final String BASE_PATH = "/cloudfoundryapplication";
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
void close() {
|
void close() {
|
||||||
HttpResources.reset();
|
HttpResources.reset();
|
||||||
@ -170,26 +172,36 @@ class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
|||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
void cloudFoundryPathsIgnoredBySpringSecurity() {
|
void cloudFoundryPathsIgnoredBySpringSecurity() {
|
||||||
this.contextRunner.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
this.contextRunner.withBean(TestEndpoint.class, TestEndpoint::new).withPropertyValues("VCAP_APPLICATION:---",
|
||||||
"vcap.application.cf_api:https://my-cloud-controller.com").run((context) -> {
|
"vcap.application.application_id:my-app-id", "vcap.application.cf_api:https://my-cloud-controller.com")
|
||||||
|
.run((context) -> {
|
||||||
WebFilterChainProxy chainProxy = context.getBean(WebFilterChainProxy.class);
|
WebFilterChainProxy chainProxy = context.getBean(WebFilterChainProxy.class);
|
||||||
List<SecurityWebFilterChain> filters = (List<SecurityWebFilterChain>) ReflectionTestUtils
|
List<SecurityWebFilterChain> filters = (List<SecurityWebFilterChain>) ReflectionTestUtils
|
||||||
.getField(chainProxy, "filters");
|
.getField(chainProxy, "filters");
|
||||||
Boolean cfRequestMatches = filters.get(0)
|
Boolean cfBaseRequestMatches = getMatches(filters, BASE_PATH);
|
||||||
.matches(MockServerWebExchange
|
Boolean cfBaseWithTrailingSlashRequestMatches = getMatches(filters, BASE_PATH + "/");
|
||||||
.from(MockServerHttpRequest.get("/cloudfoundryapplication/my-path").build()))
|
Boolean cfRequestMatches = getMatches(filters, BASE_PATH + "/test");
|
||||||
.block(Duration.ofSeconds(30));
|
Boolean cfRequestWithAdditionalPathMatches = getMatches(filters, BASE_PATH + "/test/a");
|
||||||
Boolean otherRequestMatches = filters.get(0)
|
Boolean otherCfRequestMatches = getMatches(filters, BASE_PATH + "/other-path");
|
||||||
.matches(MockServerWebExchange.from(MockServerHttpRequest.get("/some-other-path").build()))
|
Boolean otherRequestMatches = getMatches(filters, "/some-other-path");
|
||||||
.block(Duration.ofSeconds(30));
|
assertThat(cfBaseRequestMatches).isTrue();
|
||||||
|
assertThat(cfBaseWithTrailingSlashRequestMatches).isTrue();
|
||||||
assertThat(cfRequestMatches).isTrue();
|
assertThat(cfRequestMatches).isTrue();
|
||||||
|
assertThat(cfRequestWithAdditionalPathMatches).isTrue();
|
||||||
|
assertThat(otherCfRequestMatches).isFalse();
|
||||||
assertThat(otherRequestMatches).isFalse();
|
assertThat(otherRequestMatches).isFalse();
|
||||||
otherRequestMatches = filters.get(1)
|
otherRequestMatches = filters.get(1)
|
||||||
.matches(MockServerWebExchange.from(MockServerHttpRequest.get("/some-other-path").build()))
|
.matches(MockServerWebExchange.from(MockServerHttpRequest.get("/some-other-path").build()))
|
||||||
.block(Duration.ofSeconds(30));
|
.block(Duration.ofSeconds(30));
|
||||||
assertThat(otherRequestMatches).isTrue();
|
assertThat(otherRequestMatches).isTrue();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Boolean getMatches(List<SecurityWebFilterChain> filters, String urlTemplate) {
|
||||||
|
Boolean cfBaseRequestMatches = filters.get(0)
|
||||||
|
.matches(MockServerWebExchange.from(MockServerHttpRequest.get(urlTemplate).build()))
|
||||||
|
.block(Duration.ofSeconds(30));
|
||||||
|
return cfBaseRequestMatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -77,6 +77,8 @@ class CloudFoundryActuatorAutoConfigurationTests {
|
|||||||
ServletManagementContextAutoConfiguration.class, EndpointAutoConfiguration.class,
|
ServletManagementContextAutoConfiguration.class, EndpointAutoConfiguration.class,
|
||||||
WebEndpointAutoConfiguration.class, CloudFoundryActuatorAutoConfiguration.class));
|
WebEndpointAutoConfiguration.class, CloudFoundryActuatorAutoConfiguration.class));
|
||||||
|
|
||||||
|
private static String BASE_PATH = "/cloudfoundryapplication";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void cloudFoundryPlatformActive() {
|
void cloudFoundryPlatformActive() {
|
||||||
this.contextRunner.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
this.contextRunner.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
||||||
@ -160,20 +162,31 @@ class CloudFoundryActuatorAutoConfigurationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void cloudFoundryPathsIgnoredBySpringSecurity() {
|
void cloudFoundryPathsIgnoredBySpringSecurity() {
|
||||||
this.contextRunner.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id")
|
this.contextRunner.withBean(TestEndpoint.class, TestEndpoint::new)
|
||||||
|
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id")
|
||||||
.run((context) -> {
|
.run((context) -> {
|
||||||
FilterChainProxy securityFilterChain = (FilterChainProxy) context
|
FilterChainProxy securityFilterChain = (FilterChainProxy) context
|
||||||
.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
|
.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
|
||||||
SecurityFilterChain chain = securityFilterChain.getFilterChains().get(0);
|
SecurityFilterChain chain = securityFilterChain.getFilterChains().get(0);
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
|
||||||
request.setServletPath("/cloudfoundryapplication/my-path");
|
|
||||||
assertThat(chain.getFilters()).isEmpty();
|
assertThat(chain.getFilters()).isEmpty();
|
||||||
assertThat(chain.matches(request)).isTrue();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
testCloudFoundrySecurity(request, BASE_PATH, chain);
|
||||||
|
testCloudFoundrySecurity(request, BASE_PATH + "/", chain);
|
||||||
|
testCloudFoundrySecurity(request, BASE_PATH + "/test", chain);
|
||||||
|
testCloudFoundrySecurity(request, BASE_PATH + "/test/a", chain);
|
||||||
|
request.setServletPath(BASE_PATH + "/other-path");
|
||||||
|
assertThat(chain.matches(request)).isFalse();
|
||||||
request.setServletPath("/some-other-path");
|
request.setServletPath("/some-other-path");
|
||||||
assertThat(chain.matches(request)).isFalse();
|
assertThat(chain.matches(request)).isFalse();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void testCloudFoundrySecurity(MockHttpServletRequest request, String basePath,
|
||||||
|
SecurityFilterChain chain) {
|
||||||
|
request.setServletPath(basePath);
|
||||||
|
assertThat(chain.matches(request)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void cloudFoundryPlatformInactive() {
|
void cloudFoundryPlatformInactive() {
|
||||||
this.contextRunner.withPropertyValues()
|
this.contextRunner.withPropertyValues()
|
||||||
|
@ -17,8 +17,11 @@
|
|||||||
package org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet;
|
package org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@ -28,15 +31,16 @@ import org.junit.jupiter.api.Test;
|
|||||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
|
||||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException;
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException;
|
||||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason;
|
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason;
|
||||||
|
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
||||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
|
||||||
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
||||||
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
|
import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
|
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
||||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||||
|
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
|
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
|
||||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||||
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||||
@ -180,9 +184,10 @@ class CloudFoundryMvcWebEndpointIntegrationTests {
|
|||||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||||
corsConfiguration.setAllowedOrigins(Arrays.asList("https://example.com"));
|
corsConfiguration.setAllowedOrigins(Arrays.asList("https://example.com"));
|
||||||
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
|
corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
|
||||||
return new CloudFoundryWebEndpointServletHandlerMapping(new EndpointMapping("/cfApplication"),
|
Collection<ExposableWebEndpoint> webEndpoints = webEndpointDiscoverer.getEndpoints();
|
||||||
webEndpointDiscoverer.getEndpoints(), endpointMediaTypes, corsConfiguration, interceptor,
|
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>(webEndpoints);
|
||||||
new EndpointLinksResolver(webEndpointDiscoverer.getEndpoints()));
|
return new CloudFoundryWebEndpointServletHandlerMapping(new EndpointMapping("/cfApplication"), webEndpoints,
|
||||||
|
endpointMediaTypes, corsConfiguration, interceptor, allEndpoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
Loading…
Reference in New Issue
Block a user