diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfiguration.java index 9cb691523b..db9ed20424 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfiguration.java @@ -88,16 +88,17 @@ import org.springframework.util.StringUtils; * The default shell authentication method uses a username and password combination. If no * configuration is provided the default username is 'user' and the password will be * printed to console during application startup. Those default values can be overridden - * by using {@code shell.auth.simple.username} and {@code shell.auth.simple.password}. + * by using {@code management.shell.auth.simple.username} and + * {@code management.shell.auth.simple.password}. *

* If a Spring Security {@link AuthenticationManager} is detected, this configuration will * create a {@link CRaSHPlugin} to forward shell authentication requests to Spring - * Security. This authentication method will get enabled if {@code shell.auth} is set to - * {@code spring} or if no explicit {@code shell.auth} is provided and a - * {@link AuthenticationManager} is available. In the latter case shell access will be - * restricted to users having roles that match those configured in + * Security. This authentication method will get enabled if {@code management.shell.auth.type} + * is set to {@code spring} or if no explicit {@code management.shell.auth} is provided + * and a {@link AuthenticationManager} is available. In the latter case shell access will + * be restricted to users having roles that match those configured in * {@link ManagementServerProperties}. Required roles can be overridden by - * {@code shell.auth.spring.roles}. + * {@code management.shell.auth.spring.roles}. *

* To add customizations to the shell simply define beans of type {@link CRaSHPlugin} in * the application context. Those beans will get auto detected during startup and @@ -109,7 +110,7 @@ import org.springframework.util.StringUtils; * crashub.org. By default Boot will search for * commands using the following classpath scanning pattern {@code classpath*:/commands/**} * . To add different locations or override the default use - * {@code shell.command_path_patterns} in your application configuration. + * {@code management.shell.command-path-patterns} in your application configuration. * * @author Christian Dupuis * @author Matt Benson @@ -122,6 +123,8 @@ import org.springframework.util.StringUtils; ManagementWebSecurityAutoConfiguration.class }) public class CrshAutoConfiguration { + public static final String AUTH_PREFIX = ShellProperties.SHELL_PREFIX + ".auth"; + private final ShellProperties properties; public CrshAutoConfiguration(ShellProperties properties) { @@ -140,21 +143,21 @@ public class CrshAutoConfiguration { static class CrshAdditionalPropertiesConfiguration { @Bean - @ConditionalOnProperty(prefix = "shell", name = "auth", havingValue = "jaas") + @ConditionalOnProperty(prefix = AUTH_PREFIX, name = "type", havingValue = "jaas") @ConditionalOnMissingBean(CrshShellAuthenticationProperties.class) public JaasAuthenticationProperties jaasAuthenticationProperties() { return new JaasAuthenticationProperties(); } @Bean - @ConditionalOnProperty(prefix = "shell", name = "auth", havingValue = "key") + @ConditionalOnProperty(prefix = AUTH_PREFIX, name = "type", havingValue = "key") @ConditionalOnMissingBean(CrshShellAuthenticationProperties.class) public KeyAuthenticationProperties keyAuthenticationProperties() { return new KeyAuthenticationProperties(); } @Bean - @ConditionalOnProperty(prefix = "shell", name = "auth", havingValue = "simple", matchIfMissing = true) + @ConditionalOnProperty(prefix = AUTH_PREFIX, name = "type", havingValue = "simple", matchIfMissing = true) @ConditionalOnMissingBean(CrshShellAuthenticationProperties.class) public SimpleAuthenticationProperties simpleAuthenticationProperties() { return new SimpleAuthenticationProperties(); @@ -166,7 +169,7 @@ public class CrshAutoConfiguration { * Class to configure CRaSH to authenticate against Spring Security. */ @Configuration - @ConditionalOnProperty(prefix = "shell", name = "auth", havingValue = "spring", matchIfMissing = true) + @ConditionalOnProperty(prefix = AUTH_PREFIX, name = "type", havingValue = "spring", matchIfMissing = true) @ConditionalOnBean(AuthenticationManager.class) public static class AuthenticationManagerAdapterConfiguration { @@ -185,12 +188,12 @@ public class CrshAutoConfiguration { @Bean @ConditionalOnMissingBean(CrshShellAuthenticationProperties.class) public SpringAuthenticationProperties springAuthenticationProperties() { - // In case no shell.auth.type property is provided fall back to Spring Security - // based authentication and get role to access shell from + // In case no management.shell.auth.type property is provided fall back to + // Spring Security based authentication and get role to access shell from // ManagementServerProperties. - // In case shell.auth.type is set to spring and roles are configured using - // shell.auth.spring.roles the below default role will be overridden by - // ConfigurationProperties. + // In case management.shell.auth.type is set to spring and roles are + // configured using shell.auth.spring.roles the below default role will be + // overridden by ConfigurationProperties. SpringAuthenticationProperties authenticationProperties = new SpringAuthenticationProperties(); if (this.management != null) { authenticationProperties.setRoles( diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ShellProperties.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ShellProperties.java index ffa10c66ae..c3981ea1b5 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ShellProperties.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ShellProperties.java @@ -37,10 +37,13 @@ import org.springframework.util.StringUtils; * @author Christian Dupuis * @author Phillip Webb * @author Eddú Meléndez + * @author Stephane Nicoll */ -@ConfigurationProperties(prefix = "shell", ignoreUnknownFields = true) +@ConfigurationProperties(prefix = ShellProperties.SHELL_PREFIX, ignoreUnknownFields = true) public class ShellProperties { + public static final String SHELL_PREFIX = "management.shell"; + private static final Log logger = LogFactory.getLog(ShellProperties.class); private final Auth auth = new Auth(); @@ -372,7 +375,7 @@ public class ShellProperties { /** * Auth specific properties for JAAS authentication. */ - @ConfigurationProperties(prefix = "shell.auth.jaas", ignoreUnknownFields = false) + @ConfigurationProperties(prefix = SHELL_PREFIX + ".auth.jaas", ignoreUnknownFields = false) public static class JaasAuthenticationProperties extends CrshShellAuthenticationProperties { @@ -401,7 +404,7 @@ public class ShellProperties { /** * Auth specific properties for key authentication. */ - @ConfigurationProperties(prefix = "shell.auth.key", ignoreUnknownFields = false) + @ConfigurationProperties(prefix = SHELL_PREFIX + ".auth.key", ignoreUnknownFields = false) public static class KeyAuthenticationProperties extends CrshShellAuthenticationProperties { @@ -432,7 +435,7 @@ public class ShellProperties { /** * Auth specific properties for simple authentication. */ - @ConfigurationProperties(prefix = "shell.auth.simple", ignoreUnknownFields = false) + @ConfigurationProperties(prefix = SHELL_PREFIX + ".auth.simple", ignoreUnknownFields = false) public static class SimpleAuthenticationProperties extends CrshShellAuthenticationProperties { @@ -508,7 +511,7 @@ public class ShellProperties { /** * Auth specific properties for Spring authentication. */ - @ConfigurationProperties(prefix = "shell.auth.spring", ignoreUnknownFields = false) + @ConfigurationProperties(prefix = SHELL_PREFIX + ".auth.spring", ignoreUnknownFields = false) public static class SpringAuthenticationProperties extends CrshShellAuthenticationProperties { diff --git a/spring-boot-actuator/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-actuator/src/main/resources/META-INF/additional-spring-configuration-metadata.json index d67373739a..8ce47a4b0b 100644 --- a/spring-boot-actuator/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-actuator/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -255,7 +255,7 @@ ] }, { - "name": "shell.auth.type", + "name": "management.shell.auth.type", "values": [ { "value": "simple", diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfigurationTests.java index 09910bdb7b..973b8e573e 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfigurationTests.java @@ -37,6 +37,7 @@ import org.junit.After; import org.junit.Test; import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration; +import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.boot.testutil.Matched; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -64,6 +65,7 @@ import static org.hamcrest.CoreMatchers.isA; * @author Andreas Ahlenstorf * @author Eddú Meléndez * @author Matt Benson + * @author Stephane Nicoll */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class CrshAutoConfigurationTests { @@ -79,10 +81,8 @@ public class CrshAutoConfigurationTests { @Test public void testDisabledPlugins() throws Exception { - MockEnvironment env = new MockEnvironment(); - env.setProperty("shell.disabled_plugins", + load("management.shell.disabled_plugins=" + "termIOHandler, org.crsh.auth.AuthenticationPlugin, javaLanguage"); - load(env); PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class); assertThat(lifeCycle).isNotNull(); assertThat(lifeCycle.getContext().getPlugins(TermIOHandler.class)) @@ -98,9 +98,7 @@ public class CrshAutoConfigurationTests { @Test public void testAttributes() throws Exception { - this.context = new AnnotationConfigWebApplicationContext(); - this.context.register(CrshAutoConfiguration.class); - this.context.refresh(); + load(); PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class); Map attributes = lifeCycle.getContext().getAttributes(); assertThat(attributes.containsKey("spring.version")).isTrue(); @@ -111,11 +109,8 @@ public class CrshAutoConfigurationTests { @Test public void testSshConfiguration() { - MockEnvironment env = new MockEnvironment(); - env.setProperty("shell.ssh.enabled", "true"); - env.setProperty("shell.ssh.port", "3333"); - load(env); - + load("management.shell.ssh.enabled=true", + "management.shell.ssh.port=3333"); PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class); assertThat(lifeCycle.getConfig().getProperty("crash.ssh.port")).isEqualTo("3333"); assertThat(lifeCycle.getConfig().getProperty("crash.ssh.auth_timeout")) @@ -126,10 +121,8 @@ public class CrshAutoConfigurationTests { @Test public void testSshConfigurationWithKeyPath() { - MockEnvironment env = new MockEnvironment(); - env.setProperty("shell.ssh.enabled", "true"); - env.setProperty("shell.ssh.key_path", "~/.ssh/id.pem"); - load(env); + load("management.shell.ssh.enabled=true", + "management.shell.ssh.key_path=~/.ssh/id.pem"); PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class); assertThat(lifeCycle.getConfig().getProperty("crash.ssh.keypath")) .isEqualTo("~/.ssh/id.pem"); @@ -137,11 +130,9 @@ public class CrshAutoConfigurationTests { @Test public void testSshConfigurationCustomTimeouts() { - MockEnvironment env = new MockEnvironment(); - env.setProperty("shell.ssh.enabled", "true"); - env.setProperty("shell.ssh.auth-timeout", "300000"); - env.setProperty("shell.ssh.idle-timeout", "400000"); - load(env); + load("management.shell.ssh.enabled=true", + "management.shell.ssh.auth-timeout=300000", + "management.shell.ssh.idle-timeout=400000"); PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class); assertThat(lifeCycle.getConfig().getProperty("crash.ssh.auth_timeout")) .isEqualTo("300000"); @@ -149,18 +140,10 @@ public class CrshAutoConfigurationTests { .isEqualTo("400000"); } - private void load(MockEnvironment env) { - this.context = new AnnotationConfigWebApplicationContext(); - this.context.setEnvironment(env); - this.context.register(CrshAutoConfiguration.class); - this.context.refresh(); - } @Test public void testCommandResolution() { - this.context = new AnnotationConfigWebApplicationContext(); - this.context.register(CrshAutoConfiguration.class); - this.context.refresh(); + load(); PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class); int count = 0; Iterator resources = lifeCycle.getContext() @@ -182,9 +165,7 @@ public class CrshAutoConfigurationTests { @Test public void testDisabledCommandResolution() { - this.context = new AnnotationConfigWebApplicationContext(); - this.context.register(CrshAutoConfiguration.class); - this.context.refresh(); + load(); PluginLifeCycle lifeCycle = this.context.getBean(PluginLifeCycle.class); int count = 0; Iterator resources = lifeCycle.getContext() @@ -229,11 +210,10 @@ public class CrshAutoConfigurationTests { @Test public void testJaasAuthenticationProvider() { - MockEnvironment env = new MockEnvironment(); - env.setProperty("shell.auth.type", "jaas"); - env.setProperty("shell.auth.jaas.domain", "my-test-domain"); this.context = new AnnotationConfigWebApplicationContext(); - this.context.setEnvironment(env); + EnvironmentTestUtils.addEnvironment(this.context, + "management.shell.auth.type=jaas", + "management.shell.auth.jaas.domain=my-test-domain"); this.context.setServletContext(new MockServletContext()); this.context.register(SecurityConfiguration.class); this.context.register(CrshAutoConfiguration.class); @@ -246,11 +226,10 @@ public class CrshAutoConfigurationTests { @Test public void testKeyAuthenticationProvider() { - MockEnvironment env = new MockEnvironment(); - env.setProperty("shell.auth.type", "key"); - env.setProperty("shell.auth.key.path", "~/test.pem"); this.context = new AnnotationConfigWebApplicationContext(); - this.context.setEnvironment(env); + EnvironmentTestUtils.addEnvironment(this.context, + "management.shell.auth.type=key", + "management.shell.auth.key.path=~/test.pem"); this.context.setServletContext(new MockServletContext()); this.context.register(SecurityConfiguration.class); this.context.register(CrshAutoConfiguration.class); @@ -263,12 +242,11 @@ public class CrshAutoConfigurationTests { @Test public void testSimpleAuthenticationProvider() throws Exception { - MockEnvironment env = new MockEnvironment(); - env.setProperty("shell.auth.type", "simple"); - env.setProperty("shell.auth.simple.user.name", "user"); - env.setProperty("shell.auth.simple.user.password", "password"); this.context = new AnnotationConfigWebApplicationContext(); - this.context.setEnvironment(env); + EnvironmentTestUtils.addEnvironment(this.context, + "management.shell.auth.type=simple", + "management.shell.auth.simple.user.name=user", + "management.shell.auth.simple.user.password=password"); this.context.setServletContext(new MockServletContext()); this.context.register(SecurityConfiguration.class); this.context.register(CrshAutoConfiguration.class); @@ -293,10 +271,9 @@ public class CrshAutoConfigurationTests { @Test public void testSpringAuthenticationProvider() throws Exception { - MockEnvironment env = new MockEnvironment(); - env.setProperty("shell.auth.type", "spring"); this.context = new AnnotationConfigWebApplicationContext(); - this.context.setEnvironment(env); + EnvironmentTestUtils.addEnvironment(this.context, + "management.shell.auth.type=spring"); this.context.setServletContext(new MockServletContext()); this.context.register(SecurityConfiguration.class); this.context.register(CrshAutoConfiguration.class); @@ -345,6 +322,14 @@ public class CrshAutoConfigurationTests { SecurityConfiguration.PASSWORD)).isFalse(); } + private void load(String... environment) { + this.context = new AnnotationConfigWebApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, environment); + this.context.register(CrshAutoConfiguration.class); + this.context.refresh(); + } + + @Configuration public static class SecurityConfiguration { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ShellPropertiesTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ShellPropertiesTests.java index 1f23fd1acb..288a9d9553 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ShellPropertiesTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ShellPropertiesTests.java @@ -16,26 +16,27 @@ package org.springframework.boot.actuate.autoconfigure; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import java.util.Properties; import java.util.UUID; import org.crsh.plugin.PluginLifeCycle; +import org.junit.After; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; -import org.springframework.beans.MutablePropertyValues; +import org.springframework.beans.factory.BeanCreationException; import org.springframework.boot.actuate.autoconfigure.ShellProperties.CrshShellProperties; import org.springframework.boot.actuate.autoconfigure.ShellProperties.JaasAuthenticationProperties; import org.springframework.boot.actuate.autoconfigure.ShellProperties.KeyAuthenticationProperties; import org.springframework.boot.actuate.autoconfigure.ShellProperties.SimpleAuthenticationProperties; import org.springframework.boot.actuate.autoconfigure.ShellProperties.SpringAuthenticationProperties; -import org.springframework.boot.bind.RelaxedDataBinder; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.web.MockServletContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; @@ -46,271 +47,206 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link ShellProperties}. * * @author Christian Dupuis + * @author Stephane Nicoll */ public class ShellPropertiesTests { + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + @Test public void testBindingAuth() { - ShellProperties props = new ShellProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell"); - binder.bind(new MutablePropertyValues( - Collections.singletonMap("shell.auth.type", "spring"))); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); + ShellProperties props = load(ShellProperties.class, "management.shell.auth.type=spring"); assertThat(props.getAuth().getType()).isEqualTo("spring"); } @Test public void testBindingAuthIfEmpty() { - ShellProperties props = new ShellProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell"); - binder.bind( - new MutablePropertyValues(Collections.singletonMap("shell.auth.type", ""))); - assertThat(binder.getBindingResult().hasErrors()).isTrue(); - assertThat(props.getAuth().getType()).isEqualTo("simple"); + this.thrown.expect(BeanCreationException.class); + this.thrown.expectMessage("Auth type must not be empty"); + load(ShellProperties.class, "management.shell.auth.type= "); } @Test public void testBindingCommandRefreshInterval() { - ShellProperties props = new ShellProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell"); - binder.setConversionService(new DefaultConversionService()); - binder.bind(new MutablePropertyValues( - Collections.singletonMap("shell.command_refresh_interval", "1"))); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); + ShellProperties props = load(ShellProperties.class, "management.shell.command-refresh-interval=1"); assertThat(props.getCommandRefreshInterval()).isEqualTo(1); } @Test public void testBindingCommandPathPatterns() { - ShellProperties props = new ShellProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell"); - binder.setConversionService(new DefaultConversionService()); - binder.bind(new MutablePropertyValues(Collections - .singletonMap("shell.command_path_patterns", "pattern1, pattern2"))); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); + ShellProperties props = load(ShellProperties.class, + "management.shell.command-path-patterns=pattern1, pattern2"); assertThat(props.getCommandPathPatterns().length).isEqualTo(2); - Assert.assertArrayEquals(new String[] { "pattern1", "pattern2" }, + Assert.assertArrayEquals(new String[] {"pattern1", "pattern2"}, props.getCommandPathPatterns()); } @Test public void testBindingConfigPathPatterns() { - ShellProperties props = new ShellProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell"); - binder.setConversionService(new DefaultConversionService()); - binder.bind(new MutablePropertyValues(Collections - .singletonMap("shell.config_path_patterns", "pattern1, pattern2"))); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); + ShellProperties props = load(ShellProperties.class, + "management.shell.config-path-patterns=pattern1, pattern2"); assertThat(props.getConfigPathPatterns().length).isEqualTo(2); - Assert.assertArrayEquals(new String[] { "pattern1", "pattern2" }, + Assert.assertArrayEquals(new String[] {"pattern1", "pattern2"}, props.getConfigPathPatterns()); } @Test public void testBindingDisabledPlugins() { - ShellProperties props = new ShellProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell"); - binder.setConversionService(new DefaultConversionService()); - binder.bind(new MutablePropertyValues(Collections - .singletonMap("shell.disabled_plugins", "pattern1, pattern2"))); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); + ShellProperties props = load(ShellProperties.class, + "management.shell.disabled-plugins=pattern1, pattern2"); assertThat(props.getDisabledPlugins().length).isEqualTo(2); assertThat(props.getDisabledPlugins()).containsExactly("pattern1", "pattern2"); } @Test public void testBindingDisabledCommands() { - ShellProperties props = new ShellProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell"); - binder.setConversionService(new DefaultConversionService()); - binder.bind(new MutablePropertyValues(Collections - .singletonMap("shell.disabled_commands", "pattern1, pattern2"))); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); + ShellProperties props = load(ShellProperties.class, + "management.shell.disabled-commands=pattern1, pattern2"); assertThat(props.getDisabledCommands()).containsExactly("pattern1", "pattern2"); } @Test public void testBindingSsh() { - ShellProperties props = new ShellProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell"); - binder.setConversionService(new DefaultConversionService()); - Map map = new HashMap(); - map.put("shell.ssh.enabled", "true"); - map.put("shell.ssh.port", "2222"); - map.put("shell.ssh.key_path", "~/.ssh/test.pem"); - binder.bind(new MutablePropertyValues(map)); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); - + ShellProperties props = load(ShellProperties.class, + "management.shell.ssh.enabled=true", + "management.shell.ssh.port=2222", + "management.shell.ssh.key-path=~/.ssh/test.pem"); Properties p = props.asCrshShellConfig(); - assertThat(p.get("crash.ssh.port")).isEqualTo("2222"); assertThat(p.get("crash.ssh.keypath")).isEqualTo("~/.ssh/test.pem"); } @Test public void testBindingSshIgnored() { - ShellProperties props = new ShellProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell"); - binder.setConversionService(new DefaultConversionService()); - Map map = new HashMap(); - map.put("shell.ssh.enabled", "false"); - map.put("shell.ssh.port", "2222"); - map.put("shell.ssh.key_path", "~/.ssh/test.pem"); - binder.bind(new MutablePropertyValues(map)); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); - + ShellProperties props = load(ShellProperties.class, + "management.shell.ssh.enabled=false", + "management.shell.ssh.port=2222", + "management.shell.ssh.key-path=~/.ssh/test.pem"); Properties p = props.asCrshShellConfig(); - assertThat(p.get("crash.ssh.port")).isNull(); assertThat(p.get("crash.ssh.keypath")).isNull(); } @Test public void testBindingTelnet() { - ShellProperties props = new ShellProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell"); - binder.setConversionService(new DefaultConversionService()); - Map map = new HashMap(); - map.put("shell.telnet.enabled", "true"); - map.put("shell.telnet.port", "2222"); - binder.bind(new MutablePropertyValues(map)); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); - + ShellProperties props = load(ShellProperties.class, + "management.shell.telnet.enabled=true", + "management.shell.telnet.port=2222"); Properties p = props.asCrshShellConfig(); - assertThat(p.get("crash.telnet.port")).isEqualTo("2222"); } @Test public void testBindingTelnetIgnored() { - ShellProperties props = new ShellProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell"); - binder.setConversionService(new DefaultConversionService()); - Map map = new HashMap(); - map.put("shell.telnet.enabled", "false"); - map.put("shell.telnet.port", "2222"); - binder.bind(new MutablePropertyValues(map)); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); - + ShellProperties props = load(ShellProperties.class, + "management.shell.telnet.enabled=false", + "management.shell.telnet.port=2222"); Properties p = props.asCrshShellConfig(); - assertThat(p.get("crash.telnet.port")).isNull(); } @Test public void testBindingJaas() { - JaasAuthenticationProperties props = new JaasAuthenticationProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell.auth.jaas"); - binder.setConversionService(new DefaultConversionService()); - Map map = new HashMap(); - map.put("shell.auth.jaas.domain", "my-test-domain"); - binder.bind(new MutablePropertyValues(map)); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); - + JaasAuthenticationProperties props = load(JaasAuthenticationProperties.class, + "management.shell.auth.jaas.domain=my-test-domain"); Properties p = new Properties(); props.applyToCrshShellConfig(p); - assertThat(p.get("crash.auth.jaas.domain")).isEqualTo("my-test-domain"); } @Test public void testBindingKey() { - KeyAuthenticationProperties props = new KeyAuthenticationProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell.auth.key"); - binder.setConversionService(new DefaultConversionService()); - Map map = new HashMap(); - map.put("shell.auth.key.path", "~/.ssh/test.pem"); - binder.bind(new MutablePropertyValues(map)); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); - + KeyAuthenticationProperties props = load(KeyAuthenticationProperties.class, + "management.shell.auth.key.path=~/.ssh/test.pem"); Properties p = new Properties(); props.applyToCrshShellConfig(p); - assertThat(p.get("crash.auth.key.path")).isEqualTo("~/.ssh/test.pem"); } @Test public void testBindingKeyIgnored() { - KeyAuthenticationProperties props = new KeyAuthenticationProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell.auth.key"); - binder.setConversionService(new DefaultConversionService()); - Map map = new HashMap(); - binder.bind(new MutablePropertyValues(map)); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); - + KeyAuthenticationProperties props = load(KeyAuthenticationProperties.class); Properties p = new Properties(); props.applyToCrshShellConfig(p); - assertThat(p.get("crash.auth.key.path")).isNull(); } @Test public void testBindingSimple() { - SimpleAuthenticationProperties props = new SimpleAuthenticationProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell.auth.simple"); - binder.setConversionService(new DefaultConversionService()); - Map map = new HashMap(); - map.put("shell.auth.simple.user.name", "username123"); - map.put("shell.auth.simple.user.password", "password123"); - binder.bind(new MutablePropertyValues(map)); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); - + SimpleAuthenticationProperties props = load(SimpleAuthenticationProperties.class, + "management.shell.auth.simple.user.name=username123", + "management.shell.auth.simple.user.password=password123"); Properties p = new Properties(); props.applyToCrshShellConfig(p); - assertThat(p.get("crash.auth.simple.username")).isEqualTo("username123"); assertThat(p.get("crash.auth.simple.password")).isEqualTo("password123"); } @Test - public void testDefaultPasswordAutogeneratedIfUnresolvedPlaceholder() { - SimpleAuthenticationProperties security = new SimpleAuthenticationProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(security, "security"); - binder.bind(new MutablePropertyValues(Collections - .singletonMap("shell.auth.simple.user.password", "${ADMIN_PASSWORD}"))); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); + public void testDefaultPasswordAutoGeneratedIfUnresolvedPlaceholder() { + SimpleAuthenticationProperties security = load(SimpleAuthenticationProperties.class, + "management.shell.auth.simple.user.password=${ADMIN_PASSWORD}"); assertThat(security.getUser().isDefaultPassword()).isTrue(); } @Test - public void testDefaultPasswordAutogeneratedIfEmpty() { - SimpleAuthenticationProperties security = new SimpleAuthenticationProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(security, "security"); - binder.bind(new MutablePropertyValues( - Collections.singletonMap("shell.auth.simple.user.password", ""))); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); + public void testDefaultPasswordAutoGeneratedIfEmpty() { + SimpleAuthenticationProperties security = load(SimpleAuthenticationProperties.class, + "management.shell.auth.simple.user.password="); assertThat(security.getUser().isDefaultPassword()).isTrue(); } @Test public void testBindingSpring() { - SpringAuthenticationProperties props = new SpringAuthenticationProperties(); - RelaxedDataBinder binder = new RelaxedDataBinder(props, "shell.auth.spring"); - binder.bind(new MutablePropertyValues( - Collections.singletonMap("shell.auth.spring.roles", "role1, role2"))); - assertThat(binder.getBindingResult().hasErrors()).isFalse(); - + SpringAuthenticationProperties props = load(SpringAuthenticationProperties.class, + "management.shell.auth.spring.roles=role1,role2"); Properties p = new Properties(); props.applyToCrshShellConfig(p); - assertThat(p.get("crash.auth.spring.roles")).isEqualTo("role1,role2"); } @Test public void testCustomShellProperties() throws Exception { MockEnvironment env = new MockEnvironment(); - env.setProperty("shell.auth.type", "simple"); - AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); - context.setEnvironment(env); - context.setServletContext(new MockServletContext()); - context.register(TestShellConfiguration.class); - context.register(CrshAutoConfiguration.class); - context.refresh(); + env.setProperty("management.shell.auth.type", "simple"); + AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); + ctx.setEnvironment(env); + ctx.setServletContext(new MockServletContext()); + ctx.register(TestShellConfiguration.class); + ctx.register(CrshAutoConfiguration.class); + ctx.refresh(); - PluginLifeCycle lifeCycle = context.getBean(PluginLifeCycle.class); + PluginLifeCycle lifeCycle = ctx.getBean(PluginLifeCycle.class); String uuid = lifeCycle.getConfig().getProperty("test.uuid"); assertThat(uuid).isEqualTo(TestShellConfiguration.uuid); - context.close(); + ctx.close(); + } + + private T load(Class type, String... environment) { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(ctx, environment); + ctx.register(TestConfiguration.class); + ctx.refresh(); + this.context = ctx; + return this.context.getBean(type); + } + + @Configuration + @EnableConfigurationProperties({ShellProperties.class, + JaasAuthenticationProperties.class, KeyAuthenticationProperties.class, + SimpleAuthenticationProperties.class, SpringAuthenticationProperties.class}) + static class TestConfiguration { + } @Configuration diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index f16d779510..e3cfa60054 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -1007,29 +1007,29 @@ content into your application; rather pick only the properties that you need. management.info.git.enabled=true # Enable git info. management.info.git.mode=simple # Mode to use to expose git information. + # REMOTE SHELL ({sc-spring-boot-actuator}/autoconfigure/ShellProperties.{sc-ext}[ShellProperties]) + management.shell.auth.type=simple # Authentication type. Auto-detected according to the environment. + management.shell.auth.jaas.domain=my-domain # JAAS domain. + management.shell.auth.key.path= # Path to the authentication key. This should point to a valid ".pem" file. + management.shell.auth.simple.user.name=user # Login user. + management.shell.auth.simple.user.password= # Login password. + management.shell.auth.spring.roles=ADMIN # Comma-separated list of required roles to login to the CRaSH console. + management.shell.command-path-patterns=classpath*:/commands/**,classpath*:/crash/commands/** # Patterns to use to look for commands. + management.shell.command-refresh-interval=-1 # Scan for changes and update the command if necessary (in seconds). + management.shell.config-path-patterns=classpath*:/crash/* # Patterns to use to look for configurations. + management.shell.disabled-commands=jpa*,jdbc*,jndi* # Comma-separated list of commands to disable. + management.shell.disabled-plugins= # Comma-separated list of plugins to disable. Certain plugins are disabled by default based on the environment. + management.shell.ssh.auth-timeout = # Number of milliseconds after user will be prompted to login again. + management.shell.ssh.enabled=true # Enable CRaSH SSH support. + management.shell.ssh.idle-timeout = # Number of milliseconds after which unused connections are closed. + management.shell.ssh.key-path= # Path to the SSH server key. + management.shell.ssh.port=2000 # SSH port. + management.shell.telnet.enabled=false # Enable CRaSH telnet support. Enabled by default if the TelnetPlugin is available. + management.shell.telnet.port=5000 # Telnet port. + # TRACING ({sc-spring-boot-actuator}/trace/TraceProperties.{sc-ext}[TraceProperties]) management.trace.include=request-headers,response-headers,errors # Items to be included in the trace. - # REMOTE SHELL - shell.auth.type=simple # Authentication type. Auto-detected according to the environment. - shell.auth.jaas.domain=my-domain # JAAS domain. - shell.auth.key.path= # Path to the authentication key. This should point to a valid ".pem" file. - shell.auth.simple.user.name=user # Login user. - shell.auth.simple.user.password= # Login password. - shell.auth.spring.roles=ADMIN # Comma-separated list of required roles to login to the CRaSH console. - shell.command-path-patterns=classpath*:/commands/**,classpath*:/crash/commands/** # Patterns to use to look for commands. - shell.command-refresh-interval=-1 # Scan for changes and update the command if necessary (in seconds). - shell.config-path-patterns=classpath*:/crash/* # Patterns to use to look for configurations. - shell.disabled-commands=jpa*,jdbc*,jndi* # Comma-separated list of commands to disable. - shell.disabled-plugins= # Comma-separated list of plugins to disable. Certain plugins are disabled by default based on the environment. - shell.ssh.auth-timeout = # Number of milliseconds after user will be prompted to login again. - shell.ssh.enabled=true # Enable CRaSH SSH support. - shell.ssh.idle-timeout = # Number of milliseconds after which unused connections are closed. - shell.ssh.key-path= # Path to the SSH server key. - shell.ssh.port=2000 # SSH port. - shell.telnet.enabled=false # Enable CRaSH telnet support. Enabled by default if the TelnetPlugin is available. - shell.telnet.port=5000 # Telnet port. - # METRICS EXPORT ({sc-spring-boot-actuator}/metrics/export/MetricExportProperties.{sc-ext}[MetricExportProperties]) spring.metrics.export.aggregate.key-pattern= # Pattern that tells the aggregator what to do with the keys from the source repository. spring.metrics.export.aggregate.prefix= # Prefix for global repository if active. diff --git a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index 1b0f5b512d..8992129cd4 100644 --- a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -817,9 +817,10 @@ and `endpoint` commands. [[production-ready-remote-shell-credentials]] ==== Remote shell credentials -You can use the `shell.auth.simple.user.name` and `shell.auth.simple.user.password` properties -to configure custom connection credentials. It is also possible to use a -'`Spring Security`' `AuthenticationManager` to handle login duties. See the +You can use the `management.shell.auth.simple.user.name` and +`management.shell.auth.simple.user.password` properties to configure custom connection +credentials. It is also possible to use a '`Spring Security`' `AuthenticationManager` to +handle login duties. See the {dc-spring-boot-actuator}/autoconfigure/CrshAutoConfiguration.{dc-ext}[`CrshAutoConfiguration`] and {dc-spring-boot-actuator}/autoconfigure/ShellProperties.{dc-ext}[`ShellProperties`] Javadoc for full details. diff --git a/spring-boot-samples/spring-boot-sample-actuator-log4j2/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-actuator-log4j2/src/main/resources/application.properties index d336fdfcb7..23b700c536 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-log4j2/src/main/resources/application.properties +++ b/spring-boot-samples/spring-boot-sample-actuator-log4j2/src/main/resources/application.properties @@ -1,17 +1,18 @@ -#logging.file: /tmp/logs/app.log -#server.port: 8080 -#management.port: 8080 -management.address: 127.0.0.1 -endpoints.shutdown.enabled: true -server.tomcat.basedir: target/tomcat -server.tomcat.access_log_pattern: %h %t "%r" %s %b -security.require_ssl: false -service.name: Daniel -shell.ssh.enabled: true -shell.ssh.port: 2222 -#shell.telnet.enabled: false -#shell.telnet.port: 1111 -shell.auth: spring -#shell.auth: key -#shell.auth.key.path: ${user.home}/test/id_rsa.pub.pem -#shell.auth: simple +#logging.file=/tmp/logs/app.log +#server.port=8080 +#management.port=8080 +management.address=127.0.0.1 +management.shell.ssh.enabled=true +management.shell.ssh.port=2222 +#management.shell.telnet.enabled=false +#management.shell.telnet.port=1111 +management.shell.auth.type=spring +#management.shell.auth.type=key +#management.shell.auth.key.path=${user.home}/test/id_rsa.pub.pem +#management.shell.auth.type=simple + +endpoints.shutdown.enabled=true +server.tomcat.basedir=target/tomcat +server.tomcat.access_log_pattern=%h %t "%r" %s %b +security.require_ssl=false +service.name=Daniel diff --git a/spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/resources/application.properties index 3df2e92ef3..b7fe4cf9e3 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/resources/application.properties +++ b/spring-boot-samples/spring-boot-sample-actuator-noweb/src/main/resources/application.properties @@ -1,5 +1,6 @@ -service.name: Phil -shell.ssh.enabled: true -shell.ssh.port: 2222 -shell.auth: simple -shell.auth.simple.user.password: password +service.name=Phil + +management.shell.ssh.enabled=true +management.shell.ssh.port=2222 +management.shell.auth.type=simple +management.shell.auth.simple.user.password=password diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-actuator/src/main/resources/application.properties index 8b0f97f092..01a63220a3 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/main/resources/application.properties +++ b/spring-boot-samples/spring-boot-sample-actuator/src/main/resources/application.properties @@ -1,28 +1,29 @@ -# logging.file: /tmp/logs/app.log -# logging.level.org.springframework.security: DEBUG -management.address: 127.0.0.1 -#management.port: 8181 -endpoints.shutdown.enabled: true -server.tomcat.basedir: target/tomcat -server.tomcat.access_log_enabled: true -server.tomcat.access_log_pattern: %h %t "%r" %s %b -security.require_ssl: false -service.name: Phil -#spring.jackson.serialization.INDENT_OUTPUT: true -shell.ssh.enabled: true -shell.ssh.port: 2222 -#shell.telnet.enabled: false -#shell.telnet.port: 1111 -shell.auth: spring -#shell.auth: key -#shell.auth.key.path: ${user.home}/test/id_rsa.pub.pem -#shell.auth: simple -spring.jmx.enabled: true - -spring.jackson.serialization.write_dates_as_timestamps=false +# logging.file=/tmp/logs/app.log +# logging.level.org.springframework.security=DEBUG +management.address=127.0.0.1 +#management.port=8181 +management.shell.ssh.enabled=true +management.shell.ssh.port=2222 +#management.shell.telnet.enabled=false +#management.shell.telnet.port=1111 +management.shell.auth.type=spring +#management.shell.auth.type=key +#management.shell.auth.key.path=${user.home}/test/id_rsa.pub.pem +#management.shell.auth.type=simple management.info.build.mode=full +endpoints.shutdown.enabled=true +server.tomcat.basedir=target/tomcat +server.tomcat.access_log_enabled=true +server.tomcat.access_log_pattern=%h %t "%r" %s %b +security.require_ssl=false +service.name=Phil +#spring.jackson.serialization.INDENT_OUTPUT=true +spring.jmx.enabled=true + +spring.jackson.serialization.write_dates_as_timestamps=false + management.trace.include=REQUEST_HEADERS,RESPONSE_HEADERS,ERRORS,PATH_INFO,\ PATH_TRANSLATED,CONTEXT_PATH,USER_PRINCIPAL,PARAMETERS,QUERY_STRING,AUTH_TYPE,\ REMOTE_ADDRESS,SESSION_ID,REMOTE_USER