Merge branch '2.1.x'

Closes gh-17980
This commit is contained in:
Madhura Bhave 2019-08-28 13:13:48 +05:30
commit 2726540e76
28 changed files with 1232 additions and 309 deletions

View File

@ -13,41 +13,48 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.servlet;
package org.springframework.boot.actuate.autoconfigure.security.servlet;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.servlet.AntPathRequestMatcherProvider;
import org.springframework.boot.autoconfigure.security.servlet.RequestMatcherProvider;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath;
import org.springframework.boot.autoconfigure.web.servlet.JerseyApplicationPath;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
* Auto-configuration for {@link RequestMatcherProvider}.
* {@link ManagementContextConfiguration} that configures the appropriate
* {@link RequestMatcherProvider}.
*
* @author Madhura Bhave
* @since 2.0.5
* @since 2.1.8
*/
@Configuration(proxyBeanMethods = false)
@ManagementContextConfiguration
@ConditionalOnClass({ RequestMatcher.class })
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class SecurityRequestMatcherProviderAutoConfiguration {
public class SecurityRequestMatchersManagementContextConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(DispatcherServlet.class)
@ConditionalOnBean(HandlerMappingIntrospector.class)
@ConditionalOnBean(DispatcherServletPath.class)
public static class MvcRequestMatcherConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(DispatcherServlet.class)
public RequestMatcherProvider requestMatcherProvider(HandlerMappingIntrospector introspector) {
return new MvcRequestMatcherProvider(introspector);
public RequestMatcherProvider requestMatcherProvider(DispatcherServletPath servletPath) {
return new AntPathRequestMatcherProvider(servletPath::getRelativePath);
}
}
@ -60,7 +67,7 @@ public class SecurityRequestMatcherProviderAutoConfiguration {
@Bean
public RequestMatcherProvider requestMatcherProvider(JerseyApplicationPath applicationPath) {
return new JerseyRequestMatcherProvider(applicationPath);
return new AntPathRequestMatcherProvider(applicationPath::getRelativePath);
}
}

View File

@ -95,6 +95,7 @@ org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManag
org.springframework.boot.actuate.autoconfigure.endpoint.web.reactive.WebFluxEndpointManagementContextConfiguration,\
org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration,\
org.springframework.boot.actuate.autoconfigure.endpoint.web.jersey.JerseyWebEndpointManagementContextConfiguration,\
org.springframework.boot.actuate.autoconfigure.security.servlet.SecurityRequestMatchersManagementContextConfiguration,\
org.springframework.boot.actuate.autoconfigure.web.jersey.JerseySameManagementContextConfiguration,\
org.springframework.boot.actuate.autoconfigure.web.jersey.JerseyChildManagementContextConfiguration,\
org.springframework.boot.actuate.autoconfigure.web.reactive.ReactiveManagementChildContextConfiguration,\

View File

@ -15,20 +15,24 @@
*/
package org.springframework.boot.actuate.autoconfigure.security.servlet;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.function.Supplier;
import org.jolokia.http.AgentServlet;
import org.junit.jupiter.api.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.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
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.actuate.endpoint.web.EndpointServlet;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
@ -39,9 +43,6 @@ 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.
*
@ -49,8 +50,6 @@ import static org.mockito.Mockito.mock;
*/
abstract class AbstractEndpointRequestIntegrationTests {
protected abstract WebApplicationContextRunner getContextRunner();
@Test
void toEndpointShouldMatch() {
getContextRunner().run((context) -> {
@ -79,6 +78,17 @@ abstract class AbstractEndpointRequestIntegrationTests {
});
}
protected final WebApplicationContextRunner getContextRunner() {
return createContextRunner().withPropertyValues("management.endpoints.web.exposure.include=*")
.withUserConfiguration(BaseConfiguration.class, SecurityConfiguration.class).withConfiguration(
AutoConfigurations.of(JacksonAutoConfiguration.class, SecurityAutoConfiguration.class,
UserDetailsServiceAutoConfiguration.class, EndpointAutoConfiguration.class,
WebEndpointAutoConfiguration.class, ManagementContextAutoConfiguration.class));
}
protected abstract WebApplicationContextRunner createContextRunner();
protected WebTestClient getWebTestClient(AssertableWebApplicationContext context) {
int port = context.getSourceApplicationContext(AnnotationConfigServletWebServerApplicationContext.class)
.getWebServer().getPort();
@ -108,19 +118,8 @@ abstract class AbstractEndpointRequestIntegrationTests {
}
@Bean
PathMappedEndpoints pathMappedEndpoints() {
List<ExposableEndpoint<?>> 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.getEndpointId()).willReturn(EndpointId.of(id));
given(endpoint.getRootPath()).willReturn(id);
return endpoint;
TestServletEndpoint servletEndpoint() {
return new TestServletEndpoint();
}
}
@ -155,7 +154,13 @@ abstract class AbstractEndpointRequestIntegrationTests {
}
public interface TestPathMappedEndpoint extends ExposableEndpoint<Operation>, PathMappedEndpoint {
@ServletEndpoint(id = "se1")
static class TestServletEndpoint implements Supplier<EndpointServlet> {
@Override
public EndpointServlet get() {
return new EndpointServlet(AgentServlet.class);
}
}

View File

@ -15,38 +15,17 @@
*/
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.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.endpoint.EndpointId;
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.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.FilteredClassLoader;
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.test.web.reactive.server.WebTestClient;
@ -58,18 +37,6 @@ import org.springframework.test.web.reactive.server.WebTestClient;
*/
class JerseyEndpointRequestIntegrationTests extends AbstractEndpointRequestIntegrationTests {
@Override
protected WebApplicationContextRunner getContextRunner() {
return new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new)
.withClassLoader(new FilteredClassLoader("org.springframework.web.servlet.DispatcherServlet"))
.withUserConfiguration(JerseyEndpointConfiguration.class, SecurityConfiguration.class,
BaseConfiguration.class)
.withConfiguration(AutoConfigurations.of(SecurityAutoConfiguration.class,
UserDetailsServiceAutoConfiguration.class,
SecurityRequestMatcherProviderAutoConfiguration.class, JacksonAutoConfiguration.class,
JerseyAutoConfiguration.class));
}
@Test
void toLinksWhenApplicationPathSetShouldMatch() {
getContextRunner().withPropertyValues("spring.jersey.application-path=/admin").run((context) -> {
@ -99,16 +66,47 @@ class JerseyEndpointRequestIntegrationTests extends AbstractEndpointRequestInteg
});
}
@Configuration(proxyBeanMethods = false)
@Test
void toAnyEndpointShouldMatchServletEndpoint() {
getContextRunner().withPropertyValues("spring.security.user.password=password",
"management.endpoints.web.exposure.include=se1").run((context) -> {
WebTestClient webTestClient = getWebTestClient(context);
webTestClient.get().uri("/actuator/se1").exchange().expectStatus().isUnauthorized();
webTestClient.get().uri("/actuator/se1").header("Authorization", getBasicAuth()).exchange()
.expectStatus().isOk();
webTestClient.get().uri("/actuator/se1/list").exchange().expectStatus().isUnauthorized();
webTestClient.get().uri("/actuator/se1/list").header("Authorization", getBasicAuth()).exchange()
.expectStatus().isOk();
});
}
@Test
void toAnyEndpointWhenApplicationPathSetShouldMatchServletEndpoint() {
getContextRunner().withPropertyValues("spring.jersey.application-path=/admin",
"spring.security.user.password=password", "management.endpoints.web.exposure.include=se1")
.run((context) -> {
WebTestClient webTestClient = getWebTestClient(context);
webTestClient.get().uri("/admin/actuator/se1").exchange().expectStatus().isUnauthorized();
webTestClient.get().uri("/admin/actuator/se1").header("Authorization", getBasicAuth()).exchange()
.expectStatus().isOk();
webTestClient.get().uri("/admin/actuator/se1/list").exchange().expectStatus().isUnauthorized();
webTestClient.get().uri("/admin/actuator/se1/list").header("Authorization", getBasicAuth())
.exchange().expectStatus().isOk();
});
}
@Override
protected WebApplicationContextRunner createContextRunner() {
return new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new)
.withClassLoader(new FilteredClassLoader("org.springframework.web.servlet.DispatcherServlet"))
.withUserConfiguration(JerseyEndpointConfiguration.class)
.withConfiguration(AutoConfigurations.of(JerseyAutoConfiguration.class));
}
@Configuration
@EnableConfigurationProperties(WebEndpointProperties.class)
static class JerseyEndpointConfiguration {
private final ApplicationContext applicationContext;
JerseyEndpointConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean
TomcatServletWebServerFactory tomcat() {
return new TomcatServletWebServerFactory(0);
@ -119,24 +117,6 @@ class JerseyEndpointRequestIntegrationTests extends AbstractEndpointRequestInteg
return new ResourceConfig();
}
@Bean
ResourceConfigCustomizer webEndpointRegistrar() {
return this::customize;
}
private void customize(ResourceConfig config) {
List<String> 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,
Arrays.asList(EndpointId::toString), Collections.emptyList(), Collections.emptyList());
Collection<Resource> resources = new JerseyEndpointResourceFactory().createEndpointResources(
new EndpointMapping("/actuator"), discoverer.getEndpoints(), endpointMediaTypes,
new EndpointLinksResolver(discoverer.getEndpoints()), true);
config.registerResources(new HashSet<>(resources));
}
}
}

View File

@ -15,39 +15,20 @@
*/
package org.springframework.boot.actuate.autoconfigure.security.servlet;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.endpoint.EndpointId;
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.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.
@ -85,44 +66,52 @@ class MvcEndpointRequestIntegrationTests extends AbstractEndpointRequestIntegrat
});
}
@Test
void toAnyEndpointShouldMatchServletEndpoint() {
getContextRunner().withPropertyValues("spring.security.user.password=password",
"management.endpoints.web.exposure.include=se1").run((context) -> {
WebTestClient webTestClient = getWebTestClient(context);
webTestClient.get().uri("/actuator/se1").exchange().expectStatus().isUnauthorized();
webTestClient.get().uri("/actuator/se1").header("Authorization", getBasicAuth()).exchange()
.expectStatus().isOk();
webTestClient.get().uri("/actuator/se1/list").exchange().expectStatus().isUnauthorized();
webTestClient.get().uri("/actuator/se1/list").header("Authorization", getBasicAuth()).exchange()
.expectStatus().isOk();
});
}
@Test
void toAnyEndpointWhenServletPathSetShouldMatchServletEndpoint() {
getContextRunner().withPropertyValues("spring.mvc.servlet.path=/admin",
"spring.security.user.password=password", "management.endpoints.web.exposure.include=se1")
.run((context) -> {
WebTestClient webTestClient = getWebTestClient(context);
webTestClient.get().uri("/admin/actuator/se1").exchange().expectStatus().isUnauthorized();
webTestClient.get().uri("/admin/actuator/se1").header("Authorization", getBasicAuth()).exchange()
.expectStatus().isOk();
webTestClient.get().uri("/admin/actuator/se1/list").exchange().expectStatus().isUnauthorized();
webTestClient.get().uri("/admin/actuator/se1/list").header("Authorization", getBasicAuth())
.exchange().expectStatus().isOk();
});
}
@Override
protected WebApplicationContextRunner getContextRunner() {
protected WebApplicationContextRunner createContextRunner() {
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));
.withUserConfiguration(WebMvcEndpointConfiguration.class)
.withConfiguration(AutoConfigurations.of(DispatcherServletAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, WebMvcAutoConfiguration.class));
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebEndpointProperties.class)
static class WebMvcEndpointConfiguration {
private final ApplicationContext applicationContext;
WebMvcEndpointConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean
TomcatServletWebServerFactory tomcat() {
return new TomcatServletWebServerFactory(0);
}
@Bean
WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping() {
List<String> 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,
Arrays.asList(EndpointId::toString), Collections.emptyList(), Collections.emptyList());
return new WebMvcEndpointHandlerMapping(new EndpointMapping("/actuator"), discoverer.getEndpoints(),
endpointMediaTypes, new CorsConfiguration(), new EndpointLinksResolver(discoverer.getEndpoints()),
true);
}
}
}

