Skip to content

Commit 46dfe38

Browse files
committed
Rework security request matchers
Update the security request matchers so that a bean is no longer needed when the matcher is used. Matchers can now be build by starting from the `EndpointRequest` or `StaticResourceRequest` classes. For example: http.authorizeRequests() .requestMatchers(EndpointRequest.to("status", "info")).permitAll() .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR") .requestMatchers(StaticResourceRequest.toCommonLocations()).permitAll() Closes gh-7958
1 parent 2e51b48 commit 46dfe38

File tree

20 files changed

+1294
-29
lines changed

20 files changed

+1294
-29
lines changed

spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/EndpointProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
* @author Stephane Nicoll
3434
* @since 2.0.0
3535
*/
36-
public final class EndpointProvider<T extends Operation> {
36+
public class EndpointProvider<T extends Operation> {
3737

3838
private final EndpointDiscoverer<T> discoverer;
3939

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.endpoint.web;
18+
19+
import java.util.Collection;
20+
import java.util.List;
21+
import java.util.stream.Collectors;
22+
23+
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider;
24+
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
25+
import org.springframework.boot.actuate.endpoint.EndpointInfo;
26+
import org.springframework.boot.actuate.endpoint.web.WebEndpointOperation;
27+
import org.springframework.util.Assert;
28+
29+
/**
30+
* Default {@link EndpointPathProvider} implementation.
31+
*
32+
* @author Phillip Webb
33+
* @since 2.0.0
34+
*/
35+
public class DefaultEndpointPathProvider implements EndpointPathProvider {
36+
37+
private final Collection<EndpointInfo<WebEndpointOperation>> endpoints;
38+
39+
private final String contextPath;
40+
41+
public DefaultEndpointPathProvider(EndpointProvider<WebEndpointOperation> provider,
42+
ManagementServerProperties managementServerProperties) {
43+
this.endpoints = provider.getEndpoints();
44+
this.contextPath = managementServerProperties.getContextPath();
45+
}
46+
47+
@Override
48+
public List<String> getPaths() {
49+
return this.endpoints.stream().map(this::getPath).collect(Collectors.toList());
50+
}
51+
52+
@Override
53+
public String getPath(String id) {
54+
Assert.notNull(id, "ID must not be null");
55+
return this.endpoints.stream().filter((info) -> id.equals(info.getId()))
56+
.findFirst().map(this::getPath).orElse(null);
57+
}
58+
59+
private String getPath(EndpointInfo<WebEndpointOperation> endpointInfo) {
60+
return this.contextPath + "/" + endpointInfo.getId();
61+
}
62+
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.endpoint.web;
18+
19+
import java.util.List;
20+
21+
/**
22+
* Interface that provides path information for web mapped endpints.
23+
*
24+
* @author Phillip Webb
25+
* @since 2.0.0
26+
*/
27+
public interface EndpointPathProvider {
28+
29+
/**
30+
* Return all mapped endpoint paths.
31+
* @return all paths
32+
*/
33+
List<String> getPaths();
34+
35+
/**
36+
* Return the path for the endpoint with the specified ID.
37+
* @param id the endpoint ID
38+
* @return the path of the endpoint or {@code null}
39+
*/
40+
String getPath(String id);
41+
42+
}

spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import org.glassfish.jersey.server.ResourceConfig;
2222

2323
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider;
24+
import org.springframework.boot.actuate.autoconfigure.endpoint.web.DefaultEndpointPathProvider;
25+
import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathProvider;
2426
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
2527
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
2628
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
@@ -60,4 +62,12 @@ public ResourceConfigCustomizer webEndpointRegistrar(
6062
provider.getEndpoints())));
6163
}
6264

65+
@Bean
66+
@ConditionalOnMissingBean
67+
public EndpointPathProvider endpointPathProvider(
68+
EndpointProvider<WebEndpointOperation> provider,
69+
ManagementServerProperties managementServerProperties) {
70+
return new DefaultEndpointPathProvider(provider, managementServerProperties);
71+
}
72+
6373
}

spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive;
1818

