From 82f89b4ac103e32fc78fe32b4a84f7b273a2a772 Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Mon, 7 Nov 2016 15:12:43 -0800 Subject: [PATCH] Add custom headers to allowed CORS headers for CF actuators Update CORS configuration to support Authorization and X-Cf-App-Instance. See gh-7108 --- .../CloudFoundryActuatorAutoConfiguration.java | 2 ++ .../CloudFoundrySecurityInterceptor.java | 4 ++++ .../CloudFoundryActuatorAutoConfigurationTests.java | 12 ++++++++---- .../CloudFoundrySecurityInterceptorTests.java | 11 +++++++++++ 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryActuatorAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryActuatorAutoConfiguration.java index 4f3f0e9ecf5..9186d8a43fa 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryActuatorAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryActuatorAutoConfiguration.java @@ -90,6 +90,8 @@ public class CloudFoundryActuatorAutoConfiguration { corsConfiguration.addAllowedOrigin(CorsConfiguration.ALL); corsConfiguration.setAllowedMethods( Arrays.asList(HttpMethod.GET.name(), HttpMethod.POST.name())); + corsConfiguration + .setAllowedHeaders(Arrays.asList("Authorization", "X-Cf-App-Instance")); return corsConfiguration; } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptor.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptor.java index dd76fa9c87f..cc3f0c5b3b6 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptor.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptor.java @@ -25,6 +25,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.boot.actuate.cloudfoundry.CloudFoundryAuthorizationException.Reason; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; import org.springframework.util.StringUtils; +import org.springframework.web.cors.CorsUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; @@ -55,6 +56,9 @@ class CloudFoundrySecurityInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { + if (CorsUtils.isPreFlightRequest(request)) { + return true; + } try { if (!StringUtils.hasText(this.applicationId)) { throw new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE, diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryActuatorAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryActuatorAutoConfigurationTests.java index 9129816c246..a7a11b17901 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryActuatorAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryActuatorAutoConfigurationTests.java @@ -16,6 +16,8 @@ package org.springframework.boot.actuate.cloudfoundry; +import java.util.Arrays; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -75,18 +77,20 @@ public class CloudFoundryActuatorAutoConfigurationTests { @Test public void cloudFoundryPlatformActive() throws Exception { - CloudFoundryEndpointHandlerMapping handlerMapping = x(); + CloudFoundryEndpointHandlerMapping handlerMapping = getHandlerMapping(); assertThat(handlerMapping.getPrefix()).isEqualTo("/cloudfoundryapplication"); CorsConfiguration corsConfiguration = (CorsConfiguration) ReflectionTestUtils .getField(handlerMapping, "corsConfiguration"); assertThat(corsConfiguration.getAllowedOrigins()).contains("*"); assertThat(corsConfiguration.getAllowedMethods()).contains(HttpMethod.GET.name(), HttpMethod.POST.name()); + assertThat(corsConfiguration.getAllowedHeaders() + .containsAll(Arrays.asList("Authorization", "X-Cf-App-Instance"))); } @Test public void cloudFoundryPlatformActiveSetsApplicationId() throws Exception { - CloudFoundryEndpointHandlerMapping handlerMapping = x(); + CloudFoundryEndpointHandlerMapping handlerMapping = getHandlerMapping(); Object interceptor = ReflectionTestUtils.getField(handlerMapping, "securityInterceptor"); String applicationId = (String) ReflectionTestUtils.getField(interceptor, @@ -96,7 +100,7 @@ public class CloudFoundryActuatorAutoConfigurationTests { @Test public void cloudFoundryPlatformActiveSetsCloudControllerUrl() throws Exception { - CloudFoundryEndpointHandlerMapping handlerMapping = x(); + CloudFoundryEndpointHandlerMapping handlerMapping = getHandlerMapping(); Object interceptor = ReflectionTestUtils.getField(handlerMapping, "securityInterceptor"); Object interceptorSecurityService = ReflectionTestUtils.getField(interceptor, @@ -123,7 +127,7 @@ public class CloudFoundryActuatorAutoConfigurationTests { assertThat(interceptorSecurityService).isNull(); } - private CloudFoundryEndpointHandlerMapping x() { + private CloudFoundryEndpointHandlerMapping getHandlerMapping() { EnvironmentTestUtils.addEnvironment(this.context, "VCAP_APPLICATION:---", "vcap.application.application_id:my-app-id", "vcap.application.cf_api:http://my-cloud-controller.com"); diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptorTests.java index 994058a1881..a8e8ca51b21 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptorTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundrySecurityInterceptorTests.java @@ -26,6 +26,7 @@ import org.mockito.MockitoAnnotations; import org.springframework.boot.actuate.cloudfoundry.CloudFoundryAuthorizationException.Reason; import org.springframework.boot.actuate.endpoint.AbstractEndpoint; import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; @@ -69,6 +70,16 @@ public class CloudFoundrySecurityInterceptorTests { this.response = new MockHttpServletResponse(); } + @Test + public void preHandleWhenRequestIsPreFlightShouldReturnTrue() throws Exception { + this.request.setMethod("OPTIONS"); + this.request.addHeader(HttpHeaders.ORIGIN, "http://example.com"); + this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); + boolean preHandle = this.interceptor.preHandle(this.request, this.response, + this.handlerMethod); + assertThat(preHandle).isTrue(); + } + @Test public void preHandleWhenTokenIsMissingShouldReturnFalse() throws Exception { boolean preHandle = this.interceptor.preHandle(this.request, this.response,