diff --git a/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/reactive/EndpointRequest.java b/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/reactive/EndpointRequest.java index 6cf0f6b9027..db6915eda0a 100644 --- a/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/reactive/EndpointRequest.java +++ b/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/reactive/EndpointRequest.java @@ -237,6 +237,13 @@ public final class EndpointRequest { return (applicationContext != null) ? applicationContext.getParent() : null; } + protected final @Nullable String getLinksPath(String basePath) { + if (StringUtils.hasText(basePath)) { + return basePath; + } + return (this.managementPortType == ManagementPortType.DIFFERENT) ? "/" : null; + } + protected final String toString(List endpoints, String emptyValue) { return (!endpoints.isEmpty()) ? endpoints.stream() .map(this::getEndpointId) @@ -335,7 +342,8 @@ public final class EndpointRequest { streamPaths(this.includes, endpoints).forEach(paths::add); streamPaths(this.excludes, endpoints).forEach(paths::remove); List delegateMatchers = getDelegateMatchers(paths, this.httpMethod); - if (this.includeLinks && StringUtils.hasText(endpoints.getBasePath())) { + String linksPath = getLinksPath(endpoints.getBasePath()); + if (this.includeLinks && linksPath != null) { delegateMatchers.add(new LinksServerWebExchangeMatcher()); } if (delegateMatchers.isEmpty()) { @@ -371,10 +379,14 @@ public final class EndpointRequest { @Override protected ServerWebExchangeMatcher createDelegate(WebEndpointProperties properties) { - if (StringUtils.hasText(properties.getBasePath())) { - return new OrServerWebExchangeMatcher( - new PathPatternParserServerWebExchangeMatcher(properties.getBasePath()), - new PathPatternParserServerWebExchangeMatcher(properties.getBasePath() + "/")); + String linksPath = getLinksPath(properties.getBasePath()); + if (linksPath != null) { + List linksMatchers = new ArrayList<>(); + linksMatchers.add(new PathPatternParserServerWebExchangeMatcher(linksPath)); + if (!linksPath.endsWith("/")) { + linksMatchers.add(new PathPatternParserServerWebExchangeMatcher(linksPath + "/")); + } + return new OrServerWebExchangeMatcher(linksMatchers); } return EMPTY_MATCHER; } diff --git a/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/servlet/EndpointRequest.java b/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/servlet/EndpointRequest.java index b78109e3598..63beca12202 100644 --- a/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/servlet/EndpointRequest.java +++ b/module/spring-boot-security/src/main/java/org/springframework/boot/security/autoconfigure/actuate/web/servlet/EndpointRequest.java @@ -224,13 +224,27 @@ public final class EndpointRequest { } protected List getLinksMatchers(RequestMatcherFactory requestMatcherFactory, - RequestMatcherProvider matcherProvider, String basePath) { + RequestMatcherProvider matcherProvider, String linksPath) { List linksMatchers = new ArrayList<>(); - linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, null, basePath)); - linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, null, basePath, "/")); + linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, null, linksPath)); + if (!linksPath.endsWith("/")) { + linksMatchers.add(requestMatcherFactory.antPath(matcherProvider, null, linksPath, "/")); + } return linksMatchers; } + protected @Nullable String getLinksPath(WebApplicationContext context, String basePath) { + if (StringUtils.hasText(basePath)) { + return basePath; + } + ManagementPortType managementPortType = this.managementPortType; + if (managementPortType == null) { + managementPortType = ManagementPortType.get(context.getEnvironment()); + this.managementPortType = managementPortType; + } + return (managementPortType == ManagementPortType.DIFFERENT) ? "/" : null; + } + protected RequestMatcherProvider getRequestMatcherProvider(WebApplicationContext context) { try { return context.getBean(RequestMatcherProvider.class); @@ -341,8 +355,9 @@ public final class EndpointRequest { List delegateMatchers = getDelegateMatchers(requestMatcherFactory, matcherProvider, paths, this.httpMethod); String basePath = endpoints.getBasePath(); - if (this.includeLinks && StringUtils.hasText(basePath)) { - delegateMatchers.addAll(getLinksMatchers(requestMatcherFactory, matcherProvider, basePath)); + String linksPath = getLinksPath(context, basePath); + if (this.includeLinks && linksPath != null) { + delegateMatchers.addAll(getLinksMatchers(requestMatcherFactory, matcherProvider, linksPath)); } if (delegateMatchers.isEmpty()) { return EMPTY_MATCHER; @@ -375,10 +390,10 @@ public final class EndpointRequest { protected RequestMatcher createDelegate(WebApplicationContext context, RequestMatcherFactory requestMatcherFactory) { WebEndpointProperties properties = context.getBean(WebEndpointProperties.class); - String basePath = properties.getBasePath(); - if (StringUtils.hasText(basePath)) { + String linksPath = getLinksPath(context, properties.getBasePath()); + if (linksPath != null) { return new OrRequestMatcher( - getLinksMatchers(requestMatcherFactory, getRequestMatcherProvider(context), basePath)); + getLinksMatchers(requestMatcherFactory, getRequestMatcherProvider(context), linksPath)); } return EMPTY_MATCHER; } diff --git a/module/spring-boot-security/src/test/java/org/springframework/boot/security/autoconfigure/actuate/web/reactive/EndpointRequestTests.java b/module/spring-boot-security/src/test/java/org/springframework/boot/security/autoconfigure/actuate/web/reactive/EndpointRequestTests.java index 630913a10ec..5ddc5bb6a68 100644 --- a/module/spring-boot-security/src/test/java/org/springframework/boot/security/autoconfigure/actuate/web/reactive/EndpointRequestTests.java +++ b/module/spring-boot-security/src/test/java/org/springframework/boot/security/autoconfigure/actuate/web/reactive/EndpointRequestTests.java @@ -33,6 +33,7 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; +import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.web.server.WebServer; import org.springframework.boot.web.server.context.WebServerApplicationContext; import org.springframework.context.support.StaticApplicationContext; @@ -93,6 +94,15 @@ class EndpointRequestTests { assertMatcher.matches("/bar"); } + @Test + void toAnyEndpointWhenBasePathIsEmptyAndManagementPortDifferentShouldMatchLinks() { + ServerWebExchangeMatcher matcher = EndpointRequest.toAnyEndpoint(); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), + WebServerNamespace.MANAGEMENT); + assertMatcher.matches("/"); + assertMatcher.matches("/foo"); + } + @Test void toAnyEndpointShouldNotMatchOtherPath() { ServerWebExchangeMatcher matcher = EndpointRequest.toAnyEndpoint(); @@ -145,6 +155,15 @@ class EndpointRequestTests { assertMatcher.doesNotMatch("/"); } + @Test + void toLinksWhenBasePathEmptyAndManagementPortDifferentShouldMatchRoot() { + ServerWebExchangeMatcher matcher = EndpointRequest.toLinks(); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), + WebServerNamespace.MANAGEMENT); + assertMatcher.matches("/"); + assertMatcher.doesNotMatch("/foo"); + } + @Test void excludeByClassShouldNotMatchExcluded() { ServerWebExchangeMatcher matcher = EndpointRequest.toAnyEndpoint() @@ -327,10 +346,14 @@ class EndpointRequestTests { private RequestMatcherAssert assertMatcher(ServerWebExchangeMatcher matcher, @Nullable PathMappedEndpoints pathMappedEndpoints, @Nullable WebServerNamespace namespace) { - StaticApplicationContext context = new StaticApplicationContext(); + StaticWebApplicationContext context; if (namespace != null && !WebServerNamespace.SERVER.equals(namespace)) { - NamedStaticWebApplicationContext parentContext = new NamedStaticWebApplicationContext(namespace); - context.setParent(parentContext); + context = new NamedStaticWebApplicationContext(namespace); + context.setParent(new StaticWebApplicationContext()); + TestPropertyValues.of("management.server.port=0").applyTo(context); + } + else { + context = new StaticWebApplicationContext(); } context.registerBean(WebEndpointProperties.class); if (pathMappedEndpoints != null) { diff --git a/module/spring-boot-security/src/test/java/org/springframework/boot/security/autoconfigure/actuate/web/servlet/EndpointRequestTests.java b/module/spring-boot-security/src/test/java/org/springframework/boot/security/autoconfigure/actuate/web/servlet/EndpointRequestTests.java index dade805d37f..06b2c2abe94 100644 --- a/module/spring-boot-security/src/test/java/org/springframework/boot/security/autoconfigure/actuate/web/servlet/EndpointRequestTests.java +++ b/module/spring-boot-security/src/test/java/org/springframework/boot/security/autoconfigure/actuate/web/servlet/EndpointRequestTests.java @@ -35,6 +35,7 @@ import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.actuate.endpoint.web.WebServerNamespace; import org.springframework.boot.security.autoconfigure.actuate.web.servlet.EndpointRequest.AdditionalPathsEndpointRequestMatcher; import org.springframework.boot.security.autoconfigure.actuate.web.servlet.EndpointRequest.EndpointRequestMatcher; +import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.web.server.WebServer; import org.springframework.boot.web.server.context.WebServerApplicationContext; import org.springframework.http.HttpMethod; @@ -92,6 +93,15 @@ class EndpointRequestTests { assertMatcher.matches("/bar"); } + @Test + void toAnyEndpointWhenBasePathIsEmptyAndManagementPortDifferentShouldMatchLinks() { + RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), null, + WebServerNamespace.MANAGEMENT); + assertMatcher.matches("/"); + assertMatcher.matches("/foo"); + } + @Test void toAnyEndpointShouldNotMatchOtherPath() { RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); @@ -151,6 +161,15 @@ class EndpointRequestTests { assertMatcher.doesNotMatch("/"); } + @Test + void toLinksWhenBasePathEmptyAndManagementPortDifferentShouldMatchRoot() { + RequestMatcher matcher = EndpointRequest.toLinks(); + RequestMatcherAssert assertMatcher = assertMatcher(matcher, mockPathMappedEndpoints(""), null, + WebServerNamespace.MANAGEMENT); + assertMatcher.matches("/"); + assertMatcher.doesNotMatch("/foo"); + } + @Test void excludeByClassShouldNotMatchExcluded() { RequestMatcher matcher = EndpointRequest.toAnyEndpoint().excluding(FooEndpoint.class, BazServletEndpoint.class); @@ -353,10 +372,14 @@ class EndpointRequestTests { private RequestMatcherAssert assertMatcher(RequestMatcher matcher, @Nullable PathMappedEndpoints pathMappedEndpoints, @Nullable RequestMatcherProvider matcherProvider, @Nullable WebServerNamespace namespace) { - StaticWebApplicationContext context = new StaticWebApplicationContext(); + StaticWebApplicationContext context; if (namespace != null && !WebServerNamespace.SERVER.equals(namespace)) { - NamedStaticWebApplicationContext parentContext = new NamedStaticWebApplicationContext(namespace); - context.setParent(parentContext); + context = new NamedStaticWebApplicationContext(namespace); + context.setParent(new StaticWebApplicationContext()); + TestPropertyValues.of("management.server.port=0").applyTo(context); + } + else { + context = new StaticWebApplicationContext(); } context.registerBean(WebEndpointProperties.class); if (pathMappedEndpoints != null) { diff --git a/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/AbstractSampleActuatorCustomSecurityTests.java b/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/AbstractSampleActuatorCustomSecurityTests.java index 770534b42a8..6d66018bcb6 100644 --- a/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/AbstractSampleActuatorCustomSecurityTests.java +++ b/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/AbstractSampleActuatorCustomSecurityTests.java @@ -37,7 +37,7 @@ abstract class AbstractSampleActuatorCustomSecurityTests { abstract String getPath(); - abstract String getManagementPath(); + abstract String getActuatorPath(); abstract ApplicationContext getApplicationContext(); @@ -58,119 +58,110 @@ abstract class AbstractSampleActuatorCustomSecurityTests { @Test void actuatorInsecureEndpoint() { - ResponseEntity entity = restTemplate().getForEntity(getManagementPath() + "/actuator/health", - String.class); + ResponseEntity entity = restTemplate().getForEntity(getActuatorPath() + "/health", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).contains("\"status\":\"UP\""); - entity = restTemplate().getForEntity(getManagementPath() + "/actuator/health/diskSpace", String.class); + entity = restTemplate().getForEntity(getActuatorPath() + "/health/diskSpace", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).contains("\"status\":\"UP\""); } @Test void actuatorLinksWithAnonymous() { - ResponseEntity entity = restTemplate().getForEntity(getManagementPath() + "/actuator", Object.class); + ResponseEntity entity = restTemplate().getForEntity(getActuatorPath(), Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); - entity = restTemplate().getForEntity(getManagementPath() + "/actuator/", Object.class); + entity = restTemplate().getForEntity(getActuatorPath() + "/", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); } @Test void actuatorLinksWithUnauthorizedUser() { - ResponseEntity entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator", - Object.class); + ResponseEntity entity = userRestTemplate().getForEntity(getActuatorPath(), Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); - entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/", Object.class); + entity = userRestTemplate().getForEntity(getActuatorPath() + "/", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); } @Test void actuatorLinksWithAuthorizedUser() { - ResponseEntity entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator", - Object.class); + ResponseEntity entity = adminRestTemplate().getForEntity(getActuatorPath(), Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); - adminRestTemplate().getForEntity(getManagementPath() + "/actuator/", Object.class); + adminRestTemplate().getForEntity(getActuatorPath() + "/", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @Test void actuatorSecureEndpointWithAnonymous() { - ResponseEntity entity = restTemplate().getForEntity(getManagementPath() + "/actuator/env", - Object.class); + ResponseEntity entity = restTemplate().getForEntity(getActuatorPath() + "/env", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); - entity = restTemplate().getForEntity( - getManagementPath() + "/actuator/env/management.endpoints.web.exposure.include", Object.class); + entity = restTemplate().getForEntity(getActuatorPath() + "/env/management.endpoints.web.exposure.include", + Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); } @Test void actuatorSecureEndpointWithUnauthorizedUser() { - ResponseEntity entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/env", - Object.class); + ResponseEntity entity = userRestTemplate().getForEntity(getActuatorPath() + "/env", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); - entity = userRestTemplate().getForEntity( - getManagementPath() + "/actuator/env/management.endpoints.web.exposure.include", Object.class); + entity = userRestTemplate().getForEntity(getActuatorPath() + "/env/management.endpoints.web.exposure.include", + Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); } @Test void actuatorSecureEndpointWithAuthorizedUser() { - ResponseEntity entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/env", - Object.class); + ResponseEntity entity = adminRestTemplate().getForEntity(getActuatorPath() + "/env", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); - entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/env/", Object.class); + entity = adminRestTemplate().getForEntity(getActuatorPath() + "/env/", Object.class); // EndpointRequest matches the trailing slash but MVC doesn't assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); - entity = adminRestTemplate().getForEntity( - getManagementPath() + "/actuator/env/management.endpoints.web.exposure.include", Object.class); + entity = adminRestTemplate().getForEntity(getActuatorPath() + "/env/management.endpoints.web.exposure.include", + Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @Test void secureServletEndpointWithAnonymous() { - ResponseEntity entity = restTemplate().getForEntity(getManagementPath() + "/actuator/se1", - String.class); + ResponseEntity entity = restTemplate().getForEntity(getActuatorPath() + "/se1", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); - entity = restTemplate().getForEntity(getManagementPath() + "/actuator/se1/list", String.class); + entity = restTemplate().getForEntity(getActuatorPath() + "/se1/list", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); } @Test void secureServletEndpointWithUnauthorizedUser() { - ResponseEntity entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/se1", - String.class); + ResponseEntity entity = userRestTemplate().getForEntity(getActuatorPath() + "/se1", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); - entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/se1/list", String.class); + entity = userRestTemplate().getForEntity(getActuatorPath() + "/se1/list", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); } @Test void secureServletEndpointWithAuthorizedUser() { - ResponseEntity entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/se1", - String.class); + ResponseEntity entity = adminRestTemplate().getForEntity(getActuatorPath() + "/se1", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); - entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/se1/list", String.class); + entity = adminRestTemplate().getForEntity(getActuatorPath() + "/se1/list", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } @Test void actuatorCustomMvcSecureEndpointWithAnonymous() { - ResponseEntity entity = restTemplate() - .getForEntity(getManagementPath() + "/actuator/example/echo?text={t}", String.class, "test"); + ResponseEntity entity = restTemplate().getForEntity(getActuatorPath() + "/example/echo?text={t}", + String.class, "test"); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); } @Test void actuatorCustomMvcSecureEndpointWithUnauthorizedUser() { - ResponseEntity entity = userRestTemplate() - .getForEntity(getManagementPath() + "/actuator/example/echo?text={t}", String.class, "test"); + ResponseEntity entity = userRestTemplate().getForEntity(getActuatorPath() + "/example/echo?text={t}", + String.class, "test"); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); } @Test void actuatorCustomMvcSecureEndpointWithAuthorizedUser() { - ResponseEntity entity = adminRestTemplate() - .getForEntity(getManagementPath() + "/actuator/example/echo?text={t}", String.class, "test"); + ResponseEntity entity = adminRestTemplate().getForEntity(getActuatorPath() + "/example/echo?text={t}", + String.class, "test"); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).isEqualTo("test"); assertThat(entity.getHeaders().getFirst("echo")).isEqualTo("test"); @@ -178,8 +169,7 @@ abstract class AbstractSampleActuatorCustomSecurityTests { @Test void actuatorExcludedFromEndpointRequestMatcher() { - ResponseEntity entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/mappings", - Object.class); + ResponseEntity entity = userRestTemplate().getForEntity(getActuatorPath() + "/mappings", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); } diff --git a/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/CustomServletPathSampleActuatorTests.java b/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/CustomServletPathSampleActuatorTests.java index d8324c8a48f..3d186ca7ab8 100644 --- a/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/CustomServletPathSampleActuatorTests.java +++ b/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/CustomServletPathSampleActuatorTests.java @@ -42,8 +42,8 @@ class CustomServletPathSampleActuatorTests extends AbstractSampleActuatorCustomS } @Override - String getManagementPath() { - return "http://localhost:" + this.port + "/example"; + String getActuatorPath() { + return "http://localhost:" + this.port + "/example/actuator"; } @Override diff --git a/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomBasePathAndWebEndpointsBasePathSampleActuatorApplicationTests.java b/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomBasePathAndWebEndpointsBasePathSampleActuatorApplicationTests.java new file mode 100644 index 00000000000..bb118f9fe39 --- /dev/null +++ b/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomBasePathAndWebEndpointsBasePathSampleActuatorApplicationTests.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012-present 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.customsecurity; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.resttestclient.TestRestTemplate; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalManagementPort; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.ApplicationContext; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for a separate management server with custom base path and web + * endpoints base path. + * + * @author Dave Syer + * @author Madhura Bhave + */ +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "management.server.port=0", + "management.server.base-path=/management", "management.endpoints.web.base-path=/" }) +class ManagementServerWithCustomBasePathAndWebEndpointsBasePathSampleActuatorApplicationTests + extends AbstractSampleActuatorCustomSecurityTests { + + @LocalServerPort + private int port; + + @LocalManagementPort + private int managementPort; + + @Autowired + private ApplicationContext applicationContext; + + @Test + void testMissing() { + ResponseEntity entity = new TestRestTemplate("admin", "admin") + .getForEntity(getActuatorPath() + "/missing", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + assertThat(entity.getBody()).contains("\"status\":404"); + } + + @Override + String getPath() { + return "http://localhost:" + this.port; + } + + @Override + String getActuatorPath() { + return "http://localhost:" + this.managementPort + "/management"; + } + + @Override + ApplicationContext getApplicationContext() { + return this.applicationContext; + } + +} diff --git a/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementPortAndPathSampleActuatorApplicationTests.java b/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomBasePathSampleActuatorApplicationTests.java similarity index 85% rename from smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementPortAndPathSampleActuatorApplicationTests.java rename to smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomBasePathSampleActuatorApplicationTests.java index 00098f6b06e..20e1fdf2888 100644 --- a/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementPortAndPathSampleActuatorApplicationTests.java +++ b/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomBasePathSampleActuatorApplicationTests.java @@ -31,15 +31,15 @@ import org.springframework.http.ResponseEntity; import static org.assertj.core.api.Assertions.assertThat; /** - * Integration tests for separate management and main service ports with custom management - * context path. + * Integration tests for a separate management server with a custom base path. * * @author Dave Syer * @author Madhura Bhave */ @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "management.server.port=0", "management.server.base-path=/management" }) -class ManagementPortAndPathSampleActuatorApplicationTests extends AbstractSampleActuatorCustomSecurityTests { +class ManagementServerWithCustomBasePathSampleActuatorApplicationTests + extends AbstractSampleActuatorCustomSecurityTests { @LocalServerPort private int port; @@ -53,7 +53,7 @@ class ManagementPortAndPathSampleActuatorApplicationTests extends AbstractSample @Test void testMissing() { ResponseEntity entity = new TestRestTemplate("admin", "admin") - .getForEntity("http://localhost:" + this.managementPort + "/management/actuator/missing", String.class); + .getForEntity(getActuatorPath() + "/missing", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); assertThat(entity.getBody()).contains("\"status\":404"); } @@ -64,8 +64,8 @@ class ManagementPortAndPathSampleActuatorApplicationTests extends AbstractSample } @Override - String getManagementPath() { - return "http://localhost:" + this.managementPort + "/management"; + String getActuatorPath() { + return "http://localhost:" + this.managementPort + "/management/actuator"; } @Override diff --git a/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementPortCustomServletPathSampleActuatorTests.java b/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomServletPathSampleActuatorTests.java similarity index 87% rename from smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementPortCustomServletPathSampleActuatorTests.java rename to smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomServletPathSampleActuatorTests.java index df1fde29876..9a2192d5557 100644 --- a/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementPortCustomServletPathSampleActuatorTests.java +++ b/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/ManagementServerWithCustomServletPathSampleActuatorTests.java @@ -30,14 +30,14 @@ import org.springframework.http.ResponseEntity; import static org.assertj.core.api.Assertions.assertThat; /** - * Integration tests for separate management and main service ports with custom dispatcher - * servlet path. + * Integration tests for a separate management server with a custom dispatcher servlet + * path. * * @author Madhura Bhave */ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { "management.server.port=0", "spring.mvc.servlet.path=/example" }) -class ManagementPortCustomServletPathSampleActuatorTests extends AbstractSampleActuatorCustomSecurityTests { +class ManagementServerWithCustomServletPathSampleActuatorTests extends AbstractSampleActuatorCustomSecurityTests { @LocalServerPort private int port; @@ -61,8 +61,8 @@ class ManagementPortCustomServletPathSampleActuatorTests extends AbstractSampleA } @Override - String getManagementPath() { - return "http://localhost:" + this.managementPort; + String getActuatorPath() { + return "http://localhost:" + this.managementPort + "/actuator"; } @Override diff --git a/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/SampleActuatorCustomSecurityApplicationTests.java b/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/SampleActuatorCustomSecurityApplicationTests.java index 3ea64559524..63bf2a83cee 100644 --- a/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/SampleActuatorCustomSecurityApplicationTests.java +++ b/smoke-test/spring-boot-smoke-test-actuator-custom-security/src/test/java/smoketest/actuator/customsecurity/SampleActuatorCustomSecurityApplicationTests.java @@ -58,10 +58,9 @@ class SampleActuatorCustomSecurityApplicationTests extends AbstractSampleActuato @Test void mvcMatchersCanBeUsedToSecureActuators() { - ResponseEntity entity = beansRestTemplate().getForEntity(getManagementPath() + "/actuator/beans", - Object.class); + ResponseEntity entity = beansRestTemplate().getForEntity(getActuatorPath() + "/beans", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); - entity = beansRestTemplate().getForEntity(getManagementPath() + "/actuator/beans/", Object.class); + entity = beansRestTemplate().getForEntity(getActuatorPath() + "/beans/", Object.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN); } @@ -71,8 +70,8 @@ class SampleActuatorCustomSecurityApplicationTests extends AbstractSampleActuato } @Override - String getManagementPath() { - return "http://localhost:" + this.port; + String getActuatorPath() { + return "http://localhost:" + this.port + "/actuator"; } @Override