Skip to content

Commit 3a3228f

Browse files
committed
Add CORS interceptor for Cloud Foundry actuators
This interceptor processes the response with CORS headers and apepars before the Cloud Foundry security interceptor. See gh-7108
1 parent f35fa87 commit 3a3228f

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryEndpointHandlerMapping.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Set;
2424

2525
import javax.servlet.http.HttpServletRequest;
26+
import javax.servlet.http.HttpServletResponse;
2627

2728
import org.springframework.boot.actuate.endpoint.Endpoint;
2829
import org.springframework.boot.actuate.endpoint.mvc.AbstractEndpointHandlerMapping;
@@ -34,6 +35,7 @@
3435
import org.springframework.web.servlet.HandlerExecutionChain;
3536
import org.springframework.web.servlet.HandlerInterceptor;
3637
import org.springframework.web.servlet.HandlerMapping;
38+
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
3739

3840
/**
3941
* {@link HandlerMapping} to map {@link Endpoint}s to Cloud Foundry specific URLs.
@@ -45,10 +47,13 @@ class CloudFoundryEndpointHandlerMapping
4547

4648
private final HandlerInterceptor securityInterceptor;
4749

50+
private final CorsConfiguration corsConfiguration;
51+
4852
CloudFoundryEndpointHandlerMapping(Set<? extends NamedMvcEndpoint> endpoints,
4953
CorsConfiguration corsConfiguration, HandlerInterceptor securityInterceptor) {
5054
super(endpoints, corsConfiguration);
5155
this.securityInterceptor = securityInterceptor;
56+
this.corsConfiguration = corsConfiguration;
5257
}
5358

5459
@Override
@@ -97,11 +102,31 @@ protected HandlerExecutionChain getHandlerExecutionChain(Object handler,
97102

98103
private HandlerInterceptor[] addSecurityInterceptor(HandlerInterceptor[] existing) {
99104
List<HandlerInterceptor> interceptors = new ArrayList<HandlerInterceptor>();
105+
interceptors.add(new CorsInterceptor(this.corsConfiguration));
100106
interceptors.add(this.securityInterceptor);
101107
if (existing != null) {
102108
interceptors.addAll(Arrays.asList(existing));
103109
}
104110
return interceptors.toArray(new HandlerInterceptor[interceptors.size()]);
105111
}
106112

113+
/**
114+
* {@link HandlerInterceptor} that processes the response for CORS.
115+
*
116+
*/
117+
class CorsInterceptor extends HandlerInterceptorAdapter {
118+
private final CorsConfiguration config;
119+
120+
CorsInterceptor(CorsConfiguration config) {
121+
this.config = config;
122+
}
123+
124+
@Override
125+
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
126+
Object handler) throws Exception {
127+
return getCorsProcessor().processRequest(this.config, request, response);
128+
}
129+
130+
}
131+
107132
}

spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryEndpointHandlerMappingTests.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
3434
import org.springframework.context.support.StaticApplicationContext;
3535
import org.springframework.mock.web.MockHttpServletRequest;
36+
import org.springframework.mock.web.MockHttpServletResponse;
37+
import org.springframework.web.cors.CorsConfiguration;
38+
import org.springframework.web.cors.CorsProcessor;
3639
import org.springframework.web.method.HandlerMethod;
3740
import org.springframework.web.servlet.HandlerExecutionChain;
3841
import org.springframework.web.servlet.HandlerInterceptor;
@@ -47,6 +50,25 @@
4750
public class CloudFoundryEndpointHandlerMappingTests
4851
extends AbstractEndpointHandlerMappingTests {
4952

53+
@Test
54+
public void corsInterceptorShouldBeFirstAndCallCorsProcessor() throws Exception {
55+
TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a"));
56+
CorsConfiguration corsConfiguration = new CorsConfiguration();
57+
CloudFoundryEndpointHandlerMapping handlerMapping = new CloudFoundryEndpointHandlerMapping(
58+
Collections.singleton(endpoint), corsConfiguration, null);
59+
CorsProcessor corsProcessor = Mockito.mock(CorsProcessor.class);
60+
handlerMapping.setCorsProcessor(corsProcessor);
61+
MockHttpServletRequest request = new MockHttpServletRequest();
62+
HandlerExecutionChain handlerExecutionChain = handlerMapping
63+
.getHandlerExecutionChain(endpoint, request);
64+
HandlerInterceptor[] interceptors = handlerExecutionChain.getInterceptors();
65+
CloudFoundryEndpointHandlerMapping.CorsInterceptor corsInterceptor = (CloudFoundryEndpointHandlerMapping.CorsInterceptor) interceptors[0];
66+
MockHttpServletResponse response = new MockHttpServletResponse();
67+
corsInterceptor.preHandle(request, response, new Object());
68+
Mockito.verify(corsProcessor).processRequest(corsConfiguration, request,
69+
response);
70+
}
71+
5072
@Test
5173
public void getHandlerExecutionChainShouldHaveSecurityInterceptor() throws Exception {
5274
CloudFoundrySecurityInterceptor securityInterceptor = Mockito
@@ -57,7 +79,7 @@ public void getHandlerExecutionChainShouldHaveSecurityInterceptor() throws Excep
5779
HandlerExecutionChain handlerExecutionChain = handlerMapping
5880
.getHandlerExecutionChain(endpoint, new MockHttpServletRequest());
5981
HandlerInterceptor[] interceptors = handlerExecutionChain.getInterceptors();
60-
assertThat(interceptors).contains(securityInterceptor);
82+
assertThat(interceptors[1]).isEqualTo(securityInterceptor);
6183
}
6284

6385
@Test

0 commit comments

Comments
 (0)