diff --git a/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/EndpointDocumentation.java b/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/EndpointDocumentation.java index 4f933ef598..ee1294d75a 100644 --- a/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/EndpointDocumentation.java +++ b/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/EndpointDocumentation.java @@ -65,7 +65,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @ContextConfiguration(classes = SpringBootHypermediaApplication.class, loader = SpringBootContextLoader.class) @WebAppConfiguration @TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true", - "endpoints.health.sensitive=true", "endpoints.actuator.enabled=false" }) + "endpoints.health.sensitive=true", "endpoints.actuator.enabled=false", + "management.security.enabled=false" }) @DirtiesContext @AutoConfigureRestDocs(EndpointDocumentation.RESTDOCS_OUTPUT_DIR) @AutoConfigureMockMvc(print = MockMvcPrint.NONE) diff --git a/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/HealthEndpointDocumentation.java b/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/HealthEndpointDocumentation.java index 7847aa4427..d52386d588 100644 --- a/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/HealthEndpointDocumentation.java +++ b/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/HealthEndpointDocumentation.java @@ -39,7 +39,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @ContextConfiguration(classes = SpringBootHypermediaApplication.class, loader = SpringBootContextLoader.class) @WebAppConfiguration @TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true", - "endpoints.health.sensitive=false" }) + "endpoints.health.sensitive=false", "management.security.enabled=false" }) @DirtiesContext @AutoConfigureMockMvc @AutoConfigureRestDocs("target/generated-snippets") diff --git a/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/HypermediaEndpointDocumentation.java b/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/HypermediaEndpointDocumentation.java index 00b6e7852d..a94ba09a0e 100644 --- a/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/HypermediaEndpointDocumentation.java +++ b/spring-boot-actuator-docs/src/restdoc/java/org/springframework/boot/actuate/hypermedia/HypermediaEndpointDocumentation.java @@ -40,7 +40,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @ContextConfiguration(classes = SpringBootHypermediaApplication.class, loader = SpringBootContextLoader.class) @WebAppConfiguration @TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true", - "endpoints.hypermedia.enabled=true" }) + "endpoints.hypermedia.enabled=true", "management.security.enabled=false" }) @DirtiesContext @AutoConfigureMockMvc @AutoConfigureRestDocs("target/generated-snippets") diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.java index 356a10c5d8..687408b606 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.java @@ -37,6 +37,7 @@ import org.springframework.boot.actuate.endpoint.mvc.LogFileMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpointSecurityInterceptor; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint; import org.springframework.boot.autoconfigure.condition.ConditionMessage; @@ -99,6 +100,10 @@ public class EndpointWebMvcManagementContextConfiguration { EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints, corsConfiguration); mapping.setPrefix(this.managementServerProperties.getContextPath()); + MvcEndpointSecurityInterceptor securityInterceptor = new MvcEndpointSecurityInterceptor( + this.managementServerProperties.getSecurity().isEnabled(), + this.managementServerProperties.getSecurity().getRoles()); + mapping.setSecurityInterceptor(securityInterceptor); for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) { customizer.customize(mapping); } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryEndpointHandlerMapping.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryEndpointHandlerMapping.java index 8a2ecf1984..3145b5f055 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryEndpointHandlerMapping.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryEndpointHandlerMapping.java @@ -16,15 +16,9 @@ package org.springframework.boot.actuate.cloudfoundry; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; -import java.util.List; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.mvc.AbstractEndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint; @@ -32,10 +26,8 @@ import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint; import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerMapping; -import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; /** * {@link HandlerMapping} to map {@link Endpoint}s to Cloud Foundry specific URLs. @@ -45,15 +37,10 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; class CloudFoundryEndpointHandlerMapping extends AbstractEndpointHandlerMapping { - private final HandlerInterceptor securityInterceptor; - - private final CorsConfiguration corsConfiguration; - CloudFoundryEndpointHandlerMapping(Set endpoints, CorsConfiguration corsConfiguration, HandlerInterceptor securityInterceptor) { super(endpoints, corsConfiguration); - this.securityInterceptor = securityInterceptor; - this.corsConfiguration = corsConfiguration; + setSecurityInterceptor(securityInterceptor); } @Override @@ -91,42 +78,4 @@ class CloudFoundryEndpointHandlerMapping return super.getPath(endpoint); } - @Override - protected HandlerExecutionChain getHandlerExecutionChain(Object handler, - HttpServletRequest request) { - HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request); - HandlerInterceptor[] interceptors = addSecurityInterceptor( - chain.getInterceptors()); - return new HandlerExecutionChain(chain.getHandler(), interceptors); - } - - private HandlerInterceptor[] addSecurityInterceptor(HandlerInterceptor[] existing) { - List interceptors = new ArrayList(); - interceptors.add(new CorsInterceptor(this.corsConfiguration)); - interceptors.add(this.securityInterceptor); - if (existing != null) { - interceptors.addAll(Arrays.asList(existing)); - } - return interceptors.toArray(new HandlerInterceptor[interceptors.size()]); - } - - /** - * {@link HandlerInterceptor} that processes the response for CORS. - */ - class CorsInterceptor extends HandlerInterceptorAdapter { - - private final CorsConfiguration config; - - CorsInterceptor(CorsConfiguration config) { - this.config = config; - } - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, - Object handler) throws Exception { - return getCorsProcessor().processRequest(this.config, request, response); - } - - } - } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/AbstractEndpointHandlerMapping.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/AbstractEndpointHandlerMapping.java index 33591f2564..c0025913fe 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/AbstractEndpointHandlerMapping.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/AbstractEndpointHandlerMapping.java @@ -18,18 +18,24 @@ package org.springframework.boot.actuate.endpoint.mvc; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import javax.servlet.http.HttpServletRequest; + import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.context.ApplicationContext; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsUtils; +import org.springframework.web.servlet.HandlerExecutionChain; +import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; @@ -57,6 +63,8 @@ public abstract class AbstractEndpointHandlerMapping private final Set endpoints; + private HandlerInterceptor securityInterceptor; + private final CorsConfiguration corsConfiguration; private String prefix = ""; @@ -175,6 +183,45 @@ public abstract class AbstractEndpointHandlerMapping mapping.getCustomCondition()); } + @Override + protected HandlerExecutionChain getHandlerExecutionChain(Object handler, + HttpServletRequest request) { + HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request); + if (this.securityInterceptor == null || CorsUtils.isCorsRequest(request)) { + return chain; + } + return addSecurityInterceptor(chain); + } + + @Override + protected HandlerExecutionChain getCorsHandlerExecutionChain( + HttpServletRequest request, HandlerExecutionChain chain, + CorsConfiguration config) { + chain = super.getCorsHandlerExecutionChain(request, chain, config); + if (this.securityInterceptor == null) { + return chain; + } + return addSecurityInterceptor(chain); + } + + private HandlerExecutionChain addSecurityInterceptor(HandlerExecutionChain chain) { + List interceptors = new ArrayList(); + if (chain.getInterceptors() != null) { + interceptors.addAll(Arrays.asList(chain.getInterceptors())); + } + interceptors.add(this.securityInterceptor); + return new HandlerExecutionChain(chain.getHandler(), + interceptors.toArray(new HandlerInterceptor[interceptors.size()])); + } + + /** + * Set the handler interceptor that will be used for security. + * @param securityInterceptor the security handler interceptor + */ + public void setSecurityInterceptor(HandlerInterceptor securityInterceptor) { + this.securityInterceptor = securityInterceptor; + } + /** * Set the prefix used in mappings. * @param prefix the prefix diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptor.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptor.java new file mode 100644 index 0000000000..08d4dba5c0 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptor.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.endpoint.mvc; + +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.http.HttpStatus; +import org.springframework.web.cors.CorsUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +/** + * Security interceptor for MvcEndpoints. + * + * @author Madhura Bhave + * @since 1.5.0 + */ +public class MvcEndpointSecurityInterceptor extends HandlerInterceptorAdapter { + + private final boolean secure; + + private final List roles; + + public MvcEndpointSecurityInterceptor(boolean secure, List roles) { + this.secure = secure; + this.roles = roles; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, + Object handler) throws Exception { + if (CorsUtils.isPreFlightRequest(request) || !this.secure) { + return true; + } + HandlerMethod handlerMethod = (HandlerMethod) handler; + MvcEndpoint mvcEndpoint = (MvcEndpoint) handlerMethod.getBean(); + if (!mvcEndpoint.isSensitive()) { + return true; + } + for (String role : this.roles) { + if (request.isUserInRole(role)) { + return true; + } + } + setFailureResponseStatus(request, response); + return false; + } + + private void setFailureResponseStatus(HttpServletRequest request, + HttpServletResponse response) { + if (request.getUserPrincipal() != null) { + response.setStatus(HttpStatus.FORBIDDEN.value()); + } + else { + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + } + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMvcIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMvcIntegrationTests.java index 1bd3b5cde0..b6e2d3dc74 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMvcIntegrationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMvcIntegrationTests.java @@ -56,6 +56,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -73,6 +74,7 @@ import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @DirtiesContext +@TestPropertySource(properties = "management.security.enabled=false") public class EndpointMvcIntegrationTests { @LocalServerPort @@ -82,7 +84,7 @@ public class EndpointMvcIntegrationTests { private TestInterceptor interceptor; @Test - public void envEndpointHidden() throws InterruptedException { + public void envEndpointNotHidden() throws InterruptedException { String body = new TestRestTemplate().getForObject( "http://localhost:" + this.port + "/env/user.dir", String.class); assertThat(body).isNotNull().contains("spring-boot-actuator"); diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java index e0ffed25b4..ce8cfe0cdc 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java @@ -128,6 +128,7 @@ public class EndpointWebMvcAutoConfigurationTests { @Before public void defaultContextPath() { management.setContextPath(""); + management.getSecurity().setEnabled(false); server.setContextPath(""); } @@ -147,6 +148,8 @@ public class EndpointWebMvcAutoConfigurationTests { @Test public void onSamePort() throws Exception { + EnvironmentTestUtils.addEnvironment(this.applicationContext, + "management.security.enabled:false"); this.applicationContext.register(RootConfig.class, EndpointConfig.class, BaseConfiguration.class, ServerPortConfig.class, EndpointWebMvcAutoConfiguration.class); @@ -318,7 +321,8 @@ public class EndpointWebMvcAutoConfigurationTests { public void specificPortsViaProperties() throws Exception { EnvironmentTestUtils.addEnvironment(this.applicationContext, "server.port:" + ports.get().server, - "management.port:" + ports.get().management); + "management.port:" + ports.get().management, + "management.security.enabled:false"); this.applicationContext.register(RootConfig.class, EndpointConfig.class, BaseConfiguration.class, EndpointWebMvcAutoConfiguration.class, ErrorMvcAutoConfiguration.class); @@ -352,7 +356,7 @@ public class EndpointWebMvcAutoConfigurationTests { @Test public void contextPath() throws Exception { EnvironmentTestUtils.addEnvironment(this.applicationContext, - "management.contextPath:/test"); + "management.contextPath:/test", "management.security.enabled:false"); this.applicationContext.register(RootConfig.class, EndpointConfig.class, ServerPortConfig.class, PropertyPlaceholderAutoConfiguration.class, ManagementServerPropertiesAutoConfiguration.class, @@ -884,6 +888,7 @@ public class EndpointWebMvcAutoConfigurationTests { public ManagementServerProperties managementServerProperties() { ManagementServerProperties properties = new ManagementServerProperties(); properties.setPort(0); + properties.getSecurity().setEnabled(false); return properties; } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfigurationTests.java new file mode 100644 index 0000000000..92ef72ccaf --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfigurationTests.java @@ -0,0 +1,92 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure; + +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpointSecurityInterceptor; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration; +import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; +import org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration; +import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.mock.web.MockServletContext; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link EndpointWebMvcManagementContextConfiguration}. + * + * @author Madhura Bhave + */ +public class EndpointWebMvcManagementContextConfigurationTests { + + private AnnotationConfigWebApplicationContext context; + + @Before + public void setup() { + this.context = new AnnotationConfigWebApplicationContext(); + this.context.setServletContext(new MockServletContext()); + this.context.register(SecurityAutoConfiguration.class, + WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class, + ManagementServerPropertiesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, + WebClientAutoConfiguration.class, + EndpointWebMvcManagementContextConfiguration.class); + } + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void endpointHandlerMapping() throws Exception { + EnvironmentTestUtils.addEnvironment(this.context, + "management.security.enabled=false", + "management.security.roles=my-role,your-role"); + this.context.refresh(); + EndpointHandlerMapping mapping = this.context.getBean("endpointHandlerMapping", + EndpointHandlerMapping.class); + assertThat(mapping.getPrefix()).isEmpty(); + MvcEndpointSecurityInterceptor securityInterceptor = (MvcEndpointSecurityInterceptor) ReflectionTestUtils + .getField(mapping, "securityInterceptor"); + Object secure = ReflectionTestUtils.getField(securityInterceptor, "secure"); + List roles = getRoles(securityInterceptor); + assertThat(secure).isEqualTo(false); + assertThat(roles).containsExactly("my-role", "your-role"); + } + + @SuppressWarnings("unchecked") + private List getRoles(MvcEndpointSecurityInterceptor securityInterceptor) { + return (List) ReflectionTestUtils.getField(securityInterceptor, "roles"); + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java index 471d4d9259..0613caaba4 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/JolokiaAutoConfigurationTests.java @@ -17,6 +17,7 @@ package org.springframework.boot.actuate.autoconfigure; import java.util.Collection; +import java.util.Collections; import org.hamcrest.Matchers; import org.junit.After; @@ -25,6 +26,7 @@ import org.junit.Test; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; +import org.springframework.boot.actuate.endpoint.mvc.MvcEndpointSecurityInterceptor; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; @@ -141,7 +143,10 @@ public class JolokiaAutoConfigurationTests { @Bean public EndpointHandlerMapping endpointHandlerMapping( Collection endpoints) { - return new EndpointHandlerMapping(endpoints); + EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints); + mapping.setSecurityInterceptor( + new MvcEndpointSecurityInterceptor(false, Collections.EMPTY_LIST)); + return mapping; } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryEndpointHandlerMappingTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryEndpointHandlerMappingTests.java index b3d13d326f..77f61c24c9 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryEndpointHandlerMappingTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/cloudfoundry/CloudFoundryEndpointHandlerMappingTests.java @@ -19,7 +19,6 @@ package org.springframework.boot.actuate.cloudfoundry; import java.util.Collections; import org.junit.Test; -import org.mockito.Mockito; import org.springframework.boot.actuate.endpoint.AbstractEndpoint; import org.springframework.boot.actuate.endpoint.HealthEndpoint; @@ -33,16 +32,10 @@ import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.OrderedHealthAggregator; import org.springframework.context.support.StaticApplicationContext; import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsProcessor; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerExecutionChain; -import org.springframework.web.servlet.HandlerInterceptor; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; /** * Tests for {@link CloudFoundryEndpointHandlerMapping}. @@ -52,37 +45,6 @@ import static org.mockito.Mockito.verify; public class CloudFoundryEndpointHandlerMappingTests extends AbstractEndpointHandlerMappingTests { - @Test - public void corsInterceptorShouldBeFirstAndCallCorsProcessor() throws Exception { - TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a")); - CorsConfiguration corsConfiguration = new CorsConfiguration(); - CloudFoundryEndpointHandlerMapping handlerMapping = new CloudFoundryEndpointHandlerMapping( - Collections.singleton(endpoint), corsConfiguration, null); - CorsProcessor corsProcessor = mock(CorsProcessor.class); - handlerMapping.setCorsProcessor(corsProcessor); - MockHttpServletRequest request = new MockHttpServletRequest(); - HandlerExecutionChain handlerExecutionChain = handlerMapping - .getHandlerExecutionChain(endpoint, request); - HandlerInterceptor[] interceptors = handlerExecutionChain.getInterceptors(); - CloudFoundryEndpointHandlerMapping.CorsInterceptor corsInterceptor = (CloudFoundryEndpointHandlerMapping.CorsInterceptor) interceptors[0]; - MockHttpServletResponse response = new MockHttpServletResponse(); - corsInterceptor.preHandle(request, response, new Object()); - verify(corsProcessor).processRequest(corsConfiguration, request, response); - } - - @Test - public void getHandlerExecutionChainShouldHaveSecurityInterceptor() throws Exception { - CloudFoundrySecurityInterceptor securityInterceptor = Mockito - .mock(CloudFoundrySecurityInterceptor.class); - TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a")); - CloudFoundryEndpointHandlerMapping handlerMapping = new CloudFoundryEndpointHandlerMapping( - Collections.singleton(endpoint), null, securityInterceptor); - HandlerExecutionChain handlerExecutionChain = handlerMapping - .getHandlerExecutionChain(endpoint, new MockHttpServletRequest()); - HandlerInterceptor[] interceptors = handlerExecutionChain.getInterceptors(); - assertThat(interceptors[1]).isEqualTo(securityInterceptor); - } - @Test public void getHandlerExecutionChainWhenEndpointHasPathShouldMapAgainstName() throws Exception { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/AbstractEndpointHandlerMappingTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/AbstractEndpointHandlerMappingTests.java index 72a9daac08..0d70e8aba6 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/AbstractEndpointHandlerMappingTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/AbstractEndpointHandlerMappingTests.java @@ -18,6 +18,7 @@ package org.springframework.boot.actuate.endpoint.mvc; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import org.junit.Test; @@ -25,8 +26,10 @@ import org.springframework.boot.actuate.endpoint.AbstractEndpoint; import org.springframework.context.support.StaticApplicationContext; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.servlet.HandlerInterceptor; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; /** * Tests for {@link AbstractEndpointHandlerMapping}. @@ -37,6 +40,46 @@ public abstract class AbstractEndpointHandlerMappingTests { private final StaticApplicationContext context = new StaticApplicationContext(); + @Test + public void securityInterceptorShouldBePresentForNonCorsRequest() throws Exception { + HandlerInterceptor securityInterceptor = mock(HandlerInterceptor.class); + TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("a")); + AbstractEndpointHandlerMapping mapping = new TestEndpointHandlerMapping( + Collections.singletonList(endpoint)); + mapping.setApplicationContext(this.context); + mapping.setSecurityInterceptor(securityInterceptor); + mapping.afterPropertiesSet(); + assertThat(mapping.getHandler(request("POST", "/a")).getInterceptors()) + .contains(securityInterceptor); + } + + @Test + public void securityInterceptorIfNullShouldNotBeAdded() throws Exception { + TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("a")); + AbstractEndpointHandlerMapping mapping = new TestEndpointHandlerMapping( + Collections.singletonList(endpoint)); + mapping.setApplicationContext(this.context); + mapping.afterPropertiesSet(); + assertThat(mapping.getHandler(request("POST", "/a")).getInterceptors()).isNull(); + } + + @Test + public void securityInterceptorShouldBePresentAfterCorsInterceptorForCorsRequest() + throws Exception { + HandlerInterceptor securityInterceptor = mock(HandlerInterceptor.class); + TestActionEndpoint endpoint = new TestActionEndpoint(new TestEndpoint("a")); + AbstractEndpointHandlerMapping mapping = new TestEndpointHandlerMapping( + Collections.singletonList(endpoint)); + mapping.setApplicationContext(this.context); + mapping.setSecurityInterceptor(securityInterceptor); + mapping.afterPropertiesSet(); + MockHttpServletRequest request = request("POST", "/a"); + request.addHeader("Origin", "http://example.com"); + assertThat(mapping.getHandler(request).getInterceptors().length).isEqualTo(2); + assertThat(mapping.getHandler(request).getInterceptors()[1]) + .isEqualTo(securityInterceptor); + } + @Test public void pathNotMappedWhenGetPathReturnsNull() throws Exception { TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a")); diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java index 5b91e61886..1f24b65302 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/EnvironmentMvcEndpointTests.java @@ -39,6 +39,7 @@ import org.springframework.context.annotation.Import; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -57,6 +58,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @RunWith(SpringRunner.class) @SpringBootTest +@TestPropertySource(properties = "management.security.enabled=false") @DirtiesContext public class EnvironmentMvcEndpointTests { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HalBrowserMvcEndpointDisabledIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HalBrowserMvcEndpointDisabledIntegrationTests.java index e7772532b9..351c9d8a50 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HalBrowserMvcEndpointDisabledIntegrationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HalBrowserMvcEndpointDisabledIntegrationTests.java @@ -28,6 +28,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -46,6 +47,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @RunWith(SpringRunner.class) @SpringBootTest +@TestPropertySource(properties = "management.security.enabled=false") @DirtiesContext public class HalBrowserMvcEndpointDisabledIntegrationTests { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HalBrowserMvcEndpointManagementContextPathIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HalBrowserMvcEndpointManagementContextPathIntegrationTests.java index ea29faa176..e36e685da1 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HalBrowserMvcEndpointManagementContextPathIntegrationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HalBrowserMvcEndpointManagementContextPathIntegrationTests.java @@ -52,7 +52,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @RunWith(SpringRunner.class) @SpringBootTest -@TestPropertySource(properties = "management.contextPath:/admin") +@TestPropertySource(properties = { "management.contextPath:/admin", + "management.security.enabled=false" }) @DirtiesContext public class HalBrowserMvcEndpointManagementContextPathIntegrationTests { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HalBrowserMvcEndpointVanillaIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HalBrowserMvcEndpointVanillaIntegrationTests.java index 049c8dfbc3..a7ecfb7f58 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HalBrowserMvcEndpointVanillaIntegrationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HalBrowserMvcEndpointVanillaIntegrationTests.java @@ -51,7 +51,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @RunWith(SpringRunner.class) @SpringBootTest @DirtiesContext -@TestPropertySource(properties = "endpoints.hypermedia.enabled=true") +@TestPropertySource(properties = { "endpoints.hypermedia.enabled=true", + "management.security.enabled=false" }) public class HalBrowserMvcEndpointVanillaIntegrationTests { @Autowired diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HeapdumpMvcEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HeapdumpMvcEndpointTests.java index 42157bee1d..03df244c4c 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HeapdumpMvcEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/HeapdumpMvcEndpointTests.java @@ -37,6 +37,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -55,6 +56,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @RunWith(SpringRunner.class) @SpringBootTest +@TestPropertySource(properties = "management.security.enabled=false") public class HeapdumpMvcEndpointTests { @Autowired diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpointContextPathTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpointContextPathTests.java index a0a5e8c22e..403c30cbdc 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpointContextPathTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpointContextPathTests.java @@ -37,6 +37,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -56,6 +57,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @RunWith(SpringRunner.class) @SpringBootTest +@TestPropertySource(properties = "management.security.enabled=false") @ContextConfiguration(classes = { Config.class }, initializers = ContextPathListener.class) @DirtiesContext diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpointTests.java index 2eed780f5e..7c6be1e97f 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpointTests.java @@ -35,6 +35,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -56,6 +57,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @RunWith(SpringRunner.class) @DirtiesContext @SpringBootTest +@TestPropertySource(properties = "management.security.enabled=false") public class JolokiaMvcEndpointTests { @Autowired diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/LoggersMvcEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/LoggersMvcEndpointTests.java index c43b902d11..9ce1f92de7 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/LoggersMvcEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/LoggersMvcEndpointTests.java @@ -40,6 +40,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; @@ -64,6 +65,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @RunWith(SpringRunner.class) @SpringBootTest +@TestPropertySource(properties = "management.security.enabled=false") public class LoggersMvcEndpointTests { @Autowired diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MetricsMvcEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MetricsMvcEndpointTests.java index 2bf65e88b5..416ab12112 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MetricsMvcEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MetricsMvcEndpointTests.java @@ -38,6 +38,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -58,6 +59,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @RunWith(SpringRunner.class) @DirtiesContext @SpringBootTest +@TestPropertySource(properties = "management.security.enabled=false") public class MetricsMvcEndpointTests { @Autowired diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointIntegrationTests.java index 68fe00cf18..1fa3785355 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointIntegrationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointIntegrationTests.java @@ -67,15 +67,17 @@ public class MvcEndpointIntegrationTests { @Test public void defaultJsonResponseIsNotIndented() throws Exception { + TestSecurityContextHolder.getContext().setAuthentication( + new TestingAuthenticationToken("user", "N/A", "ROLE_ADMIN")); this.context = new AnnotationConfigWebApplicationContext(); - this.context.register(DefaultConfiguration.class); - MockMvc mockMvc = createMockMvc(); + this.context.register(SecureConfiguration.class); + MockMvc mockMvc = createSecureMockMvc(); mockMvc.perform(get("/mappings")).andExpect(content().string(startsWith("{\""))); } @Test public void jsonResponsesCanBeIndented() throws Exception { - assertIndentedJsonResponse(DefaultConfiguration.class); + assertIndentedJsonResponse(SecureConfiguration.class); } @Test @@ -100,9 +102,11 @@ public class MvcEndpointIntegrationTests { @Test public void jsonExtensionProvided() throws Exception { + TestSecurityContextHolder.getContext().setAuthentication( + new TestingAuthenticationToken("user", "N/A", "ROLE_ADMIN")); this.context = new AnnotationConfigWebApplicationContext(); - this.context.register(DefaultConfiguration.class); - MockMvc mockMvc = createMockMvc(); + this.context.register(SecureConfiguration.class); + MockMvc mockMvc = createSecureMockMvc(); mockMvc.perform(get("/beans.json")).andExpect(status().isOk()); } @@ -194,11 +198,13 @@ public class MvcEndpointIntegrationTests { } private void assertIndentedJsonResponse(Class configuration) throws Exception { + TestSecurityContextHolder.getContext().setAuthentication( + new TestingAuthenticationToken("user", "N/A", "ROLE_ADMIN")); this.context = new AnnotationConfigWebApplicationContext(); this.context.register(configuration); EnvironmentTestUtils.addEnvironment(this.context, "spring.jackson.serialization.indent-output:true"); - MockMvc mockMvc = createMockMvc(); + MockMvc mockMvc = createSecureMockMvc(); mockMvc.perform(get("/mappings")) .andExpect(content().string(startsWith("{" + LINE_SEPARATOR))); } @@ -230,21 +236,15 @@ public class MvcEndpointIntegrationTests { } - @ImportAutoConfiguration({ HypermediaAutoConfiguration.class, - JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, - EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class, - ManagementServerPropertiesAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class }) + @Import(SecureConfiguration.class) + @ImportAutoConfiguration({ HypermediaAutoConfiguration.class }) static class SpringHateoasConfiguration { } + @Import(SecureConfiguration.class) @ImportAutoConfiguration({ HypermediaAutoConfiguration.class, - RepositoryRestMvcAutoConfiguration.class, JacksonAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, EndpointAutoConfiguration.class, - EndpointWebMvcAutoConfiguration.class, - ManagementServerPropertiesAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class }) + RepositoryRestMvcAutoConfiguration.class }) static class SpringDataRestConfiguration { } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptorTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptorTests.java new file mode 100644 index 0000000000..8586bdd7f7 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/MvcEndpointSecurityInterceptorTests.java @@ -0,0 +1,118 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.endpoint.mvc; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import org.springframework.boot.actuate.endpoint.AbstractEndpoint; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockServletContext; +import org.springframework.web.method.HandlerMethod; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link MvcEndpointSecurityInterceptor}. + * + * @author Madhura Bhave + */ +public class MvcEndpointSecurityInterceptorTests { + + private MvcEndpointSecurityInterceptor securityInterceptor; + + private TestMvcEndpoint mvcEndpoint; + + private TestEndpoint endpoint; + + private HandlerMethod handlerMethod; + + private MockHttpServletRequest request; + + private MockHttpServletResponse response; + + private MockServletContext servletContext; + + private List roles; + + @Before + public void setup() throws Exception { + this.roles = Arrays.asList("SUPER_HERO"); + this.securityInterceptor = new MvcEndpointSecurityInterceptor(true, this.roles); + this.endpoint = new TestEndpoint("a"); + this.mvcEndpoint = new TestMvcEndpoint(this.endpoint); + this.handlerMethod = new HandlerMethod(this.mvcEndpoint, "invoke"); + this.servletContext = new MockServletContext(); + this.request = new MockHttpServletRequest(this.servletContext); + this.response = new MockHttpServletResponse(); + } + + @Test + public void securityDisabledShouldAllowAccess() throws Exception { + this.securityInterceptor = new MvcEndpointSecurityInterceptor(false, this.roles); + assertThat(this.securityInterceptor.preHandle(this.request, this.response, + this.handlerMethod)).isTrue(); + } + + @Test + public void endpointNotSensitiveShouldAllowAccess() throws Exception { + this.endpoint.setSensitive(false); + assertThat(this.securityInterceptor.preHandle(this.request, this.response, + this.handlerMethod)).isTrue(); + } + + @Test + public void sensitiveEndpointIfRoleIsPresentShouldAllowAccess() throws Exception { + this.servletContext.declareRoles("SUPER_HERO"); + assertThat(this.securityInterceptor.preHandle(this.request, this.response, + this.handlerMethod)).isTrue(); + } + + @Test + public void sensitiveEndpointIfRoleIsNotPresentShouldNotAllowAccess() + throws Exception { + this.servletContext.declareRoles("HERO"); + assertThat(this.securityInterceptor.preHandle(this.request, this.response, + this.handlerMethod)).isFalse(); + } + + private static class TestEndpoint extends AbstractEndpoint { + + TestEndpoint(String id) { + super(id); + } + + @Override + public Object invoke() { + return null; + } + + } + + private static class TestMvcEndpoint extends EndpointMvcAdapter { + + TestMvcEndpoint(TestEndpoint delegate) { + super(delegate); + } + + } + +} diff --git a/spring-boot-samples/spring-boot-sample-hypermedia-gson/src/test/java/sample/hypermedia/gson/SampleHypermediaGsonApplicationTests.java b/spring-boot-samples/spring-boot-sample-hypermedia-gson/src/test/java/sample/hypermedia/gson/SampleHypermediaGsonApplicationTests.java index ef4702e13b..40b1680e8f 100644 --- a/spring-boot-samples/spring-boot-sample-hypermedia-gson/src/test/java/sample/hypermedia/gson/SampleHypermediaGsonApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-hypermedia-gson/src/test/java/sample/hypermedia/gson/SampleHypermediaGsonApplicationTests.java @@ -35,7 +35,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @RunWith(SpringRunner.class) @SpringBootTest -@TestPropertySource(properties = "endpoints.hypermedia.enabled: true") +@TestPropertySource(properties = {"endpoints.hypermedia.enabled: true", "management.security.enabled: false"}) public class SampleHypermediaGsonApplicationTests { @Autowired