Fixed inconsistency in method naming after last polish. Method and class name should use 'crsh' instead of 'crash' to be aligned with CRaSH code base.

Implemented facility to provide custom shell properties by adding beans of type CrshShellProperties to the ApplicationContext.
pull/123/head
Christian Dupuis 11 years ago
parent dc252c7417
commit e009d3e47d

@ -45,7 +45,7 @@ import org.crsh.vfs.spi.FSDriver;
import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.properties.ShellProperties; import org.springframework.boot.actuate.properties.ShellProperties;
import org.springframework.boot.actuate.properties.ShellProperties.AuthenticationProperties; import org.springframework.boot.actuate.properties.ShellProperties.CrshShellProperties;
import org.springframework.boot.actuate.properties.ShellProperties.JaasAuthenticationProperties; import org.springframework.boot.actuate.properties.ShellProperties.JaasAuthenticationProperties;
import org.springframework.boot.actuate.properties.ShellProperties.KeyAuthenticationProperties; import org.springframework.boot.actuate.properties.ShellProperties.KeyAuthenticationProperties;
import org.springframework.boot.actuate.properties.ShellProperties.SimpleAuthenticationProperties; import org.springframework.boot.actuate.properties.ShellProperties.SimpleAuthenticationProperties;
@ -110,29 +110,25 @@ public class CrshAutoConfiguration {
@Bean @Bean
@ConditionalOnExpression("'${shell.auth:simple}' == 'jaas'") @ConditionalOnExpression("'${shell.auth:simple}' == 'jaas'")
@ConditionalOnMissingBean({ AuthenticationProperties.class }) public CrshShellProperties jaasAuthenticationProperties() {
public AuthenticationProperties jaasAuthenticationProperties() {
return new JaasAuthenticationProperties(); return new JaasAuthenticationProperties();
} }
@Bean @Bean
@ConditionalOnExpression("'${shell.auth:simple}' == 'key'") @ConditionalOnExpression("'${shell.auth:simple}' == 'key'")
@ConditionalOnMissingBean({ AuthenticationProperties.class }) public CrshShellProperties keyAuthenticationProperties() {
public AuthenticationProperties keyAuthenticationProperties() {
return new KeyAuthenticationProperties(); return new KeyAuthenticationProperties();
} }
@Bean @Bean
@ConditionalOnExpression("'${shell.auth:simple}' == 'simple'") @ConditionalOnExpression("'${shell.auth:simple}' == 'simple'")
@ConditionalOnMissingBean({ AuthenticationProperties.class }) public CrshShellProperties simpleAuthenticationProperties() {
public AuthenticationProperties simpleAuthenticationProperties() {
return new SimpleAuthenticationProperties(); return new SimpleAuthenticationProperties();
} }
@Bean @Bean
@ConditionalOnExpression("'${shell.auth:simple}' == 'spring'") @ConditionalOnExpression("'${shell.auth:simple}' == 'spring'")
@ConditionalOnMissingBean({ AuthenticationProperties.class }) public CrshShellProperties SpringAuthenticationProperties() {
public AuthenticationProperties SpringAuthenticationProperties() {
return new SpringAuthenticationProperties(); return new SpringAuthenticationProperties();
} }
@ -140,7 +136,7 @@ public class CrshAutoConfiguration {
@ConditionalOnMissingBean({ PluginLifeCycle.class }) @ConditionalOnMissingBean({ PluginLifeCycle.class })
public PluginLifeCycle shellBootstrap() { public PluginLifeCycle shellBootstrap() {
CrshBootstrapBean bootstrapBean = new CrshBootstrapBean(); CrshBootstrapBean bootstrapBean = new CrshBootstrapBean();
bootstrapBean.setConfig(this.properties.asCrashShellConfig()); bootstrapBean.setConfig(this.properties.asCrshShellConfig());
return bootstrapBean; return bootstrapBean;
} }
@ -244,7 +240,7 @@ public class CrshAutoConfiguration {
Authentication token = new UsernamePasswordAuthenticationToken(username, Authentication token = new UsernamePasswordAuthenticationToken(username,
password); password);
try { try {
// Authenticate first to make credentials are valid // Authenticate first to make sure credentials are valid
token = this.authenticationManager.authenticate(token); token = this.authenticationManager.authenticate(token);
} }
catch (AuthenticationException ex) { catch (AuthenticationException ex) {
@ -321,7 +317,7 @@ public class CrshAutoConfiguration {
List<CRaSHPlugin<?>> plugins = new ArrayList<CRaSHPlugin<?>>(); List<CRaSHPlugin<?>> plugins = new ArrayList<CRaSHPlugin<?>>();
for (CRaSHPlugin<?> p : super.getPlugins()) { for (CRaSHPlugin<?> p : super.getPlugins()) {
if (!shouldFilter(p)) { if (isEnabled(p)) {
plugins.add(p); plugins.add(p);
} }
} }
@ -329,7 +325,7 @@ public class CrshAutoConfiguration {
Collection<CRaSHPlugin> pluginBeans = this.beanFactory.getBeansOfType( Collection<CRaSHPlugin> pluginBeans = this.beanFactory.getBeansOfType(
CRaSHPlugin.class).values(); CRaSHPlugin.class).values();
for (CRaSHPlugin<?> pluginBean : pluginBeans) { for (CRaSHPlugin<?> pluginBean : pluginBeans) {
if (!shouldFilter(pluginBean)) { if (isEnabled(pluginBean)) {
plugins.add(pluginBean); plugins.add(pluginBean);
} }
} }
@ -338,33 +334,33 @@ public class CrshAutoConfiguration {
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
protected boolean shouldFilter(CRaSHPlugin<?> plugin) { protected boolean isEnabled(CRaSHPlugin<?> plugin) {
Assert.notNull(plugin); Assert.notNull(plugin, "Plugin must not be null");
if (ObjectUtils.isEmpty(this.disabledPlugins)) { if (ObjectUtils.isEmpty(this.disabledPlugins)) {
return false; return true;
} }
Set<Class> pluginClasses = ClassUtils.getAllInterfacesAsSet(plugin); Set<Class> pluginClasses = ClassUtils.getAllInterfacesAsSet(plugin);
pluginClasses.add(plugin.getClass()); pluginClasses.add(plugin.getClass());
for (Class<?> pluginClass : pluginClasses) { for (Class<?> pluginClass : pluginClasses) {
if (isDisabled(pluginClass)) { if (isEnabled(pluginClass)) {
return true; return true;
} }
} }
return false; return false;
} }
private boolean isDisabled(Class<?> pluginClass) { private boolean isEnabled(Class<?> pluginClass) {
for (String disabledPlugin : this.disabledPlugins) { for (String disabledPlugin : this.disabledPlugins) {
if (ClassUtils.getShortName(pluginClass).equalsIgnoreCase(disabledPlugin) if (ClassUtils.getShortName(pluginClass).equalsIgnoreCase(disabledPlugin)
|| ClassUtils.getQualifiedName(pluginClass).equalsIgnoreCase( || ClassUtils.getQualifiedName(pluginClass).equalsIgnoreCase(
disabledPlugin)) { disabledPlugin)) {
return true; return false;
} }
} }
return false; return true;
} }
} }

@ -41,7 +41,7 @@ public class ShellProperties {
private String auth = "simple"; private String auth = "simple";
@Autowired(required = false) @Autowired(required = false)
private AuthenticationProperties authenticationProperties = new SimpleAuthenticationProperties(); private CrshShellProperties[] additionalProperties = new CrshShellProperties[] { new SimpleAuthenticationProperties() };
private int commandRefreshInterval = -1; private int commandRefreshInterval = -1;
@ -65,15 +65,13 @@ public class ShellProperties {
return this.auth; return this.auth;
} }
public void setAuthenticationProperties( public void setAdditionalProperties(CrshShellProperties[] additionalProperties) {
AuthenticationProperties authenticationProperties) { Assert.notNull(additionalProperties, "additionalProperties must not be null");
Assert.notNull(authenticationProperties, this.additionalProperties = additionalProperties;
"AuthenticationProperties must not be null");
this.authenticationProperties = authenticationProperties;
} }
public AuthenticationProperties getAuthenticationProperties() { public CrshShellProperties[] getAdditionalProperties() {
return this.authenticationProperties; return this.additionalProperties;
} }
public void setCommandRefreshInterval(int commandRefreshInterval) { public void setCommandRefreshInterval(int commandRefreshInterval) {
@ -123,16 +121,15 @@ public class ShellProperties {
* Return a properties file configured from these settings that can be applied to a * Return a properties file configured from these settings that can be applied to a
* CRaSH shell instance. * CRaSH shell instance.
*/ */
public Properties asCrashShellConfig() { public Properties asCrshShellConfig() {
Properties properties = new Properties(); Properties properties = new Properties();
this.ssh.applyToCrashShellConfig(properties); this.ssh.applyToCrshShellConfig(properties);
this.telnet.applyToCrashShellConfig(properties); this.telnet.applyToCrshShellConfig(properties);
properties.put("crash.auth", this.auth); properties.put("crash.auth", this.auth);
if (this.authenticationProperties != null) { for (CrshShellProperties shellProperties : this.additionalProperties) {
this.authenticationProperties.applyToCrashShellConfig(properties); shellProperties.applyToCrshShellConfig(properties);
} }
if (this.commandRefreshInterval > 0) { if (this.commandRefreshInterval > 0) {
properties.put("crash.vfs.refresh_period", properties.put("crash.vfs.refresh_period",
String.valueOf(this.commandRefreshInterval)); String.valueOf(this.commandRefreshInterval));
@ -151,10 +148,22 @@ public class ShellProperties {
return properties; return properties;
} }
/**
* Base class for Auth specific properties.
*/
public static abstract class CrshShellProperties {
/**
* Apply the properties to a CRaSH configuration.
*/
protected abstract void applyToCrshShellConfig(Properties config);
}
/** /**
* SSH properties * SSH properties
*/ */
public static class Ssh { public static class Ssh extends CrshShellProperties {
private boolean enabled = true; private boolean enabled = true;
@ -162,7 +171,8 @@ public class ShellProperties {
private String port = "2000"; private String port = "2000";
protected void applyToCrashShellConfig(Properties config) { @Override
protected void applyToCrshShellConfig(Properties config) {
if (this.enabled) { if (this.enabled) {
config.put("crash.ssh.port", this.port); config.put("crash.ssh.port", this.port);
if (this.keyPath != null) { if (this.keyPath != null) {
@ -180,12 +190,12 @@ public class ShellProperties {
} }
public void setKeyPath(String keyPath) { public void setKeyPath(String keyPath) {
Assert.hasText(keyPath); Assert.hasText(keyPath, "keyPath must have text");
this.keyPath = keyPath; this.keyPath = keyPath;
} }
public void setPort(Integer port) { public void setPort(Integer port) {
Assert.notNull(port); Assert.notNull(port, "port must not be null");
this.port = port.toString(); this.port = port.toString();
} }
@ -194,13 +204,14 @@ public class ShellProperties {
/** /**
* Telnet properties * Telnet properties
*/ */
public static class Telnet { public static class Telnet extends CrshShellProperties {
private boolean enabled = false; private boolean enabled = false;
private String port = "5000"; private String port = "5000";
protected void applyToCrashShellConfig(Properties config) { @Override
protected void applyToCrshShellConfig(Properties config) {
if (this.enabled) { if (this.enabled) {
config.put("crash.telnet.port", this.port); config.put("crash.telnet.port", this.port);
} }
@ -215,39 +226,27 @@ public class ShellProperties {
} }
public void setPort(Integer port) { public void setPort(Integer port) {
Assert.notNull(port); Assert.notNull(port, "port must not be null");
this.port = port.toString(); this.port = port.toString();
} }
} }
/**
* Base class for Auth specific properties.
*/
public static abstract class AuthenticationProperties {
/**
* Apply the settings to a CRaSH configuration.
*/
protected abstract void applyToCrashShellConfig(Properties config);
}
/** /**
* Auth specific properties for JAAS authentication * Auth specific properties for JAAS authentication
*/ */
@ConfigurationProperties(name = "shell.auth.jaas", ignoreUnknownFields = false) @ConfigurationProperties(name = "shell.auth.jaas", ignoreUnknownFields = false)
public static class JaasAuthenticationProperties extends AuthenticationProperties { public static class JaasAuthenticationProperties extends CrshShellProperties {
private String domain = "my-domain"; private String domain = "my-domain";
@Override @Override
protected void applyToCrashShellConfig(Properties config) { protected void applyToCrshShellConfig(Properties config) {
config.put("crash.auth.jaas.domain", this.domain); config.put("crash.auth.jaas.domain", this.domain);
} }
public void setDomain(String domain) { public void setDomain(String domain) {
Assert.hasText(domain); Assert.hasText(domain, "domain must have text");
this.domain = domain; this.domain = domain;
} }
@ -257,19 +256,19 @@ public class ShellProperties {
* Auth specific properties for key authentication * Auth specific properties for key authentication
*/ */
@ConfigurationProperties(name = "shell.auth.key", ignoreUnknownFields = false) @ConfigurationProperties(name = "shell.auth.key", ignoreUnknownFields = false)
public static class KeyAuthenticationProperties extends AuthenticationProperties { public static class KeyAuthenticationProperties extends CrshShellProperties {
private String path; private String path;
@Override @Override
protected void applyToCrashShellConfig(Properties config) { protected void applyToCrshShellConfig(Properties config) {
if (this.path != null) { if (this.path != null) {
config.put("crash.auth.key.path", this.path); config.put("crash.auth.key.path", this.path);
} }
} }
public void setPath(String path) { public void setPath(String path) {
Assert.hasText(path); Assert.hasText(path, "path must have text");
this.path = path; this.path = path;
} }
@ -279,7 +278,7 @@ public class ShellProperties {
* Auth specific properties for simple authentication * Auth specific properties for simple authentication
*/ */
@ConfigurationProperties(name = "shell.auth.simple", ignoreUnknownFields = false) @ConfigurationProperties(name = "shell.auth.simple", ignoreUnknownFields = false)
public static class SimpleAuthenticationProperties extends AuthenticationProperties { public static class SimpleAuthenticationProperties extends CrshShellProperties {
private static Log logger = LogFactory private static Log logger = LogFactory
.getLog(SimpleAuthenticationProperties.class); .getLog(SimpleAuthenticationProperties.class);
@ -291,7 +290,7 @@ public class ShellProperties {
private boolean defaultPassword = true; private boolean defaultPassword = true;
@Override @Override
protected void applyToCrashShellConfig(Properties config) { protected void applyToCrshShellConfig(Properties config) {
config.put("crash.auth.simple.username", this.username); config.put("crash.auth.simple.username", this.username);
config.put("crash.auth.simple.password", this.password); config.put("crash.auth.simple.password", this.password);
if (this.defaultPassword) { if (this.defaultPassword) {
@ -305,7 +304,7 @@ public class ShellProperties {
} }
public void setUsername(String username) { public void setUsername(String username) {
Assert.hasLength(username); Assert.hasLength(username, "username must have text");
this.username = username; this.username = username;
} }
@ -324,12 +323,12 @@ public class ShellProperties {
* Auth specific properties for Spring authentication * Auth specific properties for Spring authentication
*/ */
@ConfigurationProperties(name = "shell.auth.spring", ignoreUnknownFields = false) @ConfigurationProperties(name = "shell.auth.spring", ignoreUnknownFields = false)
public static class SpringAuthenticationProperties extends AuthenticationProperties { public static class SpringAuthenticationProperties extends CrshShellProperties {
private String[] roles = new String[] { "ROLE_ADMIN" }; private String[] roles = new String[] { "ROLE_ADMIN" };
@Override @Override
protected void applyToCrashShellConfig(Properties config) { protected void applyToCrshShellConfig(Properties config) {
if (this.roles != null) { if (this.roles != null) {
config.put("crash.auth.spring.roles", config.put("crash.auth.spring.roles",
StringUtils.arrayToCommaDelimitedString(this.roles)); StringUtils.arrayToCommaDelimitedString(this.roles));
@ -337,7 +336,9 @@ public class ShellProperties {
} }
public void setRoles(String[] roles) { public void setRoles(String[] roles) {
Assert.notNull(roles); // 'roles' can be empty. This means no special to access right to connect to
// shell is required.
Assert.notNull(roles, "roles must not be null");
this.roles = roles; this.roles = roles;
} }

@ -20,16 +20,25 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.UUID;
import org.crsh.plugin.PluginLifeCycle;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.MutablePropertyValues;
import org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration;
import org.springframework.boot.actuate.properties.ShellProperties.CrshShellProperties;
import org.springframework.boot.actuate.properties.ShellProperties.JaasAuthenticationProperties; import org.springframework.boot.actuate.properties.ShellProperties.JaasAuthenticationProperties;
import org.springframework.boot.actuate.properties.ShellProperties.KeyAuthenticationProperties; import org.springframework.boot.actuate.properties.ShellProperties.KeyAuthenticationProperties;
import org.springframework.boot.actuate.properties.ShellProperties.SimpleAuthenticationProperties; import org.springframework.boot.actuate.properties.ShellProperties.SimpleAuthenticationProperties;
import org.springframework.boot.actuate.properties.ShellProperties.SpringAuthenticationProperties; import org.springframework.boot.actuate.properties.ShellProperties.SpringAuthenticationProperties;
import org.springframework.boot.bind.RelaxedDataBinder; import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.support.DefaultConversionService; 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;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -125,7 +134,7 @@ public class ShellPropertiesTests {
binder.bind(new MutablePropertyValues(map)); binder.bind(new MutablePropertyValues(map));
assertFalse(binder.getBindingResult().hasErrors()); assertFalse(binder.getBindingResult().hasErrors());
Properties p = props.asCrashShellConfig(); Properties p = props.asCrshShellConfig();
assertEquals("2222", p.get("crash.ssh.port")); assertEquals("2222", p.get("crash.ssh.port"));
assertEquals("~/.ssh/test.pem", p.get("crash.ssh.keypath")); assertEquals("~/.ssh/test.pem", p.get("crash.ssh.keypath"));
@ -143,7 +152,7 @@ public class ShellPropertiesTests {
binder.bind(new MutablePropertyValues(map)); binder.bind(new MutablePropertyValues(map));
assertFalse(binder.getBindingResult().hasErrors()); assertFalse(binder.getBindingResult().hasErrors());
Properties p = props.asCrashShellConfig(); Properties p = props.asCrshShellConfig();
assertNull(p.get("crash.ssh.port")); assertNull(p.get("crash.ssh.port"));
assertNull(p.get("crash.ssh.keypath")); assertNull(p.get("crash.ssh.keypath"));
@ -160,7 +169,7 @@ public class ShellPropertiesTests {
binder.bind(new MutablePropertyValues(map)); binder.bind(new MutablePropertyValues(map));
assertFalse(binder.getBindingResult().hasErrors()); assertFalse(binder.getBindingResult().hasErrors());
Properties p = props.asCrashShellConfig(); Properties p = props.asCrshShellConfig();
assertEquals("2222", p.get("crash.telnet.port")); assertEquals("2222", p.get("crash.telnet.port"));
} }
@ -176,7 +185,7 @@ public class ShellPropertiesTests {
binder.bind(new MutablePropertyValues(map)); binder.bind(new MutablePropertyValues(map));
assertFalse(binder.getBindingResult().hasErrors()); assertFalse(binder.getBindingResult().hasErrors());
Properties p = props.asCrashShellConfig(); Properties p = props.asCrshShellConfig();
assertNull(p.get("crash.telnet.port")); assertNull(p.get("crash.telnet.port"));
} }
@ -192,7 +201,7 @@ public class ShellPropertiesTests {
assertFalse(binder.getBindingResult().hasErrors()); assertFalse(binder.getBindingResult().hasErrors());
Properties p = new Properties(); Properties p = new Properties();
props.applyToCrashShellConfig(p); props.applyToCrshShellConfig(p);
assertEquals("my-test-domain", p.get("crash.auth.jaas.domain")); assertEquals("my-test-domain", p.get("crash.auth.jaas.domain"));
} }
@ -208,7 +217,7 @@ public class ShellPropertiesTests {
assertFalse(binder.getBindingResult().hasErrors()); assertFalse(binder.getBindingResult().hasErrors());
Properties p = new Properties(); Properties p = new Properties();
props.applyToCrashShellConfig(p); props.applyToCrshShellConfig(p);
assertEquals("~/.ssh/test.pem", p.get("crash.auth.key.path")); assertEquals("~/.ssh/test.pem", p.get("crash.auth.key.path"));
} }
@ -223,7 +232,7 @@ public class ShellPropertiesTests {
assertFalse(binder.getBindingResult().hasErrors()); assertFalse(binder.getBindingResult().hasErrors());
Properties p = new Properties(); Properties p = new Properties();
props.applyToCrashShellConfig(p); props.applyToCrshShellConfig(p);
assertNull(p.get("crash.auth.key.path")); assertNull(p.get("crash.auth.key.path"));
} }
@ -240,7 +249,7 @@ public class ShellPropertiesTests {
assertFalse(binder.getBindingResult().hasErrors()); assertFalse(binder.getBindingResult().hasErrors());
Properties p = new Properties(); Properties p = new Properties();
props.applyToCrashShellConfig(p); props.applyToCrshShellConfig(p);
assertEquals("username123", p.get("crash.auth.simple.username")); assertEquals("username123", p.get("crash.auth.simple.username"));
assertEquals("password123", p.get("crash.auth.simple.password")); assertEquals("password123", p.get("crash.auth.simple.password"));
@ -275,9 +284,41 @@ public class ShellPropertiesTests {
assertFalse(binder.getBindingResult().hasErrors()); assertFalse(binder.getBindingResult().hasErrors());
Properties p = new Properties(); Properties p = new Properties();
props.applyToCrashShellConfig(p); props.applyToCrshShellConfig(p);
assertEquals("role1, role2", p.get("crash.auth.spring.roles")); assertEquals("role1, role2", p.get("crash.auth.spring.roles"));
} }
@Test
public void testCustomShellProperties() throws Exception {
MockEnvironment env = new MockEnvironment();
env.setProperty("shell.auth", "simple");
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setEnvironment(env);
context.setServletContext(new MockServletContext());
context.register(TestShellConfiguration.class);
context.register(CrshAutoConfiguration.class);
context.refresh();
PluginLifeCycle lifeCycle = context.getBean(PluginLifeCycle.class);
String uuid = lifeCycle.getConfig().getProperty("test.uuid");
assertEquals(TestShellConfiguration.uuid, uuid);
}
@Configuration
public static class TestShellConfiguration {
public static String uuid = UUID.randomUUID().toString();
@Bean
public CrshShellProperties testProperties() {
return new CrshShellProperties() {
@Override
protected void applyToCrshShellConfig(Properties config) {
config.put("test.uuid", uuid);
}
};
}
}
} }

Loading…
Cancel
Save