6 changed files with 477 additions and 82 deletions
@ -0,0 +1,190 @@
@@ -0,0 +1,190 @@
|
||||
/* |
||||
* 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.autoconfigure.security.web.servlet.EndpointRequest; |
||||
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; |
||||
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.autoconfigure.logging.ConditionEvaluationReportLoggingListener; |
||||
import org.springframework.boot.logging.LogLevel; |
||||
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() |
||||
.withInitializer( |
||||
new ConditionEvaluationReportLoggingListener(LogLevel.INFO)) |
||||
.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<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; |
||||
} |
||||
|
||||
} |
||||
|
||||
@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<Operation>, 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(); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,119 @@
@@ -0,0 +1,119 @@
|
||||
/* |
||||
* 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.autoconfigure.security.web.servlet.EndpointRequest; |
||||
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.SecurityAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.security.UserDetailsServiceAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.security.web.servlet.SecurityRequestMatcherProviderAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.security.web.servlet.ServletWebSecurityAutoConfiguration; |
||||
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, |
||||
ServletWebSecurityAutoConfiguration.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<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((id) -> id.toString()), |
||||
Collections.emptyList(), Collections.emptyList()); |
||||
Collection<Resource> resources = new JerseyEndpointResourceFactory() |
||||
.createEndpointResources(new EndpointMapping("/actuator"), |
||||
discoverer.getEndpoints(), endpointMediaTypes, |
||||
new EndpointLinksResolver(discoverer.getEndpoints())); |
||||
config.registerResources(new HashSet<>(resources)); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,146 @@
@@ -0,0 +1,146 @@
|
||||
/* |
||||
* 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.autoconfigure.security.web.servlet.EndpointRequest; |
||||
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.SecurityAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.security.UserDetailsServiceAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.security.web.servlet.SecurityRequestMatcherProviderAutoConfiguration; |
||||
import org.springframework.boot.autoconfigure.security.web.servlet.ServletWebSecurityAutoConfiguration; |
||||
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 toLinksWhenServletPathSetShouldMatch() { |
||||
getContextRunner().withPropertyValues("spring.mvc.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("spring.mvc.servlet.path=/admin") |
||||
.run((context) -> { |
||||
WebTestClient webTestClient = getWebTestClient(context); |
||||
webTestClient.get().uri("/admin/actuator/e1").exchange() |
||||
.expectStatus().isOk(); |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
public void toAnyEndpointWhenServletPathSetShouldMatch() { |
||||
getContextRunner().withPropertyValues("spring.mvc.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, |
||||
ServletWebSecurityAutoConfiguration.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<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((id) -> id.toString()), |
||||
Collections.emptyList(), Collections.emptyList()); |
||||
return new WebMvcEndpointHandlerMapping(new EndpointMapping("/actuator"), |
||||
discoverer.getEndpoints(), endpointMediaTypes, |
||||
new CorsConfiguration(), |
||||
new EndpointLinksResolver(discoverer.getEndpoints())); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue