diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfiguration.java index a392327d0b0..6174c381f5e 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfiguration.java @@ -20,9 +20,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; +import org.springframework.boot.actuate.properties.ManagementServerProperties; +import org.springframework.boot.actuate.properties.ManagementServerProperties.User; import org.springframework.boot.actuate.properties.SecurityProperties; import org.springframework.boot.actuate.web.ErrorController; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -43,6 +47,7 @@ import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; @@ -112,6 +117,9 @@ public class SecurityAutoConfiguration { @Autowired private SecurityProperties security; + @Autowired + private ManagementServerProperties management; + @Autowired(required = false) private EndpointHandlerMapping endpointHandlerMapping; @@ -129,19 +137,23 @@ public class SecurityAutoConfiguration { } if (this.security.getBasic().isEnabled()) { - String[] paths = getSecurePaths(); - http.exceptionHandling().authenticationEntryPoint(entryPoint()).and() - .requestMatchers().antMatchers(paths); + http.exceptionHandling().authenticationEntryPoint(entryPoint()); http.httpBasic().and().anonymous().disable(); - http.authorizeUrls().anyRequest() - .hasRole(this.security.getBasic().getRole()); + ExpressionUrlAuthorizationConfigurer authorizeUrls = http + .authorizeUrls(); + if (getEndpointPaths(true).length > 0) { + authorizeUrls.antMatchers(getEndpointPaths(true)).hasRole( + this.management.getUser().getRole()); + } + authorizeUrls.antMatchers(getSecureApplicationPaths()) + .hasRole(this.security.getBasic().getRole()).and().httpBasic(); } // No cookies for service endpoints by default http.sessionManagement().sessionCreationPolicy(this.security.getSessions()); } - private String[] getSecurePaths() { + private String[] getSecureApplicationPaths() { List list = new ArrayList(); for (String path : this.security.getBasic().getPath()) { path = (path == null ? "" : path.trim()); @@ -203,11 +215,26 @@ public class SecurityAutoConfiguration { @Configuration public static class AuthenticationManagerConfiguration { + private static Log logger = LogFactory + .getLog(AuthenticationManagerConfiguration.class); + + @Autowired + private ManagementServerProperties management; + @Bean public AuthenticationManager authenticationManager() throws Exception { + User user = this.management.getUser(); + if (user.isDefaultPassword()) { + logger.info("Using default password for "); + } + List roles = new ArrayList(); + roles.add("USER"); + if (!"USER".equals(user.getRole())) { + roles.add(user.getRole()); + } return new AuthenticationManagerBuilder().inMemoryAuthentication() - .withUser("user").password("password").roles("USER").and().and() - .build(); + .withUser(user.getName()).password(user.getPassword()) + .roles(roles.toArray(new String[roles.size()])).and().and().build(); } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/properties/ManagementServerProperties.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/properties/ManagementServerProperties.java index cfaf790f21f..65bb156ddf9 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/properties/ManagementServerProperties.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/properties/ManagementServerProperties.java @@ -17,6 +17,7 @@ package org.springframework.boot.actuate.properties; import java.net.InetAddress; +import java.util.UUID; import javax.validation.constraints.NotNull; @@ -39,8 +40,14 @@ public class ManagementServerProperties { @NotNull private String contextPath = ""; + private User user = new User(); + private boolean allowShutdown = false; + public User getUser() { + return this.user; + } + public boolean isAllowShutdown() { return this.allowShutdown; } @@ -82,4 +89,45 @@ public class ManagementServerProperties { this.contextPath = contextPath; } + public static class User { + + private String name = "user"; + + private String password = UUID.randomUUID().toString(); + + private String role = "ADMIN"; + + private boolean defaultPassword; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.defaultPassword = false; + this.password = password; + } + + public String getRole() { + return this.role; + } + + public void setRole(String role) { + this.role = role; + } + + public boolean isDefaultPassword() { + return this.defaultPassword; + } + + } + } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfigurationTests.java index f63c0875a52..e335335c2df 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfigurationTests.java @@ -17,8 +17,6 @@ package org.springframework.boot.actuate.autoconfigure; import org.junit.Test; -import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration; -import org.springframework.boot.actuate.autoconfigure.SecurityAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -47,6 +45,7 @@ public class SecurityAutoConfigurationTests { this.context.setServletContext(new MockServletContext()); this.context.register(SecurityAutoConfiguration.class, EndpointAutoConfiguration.class, + ManagementServerPropertiesAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertNotNull(this.context.getBean(AuthenticationManager.class)); @@ -58,6 +57,7 @@ public class SecurityAutoConfigurationTests { this.context.setServletContext(new MockServletContext()); this.context.register(TestConfiguration.class, SecurityAutoConfiguration.class, EndpointAutoConfiguration.class, + ManagementServerPropertiesAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertEquals(this.context.getBean(TestConfiguration.class).authenticationManager, diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementAddressSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementAddressSampleActuatorApplicationTests.java index bc9673f9711..0467040c03b 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementAddressSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementAddressSampleActuatorApplicationTests.java @@ -30,7 +30,7 @@ import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.springframework.boot.SpringApplication; -import org.springframework.boot.sample.ops.SampleActuatorApplication; +import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; @@ -84,7 +84,7 @@ public class ManagementAddressSampleActuatorApplicationTests { @Test public void testHome() throws Exception { @SuppressWarnings("rawtypes") - ResponseEntity entity = getRestTemplate("user", "password").getForEntity( + ResponseEntity entity = getRestTemplate("user", getPassword()).getForEntity( "http://localhost:" + port, Map.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); @SuppressWarnings("unchecked") @@ -126,6 +126,10 @@ public class ManagementAddressSampleActuatorApplicationTests { assertEquals(999, body.get("status")); } + private String getPassword() { + return context.getBean(ManagementServerProperties.class).getUser().getPassword(); + } + private RestTemplate getRestTemplate() { return getRestTemplate(null, null); } diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/NoManagementSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/NoManagementSampleActuatorApplicationTests.java index dcd9aea9d9a..2567eab31a3 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/NoManagementSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/NoManagementSampleActuatorApplicationTests.java @@ -29,7 +29,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; -import org.springframework.boot.sample.ops.SampleActuatorApplication; +import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; @@ -82,7 +82,7 @@ public class NoManagementSampleActuatorApplicationTests { @Test public void testHome() throws Exception { @SuppressWarnings("rawtypes") - ResponseEntity entity = getRestTemplate("user", "password").getForEntity( + ResponseEntity entity = getRestTemplate("user", getPassword()).getForEntity( "http://localhost:8080", Map.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); @SuppressWarnings("unchecked") @@ -94,11 +94,15 @@ public class NoManagementSampleActuatorApplicationTests { public void testMetricsNotAvailable() throws Exception { testHome(); // makes sure some requests have been made @SuppressWarnings("rawtypes") - ResponseEntity entity = getRestTemplate("user", "password").getForEntity( + ResponseEntity entity = getRestTemplate("user", getPassword()).getForEntity( "http://localhost:" + managementPort + "/metrics", Map.class); assertEquals(HttpStatus.NOT_FOUND, entity.getStatusCode()); } + private String getPassword() { + return context.getBean(ManagementServerProperties.class).getUser().getPassword(); + } + private RestTemplate getRestTemplate(final String username, final String password) { List interceptors = new ArrayList(); diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java index ea35b0404cd..abadbd8c3af 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java @@ -29,7 +29,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; -import org.springframework.boot.sample.ops.SampleActuatorApplication; +import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; @@ -92,7 +92,7 @@ public class SampleActuatorApplicationTests { @Test public void testHome() throws Exception { @SuppressWarnings("rawtypes") - ResponseEntity entity = getRestTemplate("user", "password").getForEntity( + ResponseEntity entity = getRestTemplate("user", getPassword()).getForEntity( "http://localhost:8080", Map.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); @SuppressWarnings("unchecked") @@ -104,7 +104,7 @@ public class SampleActuatorApplicationTests { public void testMetrics() throws Exception { testHome(); // makes sure some requests have been made @SuppressWarnings("rawtypes") - ResponseEntity entity = getRestTemplate("user", "password").getForEntity( + ResponseEntity entity = getRestTemplate("user", getPassword()).getForEntity( "http://localhost:8080/metrics", Map.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); @SuppressWarnings("unchecked") @@ -115,7 +115,7 @@ public class SampleActuatorApplicationTests { @Test public void testEnv() throws Exception { @SuppressWarnings("rawtypes") - ResponseEntity entity = getRestTemplate("user", "password").getForEntity( + ResponseEntity entity = getRestTemplate("user", getPassword()).getForEntity( "http://localhost:8080/env", Map.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); @SuppressWarnings("unchecked") @@ -134,7 +134,7 @@ public class SampleActuatorApplicationTests { @Test public void testErrorPage() throws Exception { @SuppressWarnings("rawtypes") - ResponseEntity entity = getRestTemplate("user", "password").getForEntity( + ResponseEntity entity = getRestTemplate("user", getPassword()).getForEntity( "http://localhost:8080/foo", Map.class); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, entity.getStatusCode()); @SuppressWarnings("unchecked") @@ -157,8 +157,8 @@ public class SampleActuatorApplicationTests { @Test public void testBeans() throws Exception { @SuppressWarnings("rawtypes") - ResponseEntity entity = getRestTemplate("user", "password").getForEntity( - "http://localhost:8080/beans", List.class); + ResponseEntity entity = getRestTemplate("user", getPassword()) + .getForEntity("http://localhost:8080/beans", List.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); assertEquals(1, entity.getBody().size()); @SuppressWarnings("unchecked") @@ -167,6 +167,10 @@ public class SampleActuatorApplicationTests { ((String) body.get("context")).startsWith("application")); } + private String getPassword() { + return context.getBean(ManagementServerProperties.class).getUser().getPassword(); + } + private RestTemplate getRestTemplate() { return getRestTemplate(null, null); } diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ShutdownSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ShutdownSampleActuatorApplicationTests.java index 3c94c3c6e9a..b166c66b817 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ShutdownSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ShutdownSampleActuatorApplicationTests.java @@ -29,7 +29,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; -import org.springframework.boot.sample.ops.SampleActuatorApplication; +import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; @@ -79,7 +79,7 @@ public class ShutdownSampleActuatorApplicationTests { @Test public void testHome() throws Exception { @SuppressWarnings("rawtypes") - ResponseEntity entity = getRestTemplate("user", "password").getForEntity( + ResponseEntity entity = getRestTemplate("user", getPassword()).getForEntity( "http://localhost:8080", Map.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); @SuppressWarnings("unchecked") @@ -90,8 +90,8 @@ public class ShutdownSampleActuatorApplicationTests { @Test public void testShutdown() throws Exception { @SuppressWarnings("rawtypes") - ResponseEntity entity = getRestTemplate("user", "password").postForEntity( - "http://localhost:8080/shutdown", null, Map.class); + ResponseEntity entity = getRestTemplate("user", getPassword()) + .postForEntity("http://localhost:8080/shutdown", null, Map.class); assertEquals(HttpStatus.OK, entity.getStatusCode()); @SuppressWarnings("unchecked") Map body = entity.getBody(); @@ -99,6 +99,10 @@ public class ShutdownSampleActuatorApplicationTests { ((String) body.get("message")).contains("Shutting down")); } + private String getPassword() { + return context.getBean(ManagementServerProperties.class).getUser().getPassword(); + } + private RestTemplate getRestTemplate(final String username, final String password) { List interceptors = new ArrayList(); diff --git a/spring-boot-samples/spring-boot-sample-tomcat/src/test/java/org/springframework/boot/sample/tomcat/NonAutoConfigurationSampleTomcatApplicationTests.java b/spring-boot-samples/spring-boot-sample-tomcat/src/test/java/org/springframework/boot/sample/tomcat/NonAutoConfigurationSampleTomcatApplicationTests.java index 89bf8761d3f..9ce1ef182da 100644 --- a/spring-boot-samples/spring-boot-sample-tomcat/src/test/java/org/springframework/boot/sample/tomcat/NonAutoConfigurationSampleTomcatApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-tomcat/src/test/java/org/springframework/boot/sample/tomcat/NonAutoConfigurationSampleTomcatApplicationTests.java @@ -29,7 +29,6 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; -import org.springframework.boot.sample.tomcat.SampleTomcatApplication; import org.springframework.boot.sample.tomcat.service.HelloWorldService; import org.springframework.boot.sample.tomcat.web.SampleController; import org.springframework.context.ConfigurableApplicationContext; @@ -76,7 +75,7 @@ public class NonAutoConfigurationSampleTomcatApplicationTests { .run(NonAutoConfigurationSampleTomcatApplication.class); } }); - context = future.get(10, TimeUnit.SECONDS); + context = future.get(60, TimeUnit.SECONDS); } @AfterClass diff --git a/spring-boot-samples/spring-boot-sample-tomcat/src/test/java/org/springframework/boot/sample/tomcat/SampleTomcatApplicationTests.java b/spring-boot-samples/spring-boot-sample-tomcat/src/test/java/org/springframework/boot/sample/tomcat/SampleTomcatApplicationTests.java index 084df752739..921a9ede036 100644 --- a/spring-boot-samples/spring-boot-sample-tomcat/src/test/java/org/springframework/boot/sample/tomcat/SampleTomcatApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-tomcat/src/test/java/org/springframework/boot/sample/tomcat/SampleTomcatApplicationTests.java @@ -26,7 +26,6 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; -import org.springframework.boot.sample.tomcat.SampleTomcatApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -56,7 +55,7 @@ public class SampleTomcatApplicationTests { .run(SampleTomcatApplication.class); } }); - context = future.get(10, TimeUnit.SECONDS); + context = future.get(60, TimeUnit.SECONDS); } @AfterClass