1616
1717package org .springframework .boot .actuate .autoconfigure .cloudfoundry .reactive ;
1818
19+ import java .io .IOException ;
1920import java .time .Duration ;
2021import java .util .Arrays ;
2122import java .util .Collection ;
2425
2526import javax .net .ssl .SSLException ;
2627
28+ import okhttp3 .mockwebserver .MockResponse ;
29+ import okhttp3 .mockwebserver .MockWebServer ;
2730import org .junit .jupiter .api .AfterEach ;
2831import org .junit .jupiter .api .Test ;
2932import reactor .netty .http .HttpResources ;
5255import org .springframework .boot .autoconfigure .security .reactive .ReactiveSecurityAutoConfiguration ;
5356import org .springframework .boot .autoconfigure .web .reactive .WebFluxAutoConfiguration ;
5457import org .springframework .boot .autoconfigure .web .reactive .function .client .WebClientAutoConfiguration ;
58+ import org .springframework .boot .ssl .SslBundle ;
59+ import org .springframework .boot .ssl .jks .JksSslStoreBundle ;
60+ import org .springframework .boot .ssl .jks .JksSslStoreDetails ;
5561import org .springframework .boot .test .context .runner .ReactiveWebApplicationContextRunner ;
62+ import org .springframework .boot .testsupport .classpath .resources .WithPackageResources ;
5663import org .springframework .boot .testsupport .classpath .resources .WithResource ;
5764import org .springframework .boot .web .reactive .function .client .WebClientCustomizer ;
5865import org .springframework .context .ApplicationContext ;
5966import org .springframework .context .annotation .Bean ;
6067import org .springframework .context .annotation .Configuration ;
6168import org .springframework .http .HttpMethod ;
69+ import org .springframework .http .HttpStatusCode ;
70+ import org .springframework .http .ResponseEntity ;
6271import org .springframework .mock .http .server .reactive .MockServerHttpRequest ;
6372import org .springframework .mock .web .server .MockServerWebExchange ;
6473import org .springframework .security .core .userdetails .MapReactiveUserDetailsService ;
7887 * Tests for {@link ReactiveCloudFoundryActuatorAutoConfiguration}.
7988 *
8089 * @author Madhura Bhave
90+ * @author Moritz Halbritter
8191 */
8292class ReactiveCloudFoundryActuatorAutoConfigurationTests {
8393
@@ -300,53 +310,63 @@ void gitFullDetailsAlwaysPresent() {
300310 }
301311
302312 @ Test
303- void skipSslValidation () {
304- this .contextRunner .withConfiguration (AutoConfigurations .of (HealthEndpointAutoConfiguration .class ))
305- .withPropertyValues ("VCAP_APPLICATION:---" , "vcap.application.application_id:my-app-id" ,
306- "vcap.application.cf_api:https://my-cloud-controller.com" ,
307- "management.cloudfoundry.skip-ssl-validation:true" )
308- .run ((context ) -> {
309- CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping (context );
310- Object interceptor = ReflectionTestUtils .getField (handlerMapping , "securityInterceptor" );
311- Object interceptorSecurityService = ReflectionTestUtils .getField (interceptor ,
312- "cloudFoundrySecurityService" );
313- WebClient webClient = (WebClient ) ReflectionTestUtils .getField (interceptorSecurityService , "webClient" );
314- doesNotFailWithSslException (() -> webClient .get ()
315- .uri ("https://self-signed.badssl.com/" )
316- .retrieve ()
317- .toBodilessEntity ()
318- .block (Duration .ofSeconds (30 )));
319- });
320- }
321-
322- private static void doesNotFailWithSslException (Runnable action ) {
323- try {
324- action .run ();
325- }
326- catch (RuntimeException ex ) {
327- assertThat (findCause (ex , SSLException .class )).isNull ();
313+ @ WithPackageResources ("test.jks" )
314+ void skipSslValidation () throws IOException {
315+ JksSslStoreDetails keyStoreDetails = new JksSslStoreDetails ("JKS" , null , "classpath:test.jks" , "secret" );
316+ SslBundle sslBundle = SslBundle .of (new JksSslStoreBundle (keyStoreDetails , keyStoreDetails ));
317+ try (MockWebServer server = new MockWebServer ()) {
318+ server .useHttps (sslBundle .createSslContext ().getSocketFactory (), false );
319+ server .enqueue (new MockResponse ().setResponseCode (204 ));
320+ server .start ();
321+ this .contextRunner .withConfiguration (AutoConfigurations .of (HealthEndpointAutoConfiguration .class ))
322+ .withPropertyValues ("VCAP_APPLICATION:---" , "vcap.application.application_id:my-app-id" ,
323+ "vcap.application.cf_api:https://my-cloud-controller.com" ,
324+ "management.cloudfoundry.skip-ssl-validation:true" )
325+ .run ((context ) -> {
326+ CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping (context );
327+ Object interceptor = ReflectionTestUtils .getField (handlerMapping , "securityInterceptor" );
328+ Object interceptorSecurityService = ReflectionTestUtils .getField (interceptor ,
329+ "cloudFoundrySecurityService" );
330+ WebClient webClient = (WebClient ) ReflectionTestUtils .getField (interceptorSecurityService ,
331+ "webClient" );
332+ ResponseEntity <Void > response = webClient .get ()
333+ .uri (server .url ("/" ).uri ())
334+ .retrieve ()
335+ .toBodilessEntity ()
336+ .block (Duration .ofSeconds (30 ));
337+ assertThat (response .getStatusCode ()).isEqualTo (HttpStatusCode .valueOf (204 ));
338+ });
328339 }
329340 }
330341
331342 @ Test
332- void sslValidationNotSkippedByDefault () {
333- this .contextRunner .withConfiguration (AutoConfigurations .of (HealthEndpointAutoConfiguration .class ))
334- .withPropertyValues ("VCAP_APPLICATION:---" , "vcap.application.application_id:my-app-id" ,
335- "vcap.application.cf_api:https://my-cloud-controller.com" )
336- .run ((context ) -> {
337- CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping (context );
338- Object interceptor = ReflectionTestUtils .getField (handlerMapping , "securityInterceptor" );
339- Object interceptorSecurityService = ReflectionTestUtils .getField (interceptor ,
340- "cloudFoundrySecurityService" );
341- WebClient webClient = (WebClient ) ReflectionTestUtils .getField (interceptorSecurityService , "webClient" );
342- assertThatExceptionOfType (RuntimeException .class )
343- .isThrownBy (() -> webClient .get ()
344- .uri ("https://self-signed.badssl.com/" )
345- .retrieve ()
346- .toBodilessEntity ()
347- .block (Duration .ofSeconds (30 )))
348- .withCauseInstanceOf (SSLException .class );
349- });
343+ @ WithPackageResources ("test.jks" )
344+ void sslValidationNotSkippedByDefault () throws IOException {
345+ JksSslStoreDetails keyStoreDetails = new JksSslStoreDetails ("JKS" , null , "classpath:test.jks" , "secret" );
346+ SslBundle sslBundle = SslBundle .of (new JksSslStoreBundle (keyStoreDetails , keyStoreDetails ));
347+ try (MockWebServer server = new MockWebServer ()) {
348+ server .useHttps (sslBundle .createSslContext ().getSocketFactory (), false );
349+ server .enqueue (new MockResponse ().setResponseCode (204 ));
350+ server .start ();
351+ this .contextRunner .withConfiguration (AutoConfigurations .of (HealthEndpointAutoConfiguration .class ))
352+ .withPropertyValues ("VCAP_APPLICATION:---" , "vcap.application.application_id:my-app-id" ,
353+ "vcap.application.cf_api:https://my-cloud-controller.com" )
354+ .run ((context ) -> {
355+ CloudFoundryWebFluxEndpointHandlerMapping handlerMapping = getHandlerMapping (context );
356+ Object interceptor = ReflectionTestUtils .getField (handlerMapping , "securityInterceptor" );
357+ Object interceptorSecurityService = ReflectionTestUtils .getField (interceptor ,
358+ "cloudFoundrySecurityService" );
359+ WebClient webClient = (WebClient ) ReflectionTestUtils .getField (interceptorSecurityService ,
360+ "webClient" );
361+ assertThatExceptionOfType (RuntimeException .class )
362+ .isThrownBy (() -> webClient .get ()
363+ .uri (server .url ("/" ).uri ())
364+ .retrieve ()
365+ .toBodilessEntity ()
366+ .block (Duration .ofSeconds (30 )))
367+ .withCauseInstanceOf (SSLException .class );
368+ });
369+ }
350370 }
351371
352372 private CloudFoundryWebFluxEndpointHandlerMapping getHandlerMapping (ApplicationContext context ) {
@@ -365,16 +385,6 @@ private WebOperation findOperationWithRequestPath(ExposableWebEndpoint endpoint,
365385 "No operation found with request path " + requestPath + " from " + endpoint .getOperations ());
366386 }
367387
368- private static <E extends Throwable > E findCause (Throwable failure , Class <E > type ) {
369- while (failure != null ) {
370- if (type .isInstance (failure )) {
371- return type .cast (failure );
372- }
373- failure = failure .getCause ();
374- }
375- return null ;
376- }
377-
378388 @ Endpoint (id = "test" )
379389 static class TestEndpoint {
380390
0 commit comments