16
16
17
17
package org .springframework .boot .actuate .autoconfigure .cloudfoundry .reactive ;
18
18
19
+ import java .io .IOException ;
19
20
import java .time .Duration ;
20
21
import java .util .Arrays ;
21
22
import java .util .Collection ;
24
25
25
26
import javax .net .ssl .SSLException ;
26
27
28
+ import okhttp3 .mockwebserver .MockResponse ;
29
+ import okhttp3 .mockwebserver .MockWebServer ;
27
30
import org .junit .jupiter .api .AfterEach ;
28
31
import org .junit .jupiter .api .Test ;
29
32
import reactor .netty .http .HttpResources ;
52
55
import org .springframework .boot .autoconfigure .security .reactive .ReactiveSecurityAutoConfiguration ;
53
56
import org .springframework .boot .autoconfigure .web .reactive .WebFluxAutoConfiguration ;
54
57
import 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 ;
55
61
import org .springframework .boot .test .context .runner .ReactiveWebApplicationContextRunner ;
62
+ import org .springframework .boot .testsupport .classpath .resources .WithPackageResources ;
56
63
import org .springframework .boot .testsupport .classpath .resources .WithResource ;
57
64
import org .springframework .boot .web .reactive .function .client .WebClientCustomizer ;
58
65
import org .springframework .context .ApplicationContext ;
59
66
import org .springframework .context .annotation .Bean ;
60
67
import org .springframework .context .annotation .Configuration ;
61
68
import org .springframework .http .HttpMethod ;
69
+ import org .springframework .http .HttpStatusCode ;
70
+ import org .springframework .http .ResponseEntity ;
62
71
import org .springframework .mock .http .server .reactive .MockServerHttpRequest ;
63
72
import org .springframework .mock .web .server .MockServerWebExchange ;
64
73
import org .springframework .security .core .userdetails .MapReactiveUserDetailsService ;
78
87
* Tests for {@link ReactiveCloudFoundryActuatorAutoConfiguration}.
79
88
*
80
89
* @author Madhura Bhave
90
+ * @author Moritz Halbritter
81
91
*/
82
92
class ReactiveCloudFoundryActuatorAutoConfigurationTests {
83
93
@@ -300,53 +310,63 @@ void gitFullDetailsAlwaysPresent() {
300
310
}
301
311
302
312
@ 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
+ });
328
339
}
329
340
}
330
341
331
342
@ 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
+ }
350
370
}
351
371
352
372
private CloudFoundryWebFluxEndpointHandlerMapping getHandlerMapping (ApplicationContext context ) {
@@ -365,16 +385,6 @@ private WebOperation findOperationWithRequestPath(ExposableWebEndpoint endpoint,
365
385
"No operation found with request path " + requestPath + " from " + endpoint .getOperations ());
366
386
}
367
387
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
-
378
388
@ Endpoint (id = "test" )
379
389
static class TestEndpoint {
380
390
0 commit comments