Use endpoint mappings in CloudFoundry integration
Closes gh-35085
This commit is contained in:
		
							parent
							
								
									da10c4e98b
								
							
						
					
					
						commit
						3522714c13
					
				| 
						 | 
				
			
			@ -28,6 +28,7 @@ import reactor.core.publisher.Mono;
 | 
			
		|||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
 | 
			
		||||
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
 | 
			
		||||
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.EndpointMapping;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
 | 
			
		||||
| 
						 | 
				
			
			@ -56,12 +57,15 @@ class CloudFoundryWebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointH
 | 
			
		|||
 | 
			
		||||
	private final EndpointLinksResolver linksResolver;
 | 
			
		||||
 | 
			
		||||
	private final Collection<ExposableEndpoint<?>> allEndpoints;
 | 
			
		||||
 | 
			
		||||
	CloudFoundryWebFluxEndpointHandlerMapping(EndpointMapping endpointMapping,
 | 
			
		||||
			Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes,
 | 
			
		||||
			CorsConfiguration corsConfiguration, CloudFoundrySecurityInterceptor securityInterceptor,
 | 
			
		||||
			EndpointLinksResolver linksResolver) {
 | 
			
		||||
			Collection<ExposableEndpoint<?>> allEndpoints) {
 | 
			
		||||
		super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, true);
 | 
			
		||||
		this.linksResolver = linksResolver;
 | 
			
		||||
		this.linksResolver = new EndpointLinksResolver(allEndpoints);
 | 
			
		||||
		this.allEndpoints = allEndpoints;
 | 
			
		||||
		this.securityInterceptor = securityInterceptor;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +80,10 @@ class CloudFoundryWebFluxEndpointHandlerMapping extends AbstractWebFluxEndpointH
 | 
			
		|||
		return new CloudFoundryLinksHandler();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Collection<ExposableEndpoint<?>> getAllEndpoints() {
 | 
			
		||||
		return this.allEndpoints;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	class CloudFoundryLinksHandler implements LinksHandler {
 | 
			
		||||
 | 
			
		||||
		@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.endpoint.ExposableEndpoint;
 | 
			
		||||
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.EndpointMediaTypes;
 | 
			
		||||
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.health.HealthEndpoint;
 | 
			
		||||
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +81,8 @@ import org.springframework.web.server.WebFilter;
 | 
			
		|||
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
 | 
			
		||||
public class ReactiveCloudFoundryActuatorAutoConfiguration {
 | 
			
		||||
 | 
			
		||||
	private static final String BASE_PATH = "/cloudfoundryapplication";
 | 
			
		||||
 | 
			
		||||
	@Bean
 | 
			
		||||
	@ConditionalOnMissingBean
 | 
			
		||||
	@ConditionalOnAvailableEndpoint
 | 
			
		||||
| 
						 | 
				
			
			@ -116,9 +118,8 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration {
 | 
			
		|||
		List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
 | 
			
		||||
		allEndpoints.addAll(webEndpoints);
 | 
			
		||||
		allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
 | 
			
		||||
		return new CloudFoundryWebFluxEndpointHandlerMapping(new EndpointMapping("/cloudfoundryapplication"),
 | 
			
		||||
				webEndpoints, endpointMediaTypes, getCorsConfiguration(), securityInterceptor,
 | 
			
		||||
				new EndpointLinksResolver(allEndpoints));
 | 
			
		||||
		return new CloudFoundryWebFluxEndpointHandlerMapping(new EndpointMapping(BASE_PATH), webEndpoints,
 | 
			
		||||
				endpointMediaTypes, getCorsConfiguration(), securityInterceptor, allEndpoints);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private CloudFoundrySecurityInterceptor getSecurityInterceptor(WebClient.Builder webClientBuilder,
 | 
			
		||||
| 
						 | 
				
			
			@ -154,25 +155,33 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration {
 | 
			
		|||
	static class IgnoredPathsSecurityConfiguration {
 | 
			
		||||
 | 
			
		||||
		@Bean
 | 
			
		||||
		WebFilterChainPostProcessor webFilterChainPostProcessor() {
 | 
			
		||||
			return new WebFilterChainPostProcessor();
 | 
			
		||||
		WebFilterChainPostProcessor webFilterChainPostProcessor(
 | 
			
		||||
				CloudFoundryWebFluxEndpointHandlerMapping handlerMapping) {
 | 
			
		||||
			return new WebFilterChainPostProcessor(handlerMapping);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static class WebFilterChainPostProcessor implements BeanPostProcessor {
 | 
			
		||||
 | 
			
		||||
		private final PathMappedEndpoints pathMappedEndpoints;
 | 
			
		||||
 | 
			
		||||
		WebFilterChainPostProcessor(CloudFoundryWebFluxEndpointHandlerMapping handlerMapping) {
 | 
			
		||||
			this.pathMappedEndpoints = new PathMappedEndpoints(BASE_PATH, handlerMapping::getAllEndpoints);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
 | 
			
		||||
			if (bean instanceof WebFilterChainProxy) {
 | 
			
		||||
				return postProcess((WebFilterChainProxy) bean);
 | 
			
		||||
				return postProcess((WebFilterChainProxy) bean, this.pathMappedEndpoints);
 | 
			
		||||
			}
 | 
			
		||||
			return bean;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private WebFilterChainProxy postProcess(WebFilterChainProxy existing) {
 | 
			
		||||
		private WebFilterChainProxy postProcess(WebFilterChainProxy existing, PathMappedEndpoints pathMappedEndpoints) {
 | 
			
		||||
			List<String> paths = getPaths(pathMappedEndpoints);
 | 
			
		||||
			ServerWebExchangeMatcher cloudFoundryRequestMatcher = ServerWebExchangeMatchers
 | 
			
		||||
				.pathMatchers("/cloudfoundryapplication/**");
 | 
			
		||||
				.pathMatchers(paths.toArray(new String[] {}));
 | 
			
		||||
			WebFilter noOpFilter = (exchange, chain) -> chain.filter(exchange);
 | 
			
		||||
			MatcherSecurityWebFilterChain ignoredRequestFilterChain = new MatcherSecurityWebFilterChain(
 | 
			
		||||
					cloudFoundryRequestMatcher, Collections.singletonList(noOpFilter));
 | 
			
		||||
| 
						 | 
				
			
			@ -181,6 +190,14 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration {
 | 
			
		|||
			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.endpoint.ExposableEndpoint;
 | 
			
		||||
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.EndpointMediaTypes;
 | 
			
		||||
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.ServletEndpointsSupplier;
 | 
			
		||||
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.configuration.WebSecurityCustomizer;
 | 
			
		||||
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.servlet.DispatcherServlet;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +88,8 @@ import org.springframework.web.servlet.DispatcherServlet;
 | 
			
		|||
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
 | 
			
		||||
public class CloudFoundryActuatorAutoConfiguration {
 | 
			
		||||
 | 
			
		||||
	private static final String BASE_PATH = "/cloudfoundryapplication";
 | 
			
		||||
 | 
			
		||||
	@Bean
 | 
			
		||||
	@ConditionalOnMissingBean
 | 
			
		||||
	@ConditionalOnAvailableEndpoint
 | 
			
		||||
| 
						 | 
				
			
			@ -122,8 +127,7 @@ public class CloudFoundryActuatorAutoConfiguration {
 | 
			
		|||
		allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
 | 
			
		||||
		allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
 | 
			
		||||
		return new CloudFoundryWebEndpointServletHandlerMapping(new EndpointMapping("/cloudfoundryapplication"),
 | 
			
		||||
				webEndpoints, endpointMediaTypes, getCorsConfiguration(), securityInterceptor,
 | 
			
		||||
				new EndpointLinksResolver(allEndpoints));
 | 
			
		||||
				webEndpoints, endpointMediaTypes, getCorsConfiguration(), securityInterceptor, allEndpoints);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private CloudFoundrySecurityInterceptor getSecurityInterceptor(RestTemplateBuilder restTemplateBuilder,
 | 
			
		||||
| 
						 | 
				
			
			@ -163,8 +167,9 @@ public class CloudFoundryActuatorAutoConfiguration {
 | 
			
		|||
	public static class IgnoredCloudFoundryPathsWebSecurityConfiguration {
 | 
			
		||||
 | 
			
		||||
		@Bean
 | 
			
		||||
		IgnoredCloudFoundryPathsWebSecurityCustomizer ignoreCloudFoundryPathsWebSecurityCustomizer() {
 | 
			
		||||
			return new IgnoredCloudFoundryPathsWebSecurityCustomizer();
 | 
			
		||||
		IgnoredCloudFoundryPathsWebSecurityCustomizer ignoreCloudFoundryPathsWebSecurityCustomizer(
 | 
			
		||||
				CloudFoundryWebEndpointServletHandlerMapping handlerMapping) {
 | 
			
		||||
			return new IgnoredCloudFoundryPathsWebSecurityCustomizer(handlerMapping);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -172,9 +177,22 @@ public class CloudFoundryActuatorAutoConfiguration {
 | 
			
		|||
	@Order(SecurityProperties.IGNORED_ORDER)
 | 
			
		||||
	static class IgnoredCloudFoundryPathsWebSecurityCustomizer implements WebSecurityCustomizer {
 | 
			
		||||
 | 
			
		||||
		private final PathMappedEndpoints pathMappedEndpoints;
 | 
			
		||||
 | 
			
		||||
		IgnoredCloudFoundryPathsWebSecurityCustomizer(CloudFoundryWebEndpointServletHandlerMapping handlerMapping) {
 | 
			
		||||
			this.pathMappedEndpoints = new PathMappedEndpoints(BASE_PATH, handlerMapping::getAllEndpoints);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		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.SecurityResponse;
 | 
			
		||||
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.EndpointMapping;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
 | 
			
		||||
| 
						 | 
				
			
			@ -61,14 +62,17 @@ class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebMvcEndpoin
 | 
			
		|||
 | 
			
		||||
	private final EndpointLinksResolver linksResolver;
 | 
			
		||||
 | 
			
		||||
	private final Collection<ExposableEndpoint<?>> allEndpoints;
 | 
			
		||||
 | 
			
		||||
	CloudFoundryWebEndpointServletHandlerMapping(EndpointMapping endpointMapping,
 | 
			
		||||
			Collection<ExposableWebEndpoint> endpoints, EndpointMediaTypes endpointMediaTypes,
 | 
			
		||||
			CorsConfiguration corsConfiguration, CloudFoundrySecurityInterceptor securityInterceptor,
 | 
			
		||||
			EndpointLinksResolver linksResolver) {
 | 
			
		||||
			Collection<ExposableEndpoint<?>> allEndpoints) {
 | 
			
		||||
		super(endpointMapping, endpoints, endpointMediaTypes, corsConfiguration, true,
 | 
			
		||||
				WebMvcAutoConfiguration.pathPatternParser);
 | 
			
		||||
		this.securityInterceptor = securityInterceptor;
 | 
			
		||||
		this.linksResolver = linksResolver;
 | 
			
		||||
		this.linksResolver = new EndpointLinksResolver(allEndpoints);
 | 
			
		||||
		this.allEndpoints = allEndpoints;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
| 
						 | 
				
			
			@ -82,6 +86,10 @@ class CloudFoundryWebEndpointServletHandlerMapping extends AbstractWebMvcEndpoin
 | 
			
		|||
		return new CloudFoundryLinksHandler();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Collection<ExposableEndpoint<?>> getAllEndpoints() {
 | 
			
		||||
		return this.allEndpoints;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	class CloudFoundryLinksHandler implements LinksHandler {
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,8 +17,11 @@
 | 
			
		|||
package org.springframework.boot.actuate.autoconfigure.cloudfoundry.reactive;
 | 
			
		||||
 | 
			
		||||
import java.time.Duration;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
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.CloudFoundryAuthorizationException;
 | 
			
		||||
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.ReadOperation;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
 | 
			
		||||
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.EndpointMediaTypes;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
 | 
			
		||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
 | 
			
		||||
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
 | 
			
		||||
| 
						 | 
				
			
			@ -245,9 +249,10 @@ class CloudFoundryWebFluxEndpointIntegrationTests {
 | 
			
		|||
			CorsConfiguration corsConfiguration = new CorsConfiguration();
 | 
			
		||||
			corsConfiguration.setAllowedOrigins(Arrays.asList("https://example.com"));
 | 
			
		||||
			corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
 | 
			
		||||
			return new CloudFoundryWebFluxEndpointHandlerMapping(new EndpointMapping("/cfApplication"),
 | 
			
		||||
					webEndpointDiscoverer.getEndpoints(), endpointMediaTypes, corsConfiguration, interceptor,
 | 
			
		||||
					new EndpointLinksResolver(webEndpointDiscoverer.getEndpoints()));
 | 
			
		||||
			Collection<ExposableWebEndpoint> webEndpoints = webEndpointDiscoverer.getEndpoints();
 | 
			
		||||
			List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>(webEndpoints);
 | 
			
		||||
			return new CloudFoundryWebFluxEndpointHandlerMapping(new EndpointMapping("/cfApplication"), webEndpoints,
 | 
			
		||||
					endpointMediaTypes, corsConfiguration, interceptor, allEndpoints);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Bean
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -95,6 +95,8 @@ class ReactiveCloudFoundryActuatorAutoConfigurationTests {
 | 
			
		|||
				InfoContributorAutoConfiguration.class, InfoEndpointAutoConfiguration.class,
 | 
			
		||||
				ProjectInfoAutoConfiguration.class, ReactiveCloudFoundryActuatorAutoConfiguration.class));
 | 
			
		||||
 | 
			
		||||
	private static final String BASE_PATH = "/cloudfoundryapplication";
 | 
			
		||||
 | 
			
		||||
	@AfterEach
 | 
			
		||||
	void close() {
 | 
			
		||||
		HttpResources.reset();
 | 
			
		||||
| 
						 | 
				
			
			@ -176,21 +178,24 @@ class ReactiveCloudFoundryActuatorAutoConfigurationTests {
 | 
			
		|||
	@Test
 | 
			
		||||
	@SuppressWarnings("unchecked")
 | 
			
		||||
	void cloudFoundryPathsIgnoredBySpringSecurity() {
 | 
			
		||||
		this.contextRunner
 | 
			
		||||
		this.contextRunner.withBean(TestEndpoint.class, TestEndpoint::new)
 | 
			
		||||
			.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
 | 
			
		||||
					"vcap.application.cf_api:https://my-cloud-controller.com")
 | 
			
		||||
			.run((context) -> {
 | 
			
		||||
				WebFilterChainProxy chainProxy = context.getBean(WebFilterChainProxy.class);
 | 
			
		||||
				List<SecurityWebFilterChain> filters = (List<SecurityWebFilterChain>) ReflectionTestUtils
 | 
			
		||||
					.getField(chainProxy, "filters");
 | 
			
		||||
				Boolean cfRequestMatches = filters.get(0)
 | 
			
		||||
					.matches(MockServerWebExchange
 | 
			
		||||
						.from(MockServerHttpRequest.get("/cloudfoundryapplication/my-path").build()))
 | 
			
		||||
					.block(Duration.ofSeconds(30));
 | 
			
		||||
				Boolean otherRequestMatches = filters.get(0)
 | 
			
		||||
					.matches(MockServerWebExchange.from(MockServerHttpRequest.get("/some-other-path").build()))
 | 
			
		||||
					.block(Duration.ofSeconds(30));
 | 
			
		||||
				Boolean cfBaseRequestMatches = getMatches(filters, BASE_PATH);
 | 
			
		||||
				Boolean cfBaseWithTrailingSlashRequestMatches = getMatches(filters, BASE_PATH + "/");
 | 
			
		||||
				Boolean cfRequestMatches = getMatches(filters, BASE_PATH + "/test");
 | 
			
		||||
				Boolean cfRequestWithAdditionalPathMatches = getMatches(filters, BASE_PATH + "/test/a");
 | 
			
		||||
				Boolean otherCfRequestMatches = getMatches(filters, BASE_PATH + "/other-path");
 | 
			
		||||
				Boolean otherRequestMatches = getMatches(filters, "/some-other-path");
 | 
			
		||||
				assertThat(cfBaseRequestMatches).isTrue();
 | 
			
		||||
				assertThat(cfBaseWithTrailingSlashRequestMatches).isTrue();
 | 
			
		||||
				assertThat(cfRequestMatches).isTrue();
 | 
			
		||||
				assertThat(cfRequestWithAdditionalPathMatches).isTrue();
 | 
			
		||||
				assertThat(otherCfRequestMatches).isFalse();
 | 
			
		||||
				assertThat(otherRequestMatches).isFalse();
 | 
			
		||||
				otherRequestMatches = filters.get(1)
 | 
			
		||||
					.matches(MockServerWebExchange.from(MockServerHttpRequest.get("/some-other-path").build()))
 | 
			
		||||
| 
						 | 
				
			
			@ -200,6 +205,13 @@ class ReactiveCloudFoundryActuatorAutoConfigurationTests {
 | 
			
		|||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
	void cloudFoundryPlatformInactive() {
 | 
			
		||||
		this.contextRunner
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,6 +77,8 @@ class CloudFoundryActuatorAutoConfigurationTests {
 | 
			
		|||
				ServletManagementContextAutoConfiguration.class, EndpointAutoConfiguration.class,
 | 
			
		||||
				WebEndpointAutoConfiguration.class, CloudFoundryActuatorAutoConfiguration.class));
 | 
			
		||||
 | 
			
		||||
	private static String BASE_PATH = "/cloudfoundryapplication";
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void cloudFoundryPlatformActive() {
 | 
			
		||||
		this.contextRunner
 | 
			
		||||
| 
						 | 
				
			
			@ -168,20 +170,31 @@ class CloudFoundryActuatorAutoConfigurationTests {
 | 
			
		|||
 | 
			
		||||
	@Test
 | 
			
		||||
	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) -> {
 | 
			
		||||
				FilterChainProxy securityFilterChain = (FilterChainProxy) context
 | 
			
		||||
					.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
 | 
			
		||||
				SecurityFilterChain chain = securityFilterChain.getFilterChains().get(0);
 | 
			
		||||
				MockHttpServletRequest request = new MockHttpServletRequest();
 | 
			
		||||
				request.setServletPath("/cloudfoundryapplication/my-path");
 | 
			
		||||
				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");
 | 
			
		||||
				assertThat(chain.matches(request)).isFalse();
 | 
			
		||||
			});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static void testCloudFoundrySecurity(MockHttpServletRequest request, String basePath,
 | 
			
		||||
			SecurityFilterChain chain) {
 | 
			
		||||
		request.setServletPath(basePath);
 | 
			
		||||
		assertThat(chain.matches(request)).isTrue();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	void cloudFoundryPlatformInactive() {
 | 
			
		||||
		this.contextRunner.withPropertyValues()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,8 +17,11 @@
 | 
			
		|||
package org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet;
 | 
			
		||||
 | 
			
		||||
import java.time.Duration;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.function.BiConsumer;
 | 
			
		||||
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.CloudFoundryAuthorizationException;
 | 
			
		||||
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.ReadOperation;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.annotation.Selector;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
 | 
			
		||||
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.EndpointMediaTypes;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
 | 
			
		||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
 | 
			
		||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
 | 
			
		||||
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
 | 
			
		||||
| 
						 | 
				
			
			@ -242,9 +246,10 @@ class CloudFoundryMvcWebEndpointIntegrationTests {
 | 
			
		|||
			CorsConfiguration corsConfiguration = new CorsConfiguration();
 | 
			
		||||
			corsConfiguration.setAllowedOrigins(Arrays.asList("https://example.com"));
 | 
			
		||||
			corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST"));
 | 
			
		||||
			return new CloudFoundryWebEndpointServletHandlerMapping(new EndpointMapping("/cfApplication"),
 | 
			
		||||
					webEndpointDiscoverer.getEndpoints(), endpointMediaTypes, corsConfiguration, interceptor,
 | 
			
		||||
					new EndpointLinksResolver(webEndpointDiscoverer.getEndpoints()));
 | 
			
		||||
			Collection<ExposableWebEndpoint> webEndpoints = webEndpointDiscoverer.getEndpoints();
 | 
			
		||||
			List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>(webEndpoints);
 | 
			
		||||
			return new CloudFoundryWebEndpointServletHandlerMapping(new EndpointMapping("/cfApplication"), webEndpoints,
 | 
			
		||||
					endpointMediaTypes, corsConfiguration, interceptor, allEndpoints);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Bean
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue