├── sshd-shell-spring-boot-starter └── src │ ├── test │ ├── resources │ │ ├── banner.txt │ │ ├── db │ │ │ ├── changelog │ │ │ │ ├── db.changelog-master.yaml │ │ │ │ └── changes │ │ │ │ │ └── 01.xml │ │ │ └── migration │ │ │ │ └── test.sql │ │ ├── banner.png │ │ ├── id_rsa.pub │ │ ├── application.properties │ │ └── id_rsa │ └── java │ │ └── sshd │ │ └── shell │ │ └── springboot │ │ ├── console │ │ └── ConsoleIOTest.java │ │ ├── autoconfiguration │ │ ├── SshdShellAutoConfigurationAuthProviderBeanTest.java │ │ ├── SshdShellAutoConfigurationAuthProviderInvalidAuthTypeTest.java │ │ ├── SshSessionContextTest.java │ │ ├── SshdSftpEnabledTest.java │ │ ├── SshdShellAutoConfigurationWithPublicKeyAndBannerImageTest.java │ │ ├── SshdShellAutoConfigurationDisabledEndpointTest.java │ │ ├── SshdShellAutoConfigurationAuthProviderTest.java │ │ ├── AbstractSshSupport.java │ │ └── ConfigTest.java │ │ ├── command │ │ ├── DummyCommand.java │ │ ├── ExceptionCommand.java │ │ ├── TestCommand.java │ │ └── EndpointCommand.java │ │ └── util │ │ └── ZipUtilsTest.java │ └── main │ ├── resources │ ├── META-INF │ │ └── spring.factories │ └── config │ │ └── application.properties │ └── java │ └── sshd │ └── shell │ └── springboot │ ├── ShellException.java │ ├── console │ ├── UsageInfo.java │ ├── ColorType.java │ ├── DefaultUserInputProcessor.java │ ├── HighlightUserInputProcessor.java │ ├── ConsoleConfiguration.java │ ├── MailUserInputProcessor.java │ ├── ConsoleIO.java │ ├── BaseUserInputProcessor.java │ └── TerminalProcessor.java │ ├── autoconfiguration │ ├── Constants.java │ ├── CommandExecutor.java │ ├── SshdShellCommand.java │ ├── SshSessionContext.java │ ├── CommandExecutableDetails.java │ ├── SshdShellProperties.java │ ├── ShellBanner.java │ └── SshdShellAutoConfiguration.java │ ├── util │ ├── Assert.java │ ├── JsonUtils.java │ └── ZipUtils.java │ ├── command │ ├── AbstractSystemCommand.java │ ├── ExitCommand.java │ ├── InfoCommand.java │ ├── BeansCommand.java │ ├── FlywayCommand.java │ ├── MappingsCommand.java │ ├── HttpTraceCommand.java │ ├── ThreadDumpCommand.java │ ├── LiquibaseCommand.java │ ├── ShutdownCommand.java │ ├── ScheduledTasksCommand.java │ ├── ConditionsReportCommand.java │ ├── CommandUtils.java │ ├── ConfigurationPropertiesReportCommand.java │ ├── IntegrationGraphCommand.java │ ├── EnvironmentCommand.java │ ├── LogfileCommand.java │ ├── HealthCommand.java │ ├── CachesCommand.java │ ├── HeapDumpCommand.java │ ├── HelpCommand.java │ ├── AuditEventsCommand.java │ ├── MetricsCommand.java │ ├── SessionsCommand.java │ └── LoggersCommand.java │ └── server │ ├── SshdAuthorizedKeysAuthenticator.java │ ├── SimpleSshdPasswordAuthenticator.java │ ├── SshdNativeFileSystemFactory.java │ ├── AuthProviderSshdPasswordAuthenticator.java │ ├── SshSessionInstance.java │ └── SshdServerConfiguration.java ├── sshd-shell-spring-boot-test-app ├── src │ └── main │ │ ├── resources │ │ ├── db │ │ │ ├── changelog │ │ │ │ ├── db.changelog-master.yaml │ │ │ │ └── changes │ │ │ │ │ └── 01.xml │ │ │ └── migration │ │ │ │ └── test.sql │ │ ├── banner.png │ │ ├── templates │ │ │ └── test.html │ │ ├── application.properties │ │ └── banner.txt │ │ └── java │ │ └── demo │ │ ├── MyController.java │ │ ├── Main.java │ │ ├── EchoCommand.java │ │ ├── AdminCommand.java │ │ └── MySecurityConfig.java └── pom.xml ├── .gitignore ├── .github ├── ISSUE_TEMPLATE │ ├── Feature_request.md │ └── Bug_report.md └── workflows │ └── actions.yml ├── pom.xml └── CODE_OF_CONDUCT.md /sshd-shell-spring-boot-starter/src/test/resources/banner.txt: -------------------------------------------------------------------------------- 1 | Spring Boot -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/resources/db/changelog/db.changelog-master.yaml: -------------------------------------------------------------------------------- 1 | databaseChangeLog: 2 | - includeAll: 3 | path: classpath*:db/changelog/changes/ -------------------------------------------------------------------------------- /sshd-shell-spring-boot-test-app/src/main/resources/db/changelog/db.changelog-master.yaml: -------------------------------------------------------------------------------- 1 | databaseChangeLog: 2 | - includeAll: 3 | path: classpath*:db/changelog/changes/ -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/resources/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anand1st/sshd-shell-spring-boot/HEAD/sshd-shell-spring-boot-starter/src/test/resources/banner.png -------------------------------------------------------------------------------- /sshd-shell-spring-boot-test-app/src/main/resources/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anand1st/sshd-shell-spring-boot/HEAD/sshd-shell-spring-boot-test-app/src/main/resources/banner.png -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=sshd.shell.springboot.autoconfiguration.SshdShellAutoConfiguration -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /sshd-shell-spring-boot-starter/target/ 2 | /sshd-shell-spring-boot-test-app/target/ 3 | /sshd-shell-spring-boot-test-app/hostKey.ser 4 | /sshd-shell-spring-boot-test-app/sshd.log 5 | /.idea/ 6 | **/.DS_Store 7 | **/*.iml 8 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/resources/db/migration/test.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS `employee` ( 2 | `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 | `name` varchar(20), 4 | `email` varchar(50), 5 | `date_of_birth` timestamp 6 | ); -------------------------------------------------------------------------------- /sshd-shell-spring-boot-test-app/src/main/resources/db/migration/test.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS `employee` ( 2 | `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 | `name` varchar(20), 4 | `email` varchar(50), 5 | `date_of_birth` timestamp 6 | ); -------------------------------------------------------------------------------- /sshd-shell-spring-boot-test-app/src/main/resources/templates/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TODO supply a title 5 | 6 | 7 | 8 | 9 |
TODO write content
10 | 11 | 12 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-test-app/src/main/java/demo/MyController.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | 6 | /** 7 | * 8 | * @author anand 9 | */ 10 | @Controller 11 | public class MyController { 12 | 13 | @GetMapping("/test") 14 | public String test() { 15 | return "test"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/resources/id_rsa.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAqo77h58REjvlpIPXCGzTAwEyFiPbdPFCcMh45jkjoufU6dtqJfqcfyfdiT0quuCCMj2uCtHPy5gbWzT9bmB7f2qdff+Ap3gnRAsRtVhXxdeqVsFBgTZpXK9wXxT0E/Hrt+mknTBqRiibu1pHQU1RfJ2FwY5FQAygJeO3KaadpGFdmeFMVxZ5XGFetUziQqS0uJYT2kKLGLfG4Iaa8PJ06tpmJGuNkftEnYIKDH9FLFIwxtBThoaCl/BZIHqFu3VTb/wzfWPW7SZ/gJ+1WauwgqMaDF5ry0rCd5dcxXr5JbpDGbkbCuhTVt3CPhNtXLQqz7Uggs6/I9ipdnVtMS3J 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/workflows/actions.yml: -------------------------------------------------------------------------------- 1 | name: Build/Test Pipeline 2 | on: [push] 3 | jobs: 4 | Main: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - run: echo "Job triggered by ${{ github.event_name }} event." 8 | - name: Checkout Repository Code 9 | uses: actions/checkout@v2 10 | - name: Set up JDK 8 11 | uses: actions/setup-java@v2 12 | with: 13 | java-version: '8' 14 | distribution: 'adopt' 15 | cache: maven 16 | - name: Change to sshd-shell-spring-boot-starter directory 17 | run: cd sshd-shell-spring-boot-starter 18 | - name: Build with Maven 19 | run: mvn --batch-mode --update-snapshots test org.jacoco:jacoco-maven-plugin:report 20 | - name: Coverall Coverage Report Submission 21 | run: mvn --batch-mode org.eluder.coveralls:coveralls-maven-plugin:report --define repoToken=${{ secrets.COVERALL_REPO_TOKEN }} -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-test-app/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | logging.level.sshd.shell=DEBUG 2 | #logging.level.org.apache.sshd=DEBUG 3 | logging.file.name=sshd.log 4 | 5 | management.endpoint.shutdown.enabled=true 6 | 7 | spring.main.allow-circular-references=true 8 | 9 | spring.flyway.baseline-on-migrate=true 10 | 11 | spring.jmx.enabled=true 12 | 13 | sshd.shell.enabled=true 14 | sshd.shell.text.color=BLUE 15 | sshd.shell.prompt.color=RED 16 | sshd.shell.prompt.title=server 17 | 18 | sshd.filetransfer.enabled=true 19 | sshd.filesystem.base.dir=/Users/anand 20 | 21 | ### Enable configuration below for authentication/authorization via spring-security 22 | sshd.shell.auth.authType=AUTH_PROVIDER 23 | 24 | ### Enable configuration below for simple authentication/authorization 25 | sshd.shell.password=admin 26 | 27 | ### Enable and replace username/password below to test with gmail (ensure 'less secure app' setting is turned on) 28 | #spring.mail.username= 29 | #spring.mail.password= 30 | #spring.mail.host=smtp.gmail.com 31 | #spring.mail.port=465 32 | #spring.mail.protocol=smtps 33 | 34 | spring.redis.port=${embeddedRedisPort} -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/ShellException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot; 17 | 18 | /** 19 | * 20 | * @author anand 21 | */ 22 | public class ShellException extends Exception { 23 | 24 | private static final long serialVersionUID = 7114130906989289480L; 25 | 26 | public ShellException(String message) { 27 | super(message); 28 | } 29 | 30 | public ShellException(String message, Throwable cause) { 31 | super(message, cause); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Copyright 2017 anand. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | sshd.shell.enabled=true 16 | sshd.shell.port=0 17 | sshd.shell.hostKeyFile=target/hostKey.ser 18 | 19 | logging.level.sshd.shell=INFO 20 | logging.file.name=target/sshd.log 21 | 22 | spring.mail.host=127.0.0.1 23 | spring.mail.port=0 24 | spring.mail.username=anand@test.com 25 | spring.mail.password=xxx 26 | spring.mail.protocol=smtp 27 | 28 | management.endpoint.shutdown.enabled=true 29 | 30 | spring.cache.cache-names=test 31 | spring.cache.caffeine.spec=maximumSize=10, expireAfterAccess=30s -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/console/UsageInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.console; 17 | 18 | import java.util.List; 19 | 20 | /** 21 | * 22 | * @author anand 23 | */ 24 | @lombok.AllArgsConstructor 25 | @lombok.Getter 26 | public class UsageInfo { 27 | 28 | private final List rows; 29 | 30 | @lombok.AllArgsConstructor 31 | @lombok.Getter 32 | public static class Row { 33 | 34 | private final String usage; 35 | private final String description; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/autoconfiguration/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.autoconfiguration; 17 | 18 | /** 19 | * For internal use only. 20 | * 21 | * @author anand 22 | */ 23 | public enum Constants { 24 | 25 | ; 26 | public static final String USER = "__user"; 27 | public static final String HELP = "help"; 28 | public static final String USER_ROLES = "__userRoles"; 29 | public static final String EXECUTE = "__execute"; 30 | public static final String SHELL_BANNER = "__shellBanner"; 31 | } 32 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/resources/db/changelog/changes/01.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-test-app/src/main/resources/db/changelog/changes/01.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/autoconfiguration/CommandExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package sshd.shell.springboot.autoconfiguration; 20 | 21 | import sshd.shell.springboot.ShellException; 22 | 23 | /** 24 | * 25 | * @author anand 26 | */ 27 | @FunctionalInterface 28 | interface CommandExecutor { 29 | 30 | String get(String arg) throws InterruptedException, ShellException; 31 | } 32 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/console/ColorType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.console; 17 | 18 | import org.jline.utils.AttributedStyle; 19 | 20 | /** 21 | * 22 | * @author anand 23 | */ 24 | @lombok.AllArgsConstructor 25 | public enum ColorType { 26 | 27 | BLACK(AttributedStyle.BLACK), 28 | RED(AttributedStyle.RED), 29 | GREEN(AttributedStyle.GREEN), 30 | YELLOW(AttributedStyle.YELLOW), 31 | BLUE(AttributedStyle.BLUE), 32 | MAGENTA(AttributedStyle.MAGENTA), 33 | CYAN(AttributedStyle.CYAN), 34 | WHITE(AttributedStyle.WHITE); 35 | 36 | public final int value; 37 | } 38 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/java/sshd/shell/springboot/console/ConsoleIOTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.console; 17 | 18 | import static org.junit.Assert.assertTrue; 19 | import org.junit.Test; 20 | import sshd.shell.springboot.util.JsonUtils; 21 | 22 | /** 23 | * 24 | * @author anand 25 | */ 26 | public class ConsoleIOTest { 27 | 28 | @Test 29 | public void testConsoleIOAsJsonException() { 30 | assertTrue(JsonUtils.asJson(new X("x")).startsWith("Error processing json output")); 31 | } 32 | 33 | @lombok.AllArgsConstructor 34 | private static class X { 35 | final String x; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/util/Assert.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.util; 17 | 18 | import java.util.Objects; 19 | import sshd.shell.springboot.ShellException; 20 | 21 | /** 22 | * 23 | * @author anand 24 | */ 25 | public enum Assert { 26 | ; 27 | 28 | public static void isTrue(boolean statement, String errorMessage) throws ShellException { 29 | if (!statement) { 30 | throw new ShellException(errorMessage); 31 | } 32 | } 33 | 34 | public static void isNotNull(Object object, String errorMessage) throws ShellException { 35 | isTrue(Objects.nonNull(object), errorMessage); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/AbstractSystemCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import java.util.Arrays; 19 | import java.util.Collections; 20 | import java.util.HashSet; 21 | import java.util.Set; 22 | 23 | /** 24 | * 25 | * @author anand 26 | */ 27 | public abstract class AbstractSystemCommand { 28 | 29 | private final Set systemRoles; 30 | 31 | public AbstractSystemCommand(String[] systemRoles) { 32 | this.systemRoles = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(systemRoles))); 33 | } 34 | 35 | public final Set getSystemRoles() { 36 | return systemRoles; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | io.github.anand1st 6 | sshd-shell-spring-boot-parent 7 | 1.0-SNAPSHOT 8 | pom 9 | https://github.com/anand1st/sshd-shell-spring-boot 10 | 11 | UTF-8 12 | 13 | 14 | 15 | Apache License, Version 2.0 16 | http://www.apache.org/licenses/LICENSE-2.0.txt 17 | repo 18 | 19 | 20 | 21 | https://github.com/anand1st/sshd-shell-spring-boot.git 22 | 23 | 24 | 25 | Nithyanandan Natchimuthu 26 | anand1st@gmail.com 27 | 28 | 29 | 30 | sshd-shell-spring-boot-starter 31 | sshd-shell-spring-boot-test-app 32 | 33 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-test-app/src/main/java/demo/Main.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import javax.annotation.PreDestroy; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cache.annotation.EnableCaching; 7 | import org.springframework.util.SocketUtils; 8 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 9 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; 10 | import redis.embedded.RedisServer; 11 | 12 | /** 13 | * 14 | * @author anand 15 | */ 16 | @EnableCaching 17 | @SpringBootApplication 18 | public class Main extends WebMvcConfigurationSupport { 19 | 20 | private static final RedisServer REDIS_SERVER; 21 | 22 | static { 23 | int availablePort = SocketUtils.findAvailableTcpPort(); 24 | System.setProperty("embeddedRedisPort", String.valueOf(availablePort)); 25 | REDIS_SERVER = new RedisServer(availablePort); 26 | REDIS_SERVER.start(); 27 | } 28 | 29 | public static void main(String... args) { 30 | SpringApplication.run(Main.class, args); 31 | } 32 | 33 | @PreDestroy 34 | void cleanUp() { 35 | REDIS_SERVER.stop(); 36 | } 37 | 38 | @Override 39 | protected void addViewControllers(ViewControllerRegistry registry) { 40 | registry.addViewController("/test").setViewName("test"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/ExitCommand.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package sshd.shell.springboot.command; 20 | 21 | import org.springframework.stereotype.Component; 22 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 23 | 24 | /** 25 | * 26 | * @author anand 27 | */ 28 | @Component 29 | @lombok.NoArgsConstructor(access = lombok.AccessLevel.PACKAGE) 30 | @SshdShellCommand(value = "exit", description = "Exit shell") 31 | public final class ExitCommand { 32 | 33 | public String exit(String arg) throws InterruptedException { 34 | throw new InterruptedException("Exiting shell"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/java/sshd/shell/springboot/autoconfiguration/SshdShellAutoConfigurationAuthProviderBeanTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.autoconfiguration; 17 | 18 | import org.springframework.boot.test.context.SpringBootTest; 19 | import org.springframework.test.annotation.DirtiesContext; 20 | 21 | /** 22 | * 23 | * @author anand 24 | */ 25 | @SpringBootTest(classes = ConfigTest.class, properties = { 26 | "sshd.shell.auth.authType=AUTH_PROVIDER", 27 | "sshd.shell.username=bob", 28 | "sshd.shell.password=bob", 29 | "sshd.shell.auth.authProviderBeanName=authProvider", 30 | "spring.flyway.baseline-on-migrate=true", 31 | "spring.main.allow-circular-references=true" 32 | }) 33 | @DirtiesContext 34 | public class SshdShellAutoConfigurationAuthProviderBeanTest extends SshdShellAutoConfigurationAuthProviderTest { 35 | } 36 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/java/sshd/shell/springboot/autoconfiguration/SshdShellAutoConfigurationAuthProviderInvalidAuthTypeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.autoconfiguration; 17 | 18 | import org.springframework.boot.test.context.SpringBootTest; 19 | import org.springframework.test.annotation.DirtiesContext; 20 | 21 | /** 22 | * 23 | * @author anand 24 | */ 25 | @SpringBootTest(classes = ConfigTest.class, properties = { 26 | "sshd.shell.auth.authType=AUTH_PROVIDER", 27 | "sshd.shell.username=bob", 28 | "sshd.shell.password=bob", 29 | "sshd.shell.auth.authProviderBeanName=authProvider", 30 | "spring.flyway.baseline-on-migrate=true", 31 | "spring.main.allow-circular-references=true" 32 | }) 33 | @DirtiesContext 34 | public class SshdShellAutoConfigurationAuthProviderInvalidAuthTypeTest extends 35 | SshdShellAutoConfigurationAuthProviderTest { 36 | } 37 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/java/sshd/shell/springboot/command/DummyCommand.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package sshd.shell.springboot.command; 20 | 21 | import org.springframework.stereotype.Component; 22 | import org.springframework.transaction.annotation.Transactional; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | 25 | /** 26 | * 27 | * @author anand 28 | */ 29 | @Component 30 | @SshdShellCommand(value = "dummy", description = "dummy description") 31 | public class DummyCommand { 32 | 33 | @Transactional 34 | @SshdShellCommand(value = "run", description = "dummy run") 35 | public String run(String arg) { 36 | return "dummy run successful"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-test-app/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ${Ansi.RED} $$$ $$$$$ $$$$ $$$$ $$$$$ 2 | ${Ansi.GREEN} $$$ $$$$$$$ $$$$ $$$$ $$$$$$$ 3 | ${Ansi.BLUE} $$$ $$$$$$$ $$$$$ $$$$$ $$$$$$$ 4 | ${Ansi.RED} $$$ $$$$$$$ $$$$ $$$$ $$$$$$$ 5 | ${Ansi.GREEN} $$$ $$$$ $$$$ $$$$$ $$$$$ $$$$ $$$$ 6 | ${Ansi.BLUE} $$$ $$$$ $$$$ $$$$ $$$$ $$$$ $$$$ 7 | ${Ansi.RED} $$$ $$$$$ $$$$$ $$$$ $$$$ $$$$$ $$$$$ 8 | ${Ansi.GREEN} $$$ $$$$ $$$$ $$$$$ $$$$$ $$$$ $$$$ 9 | ${Ansi.BLUE} $$$ $$$$ $$$$ $$$$ $$$$ $$$$ $$$$ 10 | ${Ansi.RED} $$$ $$$$$ $$$$$ $$$$$ $$$$$ $$$$$ $$$$$ 11 | ${Ansi.GREEN} $$$ $$$$$$$$$$$$$ $$$$ $$$$ $$$$$$$$$$$$$ 12 | ${Ansi.BLUE} $$$$ $$$$ $$$$$$$$$$$$$ $$$$ $$$$ $$$$$$$$$$$$$ 13 | ${Ansi.RED} $$$$ $$$$ $$$$$$$$$$$$$$$ $$$$$$$ $$$$$$$$$$$$$$$ 14 | ${Ansi.GREEN} $$$$$ $$$$$ $$$$ $$$$ $$$$$$$ $$$$ $$$$ 15 | ${Ansi.BLUE} $$$$$$$$$$$ $$$$$ $$$$$ $$$$$$$ $$$$$ $$$$$ 16 | ${Ansi.RED} $$$$$$$$$ $$$$ $$$$ $$$$$ $$$$ $$$$ 17 | ${Ansi.GREEN} $$$$$$$ $$$$ $$$$ $$$$$ $$$$ $$$$ 18 | 19 | ${Ansi.RED} :: Spring Boot${spring-boot.formatted-version} :: ${Ansi.DEFAULT} 20 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-test-app/src/main/java/demo/EchoCommand.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import java.io.IOException; 4 | import org.springframework.stereotype.Component; 5 | import sshd.shell.springboot.autoconfiguration.SshSessionContext; 6 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 7 | import sshd.shell.springboot.console.ConsoleIO; 8 | 9 | /** 10 | * 11 | * @author anand 12 | */ 13 | @Component 14 | @SshdShellCommand(value = "echo", description = "Echo by users. Type 'echo' for supported subcommands") 15 | public class EchoCommand { 16 | 17 | @SshdShellCommand(value = "bob", description = "Bob's echo. Usage: echo bob ") 18 | public String bobSays(String arg) throws IOException { 19 | String name = ConsoleIO.readInput("What's your name?"); 20 | SshSessionContext.put("name", name); 21 | return "bob echoes " + arg + " and your name is " + name + ", rooted filesystem path is " 22 | + SshSessionContext.getUserDir().toString(); 23 | } 24 | 25 | @SshdShellCommand(value = "alice", description = "Alice's echo. Usage: echo alice ") 26 | public String aliceSays(String arg) { 27 | String str = ""; 28 | if (SshSessionContext.containsKey("name")) { 29 | str = ", Name " + SshSessionContext.get("name") + " exists"; 30 | } 31 | return "alice says " + arg + str; 32 | } 33 | 34 | @SshdShellCommand(value = "admin", description = "Admin's echo. Usage: echo admin ", roles = "ADMIN") 35 | public String adminSays(String arg) { 36 | return "admin says " + arg; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-test-app/src/main/java/demo/AdminCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package demo; 17 | 18 | import org.springframework.stereotype.Component; 19 | import sshd.shell.springboot.ShellException; 20 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 21 | 22 | /** 23 | * 24 | * @author anand 25 | */ 26 | @Component 27 | @SshdShellCommand(value = "admin", description = "Admin functionality. Type 'admin' for supported subcommands", 28 | roles = "ADMIN") 29 | public class AdminCommand { 30 | 31 | @SshdShellCommand(value = "manage", description = "Manage task. Usage: admin manage ", roles = "ADMIN") 32 | public String manage(String arg) { 33 | return arg + " has been managed by admin"; 34 | } 35 | 36 | @SshdShellCommand(value = "testShellException", description = "Test throwing ShellException") 37 | public String testShellException(String arg) throws ShellException { 38 | throw new ShellException("Exception Message:" + arg); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/java/sshd/shell/springboot/autoconfiguration/SshSessionContextTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.autoconfiguration; 17 | 18 | import java.io.IOException; 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertFalse; 21 | import static org.junit.Assert.assertTrue; 22 | import org.junit.Test; 23 | 24 | /** 25 | * 26 | * @author anand 27 | */ 28 | public class SshSessionContextTest { 29 | 30 | @Test 31 | public void testSshSessionContext() throws IOException { 32 | assertFalse(SshSessionContext.containsKey("test")); 33 | SshSessionContext.put("test", "test"); 34 | assertEquals("test", SshSessionContext.get("test")); 35 | assertTrue(SshSessionContext.containsKey("test")); 36 | assertEquals("test", SshSessionContext.remove("test")); 37 | assertFalse(SshSessionContext.containsKey("test")); 38 | SshSessionContext.clear(); 39 | assertTrue(SshSessionContext.isEmpty()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/server/SshdAuthorizedKeysAuthenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.server; 17 | 18 | import java.nio.file.Path; 19 | import java.security.PublicKey; 20 | import java.util.Collections; 21 | import org.apache.sshd.server.config.keys.AuthorizedKeysAuthenticator; 22 | import org.apache.sshd.server.session.ServerSession; 23 | import sshd.shell.springboot.autoconfiguration.Constants; 24 | 25 | /** 26 | * 27 | * @author anand 28 | */ 29 | class SshdAuthorizedKeysAuthenticator extends AuthorizedKeysAuthenticator { 30 | 31 | SshdAuthorizedKeysAuthenticator(Path path) { 32 | super(path); 33 | } 34 | 35 | @Override 36 | public boolean authenticate(String username, PublicKey key, ServerSession session) { 37 | if (super.authenticate(username, key, session)) { 38 | session.getIoSession().setAttribute(Constants.USER_ROLES, Collections.singleton("*")); 39 | session.getIoSession().setAttribute(Constants.USER, username); 40 | return true; 41 | } 42 | return false; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/java/sshd/shell/springboot/util/ZipUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.util; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.nio.file.Files; 21 | import java.nio.file.Path; 22 | import java.nio.file.Paths; 23 | import java.nio.file.StandardCopyOption; 24 | import static org.junit.Assert.assertEquals; 25 | import static org.junit.Assert.assertFalse; 26 | import org.junit.Test; 27 | 28 | /** 29 | * 30 | * @author anand 31 | */ 32 | public class ZipUtilsTest { 33 | 34 | @Test 35 | public void testZipFiles() throws IOException { 36 | File zipDir = new File("target"); 37 | Files.copy(Paths.get("src/test/resources/banner.txt"), new File(zipDir, "banner.txt").toPath(), 38 | StandardCopyOption.REPLACE_EXISTING); 39 | Path[] filesToZip = {Paths.get("target/banner.txt")}; 40 | Path result = ZipUtils.zipFiles(zipDir.toPath(), true, filesToZip); 41 | assertEquals(Paths.get("target/banner.txt.zip"), result); 42 | assertFalse(new File("target/banner.txt").exists()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/java/sshd/shell/springboot/command/ExceptionCommand.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package sshd.shell.springboot.command; 20 | 21 | import org.springframework.stereotype.Component; 22 | import sshd.shell.springboot.ShellException; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | 25 | /** 26 | * 27 | * @author anand 28 | */ 29 | @Component 30 | @SshdShellCommand(value = "exception", description = "throws Exceptions") 31 | public class ExceptionCommand { 32 | 33 | @SshdShellCommand(value = "iae", description = "throws IAE") 34 | public String iae(String arg) { 35 | throw new IllegalArgumentException("illegalargumentexception"); 36 | } 37 | 38 | @SshdShellCommand(value = "se", description = "throws ShellException") 39 | public String se(String arg) throws ShellException { 40 | throw new ShellException("shellexception"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/resources/id_rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAwKqO+4efERI75aSD1whs0wMBMhYj23TxQnDIeOY5I6Ln1Onb 3 | aiX6nH8n3Yk9KrrggjI9rgrRz8uYG1s0/W5ge39qnX3/gKd4J0QLEbVYV8XXqlbB 4 | QYE2aVyvcF8U9BPx67fppJ0wakYom7taR0FNUXydhcGORUAMoCXjtymmnaRhXZnh 5 | TFcWeVxhXrVM4kKktLiWE9pCixi3xuCGmvDydOraZiRrjZH7RJ2CCgx/RSxSMMbQ 6 | U4aGgpfwWSB6hbt1U2/8M31j1u0mf4CftVmrsIKjGgxea8tKwneXXMV6+SW6Qxm5 7 | GwroU1bdwj4TbVy0Ks+1IILOvyPYqXZ1bTEtyQIDAQABAoIBAA0He8Pm2Ar/gK13 8 | ArN4DmP2wA3zEVm1/trPZb9HUnXmfmcXqY70Io6Sys9giJJrMOOdIi6y7J1w10iC 9 | UoMRA9VWOI+OBcldifKd12HLMb490ufiFg+n/VYCvpqK04/uCV43gcVwG+YT+IsP 10 | kHthHylzk/OHs2dbfX8XEN9CIdJnPSrqc28sKSOu5Wp8fY1kb//UkM42RWv8ewlo 11 | 6FLDKHVbybmX4GZLLN43b6pTY5HoorYUjuBjfSKTyJwecdn02bWWGb834kv09fRH 12 | gd4HGk+iO+9jM2teppLZcRCpC3WtHVZ1KSdSkYZdNHy0oJYLI9fKZ44fJ7IbefJC 13 | kfwPYAECgYEA+wTUdYwh0YbFOIaO5cc4n46SgOQ8skJphT1/PLI8O31KRLlbT8yh 14 | lR1FLKJEuXfn/o7nGypci25uU1Wj59sIrSqHT7w+EYJE5brhR1Tq9LA25KWd3mZz 15 | 1FwwfwGnOj8xrQxoRLmV0PSdq5IEz0TOGhASmwpqlynaYqf7qbYr3cECgYEAxH1M 16 | cpakna28hpHNN2a1JT/DKG75xcGkoCy7wk20EQu20W+yWcep+mfKIYySSpQvukqU 17 | j+hVj4XI2gQA2VgO/SjrfBnPaEoSbJhodo4frV7DOFqasu8KpIWy/75Ys2ZH+oYH 18 | bTC4+FsZm+XDIGxFBSQU8AFf973EmxqiSmhi4gkCgYBcFtKG6UkC4El8SkkkXSkV 19 | yC1RIepuj1S87+m877qPLJDRk5q/NNhWpy1YiZNi4212AyEf4zOeZ34MDpAaIE4X 20 | kVGg2/N9Yug5y4yYKbwk1nzc9zFqhPWbg9K61XVvNf3YeYPLKraeuQ8bAW/IRnrC 21 | LwFUZcbi/BTFOtFl2pJPQQKBgQCb8g4wi9sOwgr0itdi9wMiEx8jAiD+kRfFgHts 22 | VRtqw+9O/eWBV/RSJvnH8rdQtZfDtNl/9Zeilz23uqGp3jfXORjcn+d7D/7bbYLL 23 | RTVi9eje2xoYlZvOw8YzL7FagPzzmlhekuLbSgJjNtHM8+E7mU18xrqR920I5PSV 24 | NY/1cQKBgH0zfFHuoHwZnSGrV5m1wsIcvjxMznM+ny9gsMv23wedojmo4b1YNBjr 25 | 4q+XkzII08QFbPmTP9zGq/twFvi2NcKkLTEZLV8tt2TnhP9jKmsnbaHD7xzNh9xn 26 | xtjO3/abxp9qjyPfwx8R8DWdjDj/PHEntOs1WRXs8Pc6nTNr9cGJ 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/util/JsonUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.util; 17 | 18 | import com.fasterxml.jackson.core.JsonProcessingException; 19 | import com.fasterxml.jackson.databind.ObjectMapper; 20 | import com.fasterxml.jackson.databind.ObjectWriter; 21 | import java.io.IOException; 22 | 23 | /** 24 | * 25 | * @author anand 26 | */ 27 | @lombok.extern.slf4j.Slf4j 28 | public enum JsonUtils { 29 | ; 30 | 31 | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().findAndRegisterModules(); 32 | private static final ObjectWriter WRITER = OBJECT_MAPPER.writer().withDefaultPrettyPrinter(); 33 | 34 | public static String asJson(Object object) { 35 | try { 36 | return WRITER.writeValueAsString(object); 37 | } catch (JsonProcessingException ex) { 38 | log.error("Error processing json output", ex); 39 | return "Error processing json output: " + ex.getMessage(); 40 | } 41 | } 42 | 43 | public static E stringToObject(String json, Class clazz) throws IOException { 44 | return OBJECT_MAPPER.readValue(json, clazz); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/autoconfiguration/SshdShellCommand.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package sshd.shell.springboot.autoconfiguration; 20 | 21 | import java.lang.annotation.ElementType; 22 | import java.lang.annotation.Retention; 23 | import java.lang.annotation.RetentionPolicy; 24 | import java.lang.annotation.Target; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Retention(RetentionPolicy.RUNTIME) 31 | @Target({ElementType.TYPE, ElementType.METHOD}) 32 | public @interface SshdShellCommand { 33 | 34 | /** 35 | * Command. 36 | * @return command 37 | */ 38 | public String value(); 39 | 40 | /** 41 | * Description of command. 42 | * @return description of command 43 | */ 44 | public String description(); 45 | 46 | /** 47 | * Roles that can execute command. 48 | * @return supported roles for executing command 49 | */ 50 | public String[] roles() default {"*"}; 51 | } 52 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/console/DefaultUserInputProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.console; 17 | 18 | import java.util.Optional; 19 | import java.util.regex.Pattern; 20 | import org.springframework.core.annotation.Order; 21 | import org.springframework.stereotype.Component; 22 | import sshd.shell.springboot.ShellException; 23 | import sshd.shell.springboot.util.Assert; 24 | 25 | /** 26 | * 27 | * @author anand 28 | */ 29 | @Component("__defaultUserInputProcessor") 30 | @Order(Integer.MAX_VALUE) 31 | class DefaultUserInputProcessor extends BaseUserInputProcessor { 32 | 33 | private final String[] bannedSymbols = {"|"}; 34 | 35 | @Override 36 | public Optional getUsageInfo() { 37 | return Optional.empty(); 38 | } 39 | 40 | @Override 41 | public Pattern getPattern() { 42 | return Pattern.compile(".+"); 43 | } 44 | 45 | @Override 46 | public void processUserInput(String userInput) throws InterruptedException, ShellException { 47 | for (String bannedSymbol : bannedSymbols) { 48 | Assert.isTrue(!userInput.contains(bannedSymbol), "Invalid command"); 49 | } 50 | ConsoleIO.writeOutput(processCommands(userInput)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/InfoCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.info.InfoEndpoint; 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | import sshd.shell.springboot.util.JsonUtils; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Component 31 | @ConditionalOnBean(InfoEndpoint.class) 32 | @ConditionalOnProperty(name = "management.endpoint.info.enabled", havingValue = "true", matchIfMissing = true) 33 | @SshdShellCommand(value = "info", description = "System status") 34 | public final class InfoCommand extends AbstractSystemCommand { 35 | 36 | private final InfoEndpoint infoEndpoint; 37 | 38 | InfoCommand(@Value("${sshd.system.command.roles.info}") String[] systemRoles, InfoEndpoint infoEndpoint) { 39 | super(systemRoles); 40 | this.infoEndpoint = infoEndpoint; 41 | } 42 | 43 | public String info(String arg) { 44 | return JsonUtils.asJson(infoEndpoint.info()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/server/SimpleSshdPasswordAuthenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.server; 17 | 18 | import java.util.Set; 19 | import org.apache.sshd.server.auth.password.PasswordAuthenticator; 20 | import org.apache.sshd.server.auth.password.PasswordChangeRequiredException; 21 | import org.apache.sshd.server.session.ServerSession; 22 | import sshd.shell.springboot.autoconfiguration.Constants; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellProperties.Shell; 24 | 25 | /** 26 | * 27 | * @author anand 28 | */ 29 | @lombok.AllArgsConstructor(access = lombok.AccessLevel.PACKAGE) 30 | class SimpleSshdPasswordAuthenticator implements PasswordAuthenticator { 31 | 32 | private final Shell props; 33 | private final Set systemCommandRoles; 34 | 35 | @Override 36 | public boolean authenticate(String username, String password, ServerSession session) throws 37 | PasswordChangeRequiredException { 38 | if (username.equals(props.getUsername()) && password.equals(props.getPassword())) { 39 | session.getIoSession().setAttribute(Constants.USER_ROLES, systemCommandRoles); 40 | session.getIoSession().setAttribute(Constants.USER, username); 41 | return true; 42 | } 43 | return false; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/BeansCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.beans.BeansEndpoint; 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | import sshd.shell.springboot.util.JsonUtils; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Component 31 | @ConditionalOnBean(BeansEndpoint.class) 32 | @ConditionalOnProperty(name = "management.endpoint.beans.enabled", havingValue = "true", matchIfMissing = true) 33 | @SshdShellCommand(value = "beans", description = "List beans") 34 | public final class BeansCommand extends AbstractSystemCommand { 35 | 36 | private final BeansEndpoint beansEndpoint; 37 | 38 | BeansCommand(@Value("${sshd.system.command.roles.beans}") String[] systemRoles, BeansEndpoint beansEndpoint) { 39 | super(systemRoles); 40 | this.beansEndpoint = beansEndpoint; 41 | } 42 | 43 | public String beans(String arg) { 44 | return JsonUtils.asJson(beansEndpoint.beans()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/FlywayCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.flyway.FlywayEndpoint; 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | import sshd.shell.springboot.util.JsonUtils; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Component 31 | @ConditionalOnBean(FlywayEndpoint.class) 32 | @ConditionalOnProperty(name = "management.endpoint.flyway.enabled", havingValue = "true", matchIfMissing = true) 33 | @SshdShellCommand(value = "flyway", description = "Flyway database migration details (if applicable)") 34 | public final class FlywayCommand extends AbstractSystemCommand { 35 | 36 | private final FlywayEndpoint flywayEndpoint; 37 | 38 | FlywayCommand(@Value("${sshd.system.command.roles.flyway}") String[] systemRoles, FlywayEndpoint flywayEndpoint) { 39 | super(systemRoles); 40 | this.flywayEndpoint = flywayEndpoint; 41 | } 42 | 43 | public String flyway(String arg) { 44 | return JsonUtils.asJson(flywayEndpoint.flywayBeans()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/MappingsCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.web.mappings.MappingsEndpoint; 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | import sshd.shell.springboot.util.JsonUtils; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Component 31 | @ConditionalOnBean(MappingsEndpoint.class) 32 | @ConditionalOnProperty(name = "management.endpoint.mappings.enabled", havingValue = "true", matchIfMissing = true) 33 | @SshdShellCommand(value = "mappings", description = "List http request mappings") 34 | public final class MappingsCommand extends AbstractSystemCommand { 35 | 36 | private final MappingsEndpoint mappingsEndpoint; 37 | 38 | MappingsCommand(@Value("${sshd.system.command.roles.mappings}") String[] systemRoles, 39 | MappingsEndpoint mappingsEndpoint) { 40 | super(systemRoles); 41 | this.mappingsEndpoint = mappingsEndpoint; 42 | } 43 | 44 | public String mappings(String arg) { 45 | return JsonUtils.asJson(mappingsEndpoint.mappings()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/HttpTraceCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.trace.http.HttpTraceEndpoint; 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | import sshd.shell.springboot.util.JsonUtils; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Component 31 | @ConditionalOnBean(HttpTraceEndpoint.class) 32 | @ConditionalOnProperty(name = "management.endpoint.httptrace.enabled", havingValue = "true", matchIfMissing = true) 33 | @SshdShellCommand(value = "httpTrace", description = "Http trace information") 34 | public final class HttpTraceCommand extends AbstractSystemCommand { 35 | 36 | private final HttpTraceEndpoint httpTraceEndpoint; 37 | 38 | HttpTraceCommand(@Value("${sshd.system.command.roles.httpTrace}") String[] systemRoles, 39 | HttpTraceEndpoint httpTraceEndpoint) { 40 | super(systemRoles); 41 | this.httpTraceEndpoint = httpTraceEndpoint; 42 | } 43 | 44 | public String httpTrace(String arg) { 45 | return JsonUtils.asJson(httpTraceEndpoint.traces()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/ThreadDumpCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.management.ThreadDumpEndpoint; 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | import sshd.shell.springboot.util.JsonUtils; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Component 31 | @ConditionalOnBean(ThreadDumpEndpoint.class) 32 | @ConditionalOnProperty(name = "management.endpoint.threaddump.enabled", havingValue = "true", matchIfMissing = true) 33 | @SshdShellCommand(value = "threadDump", description = "Print thread dump") 34 | public final class ThreadDumpCommand extends AbstractSystemCommand { 35 | 36 | private final ThreadDumpEndpoint threadDumpEndpoint; 37 | 38 | ThreadDumpCommand(@Value("${sshd.system.command.roles.threadDump}") String[] systemRoles, 39 | ThreadDumpEndpoint threadDumpEndpoint) { 40 | super(systemRoles); 41 | this.threadDumpEndpoint = threadDumpEndpoint; 42 | } 43 | 44 | public String threadDump(String arg) { 45 | return JsonUtils.asJson(threadDumpEndpoint.threadDump()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/LiquibaseCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.liquibase.LiquibaseEndpoint; 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | import sshd.shell.springboot.util.JsonUtils; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Component 31 | @ConditionalOnBean(LiquibaseEndpoint.class) 32 | @ConditionalOnProperty(name = "management.endpoint.liquibase.enabled", havingValue = "true", matchIfMissing = true) 33 | @SshdShellCommand(value = "liquibase", description = "Liquibase database migration details (if applicable)") 34 | public final class LiquibaseCommand extends AbstractSystemCommand { 35 | 36 | private final LiquibaseEndpoint liquibaseEndpoint; 37 | 38 | LiquibaseCommand(@Value("${sshd.system.command.roles.liquibase}") String[] systemRoles, 39 | LiquibaseEndpoint liquibaseEndpoint) { 40 | super(systemRoles); 41 | this.liquibaseEndpoint = liquibaseEndpoint; 42 | } 43 | 44 | public String liquibase(String arg) { 45 | return JsonUtils.asJson(liquibaseEndpoint.liquibaseBeans()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/ShutdownCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; 20 | import org.springframework.boot.actuate.context.ShutdownEndpoint; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | import sshd.shell.springboot.util.JsonUtils; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Component 31 | @ConditionalOnAvailableEndpoint(endpoint = ShutdownEndpoint.class) 32 | @ConditionalOnProperty(name = "management.endpoint.shutdown.enabled", havingValue = "true", matchIfMissing = false) 33 | @SshdShellCommand(value = "shutdown", description = "Shutdown application") 34 | public final class ShutdownCommand extends AbstractSystemCommand { 35 | 36 | private final ShutdownEndpoint shutdownEndpoint; 37 | 38 | ShutdownCommand(@Value("${sshd.system.command.roles.shutdown}") String[] systemRoles, 39 | ShutdownEndpoint shutdownEndpoint) { 40 | super(systemRoles); 41 | this.shutdownEndpoint = shutdownEndpoint; 42 | } 43 | 44 | public String shutdown(String arg) { 45 | return JsonUtils.asJson(shutdownEndpoint.shutdown()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/ScheduledTasksCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint; 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | import sshd.shell.springboot.util.JsonUtils; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Component 31 | @ConditionalOnBean(ScheduledTasksEndpoint.class) 32 | @ConditionalOnProperty(name = "management.endpoint.scheduledtasks.enabled", havingValue = "true", matchIfMissing = true) 33 | @SshdShellCommand(value = "scheduledTasks", description = "Scheduled tasks") 34 | public final class ScheduledTasksCommand extends AbstractSystemCommand { 35 | 36 | private final ScheduledTasksEndpoint scheduledTasksEndpoint; 37 | 38 | ScheduledTasksCommand(@Value("${sshd.system.command.roles.scheduledTasks}") String[] systemRoles, 39 | ScheduledTasksEndpoint scheduledTasksEndpoint) { 40 | super(systemRoles); 41 | this.scheduledTasksEndpoint = scheduledTasksEndpoint; 42 | } 43 | 44 | public String scheduledTasks(String arg) { 45 | return JsonUtils.asJson(scheduledTasksEndpoint.scheduledTasks()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/resources/config/application.properties: -------------------------------------------------------------------------------- 1 | # Copyright 2019 anand. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | sshd.system.command.roles=ADMIN 16 | sshd.system.command.roles.auditEvents=${sshd.system.command.roles} 17 | sshd.system.command.roles.beans=${sshd.system.command.roles} 18 | sshd.system.command.roles.caches=${sshd.system.command.roles} 19 | sshd.system.command.roles.conditionsReport=${sshd.system.command.roles} 20 | sshd.system.command.roles.configurationPropertiesReport=${sshd.system.command.roles} 21 | sshd.system.command.roles.environment=${sshd.system.command.roles} 22 | sshd.system.command.roles.health=${sshd.system.command.roles} 23 | sshd.system.command.roles.heapDump=${sshd.system.command.roles} 24 | sshd.system.command.roles.httpTrace=${sshd.system.command.roles} 25 | sshd.system.command.roles.info=${sshd.system.command.roles} 26 | sshd.system.command.roles.loggers=${sshd.system.command.roles} 27 | sshd.system.command.roles.mappings=${sshd.system.command.roles} 28 | sshd.system.command.roles.metrics=${sshd.system.command.roles} 29 | sshd.system.command.roles.scheduledTasks=${sshd.system.command.roles} 30 | sshd.system.command.roles.shutdown=${sshd.system.command.roles} 31 | sshd.system.command.roles.threadDump=${sshd.system.command.roles} 32 | sshd.system.command.roles.flyway=${sshd.system.command.roles} 33 | sshd.system.command.roles.integrationGraph=${sshd.system.command.roles} 34 | sshd.system.command.roles.liquibase=${sshd.system.command.roles} 35 | sshd.system.command.roles.sessions=${sshd.system.command.roles} 36 | sshd.system.command.roles.logfile=${sshd.system.command.roles} 37 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/java/sshd/shell/springboot/command/TestCommand.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package sshd.shell.springboot.command; 20 | 21 | import java.io.IOException; 22 | import org.springframework.stereotype.Component; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | import sshd.shell.springboot.console.ConsoleIO; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Component 31 | @SshdShellCommand(value = "test", description = "test description", roles = {"USER", "ADMIN"}) 32 | public class TestCommand { 33 | 34 | @SshdShellCommand(value = "run", description = "test run", roles = "USER") 35 | public String run(String arg) { 36 | return "test run " + arg; 37 | } 38 | 39 | @SshdShellCommand(value = "execute", description = "test execute", roles = "ADMIN") 40 | public String execute(String arg) { 41 | return "test execute successful"; 42 | } 43 | 44 | @SshdShellCommand(value = "interactive", description = "test interactive") 45 | public String interactive(String arg) throws IOException { 46 | String name = ConsoleIO.readInput("Name:"); 47 | ConsoleIO.writeJsonOutput(new X(name)); 48 | return "Hi " + name; 49 | } 50 | 51 | @lombok.AllArgsConstructor 52 | private static class X { 53 | public final String obj; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/ConditionsReportCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpoint; 20 | import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | import sshd.shell.springboot.util.JsonUtils; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Component 31 | @ConditionalOnAvailableEndpoint(endpoint = ConditionsReportEndpoint.class) 32 | @ConditionalOnProperty(name = "management.endpoint.conditions.enabled", havingValue = "true", matchIfMissing = true) 33 | @SshdShellCommand(value = "conditionsReport", description = "Conditions report") 34 | public final class ConditionsReportCommand extends AbstractSystemCommand { 35 | 36 | private final ConditionsReportEndpoint conditionsReportEndpoint; 37 | 38 | ConditionsReportCommand(@Value("${sshd.system.command.roles.conditionsReport}") String[] systemRoles, 39 | ConditionsReportEndpoint conditionsReportEndpoint) { 40 | super(systemRoles); 41 | this.conditionsReportEndpoint = conditionsReportEndpoint; 42 | } 43 | 44 | public String conditionsReport(String arg) { 45 | return JsonUtils.asJson(conditionsReportEndpoint.applicationConditionEvaluation()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/CommandUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.nio.file.Files; 21 | import java.nio.file.Path; 22 | import java.nio.file.Paths; 23 | import org.springframework.core.io.Resource; 24 | import sshd.shell.springboot.autoconfiguration.SshSessionContext; 25 | import sshd.shell.springboot.util.ZipUtils; 26 | 27 | /** 28 | * 29 | * @author anand 30 | */ 31 | @lombok.extern.slf4j.Slf4j 32 | public enum CommandUtils { 33 | ; 34 | 35 | public static Path sessionUserPathContainingZippedResource(Resource resource) throws IOException { 36 | Path heapDumpFilePath = Paths.get(resource.getURI()); 37 | return ZipUtils.zipFiles(sessionUserDir(), true, heapDumpFilePath); 38 | } 39 | 40 | private static Path sessionUserDir() throws IOException { 41 | File sessionUserDir = SshSessionContext.getUserDir(); 42 | if (!sessionUserDir.exists()) { 43 | Files.createDirectories(sessionUserDir.toPath()); 44 | } 45 | return sessionUserDir.toPath(); 46 | } 47 | 48 | public static String process(JsonProcessor processor) { 49 | try { 50 | return processor.process(); 51 | } catch (IOException | IllegalArgumentException ex) { 52 | log.warn("Invalid json", ex); 53 | return "Expected valid json as argument"; 54 | } 55 | } 56 | 57 | @FunctionalInterface 58 | public static interface JsonProcessor { 59 | 60 | String process() throws IOException; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/autoconfiguration/SshSessionContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.autoconfiguration; 17 | 18 | import java.io.File; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import java.util.function.Supplier; 22 | 23 | /** 24 | * 25 | * @author anand 26 | */ 27 | public enum SshSessionContext { 28 | ; 29 | 30 | private static final ThreadLocal> USER_DIR_CONTEXT = new ThreadLocal>(); 31 | private static final ThreadLocal> THREAD_CONTEXT 32 | = ThreadLocal.withInitial(() -> new HashMap<>()); 33 | 34 | public static void put(String key, Object value) { 35 | THREAD_CONTEXT.get().put(key, value); 36 | } 37 | 38 | @SuppressWarnings("unchecked") 39 | public static E get(String key) { 40 | return (E) THREAD_CONTEXT.get().get(key); 41 | } 42 | 43 | @SuppressWarnings("unchecked") 44 | public static E remove(String key) { 45 | return (E) THREAD_CONTEXT.get().remove(key); 46 | } 47 | 48 | public static boolean containsKey(String key) { 49 | return THREAD_CONTEXT.get().containsKey(key); 50 | } 51 | 52 | public static boolean isEmpty() { 53 | return THREAD_CONTEXT.get().isEmpty(); 54 | } 55 | 56 | public static void clear() { 57 | THREAD_CONTEXT.remove(); 58 | USER_DIR_CONTEXT.remove(); 59 | } 60 | 61 | public static void setUserDir(Supplier userDirSupplier) { 62 | USER_DIR_CONTEXT.set(userDirSupplier); 63 | } 64 | 65 | public static File getUserDir() { 66 | return USER_DIR_CONTEXT.get().get(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/ConfigurationPropertiesReportCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint; 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | import sshd.shell.springboot.util.JsonUtils; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Component 31 | @ConditionalOnBean(ConfigurationPropertiesReportEndpoint.class) 32 | @ConditionalOnProperty(name = "management.endpoint.configprops.enabled", havingValue = "true", matchIfMissing = true) 33 | @SshdShellCommand(value = "configurationPropertiesReport", description = "Configuration properties report") 34 | public final class ConfigurationPropertiesReportCommand extends AbstractSystemCommand { 35 | 36 | private final ConfigurationPropertiesReportEndpoint confPropsReportEndpoint; 37 | 38 | ConfigurationPropertiesReportCommand(ConfigurationPropertiesReportEndpoint confPropsReportEndpoint, 39 | @Value("${sshd.system.command.roles.configurationPropertiesReport}") String[] systemRoles) { 40 | super(systemRoles); 41 | this.confPropsReportEndpoint = confPropsReportEndpoint; 42 | } 43 | 44 | public String configurationPropertiesReport(String arg) { 45 | return JsonUtils.asJson(confPropsReportEndpoint.configurationProperties()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/autoconfiguration/CommandExecutableDetails.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.autoconfiguration; 17 | 18 | import java.util.Collection; 19 | import java.util.Collections; 20 | import java.util.Set; 21 | import org.springframework.util.CollectionUtils; 22 | import sshd.shell.springboot.ShellException; 23 | 24 | /** 25 | * 26 | * @author anand 27 | */ 28 | @lombok.extern.slf4j.Slf4j 29 | public class CommandExecutableDetails { 30 | 31 | private final Set roles; 32 | private final String command; 33 | @lombok.Getter 34 | private final String description; 35 | @lombok.Getter 36 | private final CommandExecutor commandExecutor; 37 | 38 | CommandExecutableDetails(SshdShellCommand command, Set roles, CommandExecutor commandExecutor) { 39 | this.roles = Collections.unmodifiableSet(roles); 40 | this.command = command.value(); 41 | this.description = command.description(); 42 | this.commandExecutor = commandExecutor; 43 | } 44 | 45 | public boolean matchesRole(Collection userRoles) { 46 | if (roles.contains("*") || userRoles.contains("*")) { 47 | return true; 48 | } 49 | return CollectionUtils.containsAny(roles, userRoles); 50 | } 51 | 52 | public String executeWithArg(String arg) throws InterruptedException, ShellException { 53 | return commandExecutor.get(arg); 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return new StringBuilder("Command: ").append(command).append(", description: ").append(description) 59 | .append(", roles: ").append(roles).toString(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/IntegrationGraphCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.integration.IntegrationGraphEndpoint; 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 24 | import sshd.shell.springboot.util.JsonUtils; 25 | 26 | /** 27 | * 28 | * @author anand 29 | */ 30 | @Component 31 | @ConditionalOnBean(IntegrationGraphEndpoint.class) 32 | @ConditionalOnProperty(name = "management.endpoint.integrationgraph.enabled", havingValue = "true", 33 | matchIfMissing = true) 34 | @SshdShellCommand(value = "integrationGraph", description = "Information about Spring Integration graph") 35 | public final class IntegrationGraphCommand extends AbstractSystemCommand { 36 | 37 | private final IntegrationGraphEndpoint integrationGraphEndpoint; 38 | 39 | IntegrationGraphCommand(@Value("${sshd.system.command.roles.integrationGraph}") String[] systemRoles, 40 | IntegrationGraphEndpoint integrationGraphEndpoint) { 41 | super(systemRoles); 42 | this.integrationGraphEndpoint = integrationGraphEndpoint; 43 | } 44 | 45 | public String integrationGraph(String arg) { 46 | return JsonUtils.asJson(integrationGraphEndpoint.graph()); 47 | } 48 | 49 | @SshdShellCommand(value = "rebuild", description = "Rebuild") 50 | public String rebuild(String arg) { 51 | integrationGraphEndpoint.rebuild(); 52 | return "Integration Graph rebuilt"; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/server/SshdNativeFileSystemFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.server; 17 | 18 | import java.io.IOException; 19 | import java.nio.file.FileSystem; 20 | import java.nio.file.Files; 21 | import java.nio.file.NotDirectoryException; 22 | import java.nio.file.Path; 23 | import java.nio.file.Paths; 24 | import org.apache.sshd.common.file.nativefs.NativeFileSystemFactory; 25 | import org.apache.sshd.common.file.root.RootedFileSystem; 26 | import org.apache.sshd.common.file.root.RootedFileSystemProvider; 27 | import org.apache.sshd.common.session.SessionContext; 28 | 29 | /** 30 | * 31 | * @author anand 32 | */ 33 | @lombok.RequiredArgsConstructor 34 | @lombok.extern.slf4j.Slf4j 35 | class SshdNativeFileSystemFactory extends NativeFileSystemFactory { 36 | 37 | private final String baseDir; 38 | 39 | @Override 40 | public FileSystem createFileSystem(SessionContext session) throws IOException { 41 | Path sessionUserDir = Paths.get(baseDir, session.getUsername()); 42 | processForSessionUserDirectory(sessionUserDir); 43 | return new RootedFileSystem(new RootedFileSystemProvider(), sessionUserDir, null); 44 | } 45 | 46 | private void processForSessionUserDirectory(Path sessionUserDir) throws IOException { 47 | if (Files.exists(sessionUserDir)) { 48 | validateSessionUserDir(sessionUserDir); 49 | } else { 50 | log.info("Session user directory created: {}", Files.createDirectories(sessionUserDir)); 51 | } 52 | } 53 | 54 | private void validateSessionUserDir(Path sessionUserDir) throws NotDirectoryException { 55 | if (!Files.isDirectory(sessionUserDir)) { 56 | throw new NotDirectoryException(sessionUserDir.toString()); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/server/AuthProviderSshdPasswordAuthenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.server; 17 | 18 | import java.util.stream.Collectors; 19 | import org.apache.sshd.server.auth.password.PasswordAuthenticator; 20 | import org.apache.sshd.server.auth.password.PasswordChangeRequiredException; 21 | import org.apache.sshd.server.session.ServerSession; 22 | import org.springframework.security.authentication.AuthenticationProvider; 23 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 24 | import org.springframework.security.core.Authentication; 25 | import org.springframework.security.core.AuthenticationException; 26 | import sshd.shell.springboot.autoconfiguration.Constants; 27 | 28 | /** 29 | * 30 | * @author anand 31 | */ 32 | @lombok.AllArgsConstructor(access = lombok.AccessLevel.PACKAGE) 33 | @lombok.extern.slf4j.Slf4j 34 | class AuthProviderSshdPasswordAuthenticator implements PasswordAuthenticator { 35 | 36 | private final AuthenticationProvider authProvider; 37 | 38 | @Override 39 | public boolean authenticate(String username, String password, ServerSession session) throws 40 | PasswordChangeRequiredException { 41 | try { 42 | Authentication auth = authProvider.authenticate( 43 | new UsernamePasswordAuthenticationToken(username, password)); 44 | session.getIoSession().setAttribute(Constants.USER, username); 45 | session.getIoSession().setAttribute(Constants.USER_ROLES, auth.getAuthorities().stream() 46 | .map(ga -> ga.getAuthority()).collect(Collectors.toSet())); 47 | return true; 48 | } catch (AuthenticationException ex) { 49 | log.warn(ex.getMessage()); 50 | return false; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/console/HighlightUserInputProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.console; 17 | 18 | import java.util.Arrays; 19 | import java.util.Optional; 20 | import java.util.regex.Matcher; 21 | import java.util.regex.Pattern; 22 | import org.springframework.core.annotation.Order; 23 | import org.springframework.stereotype.Component; 24 | import sshd.shell.springboot.ShellException; 25 | import sshd.shell.springboot.console.UsageInfo.Row; 26 | import sshd.shell.springboot.util.Assert; 27 | 28 | /** 29 | * 30 | * @author anand 31 | */ 32 | @Component 33 | @Order(1) 34 | class HighlightUserInputProcessor extends BaseUserInputProcessor { 35 | 36 | private final Pattern pattern = Pattern.compile(".+\\s?\\|\\s?h (.+)"); 37 | 38 | @Override 39 | public Optional getUsageInfo() { 40 | return Optional.of(new UsageInfo(Arrays.asList( 41 | new Row("h ", "Highlights in response output of command execution"), 42 | new Row("", "Example usage: help | h exit")))); 43 | } 44 | 45 | @Override 46 | public Pattern getPattern() { 47 | return pattern; 48 | } 49 | 50 | @Override 51 | public void processUserInput(String userInput) throws InterruptedException, ShellException { 52 | String textToHighlight = getHighlightedText(userInput); 53 | String[] tokens = splitAndValidateCommand(userInput, "\\|", 2); 54 | String commandExecution = tokens[0]; 55 | String output = processCommands(commandExecution); 56 | ConsoleIO.writeOutput(output, textToHighlight); 57 | } 58 | 59 | private String getHighlightedText(String userInput) throws ShellException { 60 | Matcher matcher = pattern.matcher(userInput); 61 | Assert.isTrue(matcher.find(), "Unexpected error"); 62 | return matcher.group(1).trim(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/EnvironmentCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.env.EnvironmentEndpoint; 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import org.springframework.util.StringUtils; 24 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 25 | import sshd.shell.springboot.util.JsonUtils; 26 | 27 | /** 28 | * 29 | * @author anand 30 | */ 31 | @Component 32 | @ConditionalOnBean(EnvironmentEndpoint.class) 33 | @ConditionalOnProperty(name = "management.endpoint.env.enabled", havingValue = "true", matchIfMissing = true) 34 | @SshdShellCommand(value = "environment", description = "Environment details") 35 | public final class EnvironmentCommand extends AbstractSystemCommand { 36 | 37 | private final EnvironmentEndpoint envEndpoint; 38 | 39 | EnvironmentCommand(@Value("${sshd.system.command.roles.environment}") String[] systemRoles, 40 | EnvironmentEndpoint envEndpoint) { 41 | super(systemRoles); 42 | this.envEndpoint = envEndpoint; 43 | } 44 | 45 | @SshdShellCommand(value = "pattern", description = "Get environment details with given pattern") 46 | public String withPattern(String arg) { 47 | return JsonUtils.asJson(envEndpoint.environment(arg)); 48 | } 49 | 50 | @SshdShellCommand(value = "entry", description = "Get environment details with string to match") 51 | public String withEntry(String arg) { 52 | return !StringUtils.hasText(arg) 53 | ? "Usage: environment entry " 54 | : JsonUtils.asJson(envEndpoint.environmentEntry(arg)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/LogfileCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import java.io.IOException; 19 | import java.nio.file.Path; 20 | import org.springframework.beans.factory.annotation.Value; 21 | import org.springframework.boot.actuate.logging.LogFileWebEndpoint; 22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 24 | import org.springframework.core.io.Resource; 25 | import org.springframework.stereotype.Component; 26 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 27 | 28 | /** 29 | * 30 | * @author anand 31 | */ 32 | @Component 33 | @ConditionalOnBean(LogFileWebEndpoint.class) 34 | @ConditionalOnProperty(name = "management.endpoint.logfile.enabled", havingValue = "true", matchIfMissing = true) 35 | @SshdShellCommand(value = "logfile", description = "Application log file") 36 | @lombok.extern.slf4j.Slf4j 37 | public final class LogfileCommand extends AbstractSystemCommand { 38 | 39 | private final LogFileWebEndpoint logFileWebEndpoint; 40 | 41 | LogfileCommand(@Value("${sshd.system.command.roles.logfile}") String[] systemRoles, 42 | LogFileWebEndpoint logFileWebEndpoint) { 43 | super(systemRoles); 44 | this.logFileWebEndpoint = logFileWebEndpoint; 45 | } 46 | 47 | public String logfile(String arg) throws IOException { 48 | Resource logFileResource = logFileWebEndpoint.logFile(); 49 | try { 50 | Path path = CommandUtils.sessionUserPathContainingZippedResource(logFileResource); 51 | return "Resource can be downloaded with SFTP/SCP at " + path.getFileName().toString(); 52 | } catch (IllegalStateException ex) { 53 | log.warn(ex.getMessage()); 54 | return "Resource can be found at " + logFileResource.getFile().getAbsolutePath(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/HealthCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.actuate.health.HealthEndpoint; 20 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 | import org.springframework.stereotype.Component; 23 | import org.springframework.util.StringUtils; 24 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 25 | import sshd.shell.springboot.util.JsonUtils; 26 | 27 | /** 28 | * 29 | * @author anand 30 | */ 31 | @Component 32 | @ConditionalOnBean(HealthEndpoint.class) 33 | @ConditionalOnProperty(name = "management.endpoint.health.enabled", havingValue = "true", matchIfMissing = true) 34 | @SshdShellCommand(value = "health", description = "System health info") 35 | public final class HealthCommand extends AbstractSystemCommand { 36 | 37 | private final HealthEndpoint healthEndpoint; 38 | 39 | HealthCommand(@Value("${sshd.system.command.roles.health}") String[] systemRoles, HealthEndpoint healthEndpoint) { 40 | super(systemRoles); 41 | this.healthEndpoint = healthEndpoint; 42 | } 43 | 44 | @SshdShellCommand(value = "info", description = "Health info for all components") 45 | public String healthInfo(String arg) { 46 | return JsonUtils.asJson(healthEndpoint.health()); 47 | } 48 | 49 | @SshdShellCommand(value = "component", description = "Health for specified components") 50 | public String healthForComponent(String arg) { 51 | if (!StringUtils.hasText(arg)) { 52 | return "Usage: health component , and so on"; 53 | } 54 | String[] components = StringUtils.trimAllWhitespace(arg).split(","); 55 | return JsonUtils.asJson(healthEndpoint.healthForPath(components)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/java/sshd/shell/springboot/command/EndpointCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import java.lang.reflect.Method; 19 | import java.lang.reflect.Parameter; 20 | import java.util.Comparator; 21 | import java.util.Map; 22 | 23 | import org.springframework.beans.factory.annotation.Autowired; 24 | import org.springframework.boot.actuate.endpoint.annotation.Endpoint; 25 | import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; 26 | import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; 27 | import org.springframework.boot.actuate.logging.LogFileWebEndpoint; 28 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 29 | import org.springframework.context.ApplicationContext; 30 | import org.springframework.stereotype.Component; 31 | 32 | /** 33 | * 34 | * Helper component to discover endpoints and facilitate the creation of said endpoint command by developer. 35 | * 36 | * @author anand 37 | */ 38 | @Component 39 | @ConditionalOnClass(Endpoint.class) 40 | @lombok.extern.slf4j.Slf4j 41 | public final class EndpointCommand { 42 | 43 | @Autowired 44 | EndpointCommand(ApplicationContext appCtx) { 45 | appCtx.getBeansWithAnnotation(Endpoint.class).entrySet().stream() 46 | .sorted(Map.Entry.comparingByKey()) 47 | .forEachOrdered(entry -> { 48 | log.debug("{} : {}", entry.getKey(), entry.getValue().getClass().getName()); 49 | for (Method m : entry.getValue().getClass().getDeclaredMethods()) { 50 | if (m.isAnnotationPresent(ReadOperation.class) || m.isAnnotationPresent(WriteOperation.class)) { 51 | log.debug("\tOp: {}", m.getName()); 52 | for (Parameter p : m.getParameters()) { 53 | log.debug("\t\tParameter {}, {}", p.getName(), p.getType().getName()); 54 | } 55 | } 56 | } 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/CachesCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import io.micrometer.core.instrument.util.StringUtils; 19 | import org.springframework.beans.factory.annotation.Value; 20 | import org.springframework.boot.actuate.cache.CachesEndpoint; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 23 | import org.springframework.stereotype.Component; 24 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 25 | import sshd.shell.springboot.util.JsonUtils; 26 | 27 | /** 28 | * 29 | * @author anand 30 | */ 31 | @Component 32 | @ConditionalOnBean(CachesEndpoint.class) 33 | @ConditionalOnProperty(name = "management.endpoint.caches.enabled", havingValue = "true", matchIfMissing = true) 34 | @SshdShellCommand(value = "caches", description = "Caches info") 35 | @lombok.extern.slf4j.Slf4j 36 | public final class CachesCommand extends AbstractSystemCommand { 37 | 38 | private final CachesEndpoint cachesEndpoint; 39 | 40 | CachesCommand(@Value("${sshd.system.command.roles.caches}") String[] systemRoles, CachesEndpoint cachesEndpoint) { 41 | super(systemRoles); 42 | this.cachesEndpoint = cachesEndpoint; 43 | } 44 | 45 | @SshdShellCommand(value = "list", description = "Cache info") 46 | public String cacheList(String arg) { 47 | return JsonUtils.asJson(cachesEndpoint.caches()); 48 | } 49 | 50 | @SshdShellCommand(value = "show", description = "Cache info by cache and cacheManager") 51 | public String cache(String arg) { 52 | if (StringUtils.isEmpty(arg)) { 53 | return "Usage: caches show {\"cache\":\"\", \"cacheManager\":\"\"}"; 54 | } 55 | return CommandUtils.process(() -> { 56 | Cache cache = JsonUtils.stringToObject(arg, Cache.class); 57 | return JsonUtils.asJson(cachesEndpoint.cache(cache.cache, cache.cacheManager)); 58 | }); 59 | } 60 | 61 | private static class Cache { 62 | 63 | public String cache; 64 | public String cacheManager; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/console/ConsoleConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.console; 17 | 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.stream.Collectors; 21 | import org.jline.builtins.Completers; 22 | import org.jline.builtins.Completers.TreeCompleter.Node; 23 | import static org.jline.builtins.Completers.TreeCompleter.node; 24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 25 | import org.springframework.context.annotation.Bean; 26 | import org.springframework.context.annotation.Configuration; 27 | import sshd.shell.springboot.autoconfiguration.CommandExecutableDetails; 28 | import sshd.shell.springboot.autoconfiguration.Constants; 29 | import sshd.shell.springboot.autoconfiguration.SshdShellProperties; 30 | 31 | /** 32 | * 33 | * @author anand 34 | */ 35 | @Configuration 36 | @ConditionalOnProperty(name = "sshd.shell.enabled", havingValue = "true") 37 | class ConsoleConfiguration { 38 | 39 | @Bean 40 | TerminalProcessor terminalProcessor(SshdShellProperties properties, 41 | Map> sshdShellCommands, 42 | List userInputProcessors) { 43 | return new TerminalProcessor(properties.getShell(), 44 | new Completers.TreeCompleter(buildTextCompleters(sshdShellCommands)), userInputProcessors); 45 | } 46 | 47 | private List buildTextCompleters(Map> sshdShellCommands) { 48 | return sshdShellCommands.entrySet().stream().map(entry -> buildTextCompleterNode(entry)) 49 | .collect(Collectors.toList()); 50 | } 51 | 52 | private Node buildTextCompleterNode(Map.Entry> entry) { 53 | Object[] subCommands = entry.getValue().keySet().stream() 54 | .filter(s -> !s.equals(Constants.EXECUTE)) 55 | .toArray(Object[]::new); 56 | return subCommands.length == 0 57 | ? node(entry.getKey()) 58 | : node(entry.getKey(), node(subCommands)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/HeapDumpCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import java.io.IOException; 19 | import java.nio.file.Path; 20 | import org.springframework.beans.factory.annotation.Value; 21 | import org.springframework.boot.actuate.management.HeapDumpWebEndpoint; 22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 24 | import org.springframework.core.io.Resource; 25 | import org.springframework.stereotype.Component; 26 | import org.springframework.util.StringUtils; 27 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 28 | 29 | /** 30 | * 31 | * @author anand 32 | */ 33 | @Component 34 | @ConditionalOnBean(HeapDumpWebEndpoint.class) 35 | @ConditionalOnProperty(name = "management.endpoint.env.enabled", havingValue = "true", matchIfMissing = true) 36 | @SshdShellCommand(value = "heapDump", description = "Heap dump command") 37 | @lombok.extern.slf4j.Slf4j 38 | public final class HeapDumpCommand extends AbstractSystemCommand { 39 | 40 | private final HeapDumpWebEndpoint heapDumpEndpoint; 41 | 42 | HeapDumpCommand(@Value("${sshd.system.command.roles.heapDump}") String[] systemRoles, 43 | HeapDumpWebEndpoint heapDumpEndpoint) { 44 | super(systemRoles); 45 | this.heapDumpEndpoint = heapDumpEndpoint; 46 | } 47 | 48 | @SshdShellCommand(value = "live", description = "Get heapdump with live flag") 49 | public String withLive(String arg) throws IOException { 50 | if (!StringUtils.hasText(arg)) { 51 | return "Usage: heapDump live "; 52 | } 53 | Resource heapDumpResource = heapDumpEndpoint.heapDump(Boolean.valueOf(arg)).getBody(); 54 | try { 55 | Path path = CommandUtils.sessionUserPathContainingZippedResource(heapDumpResource); 56 | return "Resource can be downloaded with SFTP/SCP at " + path.getFileName().toString(); 57 | } catch (IllegalStateException ex) { 58 | log.warn(ex.getMessage()); 59 | return "Resource can be found at " + heapDumpResource.getFile().getAbsolutePath(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/HelpCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import java.util.Collection; 19 | import java.util.List; 20 | import java.util.Locale; 21 | import java.util.Map; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.context.annotation.Lazy; 24 | import org.springframework.stereotype.Component; 25 | import sshd.shell.springboot.autoconfiguration.CommandExecutableDetails; 26 | import sshd.shell.springboot.autoconfiguration.Constants; 27 | import sshd.shell.springboot.autoconfiguration.SshSessionContext; 28 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 29 | import sshd.shell.springboot.autoconfiguration.SshdShellProperties; 30 | import sshd.shell.springboot.console.BaseUserInputProcessor; 31 | import sshd.shell.springboot.console.UsageInfo; 32 | 33 | /** 34 | * 35 | * @author anand 36 | */ 37 | @Component 38 | @lombok.NoArgsConstructor(access = lombok.AccessLevel.PACKAGE) 39 | @SshdShellCommand(value = Constants.HELP, description = "Show list of help commands") 40 | public final class HelpCommand { 41 | 42 | @Lazy 43 | @Autowired 44 | private Map> sshdShellCommands; 45 | @Autowired 46 | private List processors; 47 | @Autowired 48 | private SshdShellProperties properties; 49 | 50 | public String help(String arg) { 51 | StringBuilder sb = new StringBuilder("Supported Commands"); 52 | Collection roles = SshSessionContext.>get(Constants.USER_ROLES); 53 | String format = properties.getShell().getText().getUsageInfoFormat(); 54 | sshdShellCommands.entrySet().stream().filter(e -> e.getValue().get(Constants.EXECUTE).matchesRole(roles)) 55 | .forEachOrdered(e -> sb.append(String.format(Locale.ENGLISH, format, e.getKey(), 56 | e.getValue().get(Constants.EXECUTE).getDescription()))); 57 | sb.append("\nSupported post processors for output"); 58 | processors.stream().filter(p -> p.getUsageInfo().isPresent()).forEachOrdered(p -> { 59 | List rows = p.getUsageInfo().get().getRows(); 60 | rows.forEach(r -> sb.append(String.format(Locale.ENGLISH, format, r.getUsage(), r.getDescription()))); 61 | }); 62 | return sb.toString(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/AuditEventsCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import com.fasterxml.jackson.annotation.JsonFormat; 19 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 20 | import java.time.OffsetDateTime; 21 | import org.springframework.beans.factory.annotation.Value; 22 | import org.springframework.boot.actuate.audit.AuditEventsEndpoint; 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 25 | import org.springframework.stereotype.Component; 26 | import org.springframework.util.StringUtils; 27 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 28 | import sshd.shell.springboot.util.JsonUtils; 29 | 30 | /** 31 | * 32 | * @author anand 33 | */ 34 | @Component 35 | @ConditionalOnBean(AuditEventsEndpoint.class) 36 | @ConditionalOnProperty(name = "management.endpoint.auditevents.enabled", havingValue = "true", matchIfMissing = true) 37 | @SshdShellCommand(value = "auditEvents", description = "Event auditing") 38 | public final class AuditEventsCommand extends AbstractSystemCommand { 39 | 40 | private final AuditEventsEndpoint auditEventsEndpoint; 41 | 42 | AuditEventsCommand(@Value("${sshd.system.command.roles.auditEvents}") String[] systemRoles, 43 | AuditEventsEndpoint auditEventsEndpoint) { 44 | super(systemRoles); 45 | this.auditEventsEndpoint = auditEventsEndpoint; 46 | } 47 | 48 | @SshdShellCommand(value = "list", description = "List events") 49 | public String auditEvents(String arg) { 50 | if (!StringUtils.hasText(arg)) { 51 | return "Usage: auditEvents list {\"principal\":\"\",\"after\":\"\"," 52 | + "\"type\":\"\"}"; 53 | } 54 | return CommandUtils.process(() -> { 55 | Event event = JsonUtils.stringToObject(arg, Event.class); 56 | return JsonUtils.asJson(auditEventsEndpoint.events(event.principal, event.after, event.type)); 57 | }); 58 | } 59 | 60 | @JsonIgnoreProperties(ignoreUnknown = true) 61 | private static class Event { 62 | 63 | public String principal; 64 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm") 65 | public OffsetDateTime after; 66 | public String type; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/java/sshd/shell/springboot/autoconfiguration/SshdSftpEnabledTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.autoconfiguration; 17 | 18 | import com.jcraft.jsch.ChannelSftp; 19 | import com.jcraft.jsch.Session; 20 | import java.io.File; 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | import static org.junit.Assert.assertTrue; 24 | import org.junit.Test; 25 | import org.springframework.boot.test.context.SpringBootTest; 26 | import org.springframework.test.annotation.DirtiesContext; 27 | 28 | /** 29 | * 30 | * @author anand 31 | */ 32 | @SpringBootTest(classes = ConfigTest.class, properties = { 33 | "sshd.filetransfer.enabled=true", 34 | "sshd.filesystem.base.dir=target/sftp", 35 | "spring.jmx.enabled=true", 36 | "spring.flyway.baseline-on-migrate=true", 37 | "spring.main.allow-circular-references=true" 38 | }) 39 | @DirtiesContext 40 | public class SshdSftpEnabledTest extends AbstractSshSupport { 41 | 42 | @DirtiesContext 43 | @Test 44 | public void testSftp() throws Exception { 45 | Session session = openSession(props.getShell().getUsername(), props.getShell().getPassword()); 46 | ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); 47 | sftp.connect(); 48 | sftp.disconnect(); 49 | session.disconnect(); 50 | assertTrue(new File("target/sftp/admin").exists()); 51 | } 52 | 53 | @DirtiesContext 54 | @Test 55 | public void testSftp2() throws Exception { 56 | Session session = openSession(props.getShell().getUsername(), props.getShell().getPassword()); 57 | ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); 58 | sftp.connect(); 59 | sftp.disconnect(); 60 | session.disconnect(); 61 | assertTrue(new File("target/sftp/admin").exists()); 62 | } 63 | 64 | @Test 65 | public void testHeapDumpWifhSftpEnabled() { 66 | sshCallShell((InputStream is, OutputStream os) -> { 67 | write(os, "heapDump live true"); 68 | verifyResponseContains(is, "Resource can be downloaded with SFTP/SCP at banner.txt.zip"); 69 | }); 70 | } 71 | 72 | @Test 73 | public void testLogfileWithSftpEnabled() { 74 | sshCallShell((is, os) -> { 75 | write(os, "logfile"); 76 | verifyResponseContains(is, "Resource can be downloaded with SFTP/SCP at "); 77 | }); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/MetricsCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 19 | import com.fasterxml.jackson.annotation.JsonProperty; 20 | import java.util.List; 21 | import org.springframework.beans.factory.annotation.Value; 22 | import org.springframework.boot.actuate.metrics.MetricsEndpoint; 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 25 | import org.springframework.stereotype.Component; 26 | import org.springframework.util.StringUtils; 27 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 28 | import sshd.shell.springboot.util.JsonUtils; 29 | 30 | /** 31 | * 32 | * @author anand 33 | */ 34 | @Component 35 | @ConditionalOnBean(MetricsEndpoint.class) 36 | @ConditionalOnProperty(name = "management.endpoint.metrics.enabled", havingValue = "true", matchIfMissing = true) 37 | @SshdShellCommand(value = "metrics", description = "Metrics operations") 38 | public final class MetricsCommand extends AbstractSystemCommand { 39 | 40 | private final MetricsEndpoint metricsEndpoint; 41 | 42 | MetricsCommand(@Value("${sshd.system.command.roles.metrics}") String[] systemRoles, 43 | MetricsEndpoint metricsEndpoint) { 44 | super(systemRoles); 45 | this.metricsEndpoint = metricsEndpoint; 46 | } 47 | 48 | @SshdShellCommand(value = "listNames", description = "List names of all metrics") 49 | public String listNames(String arg) { 50 | return JsonUtils.asJson(metricsEndpoint.listNames()); 51 | } 52 | 53 | @SshdShellCommand(value = "metricName", description = "List metric name") 54 | public String metricName(String arg) { 55 | if (!StringUtils.hasText(arg)) { 56 | return "Usage: metrics metricName {\"name\":\"\",\"tags\":[\"\"]}"; 57 | } 58 | return CommandUtils.process(() -> { 59 | MetricTags mt = JsonUtils.stringToObject(arg, MetricTags.class); 60 | return JsonUtils.asJson(metricsEndpoint.metric(mt.name, mt.tags)); 61 | }); 62 | } 63 | 64 | private static class MetricTags { 65 | 66 | @JsonProperty(required = true) 67 | public String name; 68 | @JsonIgnoreProperties(ignoreUnknown = true) 69 | public List tags; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/autoconfiguration/SshdShellProperties.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package sshd.shell.springboot.autoconfiguration; 20 | 21 | import org.springframework.boot.context.properties.ConfigurationProperties; 22 | import sshd.shell.springboot.console.ColorType; 23 | 24 | /** 25 | * 26 | * @author anand 27 | */ 28 | @ConfigurationProperties(prefix = "sshd") 29 | @lombok.Data 30 | public class SshdShellProperties { 31 | 32 | private final FileTransfer filetransfer = new FileTransfer(); 33 | private final Filesystem filesystem = new Filesystem(); 34 | private final Shell shell = new Shell(); 35 | 36 | @lombok.Data 37 | public static class FileTransfer { 38 | 39 | private boolean enabled = false; 40 | } 41 | 42 | @lombok.Data 43 | public static class Filesystem { 44 | 45 | private final Base base = new Base(); 46 | 47 | @lombok.Data 48 | public static class Base { 49 | 50 | private String dir = System.getProperty("user.home"); 51 | } 52 | } 53 | 54 | @lombok.Data 55 | public static class Shell { 56 | 57 | private int port = 8022; 58 | private boolean enabled = false; 59 | private String username = "admin"; 60 | private String password; 61 | private String publicKeyFile; 62 | private String host = "127.0.0.1"; 63 | private String hostKeyFile = "hostKey.ser"; 64 | private final Prompt prompt = new Prompt(); 65 | private final Text text = new Text(); 66 | private final Auth auth = new Auth(); 67 | 68 | @lombok.Data 69 | public static class Prompt { 70 | 71 | private ColorType color = ColorType.BLACK; 72 | private String title = "app"; 73 | } 74 | 75 | @lombok.Data 76 | public static class Text { 77 | 78 | private ColorType color = ColorType.BLACK; 79 | private ColorType highlightColor = ColorType.YELLOW; 80 | private String usageInfoFormat = "%n%-35s%s"; 81 | } 82 | 83 | @lombok.Data 84 | public static class Auth { 85 | 86 | public enum AuthType { 87 | SIMPLE, 88 | AUTH_PROVIDER 89 | } 90 | 91 | private AuthType authType = AuthType.SIMPLE; 92 | private String authProviderBeanName; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-test-app/src/main/java/demo/MySecurityConfig.java: -------------------------------------------------------------------------------- 1 | package demo; 2 | 3 | import java.util.Collection; 4 | import java.util.HashSet; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.security.authentication.AuthenticationProvider; 7 | import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 10 | import org.springframework.security.core.GrantedAuthority; 11 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 12 | import org.springframework.security.core.userdetails.UserDetails; 13 | import org.springframework.security.core.userdetails.UserDetailsService; 14 | import org.springframework.security.crypto.password.NoOpPasswordEncoder; 15 | import org.springframework.security.web.SecurityFilterChain; 16 | 17 | /** 18 | * 19 | * @author anand 20 | */ 21 | @EnableWebSecurity 22 | class MySecurityConfig { 23 | 24 | private UserDetailsService userDetailsService() { 25 | return username -> new UserDetails() { 26 | private static final long serialVersionUID = 1L; 27 | 28 | @Override 29 | public Collection getAuthorities() { 30 | Collection authorities = new HashSet<>(); 31 | authorities.add(new SimpleGrantedAuthority("USER")); 32 | if (username.equals("admin")) { 33 | authorities.add(new SimpleGrantedAuthority("ADMIN")); 34 | } 35 | return authorities; 36 | } 37 | 38 | @Override 39 | public String getPassword() { 40 | return username; 41 | } 42 | 43 | @Override 44 | public String getUsername() { 45 | return username; 46 | } 47 | 48 | @Override 49 | public boolean isAccountNonExpired() { 50 | return true; 51 | } 52 | 53 | @Override 54 | public boolean isAccountNonLocked() { 55 | return true; 56 | } 57 | 58 | @Override 59 | public boolean isCredentialsNonExpired() { 60 | return true; 61 | } 62 | 63 | @Override 64 | public boolean isEnabled() { 65 | return true; 66 | } 67 | }; 68 | } 69 | 70 | @Bean 71 | public AuthenticationProvider authProvider() { 72 | DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); 73 | authProvider.setUserDetailsService(userDetailsService()); 74 | authProvider.setPasswordEncoder(NoOpPasswordEncoder.getInstance()); 75 | return authProvider; 76 | } 77 | 78 | @Bean 79 | SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 80 | http.authorizeRequests() 81 | .antMatchers("/login").permitAll() 82 | .anyRequest().authenticated() 83 | .and().formLogin().defaultSuccessUrl("/test", true); 84 | return http.build(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/test/java/sshd/shell/springboot/autoconfiguration/SshdShellAutoConfigurationWithPublicKeyAndBannerImageTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package sshd.shell.springboot.autoconfiguration; 20 | 21 | import com.jcraft.jsch.ChannelShell; 22 | import com.jcraft.jsch.JSch; 23 | import com.jcraft.jsch.JSchException; 24 | import com.jcraft.jsch.Session; 25 | import java.io.IOException; 26 | import java.io.PipedInputStream; 27 | import java.io.PipedOutputStream; 28 | import java.nio.charset.StandardCharsets; 29 | import java.util.Properties; 30 | import org.junit.Test; 31 | import org.springframework.beans.factory.annotation.Autowired; 32 | import org.springframework.boot.test.context.SpringBootTest; 33 | import org.springframework.test.annotation.DirtiesContext; 34 | 35 | /** 36 | * 37 | * @author anand 38 | */ 39 | @SpringBootTest(classes = ConfigTest.class, properties = { 40 | "sshd.shell.publicKeyFile=src/test/resources/id_rsa.pub", 41 | "banner.image.location=banner.png", 42 | "logging.level.sshd.shell=DEBUG", 43 | "spring.flyway.baseline-on-migrate=true", 44 | "spring.main.allow-circular-references=true" 45 | }) 46 | @DirtiesContext 47 | public class SshdShellAutoConfigurationWithPublicKeyAndBannerImageTest extends AbstractSshSupport { 48 | 49 | @Autowired 50 | private SshdShellProperties properties; 51 | 52 | @Test 53 | public void testTestCommand() throws JSchException, IOException { 54 | JSch jsch = new JSch(); 55 | Session session = jsch.getSession("admin", "localhost", properties.getShell().getPort()); 56 | jsch.addIdentity("src/test/resources/id_rsa"); 57 | Properties config = new Properties(); 58 | config.put("StrictHostKeyChecking", "no"); 59 | session.setConfig(config); 60 | session.connect(); 61 | ChannelShell channel = (ChannelShell) session.openChannel("shell"); 62 | try (PipedInputStream pis = new PipedInputStream(); PipedOutputStream pos = new PipedOutputStream()) { 63 | channel.setInputStream(new PipedInputStream(pos)); 64 | channel.setOutputStream(new PipedOutputStream(pis)); 65 | channel.connect(); 66 | pos.write("test run bob\r".getBytes(StandardCharsets.UTF_8)); 67 | pos.flush(); 68 | verifyResponseContains(pis, "test run bob"); 69 | } 70 | channel.disconnect(); 71 | session.disconnect(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/SessionsCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import java.util.Objects; 19 | import org.springframework.beans.factory.annotation.Value; 20 | import org.springframework.boot.actuate.session.SessionsEndpoint; 21 | import org.springframework.boot.actuate.session.SessionsEndpoint.SessionDescriptor; 22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 24 | import org.springframework.stereotype.Component; 25 | import org.springframework.util.StringUtils; 26 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 27 | import sshd.shell.springboot.util.JsonUtils; 28 | 29 | /** 30 | * 31 | * @author anand 32 | */ 33 | @Component 34 | @ConditionalOnBean(SessionsEndpoint.class) 35 | @ConditionalOnProperty(name = "management.endpoint.sessions.enabled", havingValue = "true", matchIfMissing = true) 36 | @SshdShellCommand(value = "sessions", description = "Sessions management") 37 | public final class SessionsCommand extends AbstractSystemCommand { 38 | 39 | private final SessionsEndpoint sessionsEndpoint; 40 | 41 | SessionsCommand(@Value("${sshd.system.command.roles.sessions}") String[] systemRoles, 42 | SessionsEndpoint sessionsEndpoint) { 43 | super(systemRoles); 44 | this.sessionsEndpoint = sessionsEndpoint; 45 | } 46 | 47 | @SshdShellCommand(value = "username", description = "Find sessions given username") 48 | public String findSessionsByUsername(String arg) { 49 | if (!StringUtils.hasText(arg)) { 50 | return "Usage: sessions username "; 51 | } 52 | return JsonUtils.asJson(sessionsEndpoint.sessionsForUsername(arg)); 53 | } 54 | 55 | @SshdShellCommand(value = "get", description = "Get session descriptor by session id") 56 | public String getSessionById(String arg) { 57 | if (!StringUtils.hasText(arg)) { 58 | return "Usage: sessions get "; 59 | } 60 | SessionDescriptor sessionsDescriptor = sessionsEndpoint.getSession(arg); 61 | return Objects.isNull(sessionsDescriptor) 62 | ? "No such sessionId" 63 | : JsonUtils.asJson(sessionsDescriptor); 64 | } 65 | 66 | @SshdShellCommand(value = "delete", description = "Delete by session id") 67 | public String deleteSessionById(String arg) { 68 | if (!StringUtils.hasText(arg)) { 69 | return "Usage: sessions delete "; 70 | } 71 | sessionsEndpoint.deleteSession(arg); 72 | return "Session [" + arg + "] is deleted"; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/util/ZipUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.util; 17 | 18 | import java.io.File; 19 | import java.io.FileInputStream; 20 | import java.io.FileOutputStream; 21 | import java.io.IOException; 22 | import java.nio.file.Files; 23 | import java.nio.file.Path; 24 | import java.time.LocalDateTime; 25 | import java.util.zip.ZipEntry; 26 | import java.util.zip.ZipOutputStream; 27 | import org.springframework.util.Assert; 28 | 29 | /** 30 | * 31 | * @author anand 32 | */ 33 | public enum ZipUtils { 34 | ; 35 | 36 | public static Path zipFiles(Path dirToStoreZipFile, boolean isDeleteOriginalFiles, Path... filesToZip) throws 37 | IOException { 38 | validate(dirToStoreZipFile, filesToZip); 39 | String zipFileName = getZipFileName(filesToZip); 40 | Path zipFilePath = dirToStoreZipFile.resolve(zipFileName); 41 | runZipOperation(zipFilePath, filesToZip); 42 | cleanUp(isDeleteOriginalFiles, filesToZip); 43 | return zipFilePath; 44 | } 45 | 46 | private static void validate(Path dirToStoreZipFile, Path[] filesToZip) { 47 | File dir = dirToStoreZipFile.toFile(); 48 | Assert.isTrue(dir.exists() && dir.isDirectory(), "File is does not exist or it's not a directory"); 49 | Assert.notEmpty(filesToZip, "Require at least one file for zip"); 50 | } 51 | 52 | private static String getZipFileName(Path[] filesToZip) { 53 | return (filesToZip.length == 1 54 | ? filesToZip[0].getFileName().toString() 55 | : "Files " + LocalDateTime.now().toString()) + ".zip"; 56 | } 57 | 58 | private static void runZipOperation(Path zipFilePath, Path[] filesToZip) throws IOException { 59 | try (FileOutputStream fos = new FileOutputStream(zipFilePath.toFile()); 60 | ZipOutputStream zipOut = new ZipOutputStream(fos)) { 61 | for (Path fileToZip : filesToZip) { 62 | addZipEntriesIntoZipFile(fileToZip, zipOut); 63 | } 64 | } 65 | } 66 | 67 | private static void addZipEntriesIntoZipFile(Path fileToZip, final ZipOutputStream zipOut) throws IOException { 68 | try (FileInputStream fis = new FileInputStream(fileToZip.toFile())) { 69 | ZipEntry zipEntry = new ZipEntry(fileToZip.toFile().getName()); 70 | zipOut.putNextEntry(zipEntry); 71 | byte[] bytes = new byte[1024]; 72 | int length; 73 | while ((length = fis.read(bytes)) >= 0) { 74 | zipOut.write(bytes, 0, length); 75 | } 76 | } 77 | } 78 | 79 | private static void cleanUp(boolean isDeleteOriginalFiles, Path[] filesToZip) throws IOException { 80 | if (isDeleteOriginalFiles) { 81 | for (Path fileToZip : filesToZip) { 82 | Files.deleteIfExists(fileToZip); 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /sshd-shell-spring-boot-starter/src/main/java/sshd/shell/springboot/command/LoggersCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 anand. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sshd.shell.springboot.command; 17 | 18 | import com.fasterxml.jackson.annotation.JsonProperty; 19 | import org.springframework.beans.factory.annotation.Value; 20 | import org.springframework.boot.actuate.logging.LoggersEndpoint; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 23 | import org.springframework.boot.logging.LogLevel; 24 | import org.springframework.stereotype.Component; 25 | import org.springframework.util.StringUtils; 26 | import sshd.shell.springboot.autoconfiguration.SshdShellCommand; 27 | import sshd.shell.springboot.util.JsonUtils; 28 | 29 | /** 30 | * 31 | * @author anand 32 | */ 33 | @Component 34 | @ConditionalOnBean(LoggersEndpoint.class) 35 | @ConditionalOnProperty(name = "management.endpoint.loggers.enabled", havingValue = "true", matchIfMissing = true) 36 | @SshdShellCommand(value = "loggers", description = "Logging configuration") 37 | public final class LoggersCommand extends AbstractSystemCommand { 38 | 39 | private final LoggersEndpoint loggersEndpoint; 40 | 41 | LoggersCommand(@Value("${sshd.system.command.roles.loggers}") String[] systemRoles, 42 | LoggersEndpoint loggersEndpoint) { 43 | super(systemRoles); 44 | this.loggersEndpoint = loggersEndpoint; 45 | } 46 | 47 | @SshdShellCommand(value = "info", description = "Show logging info") 48 | public String info(String arg) { 49 | return JsonUtils.asJson(loggersEndpoint.loggers()); 50 | } 51 | 52 | @SshdShellCommand(value = "level", description = "Show log levels for given logger name") 53 | public String loggerLevels(String arg) { 54 | return !StringUtils.hasText(arg) 55 | ? "Usage: loggers level " 56 | : JsonUtils.asJson(loggersEndpoint.loggerLevels(arg)); 57 | } 58 | 59 | @SshdShellCommand(value = "configure", description = "Configure log level for logger name") 60 | public String configureLogLevel(String arg) { 61 | if (!StringUtils.hasText(arg)) { 62 | return "Usage: loggers configure {\"name\":\"\",\"configuredLevel\":" 63 | + "\"