View File

@ -13,35 +13,40 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.servlet;
package org.springframework.boot.actuate.autoconfigure.security.servlet;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.security.servlet.AntPathRequestMatcherProvider;
import org.springframework.boot.autoconfigure.security.servlet.RequestMatcherProvider;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath;
import org.springframework.boot.autoconfigure.web.servlet.JerseyApplicationPath;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SecurityRequestMatcherProviderAutoConfiguration}.
* Tests for {@link SecurityRequestMatchersManagementContextConfiguration}.
*
* @author Madhura Bhave
*/
class SecurityRequestMatcherProviderAutoConfigurationTests {
class SecurityRequestMatchersManagementContextConfigurationTests {
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(SecurityRequestMatcherProviderAutoConfiguration.class));
.withConfiguration(AutoConfigurations.of(SecurityRequestMatchersManagementContextConfiguration.class));
@Test
void configurationConditionalOnWebApplication() {
new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(SecurityRequestMatcherProviderAutoConfiguration.class))
.withConfiguration(AutoConfigurations.of(SecurityRequestMatchersManagementContextConfiguration.class))
.withUserConfiguration(TestMvcConfiguration.class)
.run((context) -> assertThat(context).doesNotHaveBean(RequestMatcherProvider.class));
}
@ -55,51 +60,58 @@ class SecurityRequestMatcherProviderAutoConfigurationTests {
}
@Test
void registersMvcRequestMatcherProviderIfMvcPresent() {
this.contextRunner.withUserConfiguration(TestMvcConfiguration.class).run((context) -> assertThat(context)
.getBean(RequestMatcherProvider.class).isInstanceOf(MvcRequestMatcherProvider.class));
void registersRequestMatcherProviderIfMvcPresent() {
this.contextRunner.withUserConfiguration(TestMvcConfiguration.class).run((context) -> {
AntPathRequestMatcherProvider matcherProvider = context.getBean(AntPathRequestMatcherProvider.class);
RequestMatcher requestMatcher = matcherProvider.getRequestMatcher("/example");
assertThat(ReflectionTestUtils.getField(requestMatcher, "pattern")).isEqualTo("/custom/example");
});
}
@Test
void registersRequestMatcherForJerseyProviderIfJerseyPresentAndMvcAbsent() {
this.contextRunner.withClassLoader(new FilteredClassLoader("org.springframework.web.servlet.DispatcherServlet"))
.withUserConfiguration(TestJerseyConfiguration.class).run((context) -> assertThat(context)
.getBean(RequestMatcherProvider.class).isInstanceOf(JerseyRequestMatcherProvider.class));
.withUserConfiguration(TestJerseyConfiguration.class).run((context) -> {
AntPathRequestMatcherProvider matcherProvider = context
.getBean(AntPathRequestMatcherProvider.class);
RequestMatcher requestMatcher = matcherProvider.getRequestMatcher("/example");
assertThat(ReflectionTestUtils.getField(requestMatcher, "pattern")).isEqualTo("/admin/example");
});
}
@Test
void mvcRequestMatcherProviderConditionalOnDispatcherServletClass() {
this.contextRunner.withClassLoader(new FilteredClassLoader("org.springframework.web.servlet.DispatcherServlet"))
.run((context) -> assertThat(context).doesNotHaveBean(MvcRequestMatcherProvider.class));
.run((context) -> assertThat(context).doesNotHaveBean(AntPathRequestMatcherProvider.class));
}
@Test
void mvcRequestMatcherProviderConditionalOnDispatcherServletPathBean() {
new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(SecurityRequestMatchersManagementContextConfiguration.class))
.run((context) -> assertThat(context).doesNotHaveBean(AntPathRequestMatcherProvider.class));
}
@Test
void jerseyRequestMatcherProviderConditionalOnResourceConfigClass() {
this.contextRunner.withClassLoader(new FilteredClassLoader("org.glassfish.jersey.server.ResourceConfig"))
.run((context) -> assertThat(context).doesNotHaveBean(JerseyRequestMatcherProvider.class));
}
@Test
void mvcRequestMatcherProviderConditionalOnHandlerMappingIntrospectorBean() {
new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(SecurityRequestMatcherProviderAutoConfiguration.class))
.run((context) -> assertThat(context).doesNotHaveBean(MvcRequestMatcherProvider.class));
.run((context) -> assertThat(context).doesNotHaveBean(AntPathRequestMatcherProvider.class));
}
@Test
void jerseyRequestMatcherProviderConditionalOnJerseyApplicationPathBean() {
new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(SecurityRequestMatcherProviderAutoConfiguration.class))
.withConfiguration(AutoConfigurations.of(SecurityRequestMatchersManagementContextConfiguration.class))
.withClassLoader(new FilteredClassLoader("org.springframework.web.servlet.DispatcherServlet"))
.run((context) -> assertThat(context).doesNotHaveBean(JerseyRequestMatcherProvider.class));
.run((context) -> assertThat(context).doesNotHaveBean(AntPathRequestMatcherProvider.class));
}
@Configuration(proxyBeanMethods = false)
static class TestMvcConfiguration {
@Bean
HandlerMappingIntrospector introspector() {
return new HandlerMappingIntrospector();
DispatcherServletPath dispatcherServletPath() {
return () -> "/custom";
}
}

View File

@ -13,30 +13,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.security.servlet;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import java.util.function.Function;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
* {@link RequestMatcherProvider} that provides an {@link MvcRequestMatcher} that can be
* used for Spring MVC applications.
* {@link RequestMatcherProvider} that provides an {@link AntPathRequestMatcher}.
*
* @author Madhura Bhave
* @since 2.0.5
* @since 2.1.8
*/
public class MvcRequestMatcherProvider implements RequestMatcherProvider {
public class AntPathRequestMatcherProvider implements RequestMatcherProvider {
private final HandlerMappingIntrospector introspector;
private final Function<String, String> pathFactory;
public MvcRequestMatcherProvider(HandlerMappingIntrospector introspector) {
this.introspector = introspector;
public AntPathRequestMatcherProvider(Function<String, String> pathFactory) {
this.pathFactory = pathFactory;
}
@Override
public RequestMatcher getRequestMatcher(String pattern) {
return new MvcRequestMatcher(this.introspector, pattern);
return new AntPathRequestMatcher(this.pathFactory.apply(pattern));
}
}

View File

@ -25,7 +25,9 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
*
* @author Madhura Bhave
* @since 2.0.7
* @deprecated since 2.1.8 in favor of {@link AntPathRequestMatcher}
*/
@Deprecated
public class JerseyRequestMatcherProvider implements RequestMatcherProvider {
private final JerseyApplicationPath jerseyApplicationPath;

View File

@ -105,7 +105,6 @@ org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\

View File

@ -71,6 +71,7 @@
<module>spring-boot-smoke-test-reactive-oauth2-client</module>
<module>spring-boot-smoke-test-reactive-oauth2-resource-server</module>
<module>spring-boot-smoke-test-secure</module>
<module>spring-boot-smoke-test-secure-jersey</module>
<module>spring-boot-smoke-test-secure-webflux</module>
<module>spring-boot-smoke-test-servlet</module>
<module>spring-boot-smoke-test-session</module>

View File

@ -0,0 +1,206 @@
/*
* Copyright 2012-2019 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 java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.web.client.LocalHostUriTemplateHandler;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Abstract base class for actuator tests with custom security.
*
* @author Madhura Bhave
*/
abstract class AbstractSampleActuatorCustomSecurityTests {
abstract String getPath();
abstract String getManagementPath();
abstract Environment getEnvironment();
@Test
void homeIsSecure() {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = restTemplate().getForEntity(getPath() + "/", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
@SuppressWarnings("unchecked")
Map<String, Object> body = entity.getBody();
assertThat(body.get("error")).isEqualTo("Unauthorized");
assertThat(entity.getHeaders()).doesNotContainKey("Set-Cookie");
}
@Test
void testInsecureStaticResources() {
ResponseEntity<String> entity = restTemplate().getForEntity(getPath() + "/css/bootstrap.min.css", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("body");
}
@Test
void actuatorInsecureEndpoint() {
ResponseEntity<String> entity = restTemplate().getForEntity(getManagementPath() + "/actuator/health",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
entity = restTemplate().getForEntity(getManagementPath() + "/actuator/health/diskSpace", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
}
@Test
void actuatorLinksWithAnonymous() {
ResponseEntity<Object> entity = restTemplate().getForEntity(getManagementPath() + "/actuator", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
entity = restTemplate().getForEntity(getManagementPath() + "/actuator/", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
void actuatorLinksWithUnauthorizedUser() {
ResponseEntity<Object> entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator",
Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
void actuatorLinksWithAuthorizedUser() {
ResponseEntity<Object> entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator",
Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
adminRestTemplate().getForEntity(getManagementPath() + "/actuator/", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
void actuatorSecureEndpointWithAnonymous() {
ResponseEntity<Object> entity = restTemplate().getForEntity(getManagementPath() + "/actuator/env",
Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
entity = restTemplate().getForEntity(
getManagementPath() + "/actuator/env/management.endpoints.web.exposure.include", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
void actuatorSecureEndpointWithUnauthorizedUser() {
ResponseEntity<Object> entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/env",
Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
entity = userRestTemplate().getForEntity(
getManagementPath() + "/actuator/env/management.endpoints.web.exposure.include", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
void actuatorSecureEndpointWithAuthorizedUser() {
ResponseEntity<Object> entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/env",
Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
entity = adminRestTemplate().getForEntity(
getManagementPath() + "/actuator/env/management.endpoints.web.exposure.include", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
void secureServletEndpointWithAnonymous() {
ResponseEntity<String> entity = restTemplate().getForEntity("/actuator/jolokia", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
entity = restTemplate().getForEntity(getManagementPath() + "/actuator/jolokia/list", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
void secureServletEndpointWithUnauthorizedUser() {
ResponseEntity<String> entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/jolokia",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/jolokia/list", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
void secureServletEndpointWithAuthorizedUser() {
ResponseEntity<String> entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/jolokia",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/jolokia/list", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
void actuatorCustomMvcSecureEndpointWithAnonymous() {
ResponseEntity<String> entity = restTemplate()
.getForEntity(getManagementPath() + "/actuator/example/echo?text={t}", String.class, "test");
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
void actuatorCustomMvcSecureEndpointWithUnauthorizedUser() {
ResponseEntity<String> entity = userRestTemplate()
.getForEntity(getManagementPath() + "/actuator/example/echo?text={t}", String.class, "test");
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
void actuatorCustomMvcSecureEndpointWithAuthorizedUser() {
ResponseEntity<String> entity = adminRestTemplate()
.getForEntity(getManagementPath() + "/actuator/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");
}
@Test
void actuatorExcludedFromEndpointRequestMatcher() {
ResponseEntity<Object> entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/mappings",
Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
TestRestTemplate restTemplate() {
return configure(new TestRestTemplate());
}
TestRestTemplate adminRestTemplate() {
return configure(new TestRestTemplate("admin", "admin"));
}
TestRestTemplate userRestTemplate() {
return configure(new TestRestTemplate("user", "password"));
}
TestRestTemplate beansRestTemplate() {
return configure(new TestRestTemplate("beans", "beans"));
}
private TestRestTemplate configure(TestRestTemplate restTemplate) {
restTemplate.setUriTemplateHandler(new LocalHostUriTemplateHandler(getEnvironment()));
return restTemplate;
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2012-2019 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.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.core.env.Environment;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration tests for actuator endpoints with custom dispatcher servlet path.
*
* @author Madhura Bhave
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = "spring.mvc.servlet.path=/example")
class CustomServletPathSampleActuatorTests extends AbstractSampleActuatorCustomSecurityTests {
@LocalServerPort
private int port;
@Autowired
private Environment environment;
@Override
String getPath() {
return "http://localhost:" + this.port + "/example";
}
@Override
String getManagementPath() {
return "http://localhost:" + this.port + "/example";
}
@Override
Environment getEnvironment() {
return this.environment;
}
}

View File

@ -18,25 +18,28 @@ package smoketest.actuator.customsecurity;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for separate management and main service ports.
* Integration tests for separate management and main service ports with custom management
* context path.
*
* @author Dave Syer
* @author Madhura Bhave
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
properties = { "management.server.port=0", "management.server.servlet.context-path=/management" })
class ManagementPortAndPathSampleActuatorApplicationTests {
class ManagementPortAndPathSampleActuatorApplicationTests extends AbstractSampleActuatorCustomSecurityTests {
@LocalServerPort
private int port;
@ -44,35 +47,8 @@ class ManagementPortAndPathSampleActuatorApplicationTests {
@LocalManagementPort
private int managementPort;
@Test
void testHome() {
ResponseEntity<String> entity = new TestRestTemplate("user", "password")
.getForEntity("http://localhost:" + this.port, String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("Hello World");
}
@Test
void actuatorPathOnMainPortShouldNotMatch() {
ResponseEntity<String> entity = new TestRestTemplate()
.getForEntity("http://localhost:" + this.port + "/actuator/health", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
void testSecureActuator() {
ResponseEntity<String> entity = new TestRestTemplate()
.getForEntity("http://localhost:" + this.managementPort + "/management/actuator/env", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
void testInsecureActuator() {
ResponseEntity<String> entity = new TestRestTemplate()
.getForEntity("http://localhost:" + this.managementPort + "/management/actuator/health", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
}
@Autowired
private Environment environment;
@Test
void testMissing() {
@ -82,4 +58,19 @@ class ManagementPortAndPathSampleActuatorApplicationTests {
assertThat(entity.getBody()).contains("\"status\":404");
}
@Override
String getPath() {
return "http://localhost:" + this.port;
}
@Override
String getManagementPath() {
return "http://localhost:" + this.managementPort + "/management";
}
@Override
Environment getEnvironment() {
return this.environment;
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 2012-2019 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.actuate.autoconfigure.web.server.LocalManagementPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
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.
*
* @author Madhura Bhave
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "management.server.port=0", "spring.mvc.servlet.path=/example" })
class ManagementPortCustomServletPathSampleActuatorTests extends AbstractSampleActuatorCustomSecurityTests {
@LocalServerPort
private int port;
@LocalManagementPort
private int managementPort;
@Autowired
private Environment environment;
@Test
void actuatorPathOnMainPortShouldNotMatch() {
ResponseEntity<String> entity = new TestRestTemplate()
.getForEntity("http://localhost:" + this.port + "/example/actuator/health", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Override
String getPath() {
return "http://localhost:" + this.port + "/example";
}
@Override
String getManagementPath() {
return "http://localhost:" + this.managementPort;
}
@Override
Environment getEnvironment() {
return this.environment;
}
}

View File

@ -22,8 +22,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.LocalHostUriTemplateHandler;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@ -31,132 +30,52 @@ import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for actuator endpoints with custom security configuration.
*
* @author Madhura Bhave
* @author Stephane Nicoll
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SampleActuatorCustomSecurityApplicationTests {
class SampleActuatorCustomSecurityApplicationTests extends AbstractSampleActuatorCustomSecurityTests {
@LocalServerPort
private int port;
@Autowired
private Environment environment;
@Test
void homeIsSecure() {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = restTemplate().getForEntity("/", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
@SuppressWarnings("unchecked")
Map<String, Object> body = entity.getBody();
assertThat(body.get("error")).isEqualTo("Unauthorized");
assertThat(entity.getHeaders()).doesNotContainKey("Set-Cookie");
@Override
String getPath() {
return "http://localhost:" + this.port;
}
@Override
String getManagementPath() {
return "http://localhost:" + this.port;
}
@Override
Environment getEnvironment() {
return this.environment;
}
@Test
void testInsecureApplicationPath() {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = restTemplate().getForEntity("/foo", Map.class);
ResponseEntity<Map> entity = restTemplate().getForEntity(getPath() + "/foo", Map.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
@SuppressWarnings("unchecked")
Map<String, Object> body = entity.getBody();
assertThat((String) body.get("message")).contains("Expected exception in controller");
}
@Test
void testInsecureStaticResources() {
ResponseEntity<String> entity = restTemplate().getForEntity("/css/bootstrap.min.css", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("body");
}
@Test
void actuatorInsecureEndpoint() {
ResponseEntity<String> entity = restTemplate().getForEntity("/actuator/health", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
}
@Test
void actuatorLinksIsSecure() {
ResponseEntity<Object> entity = restTemplate().getForEntity("/actuator", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
entity = adminRestTemplate().getForEntity("/actuator", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
void actuatorSecureEndpointWithAnonymous() {
ResponseEntity<Object> entity = restTemplate().getForEntity("/actuator/env", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
void actuatorSecureEndpointWithUnauthorizedUser() {
ResponseEntity<Object> entity = userRestTemplate().getForEntity("/actuator/env", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
void actuatorSecureEndpointWithAuthorizedUser() {
ResponseEntity<Object> entity = adminRestTemplate().getForEntity("/actuator/env", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
void actuatorCustomMvcSecureEndpointWithAnonymous() {
ResponseEntity<String> entity = restTemplate().getForEntity("/actuator/example/echo?text={t}", String.class,
"test");
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
void actuatorCustomMvcSecureEndpointWithUnauthorizedUser() {
ResponseEntity<String> entity = userRestTemplate().getForEntity("/actuator/example/echo?text={t}", String.class,
"test");
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
void actuatorCustomMvcSecureEndpointWithAuthorizedUser() {
ResponseEntity<String> entity = adminRestTemplate().getForEntity("/actuator/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");
}
@Test
void actuatorExcludedFromEndpointRequestMatcher() {
ResponseEntity<Object> entity = userRestTemplate().getForEntity("/actuator/mappings", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
void mvcMatchersCanBeUsedToSecureActuators() {
ResponseEntity<Object> entity = beansRestTemplate().getForEntity("/actuator/beans", Object.class);
ResponseEntity<Object> entity = beansRestTemplate().getForEntity(getManagementPath() + "/actuator/beans",
Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
entity = beansRestTemplate().getForEntity("/actuator/beans/", Object.class);
entity = beansRestTemplate().getForEntity(getManagementPath() + "/actuator/beans/", Object.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
private TestRestTemplate restTemplate() {
return configure(new TestRestTemplate());
}
private TestRestTemplate adminRestTemplate() {
return configure(new TestRestTemplate("admin", "admin"));
}
private TestRestTemplate userRestTemplate() {
return configure(new TestRestTemplate("user", "password"));
}
private TestRestTemplate beansRestTemplate() {
return configure(new TestRestTemplate("beans", "beans"));
}
private TestRestTemplate configure(TestRestTemplate restTemplate) {
restTemplate.setUriTemplateHandler(new LocalHostUriTemplateHandler(this.environment));
return restTemplate;
}
}

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-smoke-tests</artifactId>
<version>${revision}</version>
</parent>
<artifactId>spring-boot-smoke-test-secure-jersey</artifactId>
<packaging>jar</packaging>
<name>Spring Boot Secure Jersey Smoke Test</name>
<description>Spring Boot Secure Jersey Smoke Test</description>
<properties>
<main.basedir>${basedir}/../../..</main.basedir>
</properties>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
</dependency>
<!-- Provided -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>generate build info</id>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>java9+</id>
<activation>
<jdk>[9,)</jdk>
</activation>
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,39 @@
/*
* Copyright 2012-2019 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.secure.jersey;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.springframework.stereotype.Component;
@Component
@Path("/hello")
public class Endpoint {
private final Service service;
public Endpoint(Service service) {
this.service = service;
}
@GET
public String message() {
return "Hello " + this.service.message();
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2012-2019 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.secure.jersey;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;
@Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(Endpoint.class);
register(ReverseEndpoint.class);
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2012-2019 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.secure.jersey;
import javax.validation.constraints.NotNull;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import org.springframework.stereotype.Component;
@Component
@Path("/reverse")
public class ReverseEndpoint {
@GET
public String reverse(@QueryParam("input") @NotNull String input) {
return new StringBuilder(input).reverse().toString();
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2012-2019 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.secure.jersey;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SampleSecureJerseyApplication {
public static void main(String[] args) {
SpringApplication.run(SampleSecureJerseyApplication.class, args);
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2012-2019 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.secure.jersey;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.boot.actuate.web.mappings.MappingsEndpoint;
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.security.core.userdetails.User;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@SuppressWarnings("deprecation")
@Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
return new InMemoryUserDetailsManager(
User.withDefaultPasswordEncoder().username("user").password("password").authorities("ROLE_USER")
.build(),
User.withDefaultPasswordEncoder().username("admin").password("admin")
.authorities("ROLE_ACTUATOR", "ROLE_USER").build());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.authorizeRequests()
.requestMatchers(EndpointRequest.to("health", "info")).permitAll()
.requestMatchers(EndpointRequest.toAnyEndpoint().excluding(MappingsEndpoint.class)).hasRole("ACTUATOR")
.antMatchers("/**").hasRole("USER")
.and()
.httpBasic();
// @formatter:on
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2012-2019 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.secure.jersey;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Service {
@Value("${message:World}")
private String msg;
public String message() {
return this.msg;
}
}

View File

@ -0,0 +1,4 @@
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

View File

@ -0,0 +1,161 @@
/*
* Copyright 2012-2019 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.secure.jersey;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Abstract base class for actuator tests with custom security.
*
* @author Madhura Bhave
*/
abstract class AbstractJerseySecureTests {
abstract String getPath();
abstract String getManagementPath();
@Autowired
private TestRestTemplate testRestTemplate;
@Test
void helloEndpointIsSecure() {
ResponseEntity<String> entity = restTemplate().getForEntity(getPath() + "/hello", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
void actuatorInsecureEndpoint() {
ResponseEntity<String> entity = restTemplate().getForEntity(getManagementPath() + "/actuator/health",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
entity = restTemplate().getForEntity(getManagementPath() + "/actuator/health/diskSpace", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).contains("\"status\":\"UP\"");
}
@Test
void actuatorLinksWithAnonymous() {
ResponseEntity<String> entity = restTemplate().getForEntity(getManagementPath() + "/actuator", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
entity = restTemplate().getForEntity(getManagementPath() + "/actuator/", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
void actuatorLinksWithUnauthorizedUser() {
ResponseEntity<String> entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
void actuatorLinksWithAuthorizedUser() {
ResponseEntity<String> entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
adminRestTemplate().getForEntity(getManagementPath() + "/actuator/", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
void actuatorSecureEndpointWithAnonymous() {
ResponseEntity<String> entity = restTemplate().getForEntity(getManagementPath() + "/actuator/env",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
entity = restTemplate().getForEntity(
getManagementPath() + "/actuator/env/management.endpoints.web.exposure.include", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
void actuatorSecureEndpointWithUnauthorizedUser() {
ResponseEntity<String> entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/env",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
entity = userRestTemplate().getForEntity(
getManagementPath() + "/actuator/env/management.endpoints.web.exposure.include", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
void actuatorSecureEndpointWithAuthorizedUser() {
ResponseEntity<String> entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/env",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
entity = adminRestTemplate().getForEntity(
getManagementPath() + "/actuator/env/management.endpoints.web.exposure.include", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
void secureServletEndpointWithAnonymous() {
ResponseEntity<String> entity = restTemplate().getForEntity(getManagementPath() + "/actuator/jolokia",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
entity = restTemplate().getForEntity(getManagementPath() + "/actuator/jolokia/list", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
void secureServletEndpointWithUnauthorizedUser() {
ResponseEntity<String> entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/jolokia",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/jolokia/list", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
void secureServletEndpointWithAuthorizedUser() {
ResponseEntity<String> entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/jolokia",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
entity = adminRestTemplate().getForEntity(getManagementPath() + "/actuator/jolokia/list", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
@Test
void actuatorExcludedFromEndpointRequestMatcher() {
ResponseEntity<String> entity = userRestTemplate().getForEntity(getManagementPath() + "/actuator/mappings",
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
TestRestTemplate restTemplate() {
return this.testRestTemplate;
}
TestRestTemplate adminRestTemplate() {
return this.testRestTemplate.withBasicAuth("admin", "admin");
}
TestRestTemplate userRestTemplate() {
return this.testRestTemplate.withBasicAuth("user", "password");
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2012-2019 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.secure.jersey;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
/**
* Integration tests for actuator endpoints with custom application path.
*
* @author Madhura Bhave
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = "spring.jersey.application-path=/example")
class CustomApplicationPathActuatorTests extends AbstractJerseySecureTests {
@LocalServerPort
private int port;
@Override
String getPath() {
return "http://localhost:" + this.port + "/example";
}
@Override
String getManagementPath() {
return "http://localhost:" + this.port + "/example";
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2012-2019 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.secure.jersey;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
/**
* Integration tests for actuator endpoints with custom security configuration.
*
* @author Madhura Bhave
* @author Stephane Nicoll
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class JerseySecureApplicationTests extends AbstractJerseySecureTests {
@LocalServerPort
private int port;
@Override
String getPath() {
return "http://localhost:" + this.port;
}
@Override
String getManagementPath() {
return "http://localhost:" + this.port;
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2012-2019 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.secure.jersey;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
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.
*
* @author Dave Syer
* @author Madhura Bhave
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
properties = { "management.server.port=0", "management.server.servlet.context-path=/management" })
class ManagementPortAndPathJerseyApplicationTests extends AbstractJerseySecureTests {
@LocalServerPort
private int port;
@LocalManagementPort
private int managementPort;
@Test
void testMissing() {
ResponseEntity<String> entity = new TestRestTemplate("admin", "admin")
.getForEntity("http://localhost:" + this.managementPort + "/management/actuator/missing", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
}
@Override
String getPath() {
return "http://localhost:" + this.port;
}
@Override
String getManagementPath() {
return "http://localhost:" + this.managementPort + "/management";
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2012-2019 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.secure.jersey;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for separate management and main service ports with custom
* application path.
*
* @author Madhura Bhave
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "management.server.port=0", "spring.jersey.application-path=/example" })
class ManagementPortCustomApplicationPathJerseyTests extends AbstractJerseySecureTests {
@LocalServerPort
private int port;
@LocalManagementPort
private int managementPort;
@Test
void actuatorPathOnMainPortShouldNotMatch() {
ResponseEntity<String> entity = new TestRestTemplate()
.getForEntity("http://localhost:" + this.port + "/example/actuator/health", String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Override
String getPath() {
return "http://localhost:" + this.port + "/example";
}
@Override
String getManagementPath() {
return "http://localhost:" + this.managementPort;
}
}