Skip to content

Commit 7f8bb4e

Browse files
committed
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
1 parent eef6fdb commit 7f8bb4e

File tree

4 files changed

+65
-10
lines changed

4 files changed

+65
-10
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequest.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@
2929

3030
import reactor.core.publisher.Mono;
3131

32+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
3233
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
3334
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
3435
import org.springframework.boot.security.reactive.ApplicationContextServerWebExchangeMatcher;
3536
import org.springframework.core.annotation.AnnotationUtils;
3637
import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;
3738
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
3839
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
40+
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult;
3941
import org.springframework.util.Assert;
4042
import org.springframework.web.server.ServerWebExchange;
4143

@@ -48,6 +50,9 @@
4850
*/
4951
public final class EndpointRequest {
5052

53+
private static final ServerWebExchangeMatcher EMPTY_MATCHER = (request) -> MatchResult
54+
.notMatch();
55+
5156
private EndpointRequest() {
5257
}
5358

@@ -134,19 +139,34 @@ EndpointServerWebExchangeMatcher excluding(String... endpoints) {
134139

135140
@Override
136141
protected void initialized(Supplier<PathMappedEndpoints> pathMappedEndpoints) {
142+
this.delegate = createDelegate(pathMappedEndpoints);
143+
}
144+
145+
private ServerWebExchangeMatcher createDelegate(
146+
Supplier<PathMappedEndpoints> pathMappedEndpoints) {
147+
try {
148+
return createDelegate(pathMappedEndpoints.get());
149+
}
150+
catch (NoSuchBeanDefinitionException ex) {
151+
return EMPTY_MATCHER;
152+
}
153+
}
154+
155+
private ServerWebExchangeMatcher createDelegate(
156+
PathMappedEndpoints pathMappedEndpoints) {
137157
Set<String> paths = new LinkedHashSet<>();
138158
if (this.includes.isEmpty()) {
139-
paths.addAll(pathMappedEndpoints.get().getAllPaths());
159+
paths.addAll(pathMappedEndpoints.getAllPaths());
140160
}
141161
streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add);
142162
streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove);
143-
this.delegate = new OrServerWebExchangeMatcher(getDelegateMatchers(paths));
163+
return new OrServerWebExchangeMatcher(getDelegateMatchers(paths));
144164
}
145165

146166
private Stream<String> streamPaths(List<Object> source,
147-
Supplier<PathMappedEndpoints> pathMappedEndpoints) {
167+
PathMappedEndpoints pathMappedEndpoints) {
148168
return source.stream().filter(Objects::nonNull).map(this::getEndpointId)
149-
.map(pathMappedEndpoints.get()::getPath);
169+
.map(pathMappedEndpoints::getPath);
150170
}
151171

152172
private String getEndpointId(Object source) {

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import javax.servlet.http.HttpServletRequest;
3131

32+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
3233
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
3334
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
3435
import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher;
@@ -48,6 +49,8 @@
4849
*/
4950
public final class EndpointRequest {
5051

52+
private static final RequestMatcher EMPTY_MATCHER = (request) -> false;
53+
5154
private EndpointRequest() {
5255
}
5356

@@ -131,13 +134,27 @@ EndpointRequestMatcher excluding(String... endpoints) {
131134

132135
@Override
133136
protected void initialized(Supplier<PathMappedEndpoints> pathMappedEndpoints) {
137+
this.delegate = createDelegate(pathMappedEndpoints);
138+
}
139+
140+
private RequestMatcher createDelegate(
141+
Supplier<PathMappedEndpoints> pathMappedEndpoints) {
142+
try {
143+
return createDelegate(pathMappedEndpoints.get());
144+
}
145+
catch (NoSuchBeanDefinitionException ex) {
146+
return EMPTY_MATCHER;
147+
}
148+
}
149+
150+
private RequestMatcher createDelegate(PathMappedEndpoints pathMappedEndpoints) {
134151
Set<String> paths = new LinkedHashSet<>();
135152
if (this.includes.isEmpty()) {
136-
paths.addAll(pathMappedEndpoints.get().getAllPaths());
153+
paths.addAll(pathMappedEndpoints.getAllPaths());
137154
}
138-
streamPaths(this.includes, pathMappedEndpoints.get()).forEach(paths::add);
139-
streamPaths(this.excludes, pathMappedEndpoints.get()).forEach(paths::remove);
140-
this.delegate = new OrRequestMatcher(getDelegateMatchers(paths));
155+
streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add);
156+
streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove);
157+
return new OrRequestMatcher(getDelegateMatchers(paths));
141158
}
142159

143160
private Stream<String> streamPaths(List<Object> source,

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/reactive/EndpointRequestTests.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,13 @@ public void excludeByIdShouldNotMatchExcluded() {
102102
assertMatcher(matcher).matches("/actuator/bar");
103103
}
104104

105+
@Test
106+
public void noEndpointPathsBeansShouldNeverMatch() {
107+
ServerWebExchangeMatcher matcher = EndpointRequest.toAnyEndpoint();
108+
assertMatcher(matcher, null).doesNotMatch("/actuator/foo");
109+
assertMatcher(matcher, null).doesNotMatch("/actuator/bar");
110+
}
111+
105112
private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher) {
106113
return assertMatcher(matcher, mockPathMappedEndpoints());
107114
}
@@ -123,7 +130,9 @@ private TestEndpoint mockEndpoint(String id, String rootPath) {
123130
private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher,
124131
PathMappedEndpoints pathMappedEndpoints) {
125132
StaticApplicationContext context = new StaticApplicationContext();
126-
context.registerBean(PathMappedEndpoints.class, () -> pathMappedEndpoints);
133+
if (pathMappedEndpoints != null) {
134+
context.registerBean(PathMappedEndpoints.class, () -> pathMappedEndpoints);
135+
}
127136
return assertThat(new RequestMatcherAssert(context, matcher));
128137
}
129138

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ public void excludeByIdShouldNotMatchExcluded() {
9898
assertMatcher(matcher).matches("/actuator/bar");
9999
}
100100

101+
@Test
102+
public void noEndpointPathsBeansShouldNeverMatch() {
103+
RequestMatcher matcher = EndpointRequest.toAnyEndpoint();
104+
assertMatcher(matcher, null).doesNotMatch("/actuator/foo");
105+
assertMatcher(matcher, null).doesNotMatch("/actuator/bar");
106+
}
107+
101108
private RequestMatcherAssert assertMatcher(RequestMatcher matcher) {
102109
return assertMatcher(matcher, mockPathMappedEndpoints());
103110
}
@@ -119,7 +126,9 @@ private TestEndpoint mockEndpoint(String id, String rootPath) {
119126
private RequestMatcherAssert assertMatcher(RequestMatcher matcher,
120127
PathMappedEndpoints pathMappedEndpoints) {
121128
StaticWebApplicationContext context = new StaticWebApplicationContext();
122-
context.registerBean(PathMappedEndpoints.class, () -> pathMappedEndpoints);
129+
if (pathMappedEndpoints != null) {
130+
context.registerBean(PathMappedEndpoints.class, () -> pathMappedEndpoints);
131+
}
123132
return assertThat(new RequestMatcherAssert(context, matcher));
124133
}
125134

0 commit comments

Comments
 (0)