Propagate exceptions in security matchers
Update `ApplicationContextRequestMatcher` and `ApplicationContextServerWebExchangeMatcher` to use a supplier for the context, rather than the context itself. This allow exceptions to be propagated to subclasses which may choose to deal with them. See gh-12238
This commit is contained in:
parent
802cd856aa
commit
d66496787d
|
@ -23,6 +23,7 @@ import java.util.LinkedHashSet;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -132,10 +133,10 @@ public final class EndpointRequest {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void initialized(PathMappedEndpoints pathMappedEndpoints) {
|
||||
protected void initialized(Supplier<PathMappedEndpoints> pathMappedEndpoints) {
|
||||
Set<String> paths = new LinkedHashSet<>();
|
||||
if (this.includes.isEmpty()) {
|
||||
paths.addAll(pathMappedEndpoints.getAllPaths());
|
||||
paths.addAll(pathMappedEndpoints.get().getAllPaths());
|
||||
}
|
||||
streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add);
|
||||
streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove);
|
||||
|
@ -143,9 +144,9 @@ public final class EndpointRequest {
|
|||
}
|
||||
|
||||
private Stream<String> streamPaths(List<Object> source,
|
||||
PathMappedEndpoints pathMappedEndpoints) {
|
||||
Supplier<PathMappedEndpoints> pathMappedEndpoints) {
|
||||
return source.stream().filter(Objects::nonNull).map(this::getEndpointId)
|
||||
.map(pathMappedEndpoints::getPath);
|
||||
.map(pathMappedEndpoints.get()::getPath);
|
||||
}
|
||||
|
||||
private String getEndpointId(Object source) {
|
||||
|
@ -173,7 +174,7 @@ public final class EndpointRequest {
|
|||
|
||||
@Override
|
||||
protected Mono<MatchResult> matches(ServerWebExchange exchange,
|
||||
PathMappedEndpoints context) {
|
||||
Supplier<PathMappedEndpoints> context) {
|
||||
return this.delegate.matches(exchange);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.LinkedHashSet;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -129,13 +130,13 @@ public final class EndpointRequest {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void initialized(PathMappedEndpoints pathMappedEndpoints) {
|
||||
protected void initialized(Supplier<PathMappedEndpoints> pathMappedEndpoints) {
|
||||
Set<String> paths = new LinkedHashSet<>();
|
||||
if (this.includes.isEmpty()) {
|
||||
paths.addAll(pathMappedEndpoints.getAllPaths());
|
||||
paths.addAll(pathMappedEndpoints.get().getAllPaths());
|
||||
}
|
||||
streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add);
|
||||
streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove);
|
||||
streamPaths(this.includes, pathMappedEndpoints.get()).forEach(paths::add);
|
||||
streamPaths(this.excludes, pathMappedEndpoints.get()).forEach(paths::remove);
|
||||
this.delegate = new OrRequestMatcher(getDelegateMatchers(paths));
|
||||
}
|
||||
|
||||
|
@ -169,7 +170,7 @@ public final class EndpointRequest {
|
|||
|
||||
@Override
|
||||
protected boolean matches(HttpServletRequest request,
|
||||
PathMappedEndpoints context) {
|
||||
Supplier<PathMappedEndpoints> context) {
|
||||
return this.delegate.matches(request);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.security.servlet;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.boot.autoconfigure.h2.H2ConsoleProperties;
|
||||
|
@ -69,14 +71,14 @@ public final class PathRequest {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void initialized(H2ConsoleProperties h2ConsoleProperties) {
|
||||
protected void initialized(Supplier<H2ConsoleProperties> h2ConsoleProperties) {
|
||||
this.delegate = new AntPathRequestMatcher(
|
||||
h2ConsoleProperties.getPath() + "/**");
|
||||
h2ConsoleProperties.get().getPath() + "/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(HttpServletRequest request,
|
||||
H2ConsoleProperties context) {
|
||||
Supplier<H2ConsoleProperties> context) {
|
||||
return this.delegate.matches(request);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.EnumSet;
|
|||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -133,8 +134,9 @@ public final class StaticResourceRequest {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void initialized(ServerProperties serverProperties) {
|
||||
this.delegate = new OrRequestMatcher(getDelegateMatchers(serverProperties));
|
||||
protected void initialized(Supplier<ServerProperties> serverProperties) {
|
||||
this.delegate = new OrRequestMatcher(
|
||||
getDelegateMatchers(serverProperties.get()));
|
||||
}
|
||||
|
||||
private List<RequestMatcher> getDelegateMatchers(
|
||||
|
@ -149,7 +151,8 @@ public final class StaticResourceRequest {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(HttpServletRequest request, ServerProperties context) {
|
||||
protected boolean matches(HttpServletRequest request,
|
||||
Supplier<ServerProperties> context) {
|
||||
return this.delegate.matches(request);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
|
||||
package org.springframework.boot.security.reactive;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
|
||||
|
@ -32,9 +33,8 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
* that is autowired in the usual way.
|
||||
*
|
||||
* @param <C> The type of the context that the match method actually needs to use. Can be
|
||||
* an {@link ApplicationContext}, a class of an {@link ApplicationContext#getBean(Class)
|
||||
* existing bean} or a custom type that will be
|
||||
* {@link AutowireCapableBeanFactory#createBean(Class, int, boolean) created} on demand.
|
||||
* an {@link ApplicationContext} or a class of an {@link ApplicationContext#getBean(Class)
|
||||
* existing bean}.
|
||||
* @author Madhura Bhave
|
||||
* @since 2.0.0
|
||||
*/
|
||||
|
@ -43,7 +43,7 @@ public abstract class ApplicationContextServerWebExchangeMatcher<C>
|
|||
|
||||
private final Class<? extends C> contextClass;
|
||||
|
||||
private volatile C context;
|
||||
private volatile Supplier<C> context;
|
||||
|
||||
private final Object contextLock = new Object();
|
||||
|
||||
|
@ -60,12 +60,13 @@ public abstract class ApplicationContextServerWebExchangeMatcher<C>
|
|||
/**
|
||||
* Decides whether the rule implemented by the strategy matches the supplied exchange.
|
||||
* @param exchange the source exchange
|
||||
* @param context the context instance
|
||||
* @param context a supplier for the initialized context (may throw an exception)
|
||||
* @return if the exchange matches
|
||||
*/
|
||||
protected abstract Mono<MatchResult> matches(ServerWebExchange exchange, C context);
|
||||
protected abstract Mono<MatchResult> matches(ServerWebExchange exchange,
|
||||
Supplier<C> context);
|
||||
|
||||
protected C getContext(ServerWebExchange exchange) {
|
||||
protected Supplier<C> getContext(ServerWebExchange exchange) {
|
||||
if (this.context == null) {
|
||||
synchronized (this.contextLock) {
|
||||
if (this.context == null) {
|
||||
|
@ -79,26 +80,19 @@ public abstract class ApplicationContextServerWebExchangeMatcher<C>
|
|||
|
||||
/**
|
||||
* Called once the context has been initialized.
|
||||
* @param context the initialized context
|
||||
* @param context a supplier for the initialized context (may throw an exception)
|
||||
*/
|
||||
protected void initialized(C context) {
|
||||
protected void initialized(Supplier<C> context) {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private C createContext(ServerWebExchange exchange) {
|
||||
private Supplier<C> createContext(ServerWebExchange exchange) {
|
||||
ApplicationContext context = exchange.getApplicationContext();
|
||||
Assert.state(context != null, "No WebApplicationContext found.");
|
||||
if (this.contextClass.isInstance(context)) {
|
||||
return (C) context;
|
||||
}
|
||||
try {
|
||||
return context.getBean(this.contextClass);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return (C) context.getAutowireCapableBeanFactory().createBean(
|
||||
this.contextClass, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR,
|
||||
false);
|
||||
return () -> (C) context;
|
||||
}
|
||||
return () -> context.getBean(this.contextClass);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
|
||||
package org.springframework.boot.security.servlet;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
@ -33,9 +34,8 @@ import org.springframework.web.context.support.WebApplicationContextUtils;
|
|||
* that is autowired in the usual way.
|
||||
*
|
||||
* @param <C> The type of the context that the match method actually needs to use. Can be
|
||||
* an {@link ApplicationContext}, a class of an {@link ApplicationContext#getBean(Class)
|
||||
* existing bean} or a custom type that will be
|
||||
* {@link AutowireCapableBeanFactory#createBean(Class, int, boolean) created} on demand.
|
||||
* an {@link ApplicationContext} or a class of an {@link ApplicationContext#getBean(Class)
|
||||
* existing bean}.
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
|
@ -43,7 +43,7 @@ public abstract class ApplicationContextRequestMatcher<C> implements RequestMatc
|
|||
|
||||
private final Class<? extends C> contextClass;
|
||||
|
||||
private volatile C context;
|
||||
private volatile Supplier<C> context;
|
||||
|
||||
private final Object contextLock = new Object();
|
||||
|
||||
|
@ -60,12 +60,12 @@ public abstract class ApplicationContextRequestMatcher<C> implements RequestMatc
|
|||
/**
|
||||
* Decides whether the rule implemented by the strategy matches the supplied request.
|
||||
* @param request the source request
|
||||
* @param context the context instance
|
||||
* @param context a supplier for the initialized context (may throw an exception)
|
||||
* @return if the request matches
|
||||
*/
|
||||
protected abstract boolean matches(HttpServletRequest request, C context);
|
||||
protected abstract boolean matches(HttpServletRequest request, Supplier<C> context);
|
||||
|
||||
private C getContext(HttpServletRequest request) {
|
||||
private Supplier<C> getContext(HttpServletRequest request) {
|
||||
if (this.context == null) {
|
||||
synchronized (this.contextLock) {
|
||||
if (this.context == null) {
|
||||
|
@ -79,26 +79,19 @@ public abstract class ApplicationContextRequestMatcher<C> implements RequestMatc
|
|||
|
||||
/**
|
||||
* Called once the context has been initialized.
|
||||
* @param context the initialized context
|
||||
* @param context a supplier for the initialized context (may throw an exception)
|
||||
*/
|
||||
protected void initialized(C context) {
|
||||
protected void initialized(Supplier<C> context) {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private C createContext(HttpServletRequest request) {
|
||||
private Supplier<C> createContext(HttpServletRequest request) {
|
||||
WebApplicationContext context = WebApplicationContextUtils
|
||||
.getRequiredWebApplicationContext(request.getServletContext());
|
||||
if (this.contextClass.isInstance(context)) {
|
||||
return (C) context;
|
||||
}
|
||||
try {
|
||||
return context.getBean(this.contextClass);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return (C) context.getAutowireCapableBeanFactory().createBean(
|
||||
this.contextClass, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR,
|
||||
false);
|
||||
return () -> (C) context;
|
||||
}
|
||||
return () -> context.getBean(this.contextClass);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,11 +16,14 @@
|
|||
|
||||
package org.springframework.boot.security.reactive;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
|
@ -58,8 +61,8 @@ public class ApplicationContextServerWebExchangeMatcherTests {
|
|||
StaticApplicationContext context = (StaticApplicationContext) exchange
|
||||
.getApplicationContext();
|
||||
assertThat(new TestApplicationContextServerWebExchangeMatcher<>(
|
||||
ApplicationContext.class).callMatchesAndReturnProvidedContext(exchange))
|
||||
.isEqualTo(context);
|
||||
ApplicationContext.class).callMatchesAndReturnProvidedContext(exchange)
|
||||
.get()).isEqualTo(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -70,19 +73,17 @@ public class ApplicationContextServerWebExchangeMatcherTests {
|
|||
context.registerSingleton("existingBean", ExistingBean.class);
|
||||
assertThat(
|
||||
new TestApplicationContextServerWebExchangeMatcher<>(ExistingBean.class)
|
||||
.callMatchesAndReturnProvidedContext(exchange))
|
||||
.callMatchesAndReturnProvidedContext(exchange).get())
|
||||
.isEqualTo(context.getBean(ExistingBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenContextClassIsNewBeanShouldProvideBean() {
|
||||
public void matchesWhenContextClassIsMissingBeanShouldProvideException() {
|
||||
ServerWebExchange exchange = createHttpWebHandlerAdapter();
|
||||
StaticApplicationContext context = (StaticApplicationContext) exchange
|
||||
.getApplicationContext();
|
||||
context.registerSingleton("existingBean", ExistingBean.class);
|
||||
assertThat(new TestApplicationContextServerWebExchangeMatcher<>(NewBean.class)
|
||||
.callMatchesAndReturnProvidedContext(exchange).getBean())
|
||||
.isEqualTo(context.getBean(ExistingBean.class));
|
||||
Supplier<ExistingBean> supplier = new TestApplicationContextServerWebExchangeMatcher<>(
|
||||
ExistingBean.class).callMatchesAndReturnProvidedContext(exchange);
|
||||
this.thrown.expect(NoSuchBeanDefinitionException.class);
|
||||
supplier.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -139,24 +140,25 @@ public class ApplicationContextServerWebExchangeMatcherTests {
|
|||
static class TestApplicationContextServerWebExchangeMatcher<C>
|
||||
extends ApplicationContextServerWebExchangeMatcher<C> {
|
||||
|
||||
private C providedContext;
|
||||
private Supplier<C> providedContext;
|
||||
|
||||
TestApplicationContextServerWebExchangeMatcher(Class<? extends C> context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
C callMatchesAndReturnProvidedContext(ServerWebExchange exchange) {
|
||||
Supplier<C> callMatchesAndReturnProvidedContext(ServerWebExchange exchange) {
|
||||
matches(exchange);
|
||||
return getProvidedContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<MatchResult> matches(ServerWebExchange exchange, C context) {
|
||||
protected Mono<MatchResult> matches(ServerWebExchange exchange,
|
||||
Supplier<C> context) {
|
||||
this.providedContext = context;
|
||||
return MatchResult.match();
|
||||
}
|
||||
|
||||
C getProvidedContext() {
|
||||
Supplier<C> getProvidedContext() {
|
||||
return this.providedContext;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,15 @@
|
|||
|
||||
package org.springframework.boot.security.servlet;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
|
@ -51,7 +54,7 @@ public class ApplicationContextRequestMatcherTests {
|
|||
public void matchesWhenContextClassIsApplicationContextShouldProvideContext() {
|
||||
StaticWebApplicationContext context = createWebApplicationContext();
|
||||
assertThat(new TestApplicationContextRequestMatcher<>(ApplicationContext.class)
|
||||
.callMatchesAndReturnProvidedContext(context)).isEqualTo(context);
|
||||
.callMatchesAndReturnProvidedContext(context).get()).isEqualTo(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -59,17 +62,17 @@ public class ApplicationContextRequestMatcherTests {
|
|||
StaticWebApplicationContext context = createWebApplicationContext();
|
||||
context.registerSingleton("existingBean", ExistingBean.class);
|
||||
assertThat(new TestApplicationContextRequestMatcher<>(ExistingBean.class)
|
||||
.callMatchesAndReturnProvidedContext(context))
|
||||
.callMatchesAndReturnProvidedContext(context).get())
|
||||
.isEqualTo(context.getBean(ExistingBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenContextClassIsNewBeanShouldProvideBean() {
|
||||
public void matchesWhenContextClassIsBeanThatDoesntExistShouldSupplyException() {
|
||||
StaticWebApplicationContext context = createWebApplicationContext();
|
||||
context.registerSingleton("existingBean", ExistingBean.class);
|
||||
assertThat(new TestApplicationContextRequestMatcher<>(NewBean.class)
|
||||
.callMatchesAndReturnProvidedContext(context).getBean())
|
||||
.isEqualTo(context.getBean(ExistingBean.class));
|
||||
Supplier<ExistingBean> supplier = new TestApplicationContextRequestMatcher<>(
|
||||
ExistingBean.class).callMatchesAndReturnProvidedContext(context);
|
||||
this.thrown.expect(NoSuchBeanDefinitionException.class);
|
||||
supplier.get();
|
||||
}
|
||||
|
||||
private StaticWebApplicationContext createWebApplicationContext() {
|
||||
|
@ -102,29 +105,31 @@ public class ApplicationContextRequestMatcherTests {
|
|||
static class TestApplicationContextRequestMatcher<C>
|
||||
extends ApplicationContextRequestMatcher<C> {
|
||||
|
||||
private C providedContext;
|
||||
private Supplier<C> providedContext;
|
||||
|
||||
TestApplicationContextRequestMatcher(Class<? extends C> context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public C callMatchesAndReturnProvidedContext(WebApplicationContext context) {
|
||||
public Supplier<C> callMatchesAndReturnProvidedContext(
|
||||
WebApplicationContext context) {
|
||||
return callMatchesAndReturnProvidedContext(
|
||||
new MockHttpServletRequest(context.getServletContext()));
|
||||
}
|
||||
|
||||
public C callMatchesAndReturnProvidedContext(HttpServletRequest request) {
|
||||
public Supplier<C> callMatchesAndReturnProvidedContext(
|
||||
HttpServletRequest request) {
|
||||
matches(request);
|
||||
return getProvidedContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(HttpServletRequest request, C context) {
|
||||
protected boolean matches(HttpServletRequest request, Supplier<C> context) {
|
||||
this.providedContext = context;
|
||||
return false;
|
||||
}
|
||||
|
||||
public C getProvidedContext() {
|
||||
public Supplier<C> getProvidedContext() {
|
||||
return this.providedContext;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue