Move `shell.*` to `management.shell.*`

This commit moves the `shell` namespace to `management.shell`

Closes gh-5703
This commit is contained in:
Stephane Nicoll 2016-04-15 18:10:58 +02:00
parent 6d11d73cbc
commit cc0fc07c0e
10 changed files with 223 additions and 292 deletions

View File

@ -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}.
* <p>
* 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}.
* <p>
* 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;
* <a href="http://www.crashub.org">crashub.org</a>. 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(

View File

@ -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 {

View File

@ -255,7 +255,7 @@
]
},
{
"name": "shell.auth.type",
"name": "management.shell.auth.type",
"values": [
{
"value": "simple",

View File

@ -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<String, Object> 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<Resource> 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<Resource> 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 {

View File

@ -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<String, String> map = new HashMap<String, String>();
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<String, String> map = new HashMap<String, String>();
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<String, String> map = new HashMap<String, String>();
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<String, String> map = new HashMap<String, String>();
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<String, String> map = new HashMap<String, String>();
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<String, String> map = new HashMap<String, String>();
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<String, String> map = new HashMap<String, String>();
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<String, String> map = new HashMap<String, String>();
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> T load(Class<T> 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

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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