Commit d724f154 authored by Madhura Bhave's avatar Madhura Bhave

Merge branch '2.0.x'

parents b6a6b9eb b93c2b9a
...@@ -33,6 +33,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; ...@@ -33,6 +33,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
import org.springframework.boot.autoconfigure.security.servlet.RequestMatcherProvider;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath;
import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher; import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
...@@ -158,13 +159,25 @@ public final class EndpointRequest { ...@@ -158,13 +159,25 @@ public final class EndpointRequest {
RequestMatcherFactory requestMatcherFactory); RequestMatcherFactory requestMatcherFactory);
protected List<RequestMatcher> getLinksMatchers( protected List<RequestMatcher> getLinksMatchers(
RequestMatcherFactory requestMatcherFactory, String basePath) { RequestMatcherFactory requestMatcherFactory,
RequestMatcherProvider matcherProvider, String basePath) {
List<RequestMatcher> linksMatchers = new ArrayList<>(); List<RequestMatcher> linksMatchers = new ArrayList<>();
linksMatchers.add(requestMatcherFactory.antPath(basePath)); linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, basePath));
linksMatchers.add(requestMatcherFactory.antPath(basePath, "/")); linksMatchers
.add(requestMatcherFactory.antPath(matcherProvider, basePath, "/"));
return linksMatchers; return linksMatchers;
} }
protected RequestMatcherProvider getRequestMatcherProvider(
WebApplicationContext context) {
try {
return context.getBean(RequestMatcherProvider.class);
}
catch (NoSuchBeanDefinitionException ex) {
return AntPathRequestMatcher::new;
}
}
} }
/** /**
...@@ -220,6 +233,7 @@ public final class EndpointRequest { ...@@ -220,6 +233,7 @@ public final class EndpointRequest {
RequestMatcherFactory requestMatcherFactory) { RequestMatcherFactory requestMatcherFactory) {
PathMappedEndpoints pathMappedEndpoints = context PathMappedEndpoints pathMappedEndpoints = context
.getBean(PathMappedEndpoints.class); .getBean(PathMappedEndpoints.class);
RequestMatcherProvider matcherProvider = getRequestMatcherProvider(context);
Set<String> paths = new LinkedHashSet<>(); Set<String> paths = new LinkedHashSet<>();
if (this.includes.isEmpty()) { if (this.includes.isEmpty()) {
paths.addAll(pathMappedEndpoints.getAllPaths()); paths.addAll(pathMappedEndpoints.getAllPaths());
...@@ -227,11 +241,11 @@ public final class EndpointRequest { ...@@ -227,11 +241,11 @@ public final class EndpointRequest {
streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add); streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add);
streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove); streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove);
List<RequestMatcher> delegateMatchers = getDelegateMatchers( List<RequestMatcher> delegateMatchers = getDelegateMatchers(
requestMatcherFactory, paths); requestMatcherFactory, matcherProvider, paths);
String basePath = pathMappedEndpoints.getBasePath(); String basePath = pathMappedEndpoints.getBasePath();
if (this.includeLinks && StringUtils.hasText(basePath)) { if (this.includeLinks && StringUtils.hasText(basePath)) {
delegateMatchers delegateMatchers.addAll(getLinksMatchers(requestMatcherFactory,
.addAll(getLinksMatchers(requestMatcherFactory, basePath)); matcherProvider, basePath));
} }
return new OrRequestMatcher(delegateMatchers); return new OrRequestMatcher(delegateMatchers);
} }
...@@ -261,9 +275,10 @@ public final class EndpointRequest { ...@@ -261,9 +275,10 @@ public final class EndpointRequest {
} }
private List<RequestMatcher> getDelegateMatchers( private List<RequestMatcher> getDelegateMatchers(
RequestMatcherFactory requestMatcherFactory, Set<String> paths) { RequestMatcherFactory requestMatcherFactory,
return paths.stream() RequestMatcherProvider matcherProvider, Set<String> paths) {
.map((path) -> requestMatcherFactory.antPath(path, "/**")) return paths.stream().map(
(path) -> requestMatcherFactory.antPath(matcherProvider, path, "/**"))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
...@@ -281,8 +296,8 @@ public final class EndpointRequest { ...@@ -281,8 +296,8 @@ public final class EndpointRequest {
.getBean(WebEndpointProperties.class); .getBean(WebEndpointProperties.class);
String basePath = properties.getBasePath(); String basePath = properties.getBasePath();
if (StringUtils.hasText(basePath)) { if (StringUtils.hasText(basePath)) {
return new OrRequestMatcher( return new OrRequestMatcher(getLinksMatchers(requestMatcherFactory,
getLinksMatchers(requestMatcherFactory, basePath)); getRequestMatcherProvider(context), basePath));
} }
return EMPTY_MATCHER; return EMPTY_MATCHER;
} }
...@@ -300,12 +315,13 @@ public final class EndpointRequest { ...@@ -300,12 +315,13 @@ public final class EndpointRequest {
this.prefix = prefix; this.prefix = prefix;
} }
public RequestMatcher antPath(String... parts) { public RequestMatcher antPath(RequestMatcherProvider matcherProvider,
String... parts) {
String pattern = this.prefix; String pattern = this.prefix;
for (String part : parts) { for (String part : parts) {
pattern += part; pattern += part;
} }
return new AntPathRequestMatcher(pattern); return matcherProvider.getRequestMatcher(pattern);
} }
} }
......
...@@ -31,6 +31,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; ...@@ -31,6 +31,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoint;
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpoint; import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpoint;
import org.springframework.boot.autoconfigure.security.servlet.RequestMatcherProvider;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext; import org.springframework.mock.web.MockServletContext;
...@@ -214,6 +215,26 @@ public class EndpointRequestTests { ...@@ -214,6 +215,26 @@ public class EndpointRequestTests {
assertMatcher.matches("/bar"); assertMatcher.matches("/bar");
} }
@Test
public void endpointRequestMatcherShouldUseCustomRequestMatcherProvider() {
RequestMatcher matcher = EndpointRequest.toAnyEndpoint();
RequestMatcher mockRequestMatcher = (request) -> false;
RequestMatcherAssert assertMatcher = assertMatcher(matcher,
mockPathMappedEndpoints(""), "", (pattern) -> mockRequestMatcher);
assertMatcher.doesNotMatch("/foo");
assertMatcher.doesNotMatch("/bar");
}
@Test
public void linksRequestMatcherShouldUseCustomRequestMatcherProvider() {
RequestMatcher matcher = EndpointRequest.toLinks();
RequestMatcher mockRequestMatcher = (request) -> false;
RequestMatcherAssert assertMatcher = assertMatcher(matcher,
mockPathMappedEndpoints("/actuator"), "",
(pattern) -> mockRequestMatcher);
assertMatcher.doesNotMatch("/actuator");
}
@Test @Test
public void noEndpointPathsBeansShouldNeverMatch() { public void noEndpointPathsBeansShouldNeverMatch() {
RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); RequestMatcher matcher = EndpointRequest.toAnyEndpoint();
...@@ -231,7 +252,8 @@ public class EndpointRequestTests { ...@@ -231,7 +252,8 @@ public class EndpointRequestTests {
private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String basePath, private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String basePath,
String servletPath) { String servletPath) {
return assertMatcher(matcher, mockPathMappedEndpoints(basePath), servletPath); return assertMatcher(matcher, mockPathMappedEndpoints(basePath), servletPath,
null);
} }
private PathMappedEndpoints mockPathMappedEndpoints(String basePath) { private PathMappedEndpoints mockPathMappedEndpoints(String basePath) {
...@@ -250,11 +272,12 @@ public class EndpointRequestTests { ...@@ -250,11 +272,12 @@ public class EndpointRequestTests {
private RequestMatcherAssert assertMatcher(RequestMatcher matcher, private RequestMatcherAssert assertMatcher(RequestMatcher matcher,
PathMappedEndpoints pathMappedEndpoints) { PathMappedEndpoints pathMappedEndpoints) {
return assertMatcher(matcher, pathMappedEndpoints, ""); return assertMatcher(matcher, pathMappedEndpoints, "", null);
} }
private RequestMatcherAssert assertMatcher(RequestMatcher matcher, private RequestMatcherAssert assertMatcher(RequestMatcher matcher,
PathMappedEndpoints pathMappedEndpoints, String dispatcherServletPath) { PathMappedEndpoints pathMappedEndpoints, String dispatcherServletPath,
RequestMatcherProvider matcherProvider) {
StaticWebApplicationContext context = new StaticWebApplicationContext(); StaticWebApplicationContext context = new StaticWebApplicationContext();
context.registerBean(WebEndpointProperties.class); context.registerBean(WebEndpointProperties.class);
if (pathMappedEndpoints != null) { if (pathMappedEndpoints != null) {
...@@ -269,6 +292,9 @@ public class EndpointRequestTests { ...@@ -269,6 +292,9 @@ public class EndpointRequestTests {
DispatcherServletPath path = () -> dispatcherServletPath; DispatcherServletPath path = () -> dispatcherServletPath;
context.registerBean(DispatcherServletPath.class, () -> path); context.registerBean(DispatcherServletPath.class, () -> path);
} }
if (matcherProvider != null) {
context.registerBean(RequestMatcherProvider.class, () -> matcherProvider);
}
return assertThat(new RequestMatcherAssert(context, matcher)); return assertThat(new RequestMatcherAssert(context, matcher));
} }
......
...@@ -23,6 +23,7 @@ import java.util.Collection; ...@@ -23,6 +23,7 @@ import java.util.Collection;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
...@@ -49,6 +50,8 @@ import org.springframework.web.bind.annotation.ResponseBody; ...@@ -49,6 +50,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.MatchableHandlerMapping;
import org.springframework.web.servlet.handler.RequestMatchResult;
import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition; import org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition; import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
...@@ -66,7 +69,8 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMappi ...@@ -66,7 +69,8 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMappi
* @since 2.0.0 * @since 2.0.0
*/ */
public abstract class AbstractWebMvcEndpointHandlerMapping public abstract class AbstractWebMvcEndpointHandlerMapping
extends RequestMappingInfoHandlerMapping implements InitializingBean { extends RequestMappingInfoHandlerMapping
implements InitializingBean, MatchableHandlerMapping {
private final EndpointMapping endpointMapping; private final EndpointMapping endpointMapping;
...@@ -82,6 +86,8 @@ public abstract class AbstractWebMvcEndpointHandlerMapping ...@@ -82,6 +86,8 @@ public abstract class AbstractWebMvcEndpointHandlerMapping
private final Method handleMethod = ReflectionUtils.findMethod(OperationHandler.class, private final Method handleMethod = ReflectionUtils.findMethod(OperationHandler.class,
"handle", HttpServletRequest.class, Map.class); "handle", HttpServletRequest.class, Map.class);
private static final RequestMappingInfo.BuilderConfiguration builderConfig = getBuilderConfig();
/** /**
* Creates a new {@code WebEndpointHandlerMapping} that provides mappings for the * Creates a new {@code WebEndpointHandlerMapping} that provides mappings for the
* operations of the given {@code webEndpoints}. * operations of the given {@code webEndpoints}.
...@@ -125,6 +131,29 @@ public abstract class AbstractWebMvcEndpointHandlerMapping ...@@ -125,6 +131,29 @@ public abstract class AbstractWebMvcEndpointHandlerMapping
} }
} }
@Override
public RequestMatchResult match(HttpServletRequest request, String pattern) {
RequestMappingInfo info = RequestMappingInfo.paths(pattern).options(builderConfig)
.build();
RequestMappingInfo matchingInfo = info.getMatchingCondition(request);
if (matchingInfo == null) {
return null;
}
Set<String> patterns = matchingInfo.getPatternsCondition().getPatterns();
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
return new RequestMatchResult(patterns.iterator().next(), lookupPath,
getPathMatcher());
}
private static RequestMappingInfo.BuilderConfiguration getBuilderConfig() {
RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
config.setUrlPathHelper(null);
config.setPathMatcher(null);
config.setSuffixPatternMatch(false);
config.setTrailingSlashMatch(true);
return config;
}
private void registerMappingForOperation(ExposableWebEndpoint endpoint, private void registerMappingForOperation(ExposableWebEndpoint endpoint,
WebOperation operation) { WebOperation operation) {
OperationInvoker invoker = operation::invoke; OperationInvoker invoker = operation::invoke;
...@@ -176,7 +205,9 @@ public abstract class AbstractWebMvcEndpointHandlerMapping ...@@ -176,7 +205,9 @@ public abstract class AbstractWebMvcEndpointHandlerMapping
private PatternsRequestCondition patternsRequestConditionForPattern(String path) { private PatternsRequestCondition patternsRequestConditionForPattern(String path) {
String[] patterns = new String[] { this.endpointMapping.createSubPath(path) }; String[] patterns = new String[] { this.endpointMapping.createSubPath(path) };
return new PatternsRequestCondition(patterns, null, null, false, true); return new PatternsRequestCondition(patterns, builderConfig.getUrlPathHelper(),
builderConfig.getPathMatcher(), builderConfig.useSuffixPatternMatch(),
builderConfig.useTrailingSlashMatch());
} }
@Override @Override
......
...@@ -46,6 +46,7 @@ import org.springframework.context.annotation.Configuration; ...@@ -46,6 +46,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
...@@ -53,6 +54,7 @@ import org.springframework.security.core.context.SecurityContextHolder; ...@@ -53,6 +54,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper; import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.handler.RequestMatchResult;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -104,6 +106,27 @@ public class MvcWebEndpointIntegrationTests extends ...@@ -104,6 +106,27 @@ public class MvcWebEndpointIntegrationTests extends
}); });
} }
@Test
public void matchWhenRequestHasTrailingSlashShouldNotBeNull() {
assertThat(getMatchResult("/spring/")).isNotNull();
}
@Test
public void matchWhenRequestHasSuffixShouldBeNull() {
assertThat(getMatchResult("/spring.do")).isNull();
}
private RequestMatchResult getMatchResult(String s) {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setServletPath(s);
AnnotationConfigServletWebServerApplicationContext context = createApplicationContext();
context.register(TestEndpointConfiguration.class);
context.refresh();
WebMvcEndpointHandlerMapping bean = context
.getBean(WebMvcEndpointHandlerMapping.class);
return bean.match(request, "/spring");
}
@Override @Override
protected int getPort(AnnotationConfigServletWebServerApplicationContext context) { protected int getPort(AnnotationConfigServletWebServerApplicationContext context) {
return context.getWebServer().getPort(); return context.getWebServer().getPort();
......
/*
* Copyright 2012-2018 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.servlet;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
* {@link RequestMatcherProvider} that provides an {@link MvcRequestMatcher} that can be
* used for Spring MVC applications.
*
* @author Madhura Bhave
*/
public class MvcRequestMatcherProvider implements RequestMatcherProvider {
private final HandlerMappingIntrospector introspector;
public MvcRequestMatcherProvider(HandlerMappingIntrospector introspector) {
this.introspector = introspector;
}
@Override
public RequestMatcher getRequestMatcher(String pattern) {
return new MvcRequestMatcher(this.introspector, pattern);
}
}
/*
* Copyright 2012-2018 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.servlet;
import org.springframework.security.web.util.matcher.RequestMatcher;
/**
* Interface that can be used to provide a {@link RequestMatcher} that can be used with
* Spring Security.
*
* @author Madhura Bhave
* @since 2.0.5
*/
@FunctionalInterface
public interface RequestMatcherProvider {
RequestMatcher getRequestMatcher(String pattern);
}
/*
* Copyright 2012-2018 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.servlet;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
* Auto-configuration for {@link RequestMatcherProvider}.
*
* @author Madhura Bhave
* @since 2.0.5
*/
@Configuration
@ConditionalOnClass({ RequestMatcher.class, DispatcherServlet.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnBean(HandlerMappingIntrospector.class)
public class SecurityRequestMatcherProviderAutoConfiguration {
@Bean
public RequestMatcherProvider requestMatcherProvider(
HandlerMappingIntrospector introspector) {
return new MvcRequestMatcherProvider(introspector);
}
}
...@@ -98,6 +98,7 @@ org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\ ...@@ -98,6 +98,7 @@ org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\ org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
......
/*
* Copyright 2012-2018 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.servlet;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SecurityRequestMatcherProviderAutoConfiguration}.
*
* @author Madhura Bhave
*/
public class SecurityRequestMatcherProviderAutoConfigurationTests {
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations
.of(SecurityRequestMatcherProviderAutoConfiguration.class))
.withUserConfiguration(TestConfiguration.class);
@Test
public void registersMvcRequestMatcherProviderForIfMvcPresent() {
this.contextRunner.run((context) -> assertThat(context)
.hasSingleBean(MvcRequestMatcherProvider.class));
}
@Test
public void mvcRequestMatcherProviderConditionalOnWebApplication() {
new ApplicationContextRunner()
.withConfiguration(AutoConfigurations
.of(SecurityRequestMatcherProviderAutoConfiguration.class))
.withUserConfiguration(TestConfiguration.class)
.run((context) -> assertThat(context)
.doesNotHaveBean(MvcRequestMatcherProvider.class));
}
@Test
public void mvcRequestMatcherProviderConditionalOnDispatcherServletClass() {
this.contextRunner
.withClassLoader(new FilteredClassLoader(
"org.springframework.web.servlet.DispatcherServlet"))
.run((context) -> assertThat(context)
.doesNotHaveBean(MvcRequestMatcherProvider.class));
}
@Test
public void mvcRequestMatcherProviderConditionalOnRequestMatcherClass() {
this.contextRunner
.withClassLoader(new FilteredClassLoader(
"org.springframework.security.web.util.matcher.RequestMatcher"))
.run((context) -> assertThat(context)
.doesNotHaveBean(MvcRequestMatcherProvider.class));
}
@Test
public void mvcRequestMatcherProviderConditionalOnHandlerMappingIntrospectorBean() {
new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations
.of(SecurityRequestMatcherProviderAutoConfiguration.class))
.run((context) -> assertThat(context)
.doesNotHaveBean(MvcRequestMatcherProvider.class));
}
@Configuration
static class TestConfiguration {
@Bean
public HandlerMappingIntrospector introspector() {
return new HandlerMappingIntrospector();
}
}
}
...@@ -35,6 +35,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { ...@@ -35,6 +35,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
return new InMemoryUserDetailsManager( return new InMemoryUserDetailsManager(
User.withDefaultPasswordEncoder().username("user").password("password") User.withDefaultPasswordEncoder().username("user").password("password")
.authorities("ROLE_USER").build(), .authorities("ROLE_USER").build(),
User.withDefaultPasswordEncoder().username("beans").password("beans")
.authorities("ROLE_BEANS").build(),
User.withDefaultPasswordEncoder().username("admin").password("admin") User.withDefaultPasswordEncoder().username("admin").password("admin")
.authorities("ROLE_ACTUATOR", "ROLE_USER").build()); .authorities("ROLE_ACTUATOR", "ROLE_USER").build());
} }
...@@ -43,6 +45,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { ...@@ -43,6 +45,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
// @formatter:off // @formatter:off
http.authorizeRequests() http.authorizeRequests()
.mvcMatchers("/actuator/beans").hasRole("BEANS")
.requestMatchers(EndpointRequest.to("health", "info")).permitAll() .requestMatchers(EndpointRequest.to("health", "info")).permitAll()
.requestMatchers(EndpointRequest.toAnyEndpoint().excluding(MappingsEndpoint.class)).hasRole("ACTUATOR") .requestMatchers(EndpointRequest.toAnyEndpoint().excluding(MappingsEndpoint.class)).hasRole("ACTUATOR")
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
......
...@@ -141,6 +141,16 @@ public class SampleActuatorCustomSecurityApplicationTests { ...@@ -141,6 +141,16 @@ public class SampleActuatorCustomSecurityApplicationTests {
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
} }
@Test
public void mvcMatchersCanBeUsedToSecureActuators() {
ResponseEntity<Object> entity = beansRestTemplate()
.getForEntity("/actuator/beans", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
entity = beansRestTemplate()
.getForEntity("/actuator/beans/", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
private TestRestTemplate restTemplate() { private TestRestTemplate restTemplate() {
return configure(new TestRestTemplate()); return configure(new TestRestTemplate());
} }
...@@ -153,6 +163,10 @@ public class SampleActuatorCustomSecurityApplicationTests { ...@@ -153,6 +163,10 @@ public class SampleActuatorCustomSecurityApplicationTests {
return configure(new TestRestTemplate("user", "password")); return configure(new TestRestTemplate("user", "password"));
} }
private TestRestTemplate beansRestTemplate() {
return configure(new TestRestTemplate("beans", "beans"));
}
private TestRestTemplate configure(TestRestTemplate restTemplate) { private TestRestTemplate configure(TestRestTemplate restTemplate) {
restTemplate restTemplate
.setUriTemplateHandler(new LocalHostUriTemplateHandler(this.environment)); .setUriTemplateHandler(new LocalHostUriTemplateHandler(this.environment));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment