org.springframework.boot.actuate.autoconfigure.ShellProperties.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.boot.actuate.autoconfigure.ShellProperties.java

Source

/*
 * Copyright 2013-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.actuate.autoconfigure;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.UUID;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 * Configuration properties for the shell subsystem.
 *
 * @author Christian Dupuis
 * @author Phillip Webb
 */
@ConfigurationProperties(prefix = "shell", ignoreUnknownFields = true)
public class ShellProperties {

    private static Log logger = LogFactory.getLog(ShellProperties.class);

    /**
     * Authentication type. Auto-detected according to the environment (i.e. if Spring
     * Security is available, "spring" is used by default).
     */
    private String auth = "simple";

    private boolean defaultAuth = true;

    @Autowired(required = false)
    private CrshShellProperties[] additionalProperties = new CrshShellProperties[] {
            new SimpleAuthenticationProperties() };

    /**
     * Scan for changes and update the command if necessary (in seconds).
     */
    private int commandRefreshInterval = -1;

    /**
     * Patterns to use to look for commands.
     */
    private String[] commandPathPatterns = new String[] { "classpath*:/commands/**",
            "classpath*:/crash/commands/**" };

    /**
     * Patterns to use to look for configurations.
     */
    private String[] configPathPatterns = new String[] { "classpath*:/crash/*" };

    /**
     * Comma-separated list of commands to disable.
     */
    private String[] disabledCommands = new String[] { "jpa*", "jdbc*", "jndi*" };

    /**
     * Comma-separated list of plugins to disable. Certain plugins are disabled by default
     * based on the environment.
     */
    private String[] disabledPlugins = new String[0];

    private final Ssh ssh = new Ssh();

    private final Telnet telnet = new Telnet();

    public void setAuth(String auth) {
        Assert.hasLength(auth, "Auth must not be empty");
        this.auth = auth;
        this.defaultAuth = false;
    }

    public String getAuth() {
        return this.auth;
    }

    public CrshShellProperties[] getAdditionalProperties() {
        return this.additionalProperties;
    }

    public void setCommandRefreshInterval(int commandRefreshInterval) {
        this.commandRefreshInterval = commandRefreshInterval;
    }

    public int getCommandRefreshInterval() {
        return this.commandRefreshInterval;
    }

    public void setCommandPathPatterns(String[] commandPathPatterns) {
        Assert.notEmpty(commandPathPatterns, "CommandPathPatterns must not be empty");
        this.commandPathPatterns = commandPathPatterns;
    }

    public String[] getCommandPathPatterns() {
        return this.commandPathPatterns;
    }

    public void setConfigPathPatterns(String[] configPathPatterns) {
        Assert.notEmpty(configPathPatterns, "ConfigPathPatterns must not be empty");
        this.configPathPatterns = configPathPatterns;
    }

    public String[] getConfigPathPatterns() {
        return this.configPathPatterns;
    }

    public void setDisabledCommands(String[] disabledCommands) {
        Assert.notEmpty(disabledCommands);
        this.disabledCommands = disabledCommands;
    }

    public String[] getDisabledCommands() {
        return this.disabledCommands;
    }

    public void setDisabledPlugins(String[] disabledPlugins) {
        Assert.notEmpty(disabledPlugins);
        this.disabledPlugins = disabledPlugins;
    }

    public String[] getDisabledPlugins() {
        return this.disabledPlugins;
    }

    public Ssh getSsh() {
        return this.ssh;
    }

    public Telnet getTelnet() {
        return this.telnet;
    }

    /**
     * Return a properties file configured from these settings that can be applied to a
     * CRaSH shell instance.
     * @return the CRaSH properties
     */
    public Properties asCrshShellConfig() {
        Properties properties = new Properties();
        this.ssh.applyToCrshShellConfig(properties);
        this.telnet.applyToCrshShellConfig(properties);

        for (CrshShellProperties shellProperties : this.additionalProperties) {
            shellProperties.applyToCrshShellConfig(properties);
        }

        if (this.commandRefreshInterval > 0) {
            properties.put("crash.vfs.refresh_period", String.valueOf(this.commandRefreshInterval));
        }

        // special handling for disabling Ssh and Telnet support
        List<String> dp = new ArrayList<String>(Arrays.asList(this.disabledPlugins));
        if (!this.ssh.isEnabled()) {
            dp.add("org.crsh.ssh.SSHPlugin");
        }
        if (!this.telnet.isEnabled()) {
            dp.add("org.crsh.telnet.TelnetPlugin");
        }
        this.disabledPlugins = dp.toArray(new String[dp.size()]);

        validateCrshShellConfig(properties);

        return properties;
    }

    /**
     * Basic validation of applied CRaSH shell configuration.
     * @param properties the properties to validate
     */
    protected void validateCrshShellConfig(Properties properties) {
        String finalAuth = properties.getProperty("crash.auth");
        if (!this.defaultAuth && !this.auth.equals(finalAuth)) {
            logger.warn(String.format("Shell authentication fell back to method '%s' opposed to "
                    + "configured method '%s'. Please check your classpath.", finalAuth, this.auth));
        }
        // Make sure we keep track of final authentication method
        this.auth = finalAuth;
    }

    /**
     * Base class for CRaSH properties.
     */
    public static abstract class CrshShellProperties {

        /**
         * Apply the properties to a CRaSH configuration.
         * @param config the CRaSH configuration properties
         */
        protected abstract void applyToCrshShellConfig(Properties config);

    }

    /**
     * Base class for Auth specific properties.
     */
    public static abstract class CrshShellAuthenticationProperties extends CrshShellProperties {

    }

    /**
     * SSH properties.
     */
    public static class Ssh extends CrshShellProperties {

        /**
         * Enable CRaSH SSH support.
         */
        private boolean enabled = true;

        /**
         * Path to the SSH server key.
         */
        private String keyPath;

        /**
         * SSH port.
         */
        private Integer port = 2000;

        @Override
        protected void applyToCrshShellConfig(Properties config) {
            if (this.enabled) {
                config.put("crash.ssh.port", String.valueOf(this.port));
                if (this.keyPath != null) {
                    config.put("crash.ssh.keypath", this.keyPath);
                }
            }
        }

        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }

        public boolean isEnabled() {
            return this.enabled;
        }

        public void setKeyPath(String keyPath) {
            Assert.hasText(keyPath, "keyPath must have text");
            this.keyPath = keyPath;
        }

        public String getKeyPath() {
            return this.keyPath;
        }

        public void setPort(Integer port) {
            Assert.notNull(port, "port must not be null");
            this.port = port;
        }

        public Integer getPort() {
            return this.port;
        }

    }

    /**
     * Telnet properties.
     */
    public static class Telnet extends CrshShellProperties {

        /**
         * Enable CRaSH telnet support. Enabled by default if the TelnetPlugin is
         * available.
         */
        private boolean enabled = ClassUtils.isPresent("org.crsh.telnet.TelnetPlugin",
                ClassUtils.getDefaultClassLoader());

        /**
         * Telnet port.
         */
        private Integer port = 5000;

        @Override
        protected void applyToCrshShellConfig(Properties config) {
            if (this.enabled) {
                config.put("crash.telnet.port", String.valueOf(this.port));
            }
        }

        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }

        public boolean isEnabled() {
            return this.enabled;
        }

        public void setPort(Integer port) {
            Assert.notNull(port, "port must not be null");
            this.port = port;
        }

        public Integer getPort() {
            return this.port;
        }

    }

    /**
     * Auth specific properties for JAAS authentication.
     */
    @ConfigurationProperties(prefix = "shell.auth.jaas", ignoreUnknownFields = false)
    public static class JaasAuthenticationProperties extends CrshShellAuthenticationProperties {

        /**
         * JAAS domain.
         */
        private String domain = "my-domain";

        @Override
        protected void applyToCrshShellConfig(Properties config) {
            config.put("crash.auth", "jaas");
            config.put("crash.auth.jaas.domain", this.domain);
        }

        public void setDomain(String domain) {
            Assert.hasText(domain, "domain must have text");
            this.domain = domain;
        }

        public String getDomain() {
            return this.domain;
        }

    }

    /**
     * Auth specific properties for key authentication.
     */
    @ConfigurationProperties(prefix = "shell.auth.key", ignoreUnknownFields = false)
    public static class KeyAuthenticationProperties extends CrshShellAuthenticationProperties {

        /**
         * Path to the authentication key. This should point to a valid ".pem" file.
         */
        private String path;

        @Override
        protected void applyToCrshShellConfig(Properties config) {
            config.put("crash.auth", "key");
            if (this.path != null) {
                config.put("crash.auth.key.path", this.path);
            }
        }

        public void setPath(String path) {
            Assert.hasText(path, "path must have text");
            this.path = path;
        }

        public String getPath() {
            return this.path;
        }

    }

    /**
     * Auth specific properties for simple authentication.
     */
    @ConfigurationProperties(prefix = "shell.auth.simple", ignoreUnknownFields = false)
    public static class SimpleAuthenticationProperties extends CrshShellAuthenticationProperties {

        private static Log logger = LogFactory.getLog(SimpleAuthenticationProperties.class);

        private User user = new User();

        @Override
        protected void applyToCrshShellConfig(Properties config) {
            config.put("crash.auth", "simple");
            config.put("crash.auth.simple.username", this.user.getName());
            config.put("crash.auth.simple.password", this.user.getPassword());
            if (this.user.isDefaultPassword()) {
                logger.info("\n\nUsing default password for shell access: " + this.user.getPassword() + "\n\n");
            }
        }

        public User getUser() {
            return this.user;
        }

        public void setUser(User user) {
            this.user = user;
        }

        public static class User {

            /**
             * Login user.
             */
            private String name = "user";

            /**
             * Login password.
             */
            private String password = UUID.randomUUID().toString();

            private boolean defaultPassword = true;

            boolean isDefaultPassword() {
                return this.defaultPassword;
            }

            public String getName() {
                return this.name;
            }

            public String getPassword() {
                return this.password;
            }

            public void setName(String name) {
                Assert.hasLength(name, "name must have text");
                this.name = name;
            }

            public void setPassword(String password) {
                if (password.startsWith("${") && password.endsWith("}") || !StringUtils.hasLength(password)) {
                    return;
                }
                this.password = password;
                this.defaultPassword = false;
            }

        }

    }

    /**
     * Auth specific properties for Spring authentication.
     */
    @ConfigurationProperties(prefix = "shell.auth.spring", ignoreUnknownFields = false)
    public static class SpringAuthenticationProperties extends CrshShellAuthenticationProperties {

        /**
         * Comma-separated list of required roles to login to the CRaSH console.
         */
        private String[] roles = new String[] { "ADMIN" };

        @Override
        protected void applyToCrshShellConfig(Properties config) {
            config.put("crash.auth", "spring");
            config.put("crash.auth.spring.roles", StringUtils.arrayToCommaDelimitedString(this.roles));
        }

        public void setRoles(String[] roles) {
            // 'roles' can be null. This means no special to access right to connect to
            // shell is required.
            this.roles = roles;
        }

        public String[] getRoles() {
            return this.roles;
        }

    }

}