|
|
|
@ -24,7 +24,6 @@ import java.util.stream.Collectors;
|
|
|
|
|
import javax.net.ssl.SSLException;
|
|
|
|
|
|
|
|
|
|
import org.junit.After;
|
|
|
|
|
import org.junit.Before;
|
|
|
|
|
import org.junit.Rule;
|
|
|
|
|
import org.junit.Test;
|
|
|
|
|
import org.junit.rules.ExpectedException;
|
|
|
|
@ -41,6 +40,7 @@ import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
|
|
|
|
|
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
|
|
|
|
|
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
|
|
|
|
import org.springframework.boot.actuate.endpoint.web.WebOperation;
|
|
|
|
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
|
|
|
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
|
|
|
|
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
|
|
|
|
|
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
|
|
|
@ -48,9 +48,9 @@ import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurity
|
|
|
|
|
import org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration;
|
|
|
|
|
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
|
|
|
|
|
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
|
|
|
|
|
import org.springframework.boot.test.util.TestPropertyValues;
|
|
|
|
|
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext;
|
|
|
|
|
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
|
|
|
|
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
|
|
|
|
|
import org.springframework.context.ApplicationContext;
|
|
|
|
|
import org.springframework.context.annotation.Bean;
|
|
|
|
|
import org.springframework.context.annotation.Configuration;
|
|
|
|
|
import org.springframework.http.HttpMethod;
|
|
|
|
@ -74,29 +74,32 @@ import static org.mockito.Mockito.mock;
|
|
|
|
|
*/
|
|
|
|
|
public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
|
|
|
|
|
|
|
|
|
private AnnotationConfigReactiveWebApplicationContext context;
|
|
|
|
|
|
|
|
|
|
@Rule
|
|
|
|
|
public ExpectedException thrown = ExpectedException.none();
|
|
|
|
|
|
|
|
|
|
@Before
|
|
|
|
|
public void setup() {
|
|
|
|
|
this.context = new AnnotationConfigReactiveWebApplicationContext();
|
|
|
|
|
}
|
|
|
|
|
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
|
|
|
|
|
.withConfiguration(AutoConfigurations.of(ReactiveSecurityAutoConfiguration.class,
|
|
|
|
|
ReactiveUserDetailsServiceAutoConfiguration.class,
|
|
|
|
|
WebFluxAutoConfiguration.class, JacksonAutoConfiguration.class,
|
|
|
|
|
HttpMessageConvertersAutoConfiguration.class,
|
|
|
|
|
PropertyPlaceholderAutoConfiguration.class,
|
|
|
|
|
WebClientCustomizerConfig.class, WebClientAutoConfiguration.class,
|
|
|
|
|
ManagementContextAutoConfiguration.class, EndpointAutoConfiguration.class,
|
|
|
|
|
WebEndpointAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
|
|
|
|
|
ReactiveCloudFoundryActuatorAutoConfiguration.class));
|
|
|
|
|
|
|
|
|
|
@After
|
|
|
|
|
public void close() {
|
|
|
|
|
HttpResources.reset();
|
|
|
|
|
if (this.context != null) {
|
|
|
|
|
this.context.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void cloudFoundryPlatformActive() {
|
|
|
|
|
setupContextWithCloudEnabled();
|
|
|
|
|
this.context.refresh();
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping();
|
|
|
|
|
this.contextRunner
|
|
|
|
|
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
|
|
|
|
"vcap.application.cf_api:http://my-cloud-controller.com")
|
|
|
|
|
.run(context -> {
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping(context);
|
|
|
|
|
EndpointMapping endpointMapping = (EndpointMapping) ReflectionTestUtils
|
|
|
|
|
.getField(handlerMapping, "endpointMapping");
|
|
|
|
|
assertThat(endpointMapping.getPath()).isEqualTo("/cloudfoundryapplication");
|
|
|
|
@ -107,35 +110,44 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
|
|
|
|
Arrays.asList(HttpMethod.GET.name(), HttpMethod.POST.name()));
|
|
|
|
|
assertThat(corsConfiguration.getAllowedHeaders()).containsAll(
|
|
|
|
|
Arrays.asList("Authorization", "X-Cf-App-Instance", "Content-Type"));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void cloudfoundryapplicationProducesActuatorMediaType() {
|
|
|
|
|
setupContextWithCloudEnabled();
|
|
|
|
|
this.context.refresh();
|
|
|
|
|
WebTestClient webTestClient = WebTestClient.bindToApplicationContext(this.context)
|
|
|
|
|
this.contextRunner
|
|
|
|
|
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
|
|
|
|
"vcap.application.cf_api:http://my-cloud-controller.com")
|
|
|
|
|
.run(context -> {
|
|
|
|
|
WebTestClient webTestClient = WebTestClient.bindToApplicationContext(context)
|
|
|
|
|
.build();
|
|
|
|
|
webTestClient.get().uri("/cloudfoundryapplication").header("Content-Type",
|
|
|
|
|
ActuatorMediaType.V2_JSON + ";charset=UTF-8");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void cloudFoundryPlatformActiveSetsApplicationId() {
|
|
|
|
|
setupContextWithCloudEnabled();
|
|
|
|
|
this.context.refresh();
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping();
|
|
|
|
|
this.contextRunner
|
|
|
|
|
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
|
|
|
|
"vcap.application.cf_api:http://my-cloud-controller.com")
|
|
|
|
|
.run(context -> {
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping(context);
|
|
|
|
|
Object interceptor = ReflectionTestUtils.getField(handlerMapping,
|
|
|
|
|
"securityInterceptor");
|
|
|
|
|
String applicationId = (String) ReflectionTestUtils.getField(interceptor,
|
|
|
|
|
"applicationId");
|
|
|
|
|
assertThat(applicationId).isEqualTo("my-app-id");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void cloudFoundryPlatformActiveSetsCloudControllerUrl() {
|
|
|
|
|
setupContextWithCloudEnabled();
|
|
|
|
|
this.context.refresh();
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping();
|
|
|
|
|
this.contextRunner
|
|
|
|
|
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
|
|
|
|
"vcap.application.cf_api:http://my-cloud-controller.com")
|
|
|
|
|
.run(context -> {
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping(context);
|
|
|
|
|
Object interceptor = ReflectionTestUtils.getField(handlerMapping,
|
|
|
|
|
"securityInterceptor");
|
|
|
|
|
Object interceptorSecurityService = ReflectionTestUtils.getField(interceptor,
|
|
|
|
@ -143,16 +155,15 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
|
|
|
|
String cloudControllerUrl = (String) ReflectionTestUtils
|
|
|
|
|
.getField(interceptorSecurityService, "cloudControllerUrl");
|
|
|
|
|
assertThat(cloudControllerUrl).isEqualTo("http://my-cloud-controller.com");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void cloudFoundryPlatformActiveAndCloudControllerUrlNotPresent() {
|
|
|
|
|
TestPropertyValues
|
|
|
|
|
.of("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id")
|
|
|
|
|
.applyTo(this.context);
|
|
|
|
|
setupContext();
|
|
|
|
|
this.context.refresh();
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = this.context.getBean(
|
|
|
|
|
this.contextRunner
|
|
|
|
|
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id")
|
|
|
|
|
.run(context -> {
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = context.getBean(
|
|
|
|
|
"cloudFoundryWebFluxEndpointHandlerMapping",
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping.class);
|
|
|
|
|
Object securityInterceptor = ReflectionTestUtils.getField(handlerMapping,
|
|
|
|
@ -160,14 +171,17 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
|
|
|
|
Object interceptorSecurityService = ReflectionTestUtils
|
|
|
|
|
.getField(securityInterceptor, "cloudFoundrySecurityService");
|
|
|
|
|
assertThat(interceptorSecurityService).isNull();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
public void cloudFoundryPathsIgnoredBySpringSecurity() {
|
|
|
|
|
setupContextWithCloudEnabled();
|
|
|
|
|
this.context.refresh();
|
|
|
|
|
WebFilterChainProxy chainProxy = this.context.getBean(WebFilterChainProxy.class);
|
|
|
|
|
this.contextRunner
|
|
|
|
|
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
|
|
|
|
"vcap.application.cf_api:http://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(
|
|
|
|
@ -184,45 +198,46 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
|
|
|
|
.from(MockServerHttpRequest.get("/some-other-path").build()))
|
|
|
|
|
.block();
|
|
|
|
|
assertThat(otherRequestMatches).isTrue();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void cloudFoundryPlatformInactive() {
|
|
|
|
|
setupContext();
|
|
|
|
|
this.context.refresh();
|
|
|
|
|
assertThat(this.context.containsBean("cloudFoundryWebFluxEndpointHandlerMapping"))
|
|
|
|
|
.isFalse();
|
|
|
|
|
this.contextRunner
|
|
|
|
|
.run(context -> assertThat(
|
|
|
|
|
context.containsBean("cloudFoundryWebFluxEndpointHandlerMapping"))
|
|
|
|
|
.isFalse());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void cloudFoundryManagementEndpointsDisabled() {
|
|
|
|
|
setupContextWithCloudEnabled();
|
|
|
|
|
TestPropertyValues
|
|
|
|
|
.of("VCAP_APPLICATION=---", "management.cloudfoundry.enabled:false")
|
|
|
|
|
.applyTo(this.context);
|
|
|
|
|
this.context.refresh();
|
|
|
|
|
assertThat(this.context.containsBean("cloudFoundryWebFluxEndpointHandlerMapping"))
|
|
|
|
|
.isFalse();
|
|
|
|
|
this.contextRunner.withPropertyValues("VCAP_APPLICATION=---", "management.cloudfoundry.enabled:false")
|
|
|
|
|
.run(context -> assertThat(context.containsBean("cloudFoundryWebFluxEndpointHandlerMapping"))
|
|
|
|
|
.isFalse());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void allEndpointsAvailableUnderCloudFoundryWithoutEnablingWebIncludes() {
|
|
|
|
|
setupContextWithCloudEnabled();
|
|
|
|
|
this.context.register(TestConfiguration.class);
|
|
|
|
|
this.context.refresh();
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping();
|
|
|
|
|
this.contextRunner.withUserConfiguration(TestConfiguration.class)
|
|
|
|
|
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
|
|
|
|
"vcap.application.cf_api:http://my-cloud-controller.com")
|
|
|
|
|
.run(context -> {
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping(context);
|
|
|
|
|
Collection<ExposableWebEndpoint> endpoints = handlerMapping.getEndpoints();
|
|
|
|
|
List<String> endpointIds = endpoints.stream().map(ExposableEndpoint::getId)
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
assertThat(endpointIds).contains("test");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void endpointPathCustomizationIsNotApplied() {
|
|
|
|
|
setupContextWithCloudEnabled();
|
|
|
|
|
this.context.register(TestConfiguration.class);
|
|
|
|
|
this.context.refresh();
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping();
|
|
|
|
|
this.contextRunner.withUserConfiguration(TestConfiguration.class)
|
|
|
|
|
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
|
|
|
|
"vcap.application.cf_api:http://my-cloud-controller.com")
|
|
|
|
|
.run(context -> {
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping(context);
|
|
|
|
|
Collection<ExposableWebEndpoint> endpoints = handlerMapping.getEndpoints();
|
|
|
|
|
ExposableWebEndpoint endpoint = endpoints.stream()
|
|
|
|
|
.filter((candidate) -> "test".equals(candidate.getId())).findFirst()
|
|
|
|
@ -230,27 +245,32 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
|
|
|
|
assertThat(endpoint.getOperations()).hasSize(1);
|
|
|
|
|
WebOperation operation = endpoint.getOperations().iterator().next();
|
|
|
|
|
assertThat(operation.getRequestPredicate().getPath()).isEqualTo("test");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void healthEndpointInvokerShouldBeCloudFoundryWebExtension() {
|
|
|
|
|
setupContextWithCloudEnabled();
|
|
|
|
|
this.context.refresh();
|
|
|
|
|
Collection<ExposableWebEndpoint> endpoints = getHandlerMapping().getEndpoints();
|
|
|
|
|
this.contextRunner.withConfiguration(AutoConfigurations.of(HealthEndpointAutoConfiguration.class))
|
|
|
|
|
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
|
|
|
|
"vcap.application.cf_api:http://my-cloud-controller.com")
|
|
|
|
|
.run(context -> {
|
|
|
|
|
Collection<ExposableWebEndpoint> endpoints = getHandlerMapping(context).getEndpoints();
|
|
|
|
|
ExposableWebEndpoint endpoint = endpoints.iterator().next();
|
|
|
|
|
WebOperation webOperation = endpoint.getOperations().iterator().next();
|
|
|
|
|
Object invoker = ReflectionTestUtils.getField(webOperation, "invoker");
|
|
|
|
|
assertThat(ReflectionTestUtils.getField(invoker, "target"))
|
|
|
|
|
.isInstanceOf(CloudFoundryReactiveHealthEndpointWebExtension.class);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void skipSslValidation() {
|
|
|
|
|
setupContextWithCloudEnabled();
|
|
|
|
|
TestPropertyValues.of("management.cloudfoundry.skip-ssl-validation:true")
|
|
|
|
|
.applyTo(this.context);
|
|
|
|
|
this.context.refresh();
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping();
|
|
|
|
|
this.contextRunner.withConfiguration(AutoConfigurations.of(HealthEndpointAutoConfiguration.class))
|
|
|
|
|
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
|
|
|
|
"vcap.application.cf_api:http://my-cloud-controller.com",
|
|
|
|
|
"management.cloudfoundry.skip-ssl-validation:true")
|
|
|
|
|
.run(context -> {
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping(context);
|
|
|
|
|
Object interceptor = ReflectionTestUtils.getField(handlerMapping,
|
|
|
|
|
"securityInterceptor");
|
|
|
|
|
Object interceptorSecurityService = ReflectionTestUtils.getField(interceptor,
|
|
|
|
@ -258,13 +278,16 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
|
|
|
|
WebClient webClient = (WebClient) ReflectionTestUtils
|
|
|
|
|
.getField(interceptorSecurityService, "webClient");
|
|
|
|
|
webClient.get().uri("https://self-signed.badssl.com/").exchange().block();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
public void sslValidationNotSkippedByDefault() {
|
|
|
|
|
setupContextWithCloudEnabled();
|
|
|
|
|
this.context.refresh();
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping();
|
|
|
|
|
this.contextRunner.withConfiguration(AutoConfigurations.of(HealthEndpointAutoConfiguration.class))
|
|
|
|
|
.withPropertyValues("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
|
|
|
|
"vcap.application.cf_api:http://my-cloud-controller.com")
|
|
|
|
|
.run(context -> {
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping(context);
|
|
|
|
|
Object interceptor = ReflectionTestUtils.getField(handlerMapping,
|
|
|
|
|
"securityInterceptor");
|
|
|
|
|
Object interceptorSecurityService = ReflectionTestUtils.getField(interceptor,
|
|
|
|
@ -273,30 +296,11 @@ public class ReactiveCloudFoundryActuatorAutoConfigurationTests {
|
|
|
|
|
.getField(interceptorSecurityService, "webClient");
|
|
|
|
|
this.thrown.expectCause(instanceOf(SSLException.class));
|
|
|
|
|
webClient.get().uri("https://self-signed.badssl.com/").exchange().block();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setupContextWithCloudEnabled() {
|
|
|
|
|
TestPropertyValues
|
|
|
|
|
.of("VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id",
|
|
|
|
|
"vcap.application.cf_api:http://my-cloud-controller.com")
|
|
|
|
|
.applyTo(this.context);
|
|
|
|
|
setupContext();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setupContext() {
|
|
|
|
|
this.context.register(ReactiveSecurityAutoConfiguration.class,
|
|
|
|
|
ReactiveUserDetailsServiceAutoConfiguration.class,
|
|
|
|
|
WebFluxAutoConfiguration.class, JacksonAutoConfiguration.class,
|
|
|
|
|
HttpMessageConvertersAutoConfiguration.class,
|
|
|
|
|
PropertyPlaceholderAutoConfiguration.class,
|
|
|
|
|
WebClientCustomizerConfig.class, WebClientAutoConfiguration.class,
|
|
|
|
|
ManagementContextAutoConfiguration.class, EndpointAutoConfiguration.class,
|
|
|
|
|
WebEndpointAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
|
|
|
|
|
ReactiveCloudFoundryActuatorAutoConfiguration.class);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private CloudFoundryWebFluxEndpointHandlerMapping getHandlerMapping() {
|
|
|
|
|
return this.context.getBean("cloudFoundryWebFluxEndpointHandlerMapping",
|
|
|
|
|
private CloudFoundryWebFluxEndpointHandlerMapping getHandlerMapping(ApplicationContext context) {
|
|
|
|
|
return context.getBean("cloudFoundryWebFluxEndpointHandlerMapping",
|
|
|
|
|
CloudFoundryWebFluxEndpointHandlerMapping.class);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|