Skip to content

Commit 3b49bde

Browse files
committed
Merge branch 'gh-14773' into 2.0.x
Closes gh-14773
2 parents f96d73f + a00ee15 commit 3b49bde

File tree

69 files changed

+952
-326
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+952
-326
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/AccessLevel.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,19 @@ public enum AccessLevel {
4040

4141
public static final String REQUEST_ATTRIBUTE = "cloudFoundryAccessLevel";
4242

43-
private final List<String> endpointIds;
43+
private final List<String> ids;
4444

45-
AccessLevel(String... endpointIds) {
46-
this.endpointIds = Arrays.asList(endpointIds);
45+
AccessLevel(String... ids) {
46+
this.ids = Arrays.asList(ids);
4747
}
4848

4949
/**
50-
* Returns if the access level should allow access to the specified endpoint path.
51-
* @param endpointId the endpoint ID to check
50+
* Returns if the access level should allow access to the specified ID.
51+
* @param id the ID to check
5252
* @return {@code true} if access is allowed
5353
*/
54-
public boolean isAccessAllowed(String endpointId) {
55-
return this.endpointIds.isEmpty() || this.endpointIds.contains(endpointId);
54+
public boolean isAccessAllowed(String id) {
55+
return this.ids.isEmpty() || this.ids.contains(id);
5656
}
5757

5858
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundrySecurityInterceptor.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class CloudFoundrySecurityInterceptor {
5959
this.applicationId = applicationId;
6060
}
6161

62-
Mono<SecurityResponse> preHandle(ServerWebExchange exchange, String endpointId) {
62+
Mono<SecurityResponse> preHandle(ServerWebExchange exchange, String id) {
6363
ServerHttpRequest request = exchange.getRequest();
6464
if (CorsUtils.isPreFlightRequest(request)) {
6565
return SUCCESS;
@@ -72,21 +72,21 @@ Mono<SecurityResponse> preHandle(ServerWebExchange exchange, String endpointId)
7272
return Mono.error(new CloudFoundryAuthorizationException(
7373
Reason.SERVICE_UNAVAILABLE, "Cloud controller URL is not available"));
7474
}
75-
return check(exchange, endpointId).then(SUCCESS).doOnError(this::logError)
75+
return check(exchange, id).then(SUCCESS).doOnError(this::logError)
7676
.onErrorResume(this::getErrorResponse);
7777
}
7878

7979
private void logError(Throwable ex) {
8080
logger.error(ex.getMessage(), ex);
8181
}
8282

83-
private Mono<Void> check(ServerWebExchange exchange, String path) {
83+
private Mono<Void> check(ServerWebExchange exchange, String id) {
8484
try {
8585
Token token = getToken(exchange.getRequest());
8686
return this.tokenValidator.validate(token)
8787
.then(this.cloudFoundrySecurityService
8888
.getAccessLevel(token.toString(), this.applicationId))
89-
.filter((accessLevel) -> accessLevel.isAccessAllowed(path))
89+
.filter((accessLevel) -> accessLevel.isAccessAllowed(id))
9090
.switchIfEmpty(Mono.error(new CloudFoundryAuthorizationException(
9191
Reason.ACCESS_DENIED, "Access denied")))
9292
.doOnSuccess((accessLevel) -> exchange.getAttributes()

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/CloudFoundryWebFluxEndpointHandlerMapping.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
2929
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
30+
import org.springframework.boot.actuate.endpoint.EndpointId;
3031
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
3132
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
3233
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
@@ -70,7 +71,7 @@ class CloudFoundryWebFluxEndpointHandlerMapping
7071
protected ReactiveWebOperation wrapReactiveWebOperation(ExposableWebEndpoint endpoint,
7172
WebOperation operation, ReactiveWebOperation reactiveWebOperation) {
7273
return new SecureReactiveWebOperation(reactiveWebOperation,
73-
this.securityInterceptor, endpoint.getId());
74+
this.securityInterceptor, endpoint.getEndpointId());
7475
}
7576

7677
@Override
@@ -113,10 +114,11 @@ private static class SecureReactiveWebOperation implements ReactiveWebOperation
113114

114115
private final CloudFoundrySecurityInterceptor securityInterceptor;
115116

116-
private final String endpointId;
117+
private final EndpointId endpointId;
117118

118119
SecureReactiveWebOperation(ReactiveWebOperation delegate,
119-
CloudFoundrySecurityInterceptor securityInterceptor, String endpointId) {
120+
CloudFoundrySecurityInterceptor securityInterceptor,
121+
EndpointId endpointId) {
120122
this.delegate = delegate;
121123
this.securityInterceptor = securityInterceptor;
122124
this.endpointId = endpointId;
@@ -125,7 +127,8 @@ private static class SecureReactiveWebOperation implements ReactiveWebOperation
125127
@Override
126128
public Mono<ResponseEntity<Object>> handle(ServerWebExchange exchange,
127129
Map<String, String> body) {
128-
return this.securityInterceptor.preHandle(exchange, this.endpointId)
130+
return this.securityInterceptor
131+
.preHandle(exchange, this.endpointId.toLowerCaseString())
129132
.flatMap((securityResponse) -> flatMapResponse(exchange, body,
130133
securityResponse));
131134
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundrySecurityInterceptor.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.CloudFoundryAuthorizationException.Reason;
2929
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
3030
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.Token;
31+
import org.springframework.boot.actuate.endpoint.EndpointId;
3132
import org.springframework.http.HttpMethod;
3233
import org.springframework.http.HttpStatus;
3334
import org.springframework.util.StringUtils;
@@ -59,7 +60,7 @@ class CloudFoundrySecurityInterceptor {
5960
this.applicationId = applicationId;
6061
}
6162

62-
SecurityResponse preHandle(HttpServletRequest request, String endpointId) {
63+
SecurityResponse preHandle(HttpServletRequest request, EndpointId endpointId) {
6364
if (CorsUtils.isPreFlightRequest(request)) {
6465
return SecurityResponse.success();
6566
}
@@ -90,12 +91,14 @@ SecurityResponse preHandle(HttpServletRequest request, String endpointId) {
9091
return SecurityResponse.success();
9192
}
9293

93-
private void check(HttpServletRequest request, String endpointId) throws Exception {
94+
private void check(HttpServletRequest request, EndpointId endpointId)
95+
throws Exception {
9496
Token token = getToken(request);
9597
this.tokenValidator.validate(token);
9698
AccessLevel accessLevel = this.cloudFoundrySecurityService
9799
.getAccessLevel(token.toString(), this.applicationId);
98-
if (!accessLevel.isAccessAllowed(endpointId)) {
100+
if (!accessLevel.isAccessAllowed(
101+
(endpointId != null) ? endpointId.toLowerCaseString() : "")) {
99102
throw new CloudFoundryAuthorizationException(Reason.ACCESS_DENIED,
100103
"Access denied");
101104
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryWebEndpointServletHandlerMapping.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.AccessLevel;
2929
import org.springframework.boot.actuate.autoconfigure.cloudfoundry.SecurityResponse;
30+
import org.springframework.boot.actuate.endpoint.EndpointId;
3031
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
3132
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
3233
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
@@ -68,15 +69,15 @@ class CloudFoundryWebEndpointServletHandlerMapping
6869
protected ServletWebOperation wrapServletWebOperation(ExposableWebEndpoint endpoint,
6970
WebOperation operation, ServletWebOperation servletWebOperation) {
7071
return new SecureServletWebOperation(servletWebOperation,
71-
this.securityInterceptor, endpoint.getId());
72+
this.securityInterceptor, endpoint.getEndpointId());
7273
}
7374

7475
@Override
7576
@ResponseBody
7677
protected Map<String, Map<String, Link>> links(HttpServletRequest request,
7778
HttpServletResponse response) {
7879
SecurityResponse securityResponse = this.securityInterceptor.preHandle(request,
79-
"");
80+
null);
8081
if (!securityResponse.getStatus().equals(HttpStatus.OK)) {
8182
sendFailureResponse(response, securityResponse);
8283
}
@@ -115,10 +116,11 @@ private static class SecureServletWebOperation implements ServletWebOperation {
115116

116117
private final CloudFoundrySecurityInterceptor securityInterceptor;
117118

118-
private final String endpointId;
119+
private final EndpointId endpointId;
119120

120121
SecureServletWebOperation(ServletWebOperation delegate,
121-
CloudFoundrySecurityInterceptor securityInterceptor, String endpointId) {
122+
CloudFoundrySecurityInterceptor securityInterceptor,
123+
EndpointId endpointId) {
122124
this.delegate = delegate;
123125
this.securityInterceptor = securityInterceptor;
124126
this.endpointId = endpointId;

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.time.Duration;
2020
import java.util.function.Function;
2121

22+
import org.springframework.boot.actuate.endpoint.EndpointId;
2223
import org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor;
2324
import org.springframework.boot.context.properties.bind.BindResult;
2425
import org.springframework.boot.context.properties.bind.Bindable;
@@ -33,7 +34,7 @@
3334
* @author Stephane Nicoll
3435
* @author Phillip Webb
3536
*/
36-
class EndpointIdTimeToLivePropertyFunction implements Function<String, Long> {
37+
class EndpointIdTimeToLivePropertyFunction implements Function<EndpointId, Long> {
3738

3839
private static final Bindable<Duration> DURATION = Bindable.of(Duration.class);
3940

@@ -48,9 +49,9 @@ class EndpointIdTimeToLivePropertyFunction implements Function<String, Long> {
4849
}
4950

5051
@Override
51-
public Long apply(String endpointId) {
52+
public Long apply(EndpointId endpointId) {
5253
String name = String.format("management.endpoint.%s.cache.time-to-live",
53-
endpointId);
54+
endpointId.toLowerCaseString());
5455
BindResult<Duration> duration = Binder.get(this.environment).bind(name, DURATION);
5556
return duration.map(Duration::toMillis).orElse(null);
5657
}

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121
import java.util.Collection;
2222
import java.util.Collections;
2323
import java.util.HashSet;
24+
import java.util.List;
2425
import java.util.Locale;
2526
import java.util.Set;
2627
import java.util.stream.Collectors;
2728

2829
import org.springframework.boot.actuate.endpoint.EndpointFilter;
30+
import org.springframework.boot.actuate.endpoint.EndpointId;
2931
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
3032
import org.springframework.boot.context.properties.bind.Bindable;
3133
import org.springframework.boot.context.properties.bind.Binder;
@@ -74,10 +76,19 @@ public ExposeExcludePropertyEndpointFilter(Class<E> endpointType,
7476
}
7577

7678
private Set<String> bind(Binder binder, String name) {
77-
return asSet(binder.bind(name, Bindable.listOf(String.class))
79+
return asSet(binder.bind(name, Bindable.listOf(String.class)).map(this::cleanup)
7880
.orElseGet(ArrayList::new));
7981
}
8082

83+
private List<String> cleanup(List<String> values) {
84+
return values.stream().map(this::cleanup).collect(Collectors.toList());
85+
}
86+
87+
private String cleanup(String value) {
88+
return "*".equals(value) ? "*"
89+
: EndpointId.fromPropertyValue(value).toLowerCaseString();
90+
}
91+
8192
private Set<String> asSet(Collection<String> items) {
8293
if (items == null) {
8394
return Collections.emptySet();
@@ -110,7 +121,7 @@ private boolean isExcluded(ExposableEndpoint<?> endpoint) {
110121
}
111122

112123
private boolean contains(Set<String> items, ExposableEndpoint<?> endpoint) {
113-
return items.contains(endpoint.getId().toLowerCase(Locale.ENGLISH));
124+
return items.contains(endpoint.getEndpointId().toLowerCaseString());
114125
}
115126

116127
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/condition/OnEnabledEndpointCondition.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Map;
2020
import java.util.Optional;
2121

22+
import org.springframework.boot.actuate.endpoint.EndpointId;
2223
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
2324
import org.springframework.boot.actuate.endpoint.annotation.EndpointExtension;
2425
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
@@ -54,8 +55,8 @@ public ConditionOutcome getMatchOutcome(ConditionContext context,
5455
AnnotatedTypeMetadata metadata) {
5556
Environment environment = context.getEnvironment();
5657
AnnotationAttributes attributes = getEndpointAttributes(context, metadata);
57-
String id = attributes.getString("id");
58-
String key = "management.endpoint." + id + ".enabled";
58+
EndpointId id = EndpointId.of(attributes.getString("id"));
59+
String key = "management.endpoint." + id.toLowerCaseString() + ".enabled";
5960
Boolean userDefinedEnabled = environment.getProperty(key, Boolean.class);
6061
if (userDefinedEnabled != null) {
6162
return new ConditionOutcome(userDefinedEnabled,

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/jmx/DefaultEndpointObjectNameFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ public ObjectName getObjectName(ExposableJmxEndpoint endpoint)
5252
throws MalformedObjectNameException {
5353
StringBuilder builder = new StringBuilder(this.properties.getDomain());
5454
builder.append(":type=Endpoint");
55-
builder.append(",name=" + StringUtils.capitalize(endpoint.getId()));
55+
builder.append(
56+
",name=" + StringUtils.capitalize(endpoint.getEndpointId().toString()));
5657
String baseName = builder.toString();
5758
if (this.mBeanServer != null && hasMBean(baseName)) {
5859
builder.append(",context=" + this.contextId);

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/MappingWebEndpointPathMapper.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.endpoint.web;
1818

19+
import java.util.HashMap;
1920
import java.util.Map;
2021

22+
import org.springframework.boot.actuate.endpoint.EndpointId;
2123
import org.springframework.boot.actuate.endpoint.web.PathMapper;
2224

2325
/**
@@ -28,15 +30,23 @@
2830
*/
2931
class MappingWebEndpointPathMapper implements PathMapper {
3032

31-
private final Map<String, String> pathMapping;
33+
private final Map<EndpointId, String> pathMapping;
3234

3335
MappingWebEndpointPathMapper(Map<String, String> pathMapping) {
34-
this.pathMapping = pathMapping;
36+
this.pathMapping = new HashMap<>();
37+
pathMapping.forEach((id, path) -> this.pathMapping
38+
.put(EndpointId.fromPropertyValue(id), path));
3539
}
3640

3741
@Override
42+
@Deprecated
3843
public String getRootPath(String endpointId) {
39-
return this.pathMapping.getOrDefault(endpointId, endpointId);
44+
return getRootPath(EndpointId.of(endpointId));
45+
}
46+
47+
@Override
48+
public String getRootPath(EndpointId endpointId) {
49+
return this.pathMapping.getOrDefault(endpointId, endpointId.toLowerCaseString());
4050
}
4151

4252
}

0 commit comments

Comments
 (0)