1919
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider;
20+
import org.springframework.boot.actuate.autoconfigure.endpoint.web.DefaultEndpointPathProvider;
21+
import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathProvider;
2022
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
2123
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
2224
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
@@ -49,4 +51,12 @@ public WebFluxEndpointHandlerMapping webEndpointReactiveHandlerMapping(
4951
provider.getEndpoints());
5052
}
5153

54+
@Bean
55+
@ConditionalOnMissingBean
56+
public EndpointPathProvider endpointPathProvider(
57+
EndpointProvider<WebEndpointOperation> provider,
58+
ManagementServerProperties managementServerProperties) {
59+
return new DefaultEndpointPathProvider(provider, managementServerProperties);
60+
}
61+
5262
}

spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet;
1818

1919
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointProvider;
20+
import org.springframework.boot.actuate.autoconfigure.endpoint.web.DefaultEndpointPathProvider;
21+
import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathProvider;
2022
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
2123
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties;
2224
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
@@ -61,6 +63,14 @@ public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(
6163
return handlerMapping;
6264
}
6365

66+
@Bean
67+
@ConditionalOnMissingBean
68+
public EndpointPathProvider endpointPathProvider(
69+
EndpointProvider<WebEndpointOperation> provider,
70+
ManagementServerProperties managementServerProperties) {
71+
return new DefaultEndpointPathProvider(provider, managementServerProperties);
72+
}
73+
6474
private CorsConfiguration getCorsConfiguration(CorsEndpointProperties properties) {
6575
if (CollectionUtils.isEmpty(properties.getAllowedOrigins())) {
6676
return null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.security;
18+
19+
import java.util.ArrayList;
20+
import java.util.Arrays;
21+
import java.util.Collections;
22+
import java.util.LinkedHashSet;
23+
import java.util.List;
24+
import java.util.Objects;
25+
import java.util.Set;
26+
import java.util.stream.Collectors;
27+
import java.util.stream.Stream;
28+
29+
import javax.servlet.http.HttpServletRequest;
30+
31+
import org.springframework.beans.factory.BeanCreationException;
32+
import org.springframework.boot.actuate.autoconfigure.endpoint.web.EndpointPathProvider;
33+
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
34+
import org.springframework.boot.security.ApplicationContextRequestMatcher;
35+
import org.springframework.core.annotation.AnnotationUtils;
36+
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
37+
import org.springframework.security.web.util.matcher.OrRequestMatcher;
38+
import org.springframework.security.web.util.matcher.RequestMatcher;
39+
import org.springframework.util.Assert;
40+
41+
/**
42+
* Factory that can be used to create a {@link RequestMatcher} for actuator endpoint
43+
* locations.
44+
*
45+
* @author Madhura Bhave
46+
* @author Phillip Webb
47+
* @since 2.0.0
48+
*/
49+
public final class EndpointRequest {
50+
51+
private EndpointRequest() {
52+
}
53+
54+
/**
55+
* Returns a matcher that includes all {@link Endpoint actuator endpoints}. The
56+
* {@link EndpointRequestMatcher#excluding(Class...) excluding} method can be used to
57+
* further remove specific endpoints if required. For example: <pre class="code">
58+
* EndpointRequestMatcher.toAnyEndpoint().excluding(ShutdownEndpoint.class)
59+
* </pre>
60+
* @return the configured {@link RequestMatcher}
61+
*/
62+
public static EndpointRequestMatcher toAnyEndpoint() {
63+
return new EndpointRequestMatcher();
64+
}
65+
66+
/**
67+
* Returns a matcher that includes the specified {@link Endpoint actuator endpoints}.
68+
* For example: <pre class="code">
69+
* EndpointRequestMatcher.to(ShutdownEndpoint.class, HealthEndpoint.class)
70+
* </pre>
71+
* @param endpoints the endpoints to include
72+
* @return the configured {@link RequestMatcher}
73+
*/
74+
public static EndpointRequestMatcher to(Class<?>... endpoints) {
75+
return new EndpointRequestMatcher(endpoints);
76+
}
77+
78+
/**
79+
* Returns a matcher that includes the specified {@link Endpoint actuator endpoints}.
80+
* For example: <pre class="code">
81+
* EndpointRequestMatcher.to("shutdown", "health")
82+
* </pre>
83+
* @param endpoints the endpoints to include
84+
* @return the configured {@link RequestMatcher}
85+
*/
86+
public static EndpointRequestMatcher to(String... endpoints) {
87+
return new EndpointRequestMatcher(endpoints);
88+
}
89+
90+
/**
91+
* The request matcher used to match against {@link Endpoint actuator endpoints}.
92+
*/
93+
public final static class EndpointRequestMatcher
94+
extends ApplicationContextRequestMatcher<EndpointPathProvider> {
95+
96+
private final List<Object> includes;
97+
98+
private final List<Object> excludes;
99+
100+
private RequestMatcher delegate;
101+
102+
private EndpointRequestMatcher() {
103+
super(EndpointPathProvider.class);
104+
this.includes = Collections.emptyList();
105+
this.excludes = Collections.emptyList();
106+
}
107+
108+
private EndpointRequestMatcher(Class<?>[] endpoints) {
109+
super(EndpointPathProvider.class);
110+
this.includes = Arrays.asList((Object[]) endpoints);
111+
this.excludes = Collections.emptyList();
112+
}
113+
114+
private EndpointRequestMatcher(String[] endpoints) {
115+
super(EndpointPathProvider.class);
116+
this.includes = Arrays.asList((Object[]) endpoints);
117+
this.excludes = Collections.emptyList();
118+
}
119+
120+
private EndpointRequestMatcher(List<Object> includes, List<Object> excludes) {
121+
super(EndpointPathProvider.class);
122+
this.includes = includes;
123+
this.excludes = excludes;
124+
}
125+
126+
EndpointRequestMatcher excluding(Class<?>... endpoints) {
127+
List<Object> excludes = new ArrayList<>(this.excludes);
128+
excludes.addAll(Arrays.asList((Object[]) endpoints));
129+
return new EndpointRequestMatcher(this.includes, excludes);
130+
}
131+
132+
EndpointRequestMatcher excluding(String... endpoints) {
133+
List<Object> excludes = new ArrayList<>(this.excludes);
134+
excludes.addAll(Arrays.asList((Object[]) endpoints));
135+
return new EndpointRequestMatcher(this.includes, excludes);
136+
}
137+
138+
@Override
139+
protected void initialized(EndpointPathProvider endpointPathProvider) {
140+
Set<String> paths = new LinkedHashSet<>(this.includes.isEmpty()
141+
? endpointPathProvider.getPaths() : Collections.emptyList());
142+
streamPaths(this.includes, endpointPathProvider).forEach(paths::add);
143+
streamPaths(this.excludes, endpointPathProvider).forEach(paths::remove);
144+
this.delegate = new OrRequestMatcher(getDelegateMatchers(paths));
145+
}
146+
147+
private Stream<String> streamPaths(List<Object> source,
148+
EndpointPathProvider endpointPathProvider) {
149+
return source.stream().filter(Objects::nonNull).map(this::getPathId)
150+
.map(endpointPathProvider::getPath);
151+
}
152+
153+
private String getPathId(Object source) {
154+
if (source instanceof String) {
155+
return (String) source;
156+
}
157+
if (source instanceof Class) {
158+
return getPathId((Class<?>) source);
159+
}
160+
throw new IllegalStateException("Unsupported source " + source);
161+
}
162+
163+
private String getPathId(Class<?> source) {
164+
Endpoint annotation = AnnotationUtils.findAnnotation(source, Endpoint.class);
165+
Assert.state(annotation != null,
166+
"Class " + source + " is not annotated with @Endpoint");
167+
return annotation.id();
168+
}
169+
170+
private List<RequestMatcher> getDelegateMatchers(Set<String> paths) {
171+
return paths.stream().map((path) -> new AntPathRequestMatcher(path + "/**"))
172+
.collect(Collectors.toList());
173+
}
174+
175+
@Override
176+
public boolean matches(HttpServletRequest request) {
177+
try {
178+
return super.matches(request);
179+
}
180+
catch (BeanCreationException ex) {
181+
return false;
182+
}
183+
}
184+
185+
@Override
186+
protected boolean matches(HttpServletRequest request,
187+
EndpointPathProvider context) {
188+
return this.delegate.matches(request);
189+
}
190+
191+
}
192+
193+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Auto-configuration for security.
19+
*/
20+
package org.springframework.boot.actuate.autoconfigure.security;

0 commit comments

Comments
 (0)