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
pull/12276/head
Phillip Webb 7 years ago
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…
Cancel
Save