Merge branch '1.5.x'
This commit is contained in:
commit
1cd1054514
|
|
@ -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.
|
||||||
@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.
|
||||||
@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;
|
||||||
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 {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ public class ManagementServerProperties implements SecurityPrerequisite {
|
||||||
/**
|
/**
|
||||||
* Comma-separated list of roles that can access the management endpoint.
|
* Comma-separated list of roles that can access the management endpoint.
|
||||||
*/
|
*/
|
||||||
private List<String> roles = Arrays.asList("ADMIN");
|
private List<String> roles = Arrays.asList("ACTUATOR");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Session creating policy for security use (always, never, if_required,
|
* Session creating policy for security use (always, never, if_required,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import javax.annotation.PostConstruct;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.beans.factory.ObjectProvider;
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
|
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
|
||||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||||
|
|
@ -42,11 +41,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
|
||||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||||
import org.springframework.boot.autoconfigure.security.AuthenticationManagerConfiguration;
|
import org.springframework.boot.autoconfigure.security.AuthenticationManagerConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration;
|
import org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.security.IgnoredRequestCustomizer;
|
||||||
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
|
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.security.SecurityPrerequisite;
|
import org.springframework.boot.autoconfigure.security.SecurityPrerequisite;
|
||||||
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
||||||
import org.springframework.boot.autoconfigure.security.SpringBootWebSecurityConfiguration;
|
import org.springframework.boot.autoconfigure.security.SpringBootWebSecurityConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.web.ErrorController;
|
|
||||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
@ -56,9 +55,7 @@ import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
|
||||||
import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer;
|
import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
|
||||||
|
|
@ -72,7 +69,6 @@ import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
|
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.util.ObjectUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -102,9 +98,34 @@ public class ManagementWebSecurityAutoConfiguration {
|
||||||
AnyRequestMatcher.INSTANCE);
|
AnyRequestMatcher.INSTANCE);
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean({ IgnoredPathsWebSecurityConfigurerAdapter.class })
|
public IgnoredRequestCustomizer managementIgnoredRequestCustomizer(
|
||||||
public IgnoredPathsWebSecurityConfigurerAdapter ignoredPathsWebSecurityConfigurerAdapter() {
|
ManagementServerProperties management,
|
||||||
return new IgnoredPathsWebSecurityConfigurerAdapter();
|
ObjectProvider<ManagementContextResolver> contextResolverProvider) {
|
||||||
|
return new ManagementIgnoredRequestCustomizer(management,
|
||||||
|
contextResolverProvider.getIfAvailable());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ManagementIgnoredRequestCustomizer implements IgnoredRequestCustomizer {
|
||||||
|
|
||||||
|
private final ManagementServerProperties management;
|
||||||
|
|
||||||
|
private final ManagementContextResolver contextResolver;
|
||||||
|
|
||||||
|
ManagementIgnoredRequestCustomizer(ManagementServerProperties management,
|
||||||
|
ManagementContextResolver contextResolver) {
|
||||||
|
this.management = management;
|
||||||
|
this.contextResolver = contextResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(IgnoredRequestConfigurer configurer) {
|
||||||
|
if (!this.management.getSecurity().isEnabled()) {
|
||||||
|
RequestMatcher requestMatcher = LazyEndpointPathRequestMatcher
|
||||||
|
.getRequestMatcher(this.contextResolver);
|
||||||
|
configurer.requestMatchers(requestMatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
|
@ -132,80 +153,6 @@ public class ManagementWebSecurityAutoConfiguration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the ignored paths in early
|
|
||||||
@Order(SecurityProperties.IGNORED_ORDER + 1)
|
|
||||||
private static class IgnoredPathsWebSecurityConfigurerAdapter
|
|
||||||
implements WebSecurityConfigurer<WebSecurity> {
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
private ErrorController errorController;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private SecurityProperties security;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ManagementServerProperties management;
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
private ManagementContextResolver contextResolver;
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
private ServerProperties server;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void configure(WebSecurity builder) throws Exception {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(WebSecurity builder) throws Exception {
|
|
||||||
if (this.server == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
IgnoredRequestConfigurer ignoring = builder.ignoring();
|
|
||||||
// The ignores are not cumulative, so to prevent overwriting the defaults
|
|
||||||
// we add them back.
|
|
||||||
Set<String> ignored = new LinkedHashSet<String>(
|
|
||||||
SpringBootWebSecurityConfiguration.getIgnored(this.security));
|
|
||||||
if (ignored.contains("none")) {
|
|
||||||
ignored.remove("none");
|
|
||||||
}
|
|
||||||
if (this.errorController != null) {
|
|
||||||
ignored.add(normalizePath(this.errorController.getErrorPath()));
|
|
||||||
}
|
|
||||||
RequestMatcher requestMatcher = getRequestMatcher();
|
|
||||||
String[] paths = this.server.getPathsArray(ignored);
|
|
||||||
if (!ObjectUtils.isEmpty(paths)) {
|
|
||||||
List<RequestMatcher> matchers = new ArrayList<RequestMatcher>();
|
|
||||||
for (String pattern : paths) {
|
|
||||||
matchers.add(new AntPathRequestMatcher(pattern, null));
|
|
||||||
}
|
|
||||||
if (requestMatcher != null) {
|
|
||||||
matchers.add(requestMatcher);
|
|
||||||
}
|
|
||||||
requestMatcher = new OrRequestMatcher(matchers);
|
|
||||||
}
|
|
||||||
if (requestMatcher != null) {
|
|
||||||
ignoring.requestMatchers(requestMatcher);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private RequestMatcher getRequestMatcher() {
|
|
||||||
if (this.management.getSecurity().isEnabled()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return LazyEndpointPathRequestMatcher.getRequestMatcher(this.contextResolver);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String normalizePath(String errorPath) {
|
|
||||||
String result = StringUtils.cleanPath(errorPath);
|
|
||||||
if (!result.startsWith("/")) {
|
|
||||||
result = "/" + result;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnMissingBean(WebSecurityConfiguration.class)
|
@ConditionalOnMissingBean(WebSecurityConfiguration.class)
|
||||||
@Conditional(WebSecurityEnablerCondition.class)
|
@Conditional(WebSecurityEnablerCondition.class)
|
||||||
|
|
@ -310,9 +257,7 @@ public class ManagementWebSecurityAutoConfiguration {
|
||||||
// Permit access to the non-sensitive endpoints
|
// Permit access to the non-sensitive endpoints
|
||||||
requests.requestMatchers(new LazyEndpointPathRequestMatcher(
|
requests.requestMatchers(new LazyEndpointPathRequestMatcher(
|
||||||
this.contextResolver, EndpointPaths.NON_SENSITIVE)).permitAll();
|
this.contextResolver, EndpointPaths.NON_SENSITIVE)).permitAll();
|
||||||
// Restrict the rest to the configured roles
|
requests.anyRequest().authenticated();
|
||||||
List<String> roles = this.management.getSecurity().getRoles();
|
|
||||||
requests.anyRequest().hasAnyRole(roles.toArray(new String[roles.size()]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,15 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.autoconfigure.security.IgnoredRequestCustomizer;
|
||||||
import org.springframework.boot.cloud.CloudPlatform;
|
import org.springframework.boot.cloud.CloudPlatform;
|
||||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||||
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.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
|
|
@ -95,4 +98,20 @@ public class CloudFoundryActuatorAutoConfiguration {
|
||||||
return corsConfiguration;
|
return corsConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public IgnoredRequestCustomizer cloudFoundryIgnoredRequestCustomizer() {
|
||||||
|
return new CloudFoundryIgnoredRequestCustomizer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CloudFoundryIgnoredRequestCustomizer
|
||||||
|
implements IgnoredRequestCustomizer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(WebSecurity.IgnoredRequestConfigurer configurer) {
|
||||||
|
configurer.requestMatchers(
|
||||||
|
new AntPathRequestMatcher("/cloudfoundryapplication/**"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
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;
|
||||||
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
|
||||||
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;
|
||||||
|
|
||||||
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>
|
||||||
|
|
||||||
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>
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ public class HealthMvcEndpoint extends AbstractEndpointMvcAdapter<HealthEndpoint
|
||||||
Authentication authentication = (Authentication) principal;
|
Authentication authentication = (Authentication) principal;
|
||||||
List<String> roles = Arrays.asList(StringUtils
|
List<String> roles = Arrays.asList(StringUtils
|
||||||
.trimArrayElements(StringUtils.commaDelimitedListToStringArray(
|
.trimArrayElements(StringUtils.commaDelimitedListToStringArray(
|
||||||
this.roleResolver.getProperty("roles", "ROLE_ADMIN"))));
|
this.roleResolver.getProperty("roles", "ROLE_ACTUATOR"))));
|
||||||
for (GrantedAuthority authority : authentication.getAuthorities()) {
|
for (GrantedAuthority authority : authentication.getAuthorities()) {
|
||||||
String name = authority.getAuthority();
|
String name = authority.getAuthority();
|
||||||
for (String role : roles) {
|
for (String role : roles) {
|
||||||
|
|
|
||||||
|
|
@ -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<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;
|
||||||
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;
|
||||||
@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 {
|
||||||
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 {
|
||||||
@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 {
|
||||||
|
|
||||||
@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 {
|
||||||
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 {
|
||||||
@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 {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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<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 @@
|
||||||
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;
|
||||||
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 {
|
||||||
@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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ public class ManagementWebSecurityAutoConfigurationTests {
|
||||||
ArrayList<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(
|
ArrayList<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(
|
||||||
user.getAuthorities());
|
user.getAuthorities());
|
||||||
assertThat(authorities).containsAll(AuthorityUtils
|
assertThat(authorities).containsAll(AuthorityUtils
|
||||||
.commaSeparatedStringToAuthorityList("ROLE_USER,ROLE_ADMIN"));
|
.commaSeparatedStringToAuthorityList("ROLE_USER,ROLE_ACTUATOR"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserDetails getUser() {
|
private UserDetails getUser() {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import java.util.Arrays;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
|
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
|
||||||
import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration;
|
import org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration;
|
||||||
|
|
@ -29,18 +30,24 @@ import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties
|
||||||
import org.springframework.boot.actuate.autoconfigure.ManagementWebSecurityAutoConfiguration;
|
import org.springframework.boot.actuate.autoconfigure.ManagementWebSecurityAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.security.IgnoredRequestCustomizer;
|
||||||
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
|
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
|
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration;
|
import org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
|
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
|
||||||
import org.springframework.boot.test.util.EnvironmentTestUtils;
|
import org.springframework.boot.test.util.EnvironmentTestUtils;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockServletContext;
|
import org.springframework.mock.web.MockServletContext;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
|
||||||
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 CloudFoundryActuatorAutoConfiguration}.
|
* Tests for {@link CloudFoundryActuatorAutoConfiguration}.
|
||||||
|
|
@ -116,10 +123,9 @@ public class CloudFoundryActuatorAutoConfigurationTests {
|
||||||
EnvironmentTestUtils.addEnvironment(this.context, "VCAP_APPLICATION:---",
|
EnvironmentTestUtils.addEnvironment(this.context, "VCAP_APPLICATION:---",
|
||||||
"vcap.application.application_id:my-app-id");
|
"vcap.application.application_id:my-app-id");
|
||||||
this.context.refresh();
|
this.context.refresh();
|
||||||
CloudFoundryEndpointHandlerMapping handlerMapping1 = this.context.getBean(
|
CloudFoundryEndpointHandlerMapping handlerMapping = this.context.getBean(
|
||||||
"cloudFoundryEndpointHandlerMapping",
|
"cloudFoundryEndpointHandlerMapping",
|
||||||
CloudFoundryEndpointHandlerMapping.class);
|
CloudFoundryEndpointHandlerMapping.class);
|
||||||
CloudFoundryEndpointHandlerMapping handlerMapping = handlerMapping1;
|
|
||||||
Object securityInterceptor = ReflectionTestUtils.getField(handlerMapping,
|
Object securityInterceptor = ReflectionTestUtils.getField(handlerMapping,
|
||||||
"securityInterceptor");
|
"securityInterceptor");
|
||||||
Object interceptorSecurityService = ReflectionTestUtils
|
Object interceptorSecurityService = ReflectionTestUtils
|
||||||
|
|
@ -127,13 +133,24 @@ public class CloudFoundryActuatorAutoConfigurationTests {
|
||||||
assertThat(interceptorSecurityService).isNull();
|
assertThat(interceptorSecurityService).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CloudFoundryEndpointHandlerMapping getHandlerMapping() {
|
@Test
|
||||||
|
public void cloudFoundryPathsIgnoredBySpringSecurity() throws Exception {
|
||||||
EnvironmentTestUtils.addEnvironment(this.context, "VCAP_APPLICATION:---",
|
EnvironmentTestUtils.addEnvironment(this.context, "VCAP_APPLICATION:---",
|
||||||
"vcap.application.application_id:my-app-id",
|
"vcap.application.application_id:my-app-id");
|
||||||
"vcap.application.cf_api:http://my-cloud-controller.com");
|
|
||||||
this.context.refresh();
|
this.context.refresh();
|
||||||
return this.context.getBean("cloudFoundryEndpointHandlerMapping",
|
IgnoredRequestCustomizer customizer = (IgnoredRequestCustomizer) this.context
|
||||||
CloudFoundryEndpointHandlerMapping.class);
|
.getBean("cloudFoundryIgnoredRequestCustomizer");
|
||||||
|
IgnoredRequestConfigurer configurer = mock(IgnoredRequestConfigurer.class);
|
||||||
|
customizer.customize(configurer);
|
||||||
|
ArgumentCaptor<RequestMatcher> requestMatcher = ArgumentCaptor
|
||||||
|
.forClass(RequestMatcher.class);
|
||||||
|
verify(configurer).requestMatchers(requestMatcher.capture());
|
||||||
|
RequestMatcher matcher = requestMatcher.getValue();
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
request.setServletPath("/cloudfoundryapplication/my-path");
|
||||||
|
assertThat(matcher.matches(request)).isTrue();
|
||||||
|
request.setServletPath("/some-other-path");
|
||||||
|
assertThat(matcher.matches(request)).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -152,4 +169,13 @@ public class CloudFoundryActuatorAutoConfigurationTests {
|
||||||
.isFalse();
|
.isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CloudFoundryEndpointHandlerMapping getHandlerMapping() {
|
||||||
|
EnvironmentTestUtils.addEnvironment(this.context, "VCAP_APPLICATION:---",
|
||||||
|
"vcap.application.application_id:my-app-id",
|
||||||
|
"vcap.application.cf_api:http://my-cloud-controller.com");
|
||||||
|
this.context.refresh();
|
||||||
|
return this.context.getBean("cloudFoundryEndpointHandlerMapping",
|
||||||
|
CloudFoundryEndpointHandlerMapping.class);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
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;
|
||||||
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;
|
||||||
|
|
||||||
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;
|
||||||
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 {
|
||||||
|
|
||||||
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;
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
@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;
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
@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.
|
||||||
*/
|
*/
|
||||||
@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.
|
||||||
@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;
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
@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;
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
@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;
|
||||||
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.
|
||||||
@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;
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
@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;
|
||||||
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.
|
||||||
@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 {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void defaultJsonResponseIsNotIndented() throws Exception {
|
public void defaultJsonResponseIsNotIndented() throws Exception {
|
||||||
|
TestSecurityContextHolder.getContext().setAuthentication(
|
||||||
|
new TestingAuthenticationToken("user", "N/A", "ROLE_ACTUATOR"));
|
||||||
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 {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void jsonExtensionProvided() throws Exception {
|
public void jsonExtensionProvided() throws Exception {
|
||||||
|
TestSecurityContextHolder.getContext().setAuthentication(
|
||||||
|
new TestingAuthenticationToken("user", "N/A", "ROLE_ACTUATOR"));
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,7 +151,7 @@ public class MvcEndpointIntegrationTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sensitiveEndpointsAreSecureWithNonAdminRoleWithCustomContextPath()
|
public void sensitiveEndpointsAreSecureWithNonActuatorRoleWithCustomContextPath()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
TestSecurityContextHolder.getContext().setAuthentication(
|
TestSecurityContextHolder.getContext().setAuthentication(
|
||||||
new TestingAuthenticationToken("user", "N/A", "ROLE_USER"));
|
new TestingAuthenticationToken("user", "N/A", "ROLE_USER"));
|
||||||
|
|
@ -160,10 +164,10 @@ public class MvcEndpointIntegrationTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sensitiveEndpointsAreSecureWithAdminRoleWithCustomContextPath()
|
public void sensitiveEndpointsAreSecureWithActuatorRoleWithCustomContextPath()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
TestSecurityContextHolder.getContext().setAuthentication(
|
TestSecurityContextHolder.getContext().setAuthentication(
|
||||||
new TestingAuthenticationToken("user", "N/A", "ROLE_ADMIN"));
|
new TestingAuthenticationToken("user", "N/A", "ROLE_ACTUATOR"));
|
||||||
this.context = new AnnotationConfigWebApplicationContext();
|
this.context = new AnnotationConfigWebApplicationContext();
|
||||||
this.context.register(SecureConfiguration.class);
|
this.context.register(SecureConfiguration.class);
|
||||||
EnvironmentTestUtils.addEnvironment(this.context,
|
EnvironmentTestUtils.addEnvironment(this.context,
|
||||||
|
|
@ -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_ACTUATOR"));
|
||||||
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 {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@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 {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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<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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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.autoconfigure.security;
|
||||||
|
|
||||||
|
import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customizer that can be implemented by beans to configure paths that need to be ignored
|
||||||
|
* by Spring Boot's default Spring Security configuration.
|
||||||
|
*
|
||||||
|
* @author Madhura Bhave
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
public interface IgnoredRequestCustomizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customize the provided {@link IgnoredRequestConfigurer}.
|
||||||
|
* @param configurer the configurer to customize
|
||||||
|
*/
|
||||||
|
void customize(IgnoredRequestConfigurer configurer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -22,7 +22,7 @@ import java.util.List;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
|
@ -41,6 +41,7 @@ import org.springframework.security.config.annotation.authentication.builders.Au
|
||||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
|
|
@ -48,7 +49,9 @@ import org.springframework.security.config.annotation.web.configurers.HeadersCon
|
||||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
|
||||||
import org.springframework.security.web.header.writers.HstsHeaderWriter;
|
import org.springframework.security.web.header.writers.HstsHeaderWriter;
|
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||||
|
import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
@ -94,8 +97,17 @@ public class SpringBootWebSecurityConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean({ IgnoredPathsWebSecurityConfigurerAdapter.class })
|
@ConditionalOnMissingBean({ IgnoredPathsWebSecurityConfigurerAdapter.class })
|
||||||
public IgnoredPathsWebSecurityConfigurerAdapter ignoredPathsWebSecurityConfigurerAdapter() {
|
public IgnoredPathsWebSecurityConfigurerAdapter ignoredPathsWebSecurityConfigurerAdapter(
|
||||||
return new IgnoredPathsWebSecurityConfigurerAdapter();
|
List<IgnoredRequestCustomizer> customizers) {
|
||||||
|
return new IgnoredPathsWebSecurityConfigurerAdapter(customizers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public IgnoredRequestCustomizer defaultIgnoredRequestsCustomizer(
|
||||||
|
ServerProperties server, SecurityProperties security,
|
||||||
|
ObjectProvider<ErrorController> errorController) {
|
||||||
|
return new DefaultIgnoredRequestCustomizer(server, security,
|
||||||
|
errorController.getIfAvailable());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void configureHeaders(HeadersConfigurer<?> configurer,
|
public static void configureHeaders(HeadersConfigurer<?> configurer,
|
||||||
|
|
@ -146,14 +158,12 @@ public class SpringBootWebSecurityConfiguration {
|
||||||
private static class IgnoredPathsWebSecurityConfigurerAdapter
|
private static class IgnoredPathsWebSecurityConfigurerAdapter
|
||||||
implements WebSecurityConfigurer<WebSecurity> {
|
implements WebSecurityConfigurer<WebSecurity> {
|
||||||
|
|
||||||
@Autowired(required = false)
|
private final List<IgnoredRequestCustomizer> customizers;
|
||||||
private ErrorController errorController;
|
|
||||||
|
|
||||||
@Autowired
|
IgnoredPathsWebSecurityConfigurerAdapter(
|
||||||
private SecurityProperties security;
|
List<IgnoredRequestCustomizer> customizers) {
|
||||||
|
this.customizers = customizers;
|
||||||
@Autowired
|
}
|
||||||
private ServerProperties server;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(WebSecurity builder) throws Exception {
|
public void configure(WebSecurity builder) throws Exception {
|
||||||
|
|
@ -161,15 +171,56 @@ public class SpringBootWebSecurityConfiguration {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(WebSecurity builder) throws Exception {
|
public void init(WebSecurity builder) throws Exception {
|
||||||
|
for (IgnoredRequestCustomizer customizer : this.customizers) {
|
||||||
|
customizer.customize(builder.ignoring());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DefaultIgnoredRequestCustomizer implements IgnoredRequestCustomizer {
|
||||||
|
|
||||||
|
private final ServerProperties server;
|
||||||
|
|
||||||
|
private final SecurityProperties security;
|
||||||
|
|
||||||
|
private final ErrorController errorController;
|
||||||
|
|
||||||
|
DefaultIgnoredRequestCustomizer(ServerProperties server,
|
||||||
|
SecurityProperties security, ErrorController errorController) {
|
||||||
|
this.server = server;
|
||||||
|
this.security = security;
|
||||||
|
this.errorController = errorController;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(IgnoredRequestConfigurer configurer) {
|
||||||
List<String> ignored = getIgnored(this.security);
|
List<String> ignored = getIgnored(this.security);
|
||||||
if (this.errorController != null) {
|
if (this.errorController != null) {
|
||||||
ignored.add(normalizePath(this.errorController.getErrorPath()));
|
ignored.add(normalizePath(this.errorController.getErrorPath()));
|
||||||
}
|
}
|
||||||
String[] paths = this.server.getPathsArray(ignored);
|
String[] paths = this.server.getPathsArray(ignored);
|
||||||
|
List<RequestMatcher> matchers = new ArrayList<RequestMatcher>();
|
||||||
if (!ObjectUtils.isEmpty(paths)) {
|
if (!ObjectUtils.isEmpty(paths)) {
|
||||||
builder.ignoring().antMatchers(paths);
|
for (String pattern : paths) {
|
||||||
|
matchers.add(new AntPathRequestMatcher(pattern, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!matchers.isEmpty()) {
|
||||||
|
configurer.requestMatchers(new OrRequestMatcher(matchers));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getIgnored(SecurityProperties security) {
|
||||||
|
List<String> ignored = new ArrayList<String>(security.getIgnored());
|
||||||
|
if (ignored.isEmpty()) {
|
||||||
|
ignored.addAll(DEFAULT_IGNORED);
|
||||||
|
}
|
||||||
|
else if (ignored.contains("none")) {
|
||||||
|
ignored.remove("none");
|
||||||
|
}
|
||||||
|
return ignored;
|
||||||
|
}
|
||||||
|
|
||||||
private String normalizePath(String errorPath) {
|
private String normalizePath(String errorPath) {
|
||||||
String result = StringUtils.cleanPath(errorPath);
|
String result = StringUtils.cleanPath(errorPath);
|
||||||
|
|
|
||||||
|
|
@ -90,9 +90,9 @@ public class SecurityAutoConfigurationTests {
|
||||||
PropertyPlaceholderAutoConfiguration.class);
|
PropertyPlaceholderAutoConfiguration.class);
|
||||||
this.context.refresh();
|
this.context.refresh();
|
||||||
assertThat(this.context.getBean(AuthenticationManagerBuilder.class)).isNotNull();
|
assertThat(this.context.getBean(AuthenticationManagerBuilder.class)).isNotNull();
|
||||||
// 5 for static resources and one for the rest
|
// 1 for static resources and one for the rest
|
||||||
assertThat(this.context.getBean(FilterChainProxy.class).getFilterChains())
|
assertThat(this.context.getBean(FilterChainProxy.class).getFilterChains())
|
||||||
.hasSize(6);
|
.hasSize(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
||||||
|
|
||||||
|
|
@ -84,13 +83,6 @@ public class SpringBootWebSecurityConfigurationTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDefaultIgnores() {
|
|
||||||
List<String> ignored = SpringBootWebSecurityConfiguration
|
|
||||||
.getIgnored(new SecurityProperties());
|
|
||||||
assertThat(ignored).contains("/css/**");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWebConfigurationOverrideGlobalAuthentication() throws Exception {
|
public void testWebConfigurationOverrideGlobalAuthentication() throws Exception {
|
||||||
this.context = SpringApplication.run(TestWebConfiguration.class,
|
this.context = SpringApplication.run(TestWebConfiguration.class,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ public class SampleMethodSecurityApplication extends WebMvcConfigurerAdapter {
|
||||||
@Override
|
@Override
|
||||||
public void init(AuthenticationManagerBuilder auth) throws Exception {
|
public void init(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
auth.inMemoryAuthentication().withUser("admin").password("admin")
|
auth.inMemoryAuthentication().withUser("admin").password("admin")
|
||||||
.roles("ADMIN", "USER").and().withUser("user").password("user")
|
.roles("ADMIN", "USER", "ACTUATOR").and().withUser("user").password("user")
|
||||||
.roles("USER");
|
.roles("USER");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue