Commit e80c22cb authored by Madhura Bhave's avatar Madhura Bhave

Add RequestMatcher for H2 console

Fixes gh-11704
parent db2580f8
/*
* 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 javax.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.h2.H2ConsoleProperties;
import org.springframework.boot.autoconfigure.security.StaticResourceLocation;
import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
/**
* Factory that can be used to create a {@link RequestMatcher} for commonly used paths.
*
* @author Madhura Bhave
* @author Phillip Webb
* @since 2.0.0
*/
public final class PathRequest {
private PathRequest() {
}
/**
* Returns a {@link StaticResourceRequest} that can be used to create a matcher for
* {@link StaticResourceLocation Locations}.
* @return a {@link StaticResourceRequest}
*/
public static StaticResourceRequest toStaticResources() {
return StaticResourceRequest.get();
}
/**
* Returns a matcher that includes the H2 console location. For example: <pre class="code">
* PathRequest.toH2Console()
* </pre>
* @return the configured {@link RequestMatcher}
*/
public static H2ConsoleRequestMatcher toH2Console() {
return new H2ConsoleRequestMatcher();
}
/**
* The request matcher used to match against h2 console path.
*/
public static final class H2ConsoleRequestMatcher
extends ApplicationContextRequestMatcher<H2ConsoleProperties> {
private RequestMatcher delegate;
private H2ConsoleRequestMatcher() {
super(H2ConsoleProperties.class);
}
@Override
protected void initialized(H2ConsoleProperties h2ConsoleProperties) {
this.delegate = new AntPathRequestMatcher(h2ConsoleProperties.getPath());
}
@Override
protected boolean matches(HttpServletRequest request, H2ConsoleProperties context) {
return this.delegate.matches(request);
}
}
}
...@@ -34,15 +34,18 @@ import org.springframework.security.web.util.matcher.RequestMatcher; ...@@ -34,15 +34,18 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* Factory that can be used to create a {@link RequestMatcher} for static resources in * Used to create a {@link RequestMatcher} for static resources in
* commonly used locations. * commonly used locations. Returned by {@link PathRequest#toStaticResources()}.
* *
* @author Madhura Bhave * @author Madhura Bhave
* @author Phillip Webb * @author Phillip Webb
* @since 2.0.0 * @since 2.0.0
* @see PathRequest
*/ */
public final class StaticResourceRequest { public final class StaticResourceRequest {
private static final StaticResourceRequest INSTANCE = new StaticResourceRequest();
private StaticResourceRequest() { private StaticResourceRequest() {
} }
...@@ -52,41 +55,49 @@ public final class StaticResourceRequest { ...@@ -52,41 +55,49 @@ public final class StaticResourceRequest {
* {@link StaticResourceRequestMatcher#excluding(StaticResourceLocation, StaticResourceLocation...) * {@link StaticResourceRequestMatcher#excluding(StaticResourceLocation, StaticResourceLocation...)
* excluding} method can be used to remove specific locations if required. For * excluding} method can be used to remove specific locations if required. For
* example: <pre class="code"> * example: <pre class="code">
* StaticResourceRequest.toCommonLocations().excluding(StaticResourceLocation.CSS) * StaticResourceRequest.atCommonLocations().excluding(StaticResourceLocation.CSS)
* </pre> * </pre>
* @return the configured {@link RequestMatcher} * @return the configured {@link RequestMatcher}
*/ */
public static StaticResourceRequestMatcher toCommonLocations() { public StaticResourceRequestMatcher atCommonLocations() {
return to(EnumSet.allOf(StaticResourceLocation.class)); return at(EnumSet.allOf(StaticResourceLocation.class));
} }
/** /**
* Returns a matcher that includes the specified {@link StaticResourceLocation * Returns a matcher that includes the specified {@link StaticResourceLocation
* Locations}. For example: <pre class="code"> * Locations}. For example: <pre class="code">
* StaticResourceRequest.to(StaticResourceLocation.CSS, StaticResourceLocation.JAVA_SCRIPT) * StaticResourceRequest.at(StaticResourceLocation.CSS, StaticResourceLocation.JAVA_SCRIPT)
* </pre> * </pre>
* @param first the first location to include * @param first the first location to include
* @param rest additional locations to include * @param rest additional locations to include
* @return the configured {@link RequestMatcher} * @return the configured {@link RequestMatcher}
*/ */
public static StaticResourceRequestMatcher to(StaticResourceLocation first, public StaticResourceRequestMatcher at(StaticResourceLocation first,
StaticResourceLocation... rest) { StaticResourceLocation... rest) {
return to(EnumSet.of(first, rest)); return at(EnumSet.of(first, rest));
} }
/** /**
* Returns a matcher that includes the specified {@link StaticResourceLocation * Returns a matcher that includes the specified {@link StaticResourceLocation
* Locations}. For example: <pre class="code"> * Locations}. For example: <pre class="code">
* StaticResourceRequest.to(locations) * StaticResourceRequest.at(locations)
* </pre> * </pre>
* @param locations the locations to include * @param locations the locations to include
* @return the configured {@link RequestMatcher} * @return the configured {@link RequestMatcher}
*/ */
public static StaticResourceRequestMatcher to(Set<StaticResourceLocation> locations) { public StaticResourceRequestMatcher at(Set<StaticResourceLocation> locations) {
Assert.notNull(locations, "Locations must not be null"); Assert.notNull(locations, "Locations must not be null");
return new StaticResourceRequestMatcher(new LinkedHashSet<>(locations)); return new StaticResourceRequestMatcher(new LinkedHashSet<>(locations));
} }
/**
* Return the static resource request.
* @return the static resource request
*/
static StaticResourceRequest get() {
return INSTANCE;
}
/** /**
* The request matcher used to match against resource {@link StaticResourceLocation * The request matcher used to match against resource {@link StaticResourceLocation
* Locations}. * Locations}.
......
/*
* 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 javax.servlet.http.HttpServletRequest;
import org.assertj.core.api.AssertDelegateTarget;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.StaticWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link PathRequest}.
*
* @author Madhura Bhave
*/
public class PathRequestTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void toStaticResourcesShouldReturnStaticResourceRequest() {
assertThat(PathRequest.toStaticResources()).isInstanceOf(StaticResourceRequest.class);
}
@Test
public void toH2ConsoleShouldMatchH2ConsolePath() {
RequestMatcher matcher = PathRequest.toH2Console();
assertMatcher(matcher).matches("/h2-console");
assertMatcher(matcher).doesNotMatch("/js/file.js");
}
private RequestMatcherAssert assertMatcher(RequestMatcher matcher) {
StaticWebApplicationContext context = new StaticWebApplicationContext();
context.registerBean(ServerProperties.class);
return assertThat(new RequestMatcherAssert(context, matcher));
}
private static class RequestMatcherAssert implements AssertDelegateTarget {
private final WebApplicationContext context;
private final RequestMatcher matcher;
RequestMatcherAssert(WebApplicationContext context, RequestMatcher matcher) {
this.context = context;
this.matcher = matcher;
}
public void matches(String path) {
matches(mockRequest(path));
}
private void matches(HttpServletRequest request) {
assertThat(this.matcher.matches(request))
.as("Matches " + getRequestPath(request)).isTrue();
}
public void doesNotMatch(String path) {
doesNotMatch(mockRequest(path));
}
private void doesNotMatch(HttpServletRequest request) {
assertThat(this.matcher.matches(request))
.as("Does not match " + getRequestPath(request)).isFalse();
}
private MockHttpServletRequest mockRequest(String path) {
MockServletContext servletContext = new MockServletContext();
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
this.context);
MockHttpServletRequest request = new MockHttpServletRequest(servletContext);
request.setPathInfo(path);
return request;
}
private String getRequestPath(HttpServletRequest request) {
String url = request.getServletPath();
if (request.getPathInfo() != null) {
url += request.getPathInfo();
}
return url;
}
}
}
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
package org.springframework.boot.autoconfigure.security.servlet; package org.springframework.boot.autoconfigure.security.servlet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.assertj.core.api.AssertDelegateTarget; import org.assertj.core.api.AssertDelegateTarget;
...@@ -43,12 +41,14 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -43,12 +41,14 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
public class StaticResourceRequestTests { public class StaticResourceRequestTests {
private StaticResourceRequest resourceRequest = StaticResourceRequest.get();
@Rule @Rule
public ExpectedException thrown = ExpectedException.none(); public ExpectedException thrown = ExpectedException.none();
@Test @Test
public void toCommonLocationsShouldMatchCommonLocations() { public void atCommonLocationsShouldMatchCommonLocations() {
RequestMatcher matcher = StaticResourceRequest.toCommonLocations(); RequestMatcher matcher = this.resourceRequest.atCommonLocations();
assertMatcher(matcher).matches("/css/file.css"); assertMatcher(matcher).matches("/css/file.css");
assertMatcher(matcher).matches("/js/file.js"); assertMatcher(matcher).matches("/js/file.js");
assertMatcher(matcher).matches("/images/file.css"); assertMatcher(matcher).matches("/images/file.css");
...@@ -58,42 +58,42 @@ public class StaticResourceRequestTests { ...@@ -58,42 +58,42 @@ public class StaticResourceRequestTests {
} }
@Test @Test
public void toCommonLocationsWithExcludeShouldNotMatchExcluded() { public void atCommonLocationsWithExcludeShouldNotMatchExcluded() {
RequestMatcher matcher = StaticResourceRequest.toCommonLocations() RequestMatcher matcher = this.resourceRequest.atCommonLocations()
.excluding(StaticResourceLocation.CSS); .excluding(StaticResourceLocation.CSS);
assertMatcher(matcher).doesNotMatch("/css/file.css"); assertMatcher(matcher).doesNotMatch("/css/file.css");
assertMatcher(matcher).matches("/js/file.js"); assertMatcher(matcher).matches("/js/file.js");
} }
@Test @Test
public void toLocationShouldMatchLocation() { public void atLocationShouldMatchLocation() {
RequestMatcher matcher = StaticResourceRequest.to(StaticResourceLocation.CSS); RequestMatcher matcher = this.resourceRequest.at(StaticResourceLocation.CSS);
assertMatcher(matcher).matches("/css/file.css"); assertMatcher(matcher).matches("/css/file.css");
assertMatcher(matcher).doesNotMatch("/js/file.js"); assertMatcher(matcher).doesNotMatch("/js/file.js");
} }
@Test @Test
public void toLocationWhenHasServletPathShouldMatchLocation() { public void atLocationWhenHasServletPathShouldMatchLocation() {
ServerProperties serverProperties = new ServerProperties(); ServerProperties serverProperties = new ServerProperties();
serverProperties.getServlet().setPath("/foo"); serverProperties.getServlet().setPath("/foo");
RequestMatcher matcher = StaticResourceRequest.to(StaticResourceLocation.CSS); RequestMatcher matcher = this.resourceRequest.at(StaticResourceLocation.CSS);
assertMatcher(matcher, serverProperties).matches("/foo", "/css/file.css"); assertMatcher(matcher, serverProperties).matches("/foo", "/css/file.css");
assertMatcher(matcher, serverProperties).doesNotMatch("/foo", "/js/file.js"); assertMatcher(matcher, serverProperties).doesNotMatch("/foo", "/js/file.js");
} }
@Test @Test
public void toLocationsFromSetWhenSetIsNullShouldThrowException() { public void atLocationsFromSetWhenSetIsNullShouldThrowException() {
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Locations must not be null"); this.thrown.expectMessage("Locations must not be null");
StaticResourceRequest.to((Set<StaticResourceLocation>) null); this.resourceRequest.at(null);
} }
@Test @Test
public void excludeFromSetWhenSetIsNullShouldThrowException() { public void excludeFromSetWhenSetIsNullShouldThrowException() {
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Locations must not be null"); this.thrown.expectMessage("Locations must not be null");
StaticResourceRequest.toCommonLocations() this.resourceRequest.atCommonLocations()
.excluding((Set<StaticResourceLocation>) null); .excluding(null);
} }
private RequestMatcherAssert assertMatcher(RequestMatcher matcher) { private RequestMatcherAssert assertMatcher(RequestMatcher matcher) {
......
...@@ -3013,7 +3013,7 @@ Access rules can be overridden by adding a custom `WebSecurityConfigurerAdapter` ...@@ -3013,7 +3013,7 @@ Access rules can be overridden by adding a custom `WebSecurityConfigurerAdapter`
Boot provides convenience methods that can be used to override access rules for actuator Boot provides convenience methods that can be used to override access rules for actuator
endpoints and static resources. `EndpointRequest` can be used to create a `RequestMatcher` endpoints and static resources. `EndpointRequest` can be used to create a `RequestMatcher`
that is based on the `management.endpoints.web.base-path` property. that is based on the `management.endpoints.web.base-path` property.
`StaticResourceRequest` can be used to create a `RequestMatcher` for static resources in `PathRequest` can be used to create a `RequestMatcher` for resources in
commonly used locations. commonly used locations.
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
package sample.actuator.customsecurity; package sample.actuator.customsecurity;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.boot.autoconfigure.security.servlet.StaticResourceRequest; import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
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.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
...@@ -43,7 +43,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { ...@@ -43,7 +43,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
http.authorizeRequests() http.authorizeRequests()
.requestMatchers(EndpointRequest.to("health", "info")).permitAll() .requestMatchers(EndpointRequest.to("health", "info")).permitAll()
.requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR") .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR")
.requestMatchers(StaticResourceRequest.toCommonLocations()).permitAll() .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.antMatchers("/foo").permitAll() .antMatchers("/foo").permitAll()
.antMatchers("/**").hasRole("USER") .antMatchers("/**").hasRole("USER")
.and() .and()
......
...@@ -20,7 +20,7 @@ import java.util.Date; ...@@ -20,7 +20,7 @@ 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.servlet.StaticResourceRequest; import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
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.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
...@@ -64,7 +64,7 @@ public class SampleWebSecureApplication implements WebMvcConfigurer { ...@@ -64,7 +64,7 @@ public class SampleWebSecureApplication implements WebMvcConfigurer {
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
// @formatter:off // @formatter:off
http.authorizeRequests() http.authorizeRequests()
.requestMatchers(StaticResourceRequest.toCommonLocations()).permitAll() .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().fullyAuthenticated() .anyRequest().fullyAuthenticated()
.and() .and()
.formLogin().loginPage("/login").failureUrl("/login?error").permitAll() .formLogin().loginPage("/login").failureUrl("/login?error").permitAll()
......
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