Commit d09aafac authored by Madhura Bhave's avatar Madhura Bhave

Add a security interceptor for actuator endpoints

Update `AbstractEndpointHandlerMapping` to support a security
interceptor that can be used to enforce endpoint security.

Fixes gh-6889
parent f6b06523
...@@ -65,7 +65,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -65,7 +65,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@ContextConfiguration(classes = SpringBootHypermediaApplication.class, loader = SpringBootContextLoader.class) @ContextConfiguration(classes = SpringBootHypermediaApplication.class, loader = SpringBootContextLoader.class)
@WebAppConfiguration @WebAppConfiguration
@TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true", @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 @DirtiesContext
@AutoConfigureRestDocs(EndpointDocumentation.RESTDOCS_OUTPUT_DIR) @AutoConfigureRestDocs(EndpointDocumentation.RESTDOCS_OUTPUT_DIR)
@AutoConfigureMockMvc(print = MockMvcPrint.NONE) @AutoConfigureMockMvc(print = MockMvcPrint.NONE)
......
...@@ -39,7 +39,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -39,7 +39,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@ContextConfiguration(classes = SpringBootHypermediaApplication.class, loader = SpringBootContextLoader.class) @ContextConfiguration(classes = SpringBootHypermediaApplication.class, loader = SpringBootContextLoader.class)
@WebAppConfiguration @WebAppConfiguration
@TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true", @TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true",
"endpoints.health.sensitive=false" }) "endpoints.health.sensitive=false", "management.security.enabled=false" })
@DirtiesContext @DirtiesContext
@AutoConfigureMockMvc @AutoConfigureMockMvc
@AutoConfigureRestDocs("target/generated-snippets") @AutoConfigureRestDocs("target/generated-snippets")
......
...@@ -40,7 +40,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -40,7 +40,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@ContextConfiguration(classes = SpringBootHypermediaApplication.class, loader = SpringBootContextLoader.class) @ContextConfiguration(classes = SpringBootHypermediaApplication.class, loader = SpringBootContextLoader.class)
@WebAppConfiguration @WebAppConfiguration
@TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true", @TestPropertySource(properties = { "spring.jackson.serialization.indent_output=true",
"endpoints.hypermedia.enabled=true" }) "endpoints.hypermedia.enabled=true", "management.security.enabled=false" })
@DirtiesContext @DirtiesContext
@AutoConfigureMockMvc @AutoConfigureMockMvc
@AutoConfigureRestDocs("target/generated-snippets") @AutoConfigureRestDocs("target/generated-snippets")
......
...@@ -37,6 +37,7 @@ import org.springframework.boot.actuate.endpoint.mvc.LogFileMvcEndpoint; ...@@ -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.LoggersMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; 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.MvcEndpoints;
import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionMessage; import org.springframework.boot.autoconfigure.condition.ConditionMessage;
...@@ -99,6 +100,10 @@ public class EndpointWebMvcManagementContextConfiguration { ...@@ -99,6 +100,10 @@ public class EndpointWebMvcManagementContextConfiguration {
EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints, EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints,
corsConfiguration); corsConfiguration);
mapping.setPrefix(this.managementServerProperties.getContextPath()); 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) { for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) {
customizer.customize(mapping); customizer.customize(mapping);
} }
......
...@@ -16,15 +16,9 @@ ...@@ -16,15 +16,9 @@
package org.springframework.boot.actuate.cloudfoundry; package org.springframework.boot.actuate.cloudfoundry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Set; 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.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.AbstractEndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.mvc.AbstractEndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.HalJsonMvcEndpoint;
...@@ -32,10 +26,8 @@ import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint; ...@@ -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.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping; 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. * {@link HandlerMapping} to map {@link Endpoint}s to Cloud Foundry specific URLs.
...@@ -45,15 +37,10 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; ...@@ -45,15 +37,10 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
class CloudFoundryEndpointHandlerMapping class CloudFoundryEndpointHandlerMapping
extends AbstractEndpointHandlerMapping<NamedMvcEndpoint> { extends AbstractEndpointHandlerMapping<NamedMvcEndpoint> {
private final HandlerInterceptor securityInterceptor;
private final CorsConfiguration corsConfiguration;
CloudFoundryEndpointHandlerMapping(Set<? extends NamedMvcEndpoint> endpoints, CloudFoundryEndpointHandlerMapping(Set<? extends NamedMvcEndpoint> endpoints,
CorsConfiguration corsConfiguration, HandlerInterceptor securityInterceptor) { CorsConfiguration corsConfiguration, HandlerInterceptor securityInterceptor) {
super(endpoints, corsConfiguration); super(endpoints, corsConfiguration);
this.securityInterceptor = securityInterceptor; setSecurityInterceptor(securityInterceptor);
this.corsConfiguration = corsConfiguration;
} }
@Override @Override
...@@ -91,42 +78,4 @@ class CloudFoundryEndpointHandlerMapping ...@@ -91,42 +78,4 @@ class CloudFoundryEndpointHandlerMapping
return super.getPath(endpoint); 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<HandlerInterceptor> interceptors = new ArrayList<HandlerInterceptor>();
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);
}
}
} }
...@@ -18,18 +18,24 @@ package org.springframework.boot.actuate.endpoint.mvc; ...@@ -18,18 +18,24 @@ package org.springframework.boot.actuate.endpoint.mvc;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.cors.CorsConfiguration; 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.HandlerMapping;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
...@@ -57,6 +63,8 @@ public abstract class AbstractEndpointHandlerMapping<E extends MvcEndpoint> ...@@ -57,6 +63,8 @@ public abstract class AbstractEndpointHandlerMapping<E extends MvcEndpoint>
private final Set<E> endpoints; private final Set<E> endpoints;
private HandlerInterceptor securityInterceptor;
private final CorsConfiguration corsConfiguration; private final CorsConfiguration corsConfiguration;
private String prefix = ""; private String prefix = "";
...@@ -175,6 +183,45 @@ public abstract class AbstractEndpointHandlerMapping<E extends MvcEndpoint> ...@@ -175,6 +183,45 @@ public abstract class AbstractEndpointHandlerMapping<E extends MvcEndpoint>
mapping.getCustomCondition()); 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<HandlerInterceptor> interceptors = new ArrayList<HandlerInterceptor>();
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. * Set the prefix used in mappings.
* @param prefix the prefix * @param prefix the prefix
......
/*
* 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<String> roles;
public MvcEndpointSecurityInterceptor(boolean secure, List<String> 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());
}
}
}
...@@ -56,6 +56,7 @@ import org.springframework.context.annotation.Configuration; ...@@ -56,6 +56,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -73,6 +74,7 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -73,6 +74,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@DirtiesContext @DirtiesContext
@TestPropertySource(properties = "management.security.enabled=false")
public class EndpointMvcIntegrationTests { public class EndpointMvcIntegrationTests {
@LocalServerPort @LocalServerPort
...@@ -82,7 +84,7 @@ public class EndpointMvcIntegrationTests { ...@@ -82,7 +84,7 @@ public class EndpointMvcIntegrationTests {
private TestInterceptor interceptor; private TestInterceptor interceptor;
@Test @Test
public void envEndpointHidden() throws InterruptedException { public void envEndpointNotHidden() throws InterruptedException {
String body = new TestRestTemplate().getForObject( String body = new TestRestTemplate().getForObject(
"http://localhost:" + this.port + "/env/user.dir", String.class); "http://localhost:" + this.port + "/env/user.dir", String.class);
assertThat(body).isNotNull().contains("spring-boot-actuator"); assertThat(body).isNotNull().contains("spring-boot-actuator");
......
...@@ -128,6 +128,7 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -128,6 +128,7 @@ public class EndpointWebMvcAutoConfigurationTests {
@Before @Before
public void defaultContextPath() { public void defaultContextPath() {
management.setContextPath(""); management.setContextPath("");
management.getSecurity().setEnabled(false);
server.setContextPath(""); server.setContextPath("");
} }
...@@ -147,6 +148,8 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -147,6 +148,8 @@ public class EndpointWebMvcAutoConfigurationTests {
@Test @Test
public void onSamePort() throws Exception { public void onSamePort() throws Exception {
EnvironmentTestUtils.addEnvironment(this.applicationContext,
"management.security.enabled:false");
this.applicationContext.register(RootConfig.class, EndpointConfig.class, this.applicationContext.register(RootConfig.class, EndpointConfig.class,
BaseConfiguration.class, ServerPortConfig.class, BaseConfiguration.class, ServerPortConfig.class,
EndpointWebMvcAutoConfiguration.class); EndpointWebMvcAutoConfiguration.class);
...@@ -318,7 +321,8 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -318,7 +321,8 @@ public class EndpointWebMvcAutoConfigurationTests {
public void specificPortsViaProperties() throws Exception { public void specificPortsViaProperties() throws Exception {
EnvironmentTestUtils.addEnvironment(this.applicationContext, EnvironmentTestUtils.addEnvironment(this.applicationContext,
"server.port:" + ports.get().server, "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, this.applicationContext.register(RootConfig.class, EndpointConfig.class,
BaseConfiguration.class, EndpointWebMvcAutoConfiguration.class, BaseConfiguration.class, EndpointWebMvcAutoConfiguration.class,
ErrorMvcAutoConfiguration.class); ErrorMvcAutoConfiguration.class);
...@@ -352,7 +356,7 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -352,7 +356,7 @@ public class EndpointWebMvcAutoConfigurationTests {
@Test @Test
public void contextPath() throws Exception { public void contextPath() throws Exception {
EnvironmentTestUtils.addEnvironment(this.applicationContext, EnvironmentTestUtils.addEnvironment(this.applicationContext,
"management.contextPath:/test"); "management.contextPath:/test", "management.security.enabled:false");
this.applicationContext.register(RootConfig.class, EndpointConfig.class, this.applicationContext.register(RootConfig.class, EndpointConfig.class,
ServerPortConfig.class, PropertyPlaceholderAutoConfiguration.class, ServerPortConfig.class, PropertyPlaceholderAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class, ManagementServerPropertiesAutoConfiguration.class,
...@@ -884,6 +888,7 @@ public class EndpointWebMvcAutoConfigurationTests { ...@@ -884,6 +888,7 @@ public class EndpointWebMvcAutoConfigurationTests {
public ManagementServerProperties managementServerProperties() { public ManagementServerProperties managementServerProperties() {
ManagementServerProperties properties = new ManagementServerProperties(); ManagementServerProperties properties = new ManagementServerProperties();
properties.setPort(0); properties.setPort(0);
properties.getSecurity().setEnabled(false);
return properties; return properties;
} }
......
/*
* 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<String> roles = getRoles(securityInterceptor);
assertThat(secure).isEqualTo(false);
assertThat(roles).containsExactly("my-role", "your-role");
}
@SuppressWarnings("unchecked")
private List<String> getRoles(MvcEndpointSecurityInterceptor securityInterceptor) {
return (List<String>) ReflectionTestUtils.getField(securityInterceptor, "roles");
}
}
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.After; import org.junit.After;
...@@ -25,6 +26,7 @@ import org.junit.Test; ...@@ -25,6 +26,7 @@ import org.junit.Test;
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; 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.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
...@@ -141,7 +143,10 @@ public class JolokiaAutoConfigurationTests { ...@@ -141,7 +143,10 @@ public class JolokiaAutoConfigurationTests {
@Bean @Bean
public EndpointHandlerMapping endpointHandlerMapping( public EndpointHandlerMapping endpointHandlerMapping(
Collection<? extends MvcEndpoint> endpoints) { Collection<? extends MvcEndpoint> endpoints) {
return new EndpointHandlerMapping(endpoints); EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints);
mapping.setSecurityInterceptor(
new MvcEndpointSecurityInterceptor(false, Collections.EMPTY_LIST));
return mapping;
} }
} }
......
...@@ -19,7 +19,6 @@ package org.springframework.boot.actuate.cloudfoundry; ...@@ -19,7 +19,6 @@ package org.springframework.boot.actuate.cloudfoundry;
import java.util.Collections; import java.util.Collections;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint; import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.actuate.endpoint.HealthEndpoint; import org.springframework.boot.actuate.endpoint.HealthEndpoint;
...@@ -33,16 +32,10 @@ import org.springframework.boot.actuate.health.HealthIndicator; ...@@ -33,16 +32,10 @@ import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.OrderedHealthAggregator; import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.context.support.StaticApplicationContext; import org.springframework.context.support.StaticApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest; 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.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain; import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/** /**
* Tests for {@link CloudFoundryEndpointHandlerMapping}. * Tests for {@link CloudFoundryEndpointHandlerMapping}.
...@@ -52,37 +45,6 @@ import static org.mockito.Mockito.verify; ...@@ -52,37 +45,6 @@ import static org.mockito.Mockito.verify;
public class CloudFoundryEndpointHandlerMappingTests public class CloudFoundryEndpointHandlerMappingTests
extends AbstractEndpointHandlerMappingTests { 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 @Test
public void getHandlerExecutionChainWhenEndpointHasPathShouldMapAgainstName() public void getHandlerExecutionChainWhenEndpointHasPathShouldMapAgainstName()
throws Exception { throws Exception {
......
...@@ -18,6 +18,7 @@ package org.springframework.boot.actuate.endpoint.mvc; ...@@ -18,6 +18,7 @@ package org.springframework.boot.actuate.endpoint.mvc;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import org.junit.Test; import org.junit.Test;
...@@ -25,8 +26,10 @@ import org.springframework.boot.actuate.endpoint.AbstractEndpoint; ...@@ -25,8 +26,10 @@ import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.context.support.StaticApplicationContext; import org.springframework.context.support.StaticApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.HandlerInterceptor;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link AbstractEndpointHandlerMapping}. * Tests for {@link AbstractEndpointHandlerMapping}.
...@@ -37,6 +40,46 @@ public abstract class AbstractEndpointHandlerMappingTests { ...@@ -37,6 +40,46 @@ public abstract class AbstractEndpointHandlerMappingTests {
private final StaticApplicationContext context = new StaticApplicationContext(); 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<TestActionEndpoint>(
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<TestActionEndpoint>(
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<TestActionEndpoint>(
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 @Test
public void pathNotMappedWhenGetPathReturnsNull() throws Exception { public void pathNotMappedWhenGetPathReturnsNull() throws Exception {
TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a")); TestMvcEndpoint endpoint = new TestMvcEndpoint(new TestEndpoint("a"));
......
...@@ -39,6 +39,7 @@ import org.springframework.context.annotation.Import; ...@@ -39,6 +39,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MapPropertySource;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
...@@ -57,6 +58,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -57,6 +58,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest
@TestPropertySource(properties = "management.security.enabled=false")
@DirtiesContext @DirtiesContext
public class EnvironmentMvcEndpointTests { public class EnvironmentMvcEndpointTests {
......
...@@ -28,6 +28,7 @@ import org.springframework.boot.test.context.SpringBootTest; ...@@ -28,6 +28,7 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
...@@ -46,6 +47,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -46,6 +47,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest
@TestPropertySource(properties = "management.security.enabled=false")
@DirtiesContext @DirtiesContext
public class HalBrowserMvcEndpointDisabledIntegrationTests { public class HalBrowserMvcEndpointDisabledIntegrationTests {
......
...@@ -52,7 +52,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -52,7 +52,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest
@TestPropertySource(properties = "management.contextPath:/admin") @TestPropertySource(properties = { "management.contextPath:/admin",
"management.security.enabled=false" })
@DirtiesContext @DirtiesContext
public class HalBrowserMvcEndpointManagementContextPathIntegrationTests { public class HalBrowserMvcEndpointManagementContextPathIntegrationTests {
......
...@@ -51,7 +51,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -51,7 +51,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest
@DirtiesContext @DirtiesContext
@TestPropertySource(properties = "endpoints.hypermedia.enabled=true") @TestPropertySource(properties = { "endpoints.hypermedia.enabled=true",
"management.security.enabled=false" })
public class HalBrowserMvcEndpointVanillaIntegrationTests { public class HalBrowserMvcEndpointVanillaIntegrationTests {
@Autowired @Autowired
......
...@@ -37,6 +37,7 @@ import org.springframework.boot.test.context.SpringBootTest; ...@@ -37,6 +37,7 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.MvcResult;
...@@ -55,6 +56,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -55,6 +56,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest
@TestPropertySource(properties = "management.security.enabled=false")
public class HeapdumpMvcEndpointTests { public class HeapdumpMvcEndpointTests {
@Autowired @Autowired
......
...@@ -37,6 +37,7 @@ import org.springframework.context.annotation.Configuration; ...@@ -37,6 +37,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
...@@ -56,6 +57,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -56,6 +57,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest
@TestPropertySource(properties = "management.security.enabled=false")
@ContextConfiguration(classes = { @ContextConfiguration(classes = {
Config.class }, initializers = ContextPathListener.class) Config.class }, initializers = ContextPathListener.class)
@DirtiesContext @DirtiesContext
......
...@@ -35,6 +35,7 @@ import org.springframework.context.ConfigurableApplicationContext; ...@@ -35,6 +35,7 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
...@@ -56,6 +57,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -56,6 +57,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@DirtiesContext @DirtiesContext
@SpringBootTest @SpringBootTest
@TestPropertySource(properties = "management.security.enabled=false")
public class JolokiaMvcEndpointTests { public class JolokiaMvcEndpointTests {
@Autowired @Autowired
......
...@@ -40,6 +40,7 @@ import org.springframework.context.annotation.Bean; ...@@ -40,6 +40,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
...@@ -64,6 +65,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -64,6 +65,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest
@TestPropertySource(properties = "management.security.enabled=false")
public class LoggersMvcEndpointTests { public class LoggersMvcEndpointTests {
@Autowired @Autowired
......
...@@ -38,6 +38,7 @@ import org.springframework.context.annotation.Bean; ...@@ -38,6 +38,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
...@@ -58,6 +59,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -58,6 +59,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@DirtiesContext @DirtiesContext
@SpringBootTest @SpringBootTest
@TestPropertySource(properties = "management.security.enabled=false")
public class MetricsMvcEndpointTests { public class MetricsMvcEndpointTests {
@Autowired @Autowired
......
...@@ -67,15 +67,17 @@ public class MvcEndpointIntegrationTests { ...@@ -67,15 +67,17 @@ public class MvcEndpointIntegrationTests {
@Test @Test
public void defaultJsonResponseIsNotIndented() throws Exception { public void defaultJsonResponseIsNotIndented() throws Exception {
TestSecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken("user", "N/A", "ROLE_ADMIN"));
this.context = new AnnotationConfigWebApplicationContext(); this.context = new AnnotationConfigWebApplicationContext();
this.context.register(DefaultConfiguration.class); this.context.register(SecureConfiguration.class);
MockMvc mockMvc = createMockMvc(); MockMvc mockMvc = createSecureMockMvc();
mockMvc.perform(get("/mappings")).andExpect(content().string(startsWith("{\""))); mockMvc.perform(get("/mappings")).andExpect(content().string(startsWith("{\"")));
} }
@Test @Test
public void jsonResponsesCanBeIndented() throws Exception { public void jsonResponsesCanBeIndented() throws Exception {
assertIndentedJsonResponse(DefaultConfiguration.class); assertIndentedJsonResponse(SecureConfiguration.class);
} }
@Test @Test
...@@ -100,9 +102,11 @@ public class MvcEndpointIntegrationTests { ...@@ -100,9 +102,11 @@ public class MvcEndpointIntegrationTests {
@Test @Test
public void jsonExtensionProvided() throws Exception { public void jsonExtensionProvided() throws Exception {
TestSecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken("user", "N/A", "ROLE_ADMIN"));
this.context = new AnnotationConfigWebApplicationContext(); this.context = new AnnotationConfigWebApplicationContext();
this.context.register(DefaultConfiguration.class); this.context.register(SecureConfiguration.class);
MockMvc mockMvc = createMockMvc(); MockMvc mockMvc = createSecureMockMvc();
mockMvc.perform(get("/beans.json")).andExpect(status().isOk()); mockMvc.perform(get("/beans.json")).andExpect(status().isOk());
} }
...@@ -194,11 +198,13 @@ public class MvcEndpointIntegrationTests { ...@@ -194,11 +198,13 @@ public class MvcEndpointIntegrationTests {
} }
private void assertIndentedJsonResponse(Class<?> configuration) throws Exception { private void assertIndentedJsonResponse(Class<?> configuration) throws Exception {
TestSecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken("user", "N/A", "ROLE_ADMIN"));
this.context = new AnnotationConfigWebApplicationContext(); this.context = new AnnotationConfigWebApplicationContext();
this.context.register(configuration); this.context.register(configuration);
EnvironmentTestUtils.addEnvironment(this.context, EnvironmentTestUtils.addEnvironment(this.context,
"spring.jackson.serialization.indent-output:true"); "spring.jackson.serialization.indent-output:true");
MockMvc mockMvc = createMockMvc(); MockMvc mockMvc = createSecureMockMvc();
mockMvc.perform(get("/mappings")) mockMvc.perform(get("/mappings"))
.andExpect(content().string(startsWith("{" + LINE_SEPARATOR))); .andExpect(content().string(startsWith("{" + LINE_SEPARATOR)));
} }
...@@ -230,21 +236,15 @@ public class MvcEndpointIntegrationTests { ...@@ -230,21 +236,15 @@ public class MvcEndpointIntegrationTests {
} }
@ImportAutoConfiguration({ HypermediaAutoConfiguration.class, @Import(SecureConfiguration.class)
JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, @ImportAutoConfiguration({ HypermediaAutoConfiguration.class })
EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class })
static class SpringHateoasConfiguration { static class SpringHateoasConfiguration {
} }
@Import(SecureConfiguration.class)
@ImportAutoConfiguration({ HypermediaAutoConfiguration.class, @ImportAutoConfiguration({ HypermediaAutoConfiguration.class,
RepositoryRestMvcAutoConfiguration.class, JacksonAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class })
HttpMessageConvertersAutoConfiguration.class, EndpointAutoConfiguration.class,
EndpointWebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class })
static class SpringDataRestConfiguration { static class SpringDataRestConfiguration {
} }
......
/*
* 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<String> 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<Object> {
TestEndpoint(String id) {
super(id);
}
@Override
public Object invoke() {
return null;
}
}
private static class TestMvcEndpoint extends EndpointMvcAdapter {
TestMvcEndpoint(TestEndpoint delegate) {
super(delegate);
}
}
}
...@@ -35,7 +35,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -35,7 +35,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest
@TestPropertySource(properties = "endpoints.hypermedia.enabled: true") @TestPropertySource(properties = {"endpoints.hypermedia.enabled: true", "management.security.enabled: false"})
public class SampleHypermediaGsonApplicationTests { public class SampleHypermediaGsonApplicationTests {
@Autowired @Autowired
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment