From 7f8bb4e8eb370b056d58233e5e23bbdd89fc9a5f Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 27 Feb 2018 17:20:12 -0800 Subject: [PATCH] Allow EndpointRequest matching without path bean Update `EndpointRequest` to that the `PathMappedEndpoints` bean is optional. A missing bean is treated as if there are no path mapped endpoints. Fixes gh-12238 --- .../security/reactive/EndpointRequest.java | 28 ++++++++++++++++--- .../security/servlet/EndpointRequest.java | 25 ++++++++++++++--- .../reactive/EndpointRequestTests.java | 11 +++++++- .../servlet/EndpointRequestTests.java | 11 +++++++- 4 files changed, 65 insertions(+), 10 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java index 89fdda52671..ddd81061757 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java @@ -29,6 +29,7 @@ import java.util.stream.Stream; import reactor.core.publisher.Mono; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.security.reactive.ApplicationContextServerWebExchangeMatcher; @@ -36,6 +37,7 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult; import org.springframework.util.Assert; import org.springframework.web.server.ServerWebExchange; @@ -48,6 +50,9 @@ import org.springframework.web.server.ServerWebExchange; */ public final class EndpointRequest { + private static final ServerWebExchangeMatcher EMPTY_MATCHER = (request) -> MatchResult + .notMatch(); + private EndpointRequest() { } @@ -134,19 +139,34 @@ public final class EndpointRequest { @Override protected void initialized(Supplier pathMappedEndpoints) { + this.delegate = createDelegate(pathMappedEndpoints); + } + + private ServerWebExchangeMatcher createDelegate( + Supplier pathMappedEndpoints) { + try { + return createDelegate(pathMappedEndpoints.get()); + } + catch (NoSuchBeanDefinitionException ex) { + return EMPTY_MATCHER; + } + } + + private ServerWebExchangeMatcher createDelegate( + PathMappedEndpoints pathMappedEndpoints) { Set paths = new LinkedHashSet<>(); if (this.includes.isEmpty()) { - paths.addAll(pathMappedEndpoints.get().getAllPaths()); + paths.addAll(pathMappedEndpoints.getAllPaths()); } streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add); streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove); - this.delegate = new OrServerWebExchangeMatcher(getDelegateMatchers(paths)); + return new OrServerWebExchangeMatcher(getDelegateMatchers(paths)); } private Stream streamPaths(List source, - Supplier pathMappedEndpoints) { + PathMappedEndpoints pathMappedEndpoints) { return source.stream().filter(Objects::nonNull).map(this::getEndpointId) - .map(pathMappedEndpoints.get()::getPath); + .map(pathMappedEndpoints::getPath); } private String getEndpointId(Object source) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java index dcab2acf711..0a0f8a71de0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java @@ -29,6 +29,7 @@ import java.util.stream.Stream; import javax.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher; @@ -48,6 +49,8 @@ import org.springframework.util.Assert; */ public final class EndpointRequest { + private static final RequestMatcher EMPTY_MATCHER = (request) -> false; + private EndpointRequest() { } @@ -131,13 +134,27 @@ public final class EndpointRequest { @Override protected void initialized(Supplier pathMappedEndpoints) { + this.delegate = createDelegate(pathMappedEndpoints); + } + + private RequestMatcher createDelegate( + Supplier pathMappedEndpoints) { + try { + return createDelegate(pathMappedEndpoints.get()); + } + catch (NoSuchBeanDefinitionException ex) { + return EMPTY_MATCHER; + } + } + + private RequestMatcher createDelegate(PathMappedEndpoints pathMappedEndpoints) { Set paths = new LinkedHashSet<>(); if (this.includes.isEmpty()) { - paths.addAll(pathMappedEndpoints.get().getAllPaths()); + paths.addAll(pathMappedEndpoints.getAllPaths()); } - streamPaths(this.includes, pathMappedEndpoints.get()).forEach(paths::add); - streamPaths(this.excludes, pathMappedEndpoints.get()).forEach(paths::remove); - this.delegate = new OrRequestMatcher(getDelegateMatchers(paths)); + streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add); + streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove); + return new OrRequestMatcher(getDelegateMatchers(paths)); } private Stream streamPaths(List source, diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java index 8627e6d54cb..5afe9324500 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java @@ -102,6 +102,13 @@ public class EndpointRequestTests { assertMatcher(matcher).matches("/actuator/bar"); } + @Test + public void noEndpointPathsBeansShouldNeverMatch() { + ServerWebExchangeMatcher matcher = EndpointRequest.toAnyEndpoint(); + assertMatcher(matcher, null).doesNotMatch("/actuator/foo"); + assertMatcher(matcher, null).doesNotMatch("/actuator/bar"); + } + private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher) { return assertMatcher(matcher, mockPathMappedEndpoints()); } @@ -123,7 +130,9 @@ public class EndpointRequestTests { private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher, PathMappedEndpoints pathMappedEndpoints) { StaticApplicationContext context = new StaticApplicationContext(); - context.registerBean(PathMappedEndpoints.class, () -> pathMappedEndpoints); + if (pathMappedEndpoints != null) { + context.registerBean(PathMappedEndpoints.class, () -> pathMappedEndpoints); + } return assertThat(new RequestMatcherAssert(context, matcher)); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java index 26b84a72a4e..ab9443e88b6 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java @@ -98,6 +98,13 @@ public class EndpointRequestTests { assertMatcher(matcher).matches("/actuator/bar"); } + @Test + public void noEndpointPathsBeansShouldNeverMatch() { + RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); + assertMatcher(matcher, null).doesNotMatch("/actuator/foo"); + assertMatcher(matcher, null).doesNotMatch("/actuator/bar"); + } + private RequestMatcherAssert assertMatcher(RequestMatcher matcher) { return assertMatcher(matcher, mockPathMappedEndpoints()); } @@ -119,7 +126,9 @@ public class EndpointRequestTests { private RequestMatcherAssert assertMatcher(RequestMatcher matcher, PathMappedEndpoints pathMappedEndpoints) { StaticWebApplicationContext context = new StaticWebApplicationContext(); - context.registerBean(PathMappedEndpoints.class, () -> pathMappedEndpoints); + if (pathMappedEndpoints != null) { + context.registerBean(PathMappedEndpoints.class, () -> pathMappedEndpoints); + } return assertThat(new RequestMatcherAssert(context, matcher)); }