Polish 'Fail fast when base path and an endpoint mapping are set to '/''
See gh-45251
This commit is contained in:
parent
8f535b266c
commit
7dac8ca345
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -23,6 +23,7 @@ import org.springframework.beans.factory.ObjectProvider;
|
|||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.EndpointExposure;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludeExcludeEndpointFilter;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointAccessResolver;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointsSupplier;
|
||||
|
@ -33,10 +34,12 @@ import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
|||
import org.springframework.boot.actuate.endpoint.web.AdditionalPathsMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
|
||||
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
|
||||
import org.springframework.boot.actuate.endpoint.web.PathMapper;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebOperation;
|
||||
import org.springframework.boot.actuate.endpoint.web.WebServerNamespace;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
|
@ -47,6 +50,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for web {@link Endpoint @Endpoint}
|
||||
|
@ -54,6 +59,7 @@ import org.springframework.context.annotation.Configuration;
|
|||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Yongjun Hong
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@AutoConfiguration(after = EndpointAutoConfiguration.class)
|
||||
|
@ -109,7 +115,32 @@ public class WebEndpointAutoConfiguration {
|
|||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public PathMappedEndpoints pathMappedEndpoints(Collection<EndpointsSupplier<?>> endpointSuppliers) {
|
||||
return new PathMappedEndpoints(this.properties.getBasePath(), endpointSuppliers);
|
||||
String basePath = this.properties.getBasePath();
|
||||
PathMappedEndpoints pathMappedEndpoints = new PathMappedEndpoints(basePath, endpointSuppliers);
|
||||
if ((!StringUtils.hasText(basePath) || "/".equals(basePath))
|
||||
&& ManagementPortType.get(this.applicationContext.getEnvironment()) == ManagementPortType.SAME) {
|
||||
assertHasNoRootPaths(pathMappedEndpoints);
|
||||
}
|
||||
return pathMappedEndpoints;
|
||||
}
|
||||
|
||||
private void assertHasNoRootPaths(PathMappedEndpoints endpoints) {
|
||||
for (PathMappedEndpoint endpoint : endpoints) {
|
||||
if (endpoint instanceof ExposableWebEndpoint webEndpoint) {
|
||||
Assert.state(!isMappedToRootPath(webEndpoint),
|
||||
() -> "Management base path and the '" + webEndpoint.getEndpointId()
|
||||
+ "' actuator endpoint are both mapped to '/' "
|
||||
+ "on the server port which will block access to other endpoints. "
|
||||
+ "Please use a different path for management endpoints or map them to a "
|
||||
+ "dedicated management port.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMappedToRootPath(PathMappedEndpoint endpoint) {
|
||||
return endpoint.getRootPath().equals("/")
|
||||
|| endpoint.getAdditionalPaths(WebServerNamespace.SERVER).contains("/");
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -69,7 +69,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @author Yongjun Hong
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ManagementContextConfiguration(proxyBeanMethods = false)
|
||||
|
@ -94,18 +93,6 @@ public class WebMvcEndpointManagementContextConfiguration {
|
|||
allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
|
||||
String basePath = webEndpointProperties.getBasePath();
|
||||
EndpointMapping endpointMapping = new EndpointMapping(basePath);
|
||||
|
||||
if (basePath.isEmpty() && ManagementPortType.get(environment).equals(ManagementPortType.SAME)) {
|
||||
for (ExposableWebEndpoint endpoint : webEndpoints) {
|
||||
if ("/".equals(endpoint.getRootPath())) {
|
||||
throw new IllegalStateException(
|
||||
"Management endpoints and endpoint path are both mapped to '/' on the server port which will "
|
||||
+ "block access to other endpoints. Please use a different path for management endpoints or "
|
||||
+ "map them to a dedicated management port.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean shouldRegisterLinksMapping = shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
|
||||
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes,
|
||||
corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -17,7 +17,6 @@
|
|||
package org.springframework.boot.actuate.autoconfigure.web.server;
|
||||
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextFactory;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
|
@ -45,7 +44,7 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
@AutoConfiguration
|
||||
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
|
||||
@EnableConfigurationProperties({ WebEndpointProperties.class, ManagementServerProperties.class })
|
||||
@EnableConfigurationProperties(ManagementServerProperties.class)
|
||||
public class ManagementContextAutoConfiguration {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -61,7 +61,8 @@ class DiscoveredWebEndpoint extends AbstractDiscoveredEndpoint<WebOperation> imp
|
|||
}
|
||||
|
||||
private Stream<String> getAdditionalPaths(WebServerNamespace webServerNamespace, AdditionalPathsMapper mapper) {
|
||||
return mapper.getAdditionalPaths(getEndpointId(), webServerNamespace).stream();
|
||||
List<String> additionalPaths = mapper.getAdditionalPaths(getEndpointId(), webServerNamespace);
|
||||
return (additionalPaths != null) ? additionalPaths.stream() : Stream.empty();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
package smoketest.actuator;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* Verifies that an exception is thrown when management and server endpoint paths
|
||||
* conflict.
|
||||
*
|
||||
* @author Yongjun Hong
|
||||
*/
|
||||
class ManagementEndpointConflictSmokeTest {
|
||||
|
||||
@Test
|
||||
void shouldThrowExceptionWhenManagementAndServerPathsConflict() {
|
||||
assertThatThrownBy(() -> {
|
||||
SpringApplication.run(SampleActuatorApplication.class, "--management.endpoints.web.base-path=/",
|
||||
"--management.endpoints.web.path-mapping.health=/");
|
||||
}).isInstanceOf(BeanCreationException.class)
|
||||
.hasMessageContaining("Management endpoints and endpoint path are both mapped to '/'");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package smoketest.actuator;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Verifies that an exception is thrown when management and server endpoint paths
|
||||
* conflict.
|
||||
*
|
||||
* @author Yongjun Hong
|
||||
*/
|
||||
class ManagementEndpointConflictSmokeTests {
|
||||
|
||||
@Test
|
||||
void shouldThrowExceptionWhenManagementAndServerPathsConflict() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> SpringApplication.run(SampleActuatorApplication.class,
|
||||
"--management.endpoints.web.base-path=/", "--management.endpoints.web.path-mapping.health=/"))
|
||||
.withMessageContaining("Management base path and the 'health' actuator endpoint are both mapped to '/'");
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue