From 0fd873f0f9db1d79c30695112a487659d87425a2 Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Tue, 18 Sep 2018 21:57:15 -0700 Subject: [PATCH 1/2] Servlet path not explicitly required for EndpointRequest --- .../security/servlet/EndpointRequest.java | 21 +- ...stractEndpointRequestIntegrationTests.java | 183 ++++++++++++++++++ ...JerseyEndpointRequestIntegrationTests.java | 117 +++++++++++ .../MvcEndpointRequestIntegrationTests.java | 132 +++++++++++++ 4 files changed, 434 insertions(+), 19 deletions(-) create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/AbstractEndpointRequestIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/JerseyEndpointRequestIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/MvcEndpointRequestIntegrationTests.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java index 625ec9f7c4..92db210eaf 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java @@ -35,7 +35,6 @@ import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.autoconfigure.security.servlet.RequestMatcherProvider; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -139,23 +138,13 @@ public final class EndpointRequest { private RequestMatcher createDelegate(WebApplicationContext context) { try { - String pathPrefix = getPathPrefix(context); - return createDelegate(context, new RequestMatcherFactory(pathPrefix)); + return createDelegate(context, new RequestMatcherFactory()); } catch (NoSuchBeanDefinitionException ex) { return EMPTY_MATCHER; } } - private String getPathPrefix(WebApplicationContext context) { - try { - return context.getBean(DispatcherServletPath.class).getPrefix(); - } - catch (NoSuchBeanDefinitionException ex) { - return ""; - } - } - protected abstract RequestMatcher createDelegate(WebApplicationContext context, RequestMatcherFactory requestMatcherFactory); @@ -313,15 +302,9 @@ public final class EndpointRequest { */ private static class RequestMatcherFactory { - private final String prefix; - - RequestMatcherFactory(String prefix) { - this.prefix = prefix; - } - public RequestMatcher antPath(RequestMatcherProvider matcherProvider, String... parts) { - StringBuilder pattern = new StringBuilder(this.prefix); + StringBuilder pattern = new StringBuilder(); for (String part : parts) { pattern.append(part); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/AbstractEndpointRequestIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/AbstractEndpointRequestIntegrationTests.java new file mode 100644 index 0000000000..57f4234eef --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/AbstractEndpointRequestIntegrationTests.java @@ -0,0 +1,183 @@ +/* + * Copyright 2012-2018 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 + * + * http://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 org.springframework.boot.actuate.autoconfigure.security.servlet; + +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; + +import org.junit.Test; + +import org.springframework.boot.actuate.endpoint.ExposableEndpoint; +import org.springframework.boot.actuate.endpoint.Operation; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoint; +import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; +import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.test.web.reactive.server.WebTestClient; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Abstract base class for {@link EndpointRequest} tests. + * + * @author Madhura Bhave + */ +public abstract class AbstractEndpointRequestIntegrationTests { + + protected abstract WebApplicationContextRunner getContextRunner(); + + @Test + public void toEndpointShouldMatch() { + getContextRunner().run((context) -> { + WebTestClient webTestClient = getWebTestClient(context); + webTestClient.get().uri("/actuator/e1").exchange().expectStatus().isOk(); + }); + } + + @Test + public void toAllEndpointsShouldMatch() { + getContextRunner().withPropertyValues("spring.security.user.password=password") + .run((context) -> { + WebTestClient webTestClient = getWebTestClient(context); + webTestClient.get().uri("/actuator/e2").exchange().expectStatus() + .isUnauthorized(); + webTestClient.get().uri("/actuator/e2") + .header("Authorization", getBasicAuth()).exchange() + .expectStatus().isOk(); + }); + } + + @Test + public void toLinksShouldMatch() { + getContextRunner().run((context) -> { + WebTestClient webTestClient = getWebTestClient(context); + webTestClient.get().uri("/actuator").exchange().expectStatus().isOk(); + webTestClient.get().uri("/actuator/").exchange().expectStatus().isOk(); + }); + } + + protected WebTestClient getWebTestClient(AssertableWebApplicationContext context) { + int port = context + .getSourceApplicationContext( + AnnotationConfigServletWebServerApplicationContext.class) + .getWebServer().getPort(); + return WebTestClient.bindToServer().baseUrl("http://localhost:" + port).build(); + } + + String getBasicAuth() { + return "Basic " + Base64.getEncoder().encodeToString("user:password".getBytes()); + } + + static class BaseConfiguration { + + @Bean + public TestEndpoint1 endpoint1() { + return new TestEndpoint1(); + } + + @Bean + public TestEndpoint2 endpoint2() { + return new TestEndpoint2(); + } + + @Bean + public TestEndpoint3 endpoint3() { + return new TestEndpoint3(); + } + + @Bean + public PathMappedEndpoints pathMappedEndpoints() { + List> endpoints = new ArrayList<>(); + endpoints.add(mockEndpoint("e1")); + endpoints.add(mockEndpoint("e2")); + endpoints.add(mockEndpoint("e3")); + return new PathMappedEndpoints("/actuator", () -> endpoints); + } + + private TestPathMappedEndpoint mockEndpoint(String id) { + TestPathMappedEndpoint endpoint = mock(TestPathMappedEndpoint.class); + given(endpoint.getId()).willReturn(id); + given(endpoint.getRootPath()).willReturn(id); + return endpoint; + } + + } + + @Endpoint(id = "e1") + static class TestEndpoint1 { + + @ReadOperation + public Object getAll() { + return "endpoint 1"; + } + + } + + @Endpoint(id = "e2") + static class TestEndpoint2 { + + @ReadOperation + public Object getAll() { + return "endpoint 2"; + } + + } + + @Endpoint(id = "e3") + static class TestEndpoint3 { + + @ReadOperation + public Object getAll() { + return null; + } + + } + + interface TestPathMappedEndpoint + extends ExposableEndpoint, PathMappedEndpoint { + + } + + @Configuration + static class SecurityConfiguration { + + @Bean + public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter() { + return new WebSecurityConfigurerAdapter() { + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().requestMatchers(EndpointRequest.toLinks()) + .permitAll() + .requestMatchers(EndpointRequest.to(TestEndpoint1.class)) + .permitAll().requestMatchers(EndpointRequest.toAnyEndpoint()) + .authenticated().anyRequest().hasRole("ADMIN").and() + .httpBasic(); + } + }; + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/JerseyEndpointRequestIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/JerseyEndpointRequestIntegrationTests.java new file mode 100644 index 0000000000..9ddd2330fe --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/JerseyEndpointRequestIntegrationTests.java @@ -0,0 +1,117 @@ +/* + * Copyright 2012-2018 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 + * + * http://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 org.springframework.boot.actuate.autoconfigure.security.servlet; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.server.model.Resource; + +import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; +import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType; +import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper; +import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; +import org.springframework.boot.actuate.endpoint.web.EndpointMapping; +import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; +import org.springframework.boot.actuate.endpoint.web.PathMapper; +import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.web.jersey.JerseyEndpointResourceFactory; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration; +import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Integration tests for {@link EndpointRequest} with Jersey. + * + * @author Madhura Bhave + */ +public class JerseyEndpointRequestIntegrationTests + extends AbstractEndpointRequestIntegrationTests { + + @Override + protected WebApplicationContextRunner getContextRunner() { + return new WebApplicationContextRunner( + AnnotationConfigServletWebServerApplicationContext::new) + .withUserConfiguration(JerseyEndpointConfiguration.class, + SecurityConfiguration.class, BaseConfiguration.class) + .withConfiguration(AutoConfigurations.of( + SecurityAutoConfiguration.class, + UserDetailsServiceAutoConfiguration.class, + SecurityRequestMatcherProviderAutoConfiguration.class, + JacksonAutoConfiguration.class, + JerseyAutoConfiguration.class)); + } + + @Configuration + @EnableConfigurationProperties(WebEndpointProperties.class) + static class JerseyEndpointConfiguration { + + private final ApplicationContext applicationContext; + + JerseyEndpointConfiguration(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Bean + public TomcatServletWebServerFactory tomcat() { + return new TomcatServletWebServerFactory(0); + } + + @Bean + public ResourceConfig resourceConfig() { + return new ResourceConfig(); + } + + @Bean + public ResourceConfigCustomizer webEndpointRegistrar() { + return this::customize; + } + + private void customize(ResourceConfig config) { + List mediaTypes = Arrays.asList( + javax.ws.rs.core.MediaType.APPLICATION_JSON, + ActuatorMediaType.V2_JSON); + EndpointMediaTypes endpointMediaTypes = new EndpointMediaTypes(mediaTypes, + mediaTypes); + WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer( + this.applicationContext, new ConversionServiceParameterValueMapper(), + endpointMediaTypes, PathMapper.useEndpointId(), + Collections.emptyList(), Collections.emptyList()); + Collection resources = new JerseyEndpointResourceFactory() + .createEndpointResources(new EndpointMapping("/actuator"), + discoverer.getEndpoints(), endpointMediaTypes, + new EndpointLinksResolver(discoverer.getEndpoints())); + config.registerResources(new HashSet<>(resources)); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/MvcEndpointRequestIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/MvcEndpointRequestIntegrationTests.java new file mode 100644 index 0000000000..c8856350c5 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/MvcEndpointRequestIntegrationTests.java @@ -0,0 +1,132 @@ +/* + * Copyright 2012-2018 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 + * + * http://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 org.springframework.boot.actuate.autoconfigure.security.servlet; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties; +import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType; +import org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper; +import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver; +import org.springframework.boot.actuate.endpoint.web.EndpointMapping; +import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes; +import org.springframework.boot.actuate.endpoint.web.PathMapper; +import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer; +import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; +import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration; +import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.cors.CorsConfiguration; + +/** + * Integration tests for {@link EndpointRequest} with Spring MVC. + * + * @author Madhura Bhave + */ +public class MvcEndpointRequestIntegrationTests + extends AbstractEndpointRequestIntegrationTests { + + @Test + public void toEndpointWhenServletPathSetShouldMatch() { + getContextRunner().withPropertyValues("server.servlet.path=/admin") + .run((context) -> { + WebTestClient webTestClient = getWebTestClient(context); + webTestClient.get().uri("/admin/actuator/e1").exchange() + .expectStatus().isOk(); + }); + } + + @Test + public void toAnyEndpointWhenServletPathSetShouldMatch() { + getContextRunner().withPropertyValues("server.servlet.path=/admin", + "spring.security.user.password=password").run((context) -> { + WebTestClient webTestClient = getWebTestClient(context); + webTestClient.get().uri("/admin/actuator/e2").exchange() + .expectStatus().isUnauthorized(); + webTestClient.get().uri("/admin/actuator/e2") + .header("Authorization", getBasicAuth()).exchange() + .expectStatus().isOk(); + }); + } + + @Override + protected WebApplicationContextRunner getContextRunner() { + return new WebApplicationContextRunner( + AnnotationConfigServletWebServerApplicationContext::new) + .withUserConfiguration(WebMvcEndpointConfiguration.class, + SecurityConfiguration.class, BaseConfiguration.class) + .withConfiguration(AutoConfigurations.of( + SecurityAutoConfiguration.class, + UserDetailsServiceAutoConfiguration.class, + WebMvcAutoConfiguration.class, + SecurityRequestMatcherProviderAutoConfiguration.class, + JacksonAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + DispatcherServletAutoConfiguration.class)); + } + + @Configuration + @EnableConfigurationProperties(WebEndpointProperties.class) + static class WebMvcEndpointConfiguration { + + private final ApplicationContext applicationContext; + + WebMvcEndpointConfiguration(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Bean + public TomcatServletWebServerFactory tomcat() { + return new TomcatServletWebServerFactory(0); + } + + @Bean + public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping() { + List mediaTypes = Arrays.asList(MediaType.APPLICATION_JSON_VALUE, + ActuatorMediaType.V2_JSON); + EndpointMediaTypes endpointMediaTypes = new EndpointMediaTypes(mediaTypes, + mediaTypes); + WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer( + this.applicationContext, new ConversionServiceParameterValueMapper(), + endpointMediaTypes, PathMapper.useEndpointId(), + Collections.emptyList(), Collections.emptyList()); + return new WebMvcEndpointHandlerMapping(new EndpointMapping("/actuator"), + discoverer.getEndpoints(), endpointMediaTypes, + new CorsConfiguration(), + new EndpointLinksResolver(discoverer.getEndpoints())); + } + + } + +} From d4cad5e916c3fa0e10fa1469583d20ba323b8615 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 16 Oct 2018 12:21:37 +0100 Subject: [PATCH 2/2] Polish "Servlet path not explicitly required for EndpointRequest" Closes gh-14503 --- ...stractEndpointRequestIntegrationTests.java | 3 +- .../servlet/EndpointRequestTests.java | 80 ++++--------------- .../MvcEndpointRequestIntegrationTests.java | 12 +++ 3 files changed, 31 insertions(+), 64 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/AbstractEndpointRequestIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/AbstractEndpointRequestIntegrationTests.java index 57f4234eef..d468fe63a5 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/AbstractEndpointRequestIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/AbstractEndpointRequestIntegrationTests.java @@ -21,6 +21,7 @@ import java.util.List; import org.junit.Test; +import org.springframework.boot.actuate.endpoint.EndpointId; import org.springframework.boot.actuate.endpoint.ExposableEndpoint; import org.springframework.boot.actuate.endpoint.Operation; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; @@ -118,7 +119,7 @@ public abstract class AbstractEndpointRequestIntegrationTests { private TestPathMappedEndpoint mockEndpoint(String id) { TestPathMappedEndpoint endpoint = mock(TestPathMappedEndpoint.class); - given(endpoint.getId()).willReturn(id); + given(endpoint.getEndpointId()).willReturn(EndpointId.of(id)); given(endpoint.getRootPath()).willReturn(id); return endpoint; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java index 3ea45365ad..56ecc0ad9e 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequestTests.java @@ -33,7 +33,6 @@ import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoint; import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpoint; import org.springframework.boot.autoconfigure.security.servlet.RequestMatcherProvider; -import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockServletContext; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -55,19 +54,19 @@ public class EndpointRequestTests { @Test public void toAnyEndpointShouldMatchEndpointPath() { RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); - assertMatcher(matcher, "/actuator", "/").matches("/actuator/foo"); - assertMatcher(matcher, "/actuator", "/").matches("/actuator/foo/zoo/"); - assertMatcher(matcher, "/actuator", "/").matches("/actuator/bar"); - assertMatcher(matcher, "/actuator", "/").matches("/actuator/bar/baz"); - assertMatcher(matcher, "/actuator", "/").matches("/actuator"); + assertMatcher(matcher, "/actuator").matches("/actuator/foo"); + assertMatcher(matcher, "/actuator").matches("/actuator/foo/zoo/"); + assertMatcher(matcher, "/actuator").matches("/actuator/bar"); + assertMatcher(matcher, "/actuator").matches("/actuator/bar/baz"); + assertMatcher(matcher, "/actuator").matches("/actuator"); } @Test public void toAnyEndpointShouldMatchEndpointPathWithTrailingSlash() { RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); - assertMatcher(matcher, "/actuator", "/").matches("/actuator/foo/"); - assertMatcher(matcher, "/actuator", "/").matches("/actuator/bar/"); - assertMatcher(matcher, "/actuator", "/").matches("/actuator/"); + assertMatcher(matcher, "/actuator").matches("/actuator/foo/"); + assertMatcher(matcher, "/actuator").matches("/actuator/bar/"); + assertMatcher(matcher, "/actuator").matches("/actuator/"); } @Test @@ -85,26 +84,13 @@ public class EndpointRequestTests { assertMatcher(matcher).doesNotMatch("/actuator/baz"); } - @Test - public void toAnyEndpointWhenServletPathNotEmptyShouldMatch() { - RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); - assertMatcher(matcher, "/actuator", "/spring").matches("/spring", - "/actuator/foo"); - assertMatcher(matcher, "/actuator", "/spring").matches("/spring", - "/actuator/bar"); - assertMatcher(matcher, "/actuator", "/spring").matches("/spring", "/actuator"); - assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("/spring", - "/actuator/baz"); - assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("", "/actuator/foo"); - } - @Test public void toAnyEndpointWhenDispatcherServletPathProviderNotAvailableUsesEmptyPath() { RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); - assertMatcher(matcher, "/actuator", null).matches("/actuator/foo"); - assertMatcher(matcher, "/actuator", null).matches("/actuator/bar"); - assertMatcher(matcher, "/actuator", null).matches("/actuator"); - assertMatcher(matcher, "/actuator", null).doesNotMatch("/actuator/baz"); + assertMatcher(matcher, "/actuator").matches("/actuator/foo"); + assertMatcher(matcher, "/actuator").matches("/actuator/bar"); + assertMatcher(matcher, "/actuator").matches("/actuator"); + assertMatcher(matcher, "/actuator").doesNotMatch("/actuator/baz"); } @Test @@ -151,14 +137,6 @@ public class EndpointRequestTests { assertMatcher.doesNotMatch("/"); } - @Test - public void toLinksWhenServletPathNotEmptyShouldMatch() { - RequestMatcher matcher = EndpointRequest.toLinks(); - RequestMatcherAssert assertMatcher = assertMatcher(matcher, "/actuator", - "/spring"); - assertMatcher.matches("/spring/actuator"); - } - @Test public void excludeByClassShouldNotMatchExcluded() { RequestMatcher matcher = EndpointRequest.toAnyEndpoint() @@ -221,7 +199,7 @@ public class EndpointRequestTests { RequestMatcher matcher = EndpointRequest.toAnyEndpoint(); RequestMatcher mockRequestMatcher = (request) -> false; RequestMatcherAssert assertMatcher = assertMatcher(matcher, - mockPathMappedEndpoints(""), "", (pattern) -> mockRequestMatcher); + mockPathMappedEndpoints(""), (pattern) -> mockRequestMatcher); assertMatcher.doesNotMatch("/foo"); assertMatcher.doesNotMatch("/bar"); } @@ -231,8 +209,7 @@ public class EndpointRequestTests { RequestMatcher matcher = EndpointRequest.toLinks(); RequestMatcher mockRequestMatcher = (request) -> false; RequestMatcherAssert assertMatcher = assertMatcher(matcher, - mockPathMappedEndpoints("/actuator"), "", - (pattern) -> mockRequestMatcher); + mockPathMappedEndpoints("/actuator"), (pattern) -> mockRequestMatcher); assertMatcher.doesNotMatch("/actuator"); } @@ -248,13 +225,7 @@ public class EndpointRequestTests { } private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String basePath) { - return assertMatcher(matcher, mockPathMappedEndpoints(basePath)); - } - - private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String basePath, - String servletPath) { - return assertMatcher(matcher, mockPathMappedEndpoints(basePath), servletPath, - null); + return assertMatcher(matcher, mockPathMappedEndpoints(basePath), null); } private PathMappedEndpoints mockPathMappedEndpoints(String basePath) { @@ -273,11 +244,11 @@ public class EndpointRequestTests { private RequestMatcherAssert assertMatcher(RequestMatcher matcher, PathMappedEndpoints pathMappedEndpoints) { - return assertMatcher(matcher, pathMappedEndpoints, "", null); + return assertMatcher(matcher, pathMappedEndpoints, null); } private RequestMatcherAssert assertMatcher(RequestMatcher matcher, - PathMappedEndpoints pathMappedEndpoints, String dispatcherServletPath, + PathMappedEndpoints pathMappedEndpoints, RequestMatcherProvider matcherProvider) { StaticWebApplicationContext context = new StaticWebApplicationContext(); context.registerBean(WebEndpointProperties.class); @@ -289,10 +260,6 @@ public class EndpointRequestTests { properties.setBasePath(pathMappedEndpoints.getBasePath()); } } - if (dispatcherServletPath != null) { - DispatcherServletPath path = () -> dispatcherServletPath; - context.registerBean(DispatcherServletPath.class, () -> path); - } if (matcherProvider != null) { context.registerBean(RequestMatcherProvider.class, () -> matcherProvider); } @@ -314,10 +281,6 @@ public class EndpointRequestTests { matches(mockRequest(servletPath)); } - public void matches(String servletPath, String pathInfo) { - matches(mockRequest(servletPath, pathInfo)); - } - private void matches(HttpServletRequest request) { assertThat(this.matcher.matches(request)) .as("Matches " + getRequestPath(request)).isTrue(); @@ -327,20 +290,12 @@ public class EndpointRequestTests { doesNotMatch(mockRequest(servletPath)); } - public void doesNotMatch(String servletPath, String pathInfo) { - doesNotMatch(mockRequest(servletPath, pathInfo)); - } - private void doesNotMatch(HttpServletRequest request) { assertThat(this.matcher.matches(request)) .as("Does not match " + getRequestPath(request)).isFalse(); } private MockHttpServletRequest mockRequest(String servletPath) { - return mockRequest(servletPath, null); - } - - private MockHttpServletRequest mockRequest(String servletPath, String path) { MockServletContext servletContext = new MockServletContext(); servletContext.setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, @@ -349,7 +304,6 @@ public class EndpointRequestTests { if (servletPath != null) { request.setServletPath(servletPath); } - request.setPathInfo(path); return request; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/MvcEndpointRequestIntegrationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/MvcEndpointRequestIntegrationTests.java index c8856350c5..c2e10ee5fb 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/MvcEndpointRequestIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/MvcEndpointRequestIntegrationTests.java @@ -57,6 +57,18 @@ import org.springframework.web.cors.CorsConfiguration; public class MvcEndpointRequestIntegrationTests extends AbstractEndpointRequestIntegrationTests { + @Test + public void toLinksWhenServletPathSetShouldMatch() { + getContextRunner().withPropertyValues("server.servlet.path=/admin") + .run((context) -> { + WebTestClient webTestClient = getWebTestClient(context); + webTestClient.get().uri("/admin/actuator/").exchange().expectStatus() + .isOk(); + webTestClient.get().uri("/admin/actuator").exchange().expectStatus() + .isOk(); + }); + } + @Test public void toEndpointWhenServletPathSetShouldMatch() { getContextRunner().withPropertyValues("server.servlet.path=/admin")