Commit 0f99b29b authored by Phillip Webb's avatar Phillip Webb

Temporarily remove security matchers

Temporarily back out `SpringBootSecurity` to enable easier
package refactoring.

See gh-10261
parent ecb8461e
...@@ -16,14 +16,10 @@ ...@@ -16,14 +16,10 @@
package org.springframework.boot.autoconfigure.security; package org.springframework.boot.autoconfigure.security;
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;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorController;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.endpoint.DefaultEndpointPathResolver;
import org.springframework.boot.endpoint.EndpointPathResolver;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -56,20 +52,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur ...@@ -56,20 +52,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
AuthenticationManagerConfiguration.class, SecurityDataConfiguration.class }) AuthenticationManagerConfiguration.class, SecurityDataConfiguration.class })
public class SecurityAutoConfiguration { public class SecurityAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public EndpointPathResolver endpointPathResolver() {
return new DefaultEndpointPathResolver();
}
@Bean
public SpringBootSecurity springBootSecurity(
EndpointPathResolver endpointPathResolver,
ObjectProvider<ErrorController> errorController) {
return new SpringBootSecurity(endpointPathResolver,
errorController.getIfAvailable());
}
@Bean @Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class) @ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher( public DefaultAuthenticationEventPublisher authenticationEventPublisher(
......
/*
* Copyright 2012-2017 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 java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorController;
import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.EndpointPathResolver;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Provides request matchers that can be used to configure security for static resources
* and the error controller path in a custom {@link WebSecurityConfigurerAdapter}.
*
* @author Madhura Bhave
* @since 2.0.0
*/
public final class SpringBootSecurity {
/**
* Used as a wildcard matcher for all endpoints.
*/
public final static String ALL_ENDPOINTS = "**";
private static String[] STATIC_RESOURCES = new String[] { "/css/**", "/js/**",
"/images/**", "/webjars/**", "/**/favicon.ico" };
private final EndpointPathResolver endpointPathResolver;
private final ErrorController errorController;
SpringBootSecurity(EndpointPathResolver endpointPathResolver,
ErrorController errorController) {
this.endpointPathResolver = endpointPathResolver;
this.errorController = errorController;
}
/**
* Returns a {@link RequestMatcher} that matches on all endpoint paths with given ids.
* @param ids the endpoint ids
* @return the request matcher
*/
public RequestMatcher endpointIds(String... ids) {
Assert.notEmpty(ids, "At least one endpoint id must be specified.");
List<String> pathList = Arrays.asList(ids);
if (pathList.contains(ALL_ENDPOINTS)) {
return new AntPathRequestMatcher(
this.endpointPathResolver.resolvePath(ALL_ENDPOINTS), null);
}
return getEndpointsRequestMatcher(pathList);
}
/**
* Returns a {@link RequestMatcher} that matches on all endpoint paths for the given
* classes with the {@link Endpoint} annotation.
* @param endpoints the endpoint classes
* @return the request matcher
*/
public RequestMatcher endpoints(Class<?>... endpoints) {
Assert.notEmpty(endpoints, "At least one endpoint must be specified.");
List<String> paths = Arrays.stream(endpoints).map((e) -> {
if (e.isAnnotationPresent(Endpoint.class)) {
return e.getAnnotation(Endpoint.class).id();
}
throw new IllegalArgumentException(
"Only classes annotated with @Endpoint are supported.");
}).collect(Collectors.toList());
return getEndpointsRequestMatcher(paths);
}
/**
* Returns a {@link RequestMatcher} that matches on all static resources.
* @return the request matcher
*/
public RequestMatcher staticResources() {
return getRequestMatcher(STATIC_RESOURCES);
}
/**
* Returns a {@link RequestMatcher} that matches on the {@link ErrorController} path,
* if present.
* @return the request matcher
*/
public RequestMatcher error() {
if (this.errorController == null) {
throw new IllegalStateException(
"Path for error controller could not be determined.");
}
String path = normalizePath(this.errorController.getErrorPath());
return new AntPathRequestMatcher(path + "/**", null);
}
private RequestMatcher getEndpointsRequestMatcher(List<String> ids) {
List<RequestMatcher> matchers = new ArrayList<>();
for (String id : ids) {
String path = this.endpointPathResolver.resolvePath(id);
matchers.add(new AntPathRequestMatcher(path + "/**", null));
}
return new OrRequestMatcher(matchers);
}
private static RequestMatcher getRequestMatcher(String... paths) {
List<RequestMatcher> matchers = new ArrayList<>();
for (String path : paths) {
matchers.add(new AntPathRequestMatcher(path, null));
}
return new OrRequestMatcher(matchers);
}
private String normalizePath(String errorPath) {
String result = StringUtils.cleanPath(errorPath);
if (!result.startsWith("/")) {
result = "/" + result;
}
return result;
}
}
/*
* Copyright 2012-2017 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.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorController;
import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.EndpointPathResolver;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SpringBootSecurity}.
*
* @author Madhura Bhave
*/
public class SpringBootSecurityTests {
private SpringBootSecurity bootSecurity;
private EndpointPathResolver endpointPathResolver = new TestEndpointPathResolver();
private ErrorController errorController = new TestErrorController();
private MockHttpServletRequest request = new MockHttpServletRequest();
private static String[] STATIC_RESOURCES = new String[] { "/css/**", "/js/**",
"/images/**", "/webjars/**", "/**/favicon.ico" };
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void setUp() throws Exception {
this.bootSecurity = new SpringBootSecurity(this.endpointPathResolver,
this.errorController);
}
@Test
public void endpointIdsShouldThrowIfNoEndpointPaths() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("At least one endpoint id must be specified.");
this.bootSecurity.endpointIds();
}
@Test
public void endpointIdsShouldReturnRequestMatcherWithEndpointPaths()
throws Exception {
RequestMatcher requestMatcher = this.bootSecurity.endpointIds("id-1", "id-2");
assertThat(requestMatcher).isInstanceOf(OrRequestMatcher.class);
this.request.setServletPath("/test/id-1");
assertThat(requestMatcher.matches(this.request)).isTrue();
this.request.setServletPath("/test/id-2");
assertThat(requestMatcher.matches(this.request)).isTrue();
this.request.setServletPath("/test/other-id");
assertThat(requestMatcher.matches(this.request)).isFalse();
}
@Test
public void endpointIdsShouldReturnRequestMatcherWithAllEndpointPaths()
throws Exception {
RequestMatcher requestMatcher = this.bootSecurity
.endpointIds(SpringBootSecurity.ALL_ENDPOINTS);
this.request.setServletPath("/test/id-1");
assertThat(requestMatcher.matches(this.request)).isTrue();
this.request.setServletPath("/test/id-2");
assertThat(requestMatcher.matches(this.request)).isTrue();
this.request.setServletPath("/test/other-id");
assertThat(requestMatcher.matches(this.request)).isTrue();
}
@Test
public void endpointsShouldReturnRequestMatcherWithEndpointPaths() throws Exception {
RequestMatcher requestMatcher = this.bootSecurity.endpoints(TestEndpoint1.class);
assertThat(requestMatcher).isInstanceOf(OrRequestMatcher.class);
this.request.setServletPath("/test/id-1");
assertThat(requestMatcher.matches(this.request)).isTrue();
this.request.setServletPath("/test/id-2");
assertThat(requestMatcher.matches(this.request)).isFalse();
}
@Test
public void endpointsShouldThrowIfNoEndpointPaths() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("At least one endpoint must be specified.");
this.bootSecurity.endpoints();
}
@Test
public void endpointsShouldThrowExceptionWhenClassNotEndpoint() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Only classes annotated with @Endpoint are supported.");
this.bootSecurity.endpoints(FakeEndpoint.class);
}
@Test
public void staticResourcesShouldReturnRequestMatcherWithStaticResources()
throws Exception {
RequestMatcher requestMatcher = this.bootSecurity.staticResources();
assertThat(requestMatcher).isInstanceOf(OrRequestMatcher.class);
for (String resource : STATIC_RESOURCES) {
this.request.setServletPath(resource);
assertThat(requestMatcher.matches(this.request)).isTrue();
}
}
@Test
public void errorShouldReturnRequestMatcherWithErrorControllerPath()
throws Exception {
RequestMatcher requestMatcher = this.bootSecurity.error();
assertThat(requestMatcher).isInstanceOf(AntPathRequestMatcher.class);
this.request.setServletPath("/test/error");
assertThat(requestMatcher.matches(this.request)).isTrue();
}
@Test
public void errorShouldThrowExceptionWhenNoErrorController() throws Exception {
this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("Path for error controller could not be determined.");
this.bootSecurity = new SpringBootSecurity(this.endpointPathResolver, null);
this.bootSecurity.error();
}
static class TestEndpointPathResolver implements EndpointPathResolver {
@Override
public String resolvePath(String endpointId) {
return "/test/" + endpointId;
}
}
static class TestErrorController implements ErrorController {
@Override
public String getErrorPath() {
return "/test/error";
}
}
@Endpoint(id = "id-1")
static class TestEndpoint1 {
}
@Endpoint(id = "id-2")
static class TestEndpoint2 {
}
static class FakeEndpoint {
}
}
package sample.actuator.customsecurity; package sample.actuator.customsecurity;
import org.springframework.boot.autoconfigure.security.SpringBootSecurity;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
...@@ -9,12 +8,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur ...@@ -9,12 +8,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
@Configuration @Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private SpringBootSecurity bootSecurity;
public SecurityConfiguration(SpringBootSecurity bootSecurity) {
this.bootSecurity = bootSecurity;
}
@Override @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password") auth.inMemoryAuthentication().withUser("user").password("password")
...@@ -24,18 +17,18 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { ...@@ -24,18 +17,18 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
// FIXME
// @formatter:off // @formatter:off
http.authorizeRequests() // http.authorizeRequests()
.requestMatchers(this.bootSecurity.endpointIds("status", "info")).permitAll() // .requestMatchers(endpointIds("status", "info")).permitAll()
.requestMatchers(this.bootSecurity.endpointIds(SpringBootSecurity.ALL_ENDPOINTS)).hasRole("ACTUATOR") // .requestMatchers(endpointIds(SpringBootSecurity.ALL_ENDPOINTS)).hasRole("ACTUATOR")
.requestMatchers(this.bootSecurity.staticResources()).permitAll() // .requestMatchers(staticResources()).permitAll()
.antMatchers("/foo").permitAll() // .antMatchers("/foo").permitAll()
.antMatchers("/**").hasRole("USER") // .antMatchers("/**").hasRole("USER")
.and() // .and()
.cors() // .cors()
.and() // .and()
.httpBasic(); // .httpBasic();
// @formatter:on // @formatter:on
} }
......
package sample.secure.oauth2.actuator; package sample.secure.oauth2.actuator;
import org.springframework.boot.autoconfigure.security.SpringBootSecurity;
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.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
...@@ -15,19 +14,14 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur ...@@ -15,19 +14,14 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
@Order(2) // before the resource server configuration @Order(2) // before the resource server configuration
public class ActuatorSecurityConfiguration extends WebSecurityConfigurerAdapter { public class ActuatorSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final SpringBootSecurity springBootSecurity;
public ActuatorSecurityConfiguration(SpringBootSecurity springBootSecurity) {
this.springBootSecurity = springBootSecurity;
}
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
// FIXME
// @formatter:off // @formatter:off
http.requestMatcher(this.springBootSecurity.endpointIds(SpringBootSecurity.ALL_ENDPOINTS)).authorizeRequests() // http.requestMatcher(ALL_ENDPOINTS).authorizeRequests()
.antMatchers("/**").authenticated() // .antMatchers("/**").authenticated()
.and() // .and()
.httpBasic(); // .httpBasic();
// @formatter:on // @formatter:on
} }
......
...@@ -20,7 +20,6 @@ import java.util.Date; ...@@ -20,7 +20,6 @@ import java.util.Date;
import java.util.Map; import java.util.Map;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.SpringBootSecurity;
import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
...@@ -103,17 +102,11 @@ public class SampleMethodSecurityApplication implements WebMvcConfigurer { ...@@ -103,17 +102,11 @@ public class SampleMethodSecurityApplication implements WebMvcConfigurer {
@Order(1) @Order(1)
protected static class ActuatorSecurity extends WebSecurityConfigurerAdapter { protected static class ActuatorSecurity extends WebSecurityConfigurerAdapter {
private final SpringBootSecurity springBootSecurity;
public ActuatorSecurity(SpringBootSecurity springBootSecurity) {
this.springBootSecurity = springBootSecurity;
}
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher( // FIXME
this.springBootSecurity.endpointIds(SpringBootSecurity.ALL_ENDPOINTS)) // http.requestMatcher(ALL_ENDPOINTS)
.authorizeRequests().anyRequest().authenticated().and().httpBasic(); // .authorizeRequests().anyRequest().authenticated().and().httpBasic();
} }
} }
......
...@@ -20,7 +20,6 @@ import java.util.Date; ...@@ -20,7 +20,6 @@ import java.util.Date;
import java.util.Map; import java.util.Map;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.SpringBootSecurity;
import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
...@@ -61,22 +60,17 @@ public class SampleWebSecureApplication implements WebMvcConfigurer { ...@@ -61,22 +60,17 @@ public class SampleWebSecureApplication implements WebMvcConfigurer {
@Configuration @Configuration
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter { protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
private final SpringBootSecurity springBootSecurity;
public ApplicationSecurity(SpringBootSecurity springBootSecurity) {
this.springBootSecurity = springBootSecurity;
}
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
// FIXME
// @formatter:off // @formatter:off
http.authorizeRequests() // http.authorizeRequests()
.requestMatchers(this.springBootSecurity.staticResources()).permitAll() // .requestMatchers(staticResources()).permitAll()
.anyRequest().fullyAuthenticated() // .anyRequest().fullyAuthenticated()
.and() // .and()
.formLogin().loginPage("/login").failureUrl("/login?error").permitAll() // .formLogin().loginPage("/login").failureUrl("/login?error").permitAll()
.and() // .and()
.logout().permitAll(); // .logout().permitAll();
// @formatter:on // @formatter:on
} }
......
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