Commit 7f8bb4e8 authored by Phillip Webb's avatar Phillip Webb

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
parent eef6fdb0
......@@ -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> pathMappedEndpoints) {
this.delegate = createDelegate(pathMappedEndpoints);
}
private ServerWebExchangeMatcher createDelegate(
Supplier<PathMappedEndpoints> pathMappedEndpoints) {
try {
return createDelegate(pathMappedEndpoints.get());
}
catch (NoSuchBeanDefinitionException ex) {
return EMPTY_MATCHER;
}
}
private ServerWebExchangeMatcher createDelegate(
PathMappedEndpoints pathMappedEndpoints) {
Set<String> 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<String> streamPaths(List<Object> source,
Supplier<PathMappedEndpoints> pathMappedEndpoints) {
PathMappedEndpoints pathMappedEndpoints) {
return source.stream().filter(Objects::nonNull).map(this::getEndpointId)
.map(pathMappedEndpoints.get()::getPath);
.map(pathMappedEndpoints::getPath);
}
private String getEndpointId(Object source) {
......
......@@ -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> pathMappedEndpoints) {
this.delegate = createDelegate(pathMappedEndpoints);
}
private RequestMatcher createDelegate(
Supplier<PathMappedEndpoints> pathMappedEndpoints) {
try {
return createDelegate(pathMappedEndpoints.get());
}
catch (NoSuchBeanDefinitionException ex) {
return EMPTY_MATCHER;
}
}
private RequestMatcher createDelegate(PathMappedEndpoints pathMappedEndpoints) {
Set<String> 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<String> streamPaths(List<Object> source,
......
......@@ -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));
}
......
......@@ -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));
}
......
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