diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementWebSecurityAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementWebSecurityAutoConfiguration.java index 51507d52af..7eac112ff7 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementWebSecurityAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementWebSecurityAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -257,10 +257,11 @@ public class ManagementWebSecurityAutoConfiguration { private void configurePermittedRequests( ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry requests) { + requests.requestMatchers(new LazyEndpointPathRequestMatcher( + this.contextResolver, EndpointPaths.SENSITIVE)).authenticated(); // Permit access to the non-sensitive endpoints requests.requestMatchers(new LazyEndpointPathRequestMatcher( this.contextResolver, EndpointPaths.NON_SENSITIVE)).permitAll(); - requests.anyRequest().authenticated(); } } @@ -276,6 +277,15 @@ public class ManagementWebSecurityAutoConfiguration { return !endpoint.isSensitive(); } + }, + + SENSITIVE { + + @Override + protected boolean isIncluded(MvcEndpoint endpoint) { + return endpoint.isSensitive(); + } + }; public String[] getPaths(EndpointHandlerMapping endpointHandlerMapping) { @@ -289,12 +299,9 @@ public class ManagementWebSecurityAutoConfiguration { String path = endpointHandlerMapping.getPath(endpoint.getPath()); paths.add(path); if (!path.equals("")) { - if (endpoint.isSensitive()) { - // Ensure that nested paths are secured - paths.add(path + "/**"); - // Add Spring MVC-generated additional paths - paths.add(path + ".*"); - } + paths.add(path + "/**"); + // Add Spring MVC-generated additional paths + paths.add(path + ".*"); } paths.add(path + "/"); } diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index e915be811d..489503b52d 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -115,6 +115,7 @@ spring-boot-sample-websocket-undertow spring-boot-sample-webservices spring-boot-sample-xml + spring-boot-sample-hypermedia-ui-secure diff --git a/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/pom.xml b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/pom.xml new file mode 100644 index 0000000000..2e53dc4b89 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/pom.xml @@ -0,0 +1,53 @@ + + + + spring-boot-samples + org.springframework.boot + 1.5.0.BUILD-SNAPSHOT + + 4.0.0 + spring-boot-sample-hypermedia-ui-secure + Spring Boot Hypermedia UI Secure Sample + Spring Boot Hypermedia UI Secure Sample + + ${basedir}/../.. + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-hateoas + + + org.springframework.boot + spring-boot-actuator-docs + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplication.java b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplication.java new file mode 100644 index 0000000000..476bf446af --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012-2017 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 sample.hypermedia.ui.secure; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SampleHypermediaUiSecureApplication { + + public static void main(String[] args) { + SpringApplication.run(SampleHypermediaUiSecureApplication.class, args); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/resources/static/index.html b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/resources/static/index.html new file mode 100644 index 0000000000..66a41f708c --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/main/resources/static/index.html @@ -0,0 +1,5 @@ + + +

Hello World!

+ + \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationTests.java b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationTests.java new file mode 100644 index 0000000000..3c043ff8ae --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2012-2017 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 sample.hypermedia.ui.secure; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { + "endpoints.env.sensitive=false" }) +public class SampleHypermediaUiSecureApplicationTests { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void links() { + String response = this.restTemplate.getForObject("/actuator", String.class); + assertThat(response).contains("\"_links\":"); + } + + @Test + public void testInSecureNestedPath() throws Exception { + ResponseEntity entity = this.restTemplate.getForEntity("/env", + String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + ResponseEntity user = this.restTemplate.getForEntity("/env/user", + String.class); + assertThat(user.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(user.getBody()).contains("{\"user\":"); + } + + @Test + public void testSecurePath() throws Exception { + ResponseEntity entity = this.restTemplate.getForEntity("/metrics", + String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); + } +} diff --git a/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationWithContextPathTests.java b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationWithContextPathTests.java new file mode 100644 index 0000000000..cd921fe209 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-hypermedia-ui-secure/src/test/java/sample/hypermedia/ui/secure/SampleHypermediaUiSecureApplicationWithContextPathTests.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2017 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 sample.hypermedia.ui.secure; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { + "management.context-path=/admin" }) +public class SampleHypermediaUiSecureApplicationWithContextPathTests { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void links() { + String response = this.restTemplate.getForObject("/admin", String.class); + assertThat(response).contains("\"_links\":"); + } + + @Test + public void testSecurePath() throws Exception { + ResponseEntity entity = this.restTemplate.getForEntity("/admin/metrics", + String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); + } + +}