├── .changelog-generator.json
├── .github
├── dependabot.yml
└── workflows
│ ├── build-dependabot.yml
│ ├── build.yml
│ ├── publish-manual.yml
│ ├── publish.yml
│ ├── release.yml
│ ├── reusable-build.yml
│ └── reusable-publish.yml
├── .gitignore
├── .idea
└── runConfigurations
│ ├── build.xml
│ └── release.xml
├── LICENSE
├── README.md
├── lombok.config
├── pom.xml
├── samples
├── basic
│ ├── README.md
│ ├── pom.xml
│ └── src
│ │ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── github
│ │ │ │ └── fonimus
│ │ │ │ └── ssh
│ │ │ │ └── shell
│ │ │ │ └── basic
│ │ │ │ ├── BasicApplication.java
│ │ │ │ ├── BasicCommands.java
│ │ │ │ └── BasicController.java
│ │ └── resources
│ │ │ └── application.yml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── github
│ │ └── fonimus
│ │ └── ssh
│ │ └── shell
│ │ └── basic
│ │ └── DemoApplicationWebTest.java
├── complete
│ ├── README.md
│ ├── pom.xml
│ └── src
│ │ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── github
│ │ │ │ └── fonimus
│ │ │ │ └── ssh
│ │ │ │ └── shell
│ │ │ │ └── complete
│ │ │ │ ├── CompleteApplication.java
│ │ │ │ ├── CompleteCommands.java
│ │ │ │ ├── CompleteConfiguration.java
│ │ │ │ ├── CompleteController.java
│ │ │ │ ├── CompletePromptProvider.java
│ │ │ │ └── CompleteSecurity.java
│ │ └── resources
│ │ │ ├── .ssh
│ │ │ └── authorized.keys
│ │ │ ├── application.yml
│ │ │ └── banner.txt
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── github
│ │ └── fonimus
│ │ └── ssh
│ │ └── shell
│ │ └── complete
│ │ ├── AbstractDemoApplicationTest.java
│ │ ├── DemoApplicationTest.java
│ │ └── DemoApplicationWebEnvTest.java
└── script.txt
└── starter
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── github
│ │ └── fonimus
│ │ └── ssh
│ │ └── shell
│ │ ├── ExtendedCompleterAdapter.java
│ │ ├── ExtendedCompletionProposal.java
│ │ ├── ExtendedInput.java
│ │ ├── ExtendedInteractiveShellRunner.java
│ │ ├── ExtendedShell.java
│ │ ├── PromptColor.java
│ │ ├── SimpleTable.java
│ │ ├── SshContext.java
│ │ ├── SshIO.java
│ │ ├── SshShellAutoConfiguration.java
│ │ ├── SshShellCommandFactory.java
│ │ ├── SshShellConfiguration.java
│ │ ├── SshShellHelper.java
│ │ ├── SshShellProperties.java
│ │ ├── SshShellRunnable.java
│ │ ├── SshShellTerminalDelegate.java
│ │ ├── SshShellUtils.java
│ │ ├── auth
│ │ ├── SshAuthentication.java
│ │ ├── SshShellAuthenticationProvider.java
│ │ ├── SshShellPasswordAuthenticationProvider.java
│ │ ├── SshShellPublicKeyAuthenticationProvider.java
│ │ └── SshShellSecurityAuthenticationProvider.java
│ │ ├── commands
│ │ ├── AbstractCommand.java
│ │ ├── AvailabilityException.java
│ │ ├── ColorAligner.java
│ │ ├── CommandProperties.java
│ │ ├── DatasourceCommand.java
│ │ ├── HistoryCommand.java
│ │ ├── JmxCommand.java
│ │ ├── ManageSessionsCommand.java
│ │ ├── PostProcessorsCommand.java
│ │ ├── ScriptCommand.java
│ │ ├── SshShellComponent.java
│ │ ├── StacktraceCommand.java
│ │ ├── TasksCommand.java
│ │ ├── actuator
│ │ │ └── ActuatorCommand.java
│ │ └── system
│ │ │ └── SystemCommand.java
│ │ ├── interactive
│ │ ├── Interactive.java
│ │ ├── InteractiveInput.java
│ │ ├── InteractiveInputIO.java
│ │ ├── KeyBinding.java
│ │ ├── KeyBindingInput.java
│ │ └── StoppableInteractiveInput.java
│ │ ├── listeners
│ │ ├── SshShellEvent.java
│ │ ├── SshShellEventType.java
│ │ ├── SshShellListener.java
│ │ └── SshShellListenerService.java
│ │ ├── manage
│ │ └── SshShellSessionManager.java
│ │ ├── postprocess
│ │ ├── ExtendedResultHandlerService.java
│ │ ├── PostProcessor.java
│ │ ├── PostProcessorException.java
│ │ ├── PostProcessorObject.java
│ │ └── provided
│ │ │ ├── GrepPostProcessor.java
│ │ │ ├── HighlightPostProcessor.java
│ │ │ ├── JsonPointerPostProcessor.java
│ │ │ ├── PrettyJsonPostProcessor.java
│ │ │ └── SavePostProcessor.java
│ │ └── providers
│ │ └── ExtendedFileValueProvider.java
└── resources
│ └── META-INF
│ └── spring
│ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
└── test
├── java
└── com
│ └── github
│ └── fonimus
│ └── ssh
│ └── shell
│ ├── AbstractCommandTest.java
│ ├── AbstractShellHelperTest.java
│ ├── AbstractTest.java
│ ├── ExtendedCompleterAdapterTest.java
│ ├── ExtendedInputTest.java
│ ├── ExtendedShellTest.java
│ ├── JavaConnect.java
│ ├── SshHelperTest.java
│ ├── SshShellApplicationCustomAuthenticatorTest.java
│ ├── SshShellApplicationExclusionsTest.java
│ ├── SshShellApplicationLazyInitTest.java
│ ├── SshShellApplicationSecurityTest.java
│ ├── SshShellApplicationTest.java
│ ├── SshShellApplicationWebTest.java
│ ├── SshShellHelperTest.java
│ ├── SshShellTerminalDelegateTest.java
│ ├── SshShellUtilsTest.java
│ ├── auth
│ ├── SshShellPublicKeyAuthenticationProviderTest.java
│ └── SshShellSecurityAuthenticationProviderTest.java
│ ├── commands
│ ├── HistoryCommandTest.java
│ ├── PostProcessorsCommandTest.java
│ ├── ScriptCommandTest.java
│ ├── StacktraceCommandTest.java
│ └── system
│ │ └── SystemCommandTest.java
│ ├── conf
│ ├── SshShellPasswordConfigurationTest.java
│ ├── SshShellSecurityConfigurationTest.java
│ ├── SshShellSessionConfigurationTest.java
│ └── TaskServiceTest.java
│ ├── listeners
│ └── SshShellListenerServiceTest.java
│ ├── postprocess
│ ├── ExtendedResultHandlerServiceTest.java
│ ├── GrepPostProcessorTest.java
│ ├── HighlightPostProcessorTest.java
│ ├── JsonPointerPostProcessorTest.java
│ ├── PrettyJsonPostProcessorTest.java
│ └── SavePostProcessorTest.java
│ └── providers
│ └── ExtendedFileValueProviderTest.java
└── resources
├── .ssh
├── authorized.keys
├── pub.der
└── wrong_pub.der
└── script.txt
/.changelog-generator.json:
--------------------------------------------------------------------------------
1 | {
2 | "categories": [
3 | {
4 | "title": "## 🚀 Features",
5 | "labels": [
6 | "feature",
7 | "enhancement"
8 | ]
9 | },
10 | {
11 | "title": "## 🐛 Fixes",
12 | "labels": [
13 | "fix",
14 | "bug"
15 | ]
16 | },
17 | {
18 | "title": "## ⬆️ Dependencies",
19 | "labels": [
20 | "dependencies"
21 | ]
22 | },
23 | {
24 | "title": "## 🧪 Tests",
25 | "labels": [
26 | "test"
27 | ]
28 | },
29 | {
30 | "title": "## 📝 Documentation",
31 | "labels": [
32 | "documentation"
33 | ]
34 | }
35 | ],
36 | "ignore_labels": [
37 | "ignore"
38 | ],
39 | "pr_template": "- #${{NUMBER}} - ${{TITLE}}",
40 | "empty_template": "No changes"
41 | }
42 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | updates:
4 |
5 | - package-ecosystem: github-actions
6 | directory: "/"
7 | open-pull-requests-limit: 99
8 | schedule:
9 | interval: daily
10 | time: "04:00"
11 |
12 | - package-ecosystem: maven
13 | directory: "/"
14 | open-pull-requests-limit: 99
15 | schedule:
16 | interval: daily
17 | time: "04:00"
18 |
--------------------------------------------------------------------------------
/.github/workflows/build-dependabot.yml:
--------------------------------------------------------------------------------
1 | name: Build Dependabot
2 |
3 | on:
4 | pull_request_target
5 |
6 | jobs:
7 | build:
8 | name: Build
9 | if: ${{ github.actor == 'dependabot[bot]' }}
10 | uses: ./.github/workflows/reusable-build.yml
11 | with:
12 | sonar_analysis: false
13 | ref: ${{ github.event.pull_request.head.sha }}
14 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches:
6 | - '**'
7 | paths-ignore:
8 | - '**.md'
9 | workflow_dispatch:
10 |
11 | jobs:
12 | build:
13 | name: Build
14 | if: ${{ github.actor != 'dependabot[bot]' }}
15 | uses: ./.github/workflows/reusable-build.yml
16 | secrets:
17 | sonar_token: ${{ secrets.SONAR_TOKEN }}
18 | gh_token: ${{ secrets.GITHUB_TOKEN }}
19 |
--------------------------------------------------------------------------------
/.github/workflows/publish-manual.yml:
--------------------------------------------------------------------------------
1 | name: Publish Release Manual
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | version:
7 | description: 'Specified version to publish'
8 | required: true
9 |
10 | jobs:
11 | publish-manual:
12 | name: Manual Publish
13 | uses: ./.github/workflows/reusable-publish.yml
14 | with:
15 | version: ${{ inputs.version }}
16 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - '**'
7 |
8 | jobs:
9 | publish-auto:
10 | name: Auto Publish
11 | uses: ./.github/workflows/reusable-publish.yml
12 | with:
13 | version: ${{ github.ref }}
14 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | type:
7 | type: choice
8 | description: 'Type of release'
9 | required: true
10 | options:
11 | - patch
12 | - minor
13 | - major
14 | version:
15 | description: 'Specified version to release'
16 | required: false
17 | default: ''
18 |
19 | jobs:
20 | release:
21 |
22 | name: Release
23 |
24 | runs-on: ubuntu-latest
25 |
26 | steps:
27 |
28 | - name: Checkout project
29 | uses: actions/checkout@v4
30 | with:
31 | persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token
32 | - name: Set up JDK 17
33 | uses: actions/setup-java@v4.2.1
34 | with:
35 | java-version: 17
36 | distribution: temurin
37 | cache: maven
38 | - name: Import GPG key
39 | id: import_gpg
40 | uses: crazy-max/ghaction-import-gpg@v6.1.0
41 | with:
42 | gpg_private_key: ${{ secrets.GPG_SIGNING_KEY }}
43 | passphrase: ${{ secrets.GPG_PASSPHRASE }}
44 | - name: GPG user IDs
45 | run: |
46 | echo "fingerprint: ${{ steps.import_gpg.outputs.fingerprint }}"
47 | echo "keyid: ${{ steps.import_gpg.outputs.keyid }}"
48 | echo "name: ${{ steps.import_gpg.outputs.name }}"
49 | echo "email: ${{ steps.import_gpg.outputs.email }}"
50 | - name: Get maven version
51 | id: version
52 | run: |
53 | export version=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
54 | export version=$(echo "${version%%-*}")
55 | echo "::set-output name=version::$(echo $version)"
56 | - name: Print current version
57 | run: echo "Current version = ${{ steps.version.outputs.version }}"
58 | - name: 'Compute next versions'
59 | id: next
60 | uses: "WyriHaximus/github-action-next-semvers@v1"
61 | with:
62 | version: ${{ steps.version.outputs.version }}
63 | - name: Choose next version
64 | id: choose
65 | run: |
66 | export version=$(echo ${{ github.event.inputs.version }})
67 | if [[ $version != '' ]] ; then export next=$version; elif [[ ${{ github.event.inputs.type }} == "major" ]] ; then export next=${{ steps.next.outputs.major }}; elif [[ ${{ github.event.inputs.type }} == "minor" ]] ; then export next=${{ steps.next.outputs.minor }}; else export next=${{ steps.version.outputs.version }}; fi
68 | echo "::set-output name=next::$(echo $next)"
69 | export purenext=$(echo "${next%%-*}")
70 | echo "::set-output name=purenext::$(echo $purenext)"
71 | - name: 'Get next snapshot version'
72 | id: snapshot
73 | uses: "WyriHaximus/github-action-next-semvers@v1"
74 | with:
75 | version: ${{ steps.choose.outputs.purenext }}
76 | - name: Set release and snapshot versions
77 | id: versions
78 | run: |
79 | echo "::set-output name=snapshot::$(echo ${{ steps.snapshot.outputs.patch }}-SNAPSHOT)"
80 | echo "::set-output name=release::$(echo ${{ steps.choose.outputs.next }})"
81 | - name: Log versions
82 | run: echo "Releasing version ${{ steps.versions.outputs.release }}, new dev version will be ${{ steps.versions.outputs.snapshot }}"
83 | - name: Configure git user
84 | run: |
85 | git config user.email "actions@github.com"
86 | git config user.name "GitHub Actions"
87 | - name: Configure maven settings
88 | uses: s4u/maven-settings-action@v3.0.0
89 | with:
90 | servers: '[{ "id": "ossrh", "username": "${{ secrets.OSSRH_USERNAME }}", "password": "${{ secrets.OSSRH_PASSWORD }}" }]'
91 | - name: Release
92 | run: mvn -B -ntp release:prepare release:clean -DreleaseVersion=${{ steps.versions.outputs.release }} -DdevelopmentVersion=${{ steps.versions.outputs.snapshot }}
93 | - name: Push changes
94 | uses: ad-m/github-push-action@master
95 | with:
96 | github_token: ${{ secrets.GITHUB_TOKEN }}
97 | branch: ${{ github.ref }}
98 | tags: true
99 | force: true
100 |
--------------------------------------------------------------------------------
/.github/workflows/reusable-build.yml:
--------------------------------------------------------------------------------
1 | name: -Reusable Build
2 |
3 | on:
4 | workflow_call:
5 | inputs:
6 | ref:
7 | required: false
8 | type: string
9 | default: ''
10 | description: Ref to checkout
11 | sonar_analysis:
12 | required: false
13 | type: boolean
14 | default: true
15 | description: Whether to make sonar analysis (sonar_token and gh_token become mandatory)
16 | secrets:
17 | sonar_token:
18 | required: false
19 | gh_token:
20 | required: false
21 |
22 | jobs:
23 | build:
24 | name: Build and analyze
25 | runs-on: ubuntu-latest
26 | steps:
27 | - uses: actions/checkout@v4
28 | with:
29 | ref: ${{ inputs.ref }}
30 | - name: Set up JDK 17
31 | uses: actions/setup-java@v4.2.1
32 | with:
33 | java-version: 17
34 | distribution: temurin
35 | cache: maven
36 |
37 | - name: Build
38 | run: mvn -B -ntp verify
39 |
40 | - name: Cache sonar packages
41 | if: ${{ inputs.sonar_analysis == true }}
42 | uses: actions/cache@v4.0.2
43 | with:
44 | path: ~/.sonar/cache
45 | key: ${{ runner.os }}-sonar
46 | restore-keys: ${{ runner.os }}-sonar
47 |
48 | - name: Analyze
49 | if: ${{ inputs.sonar_analysis == true }}
50 | run: mvn -B -ntp org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
51 | env:
52 | GITHUB_TOKEN: ${{ secrets.gh_token }}
53 | SONAR_TOKEN: ${{ secrets.sonar_token }}
54 |
--------------------------------------------------------------------------------
/.github/workflows/reusable-publish.yml:
--------------------------------------------------------------------------------
1 | name: -Reusable Publish Release
2 |
3 | on:
4 | workflow_call:
5 | inputs:
6 | version:
7 | required: true
8 | type: string
9 | description: Version to publish
10 |
11 | jobs:
12 | publish:
13 | name: Publish ${{ inputs.version }}
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v4
18 |
19 | - name: Is Pre Release ?
20 | id: pre_release
21 | if: github.event_name != 'workflow_dispatch'
22 | uses: heineiuo/create-changelogs@v0.2.8
23 |
24 | - name: Build Changelog
25 | id: changelog
26 | uses: mikepenz/release-changelog-builder-action@v4
27 | with:
28 | configuration: .changelog-generator.json
29 | ignorePreReleases: true
30 | env:
31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32 |
33 | - name: Create Release
34 | id: create-release
35 | uses: actions/create-release@latest
36 | env:
37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38 | with:
39 | tag_name: ${{ inputs.version }}
40 | release_name: Release ${{ inputs.version }}
41 | body: ${{ steps.changelog.outputs.changelog }}
42 | draft: false
43 | prerelease: ${{ github.event_name != 'workflow_dispatch' && steps.pre_release.outputs.release_type == 'prerelease' }}
44 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # project
2 | target/
3 |
4 | # intellij
5 | .idea/*
6 | !.idea/runConfigurations
7 | *.iml
8 |
9 | # public key file example
10 | samples/public-keys-sample
11 |
12 | # mac os
13 | .DS_Store
14 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/release.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/lombok.config:
--------------------------------------------------------------------------------
1 | lombok.log.fieldName = LOGGER
2 | lombok.equalsAndHashCode.callSuper = call
3 | lombok.addLombokGeneratedAnnotation = true
4 | lombok.copyableAnnotations += org.springframework.context.annotation.Lazy
5 | lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Autowired
6 |
--------------------------------------------------------------------------------
/samples/basic/README.md:
--------------------------------------------------------------------------------
1 | # Usage
2 |
3 | 1. Build sample application
4 |
5 | ```bash
6 | mvn clean install -f samples/basic [-DskipTests]
7 | ```
8 | 1. Start application
9 |
10 | ```bash
11 | java -jar samples/basic/target/ssh-shell-spring-boot-basic-sample[-version].jar
12 | ```
13 | 1. Connect to application via ssh (default password: pass)
14 |
15 | ```bash
16 | ~/home$ ssh -p 2222 user@localhost
17 | Password authentication
18 | Password: [password]
19 |
20 | Please type `help` to see available commands
21 | basic::>help
22 | AVAILABLE COMMANDS
23 |
24 | Built-In Commands
25 | clear: Clear the shell screen.
26 | exit, quit: Exit the shell.
27 | help: Display help about available commands.
28 | history: Display or save the history of previously run commands
29 | postprocessors: Display the available post processors
30 | script: Read and execute commands from a file.
31 | stacktrace: Display the full stacktrace of the last error.
32 |
33 | Demo Command
34 | authentication: Authentication command
35 | echo: Echo command
36 | pojo: Pojo command
37 | table-complex: Complex table command
38 | table-simple: Simple table command
39 |
40 | Jmx Commands
41 | jmx-info: Displays information about jmx mbean. Use -a option to query attribute values.
42 | jmx-invoke: Invoke operation on object name.
43 | jmx-list: List jmx mbeans.
44 |
45 | Manage Sessions Commands
46 | * manage-sessions-info: Displays session
47 | * manage-sessions-list: Displays active sessions
48 | * manage-sessions-stop: Stop session
49 |
50 | System Commands
51 | system-env: List system environment.
52 | system-properties: List system properties.
53 | system-threads: Thread command.
54 |
55 | Commands marked with (*) are currently unavailable.
56 | Type `help ` to learn more.
57 | ```
58 |
--------------------------------------------------------------------------------
/samples/basic/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 | ssh-shell-spring-boot-parent
21 | com.github.fonimus
22 | 3.1.1-SNAPSHOT
23 | ../../pom.xml
24 |
25 | 4.0.0
26 |
27 | ssh-shell-spring-boot-basic-sample
28 | Ssh shell spring boot basic sample
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter
38 |
39 |
40 | org.springframework.boot
41 | spring-boot-starter-web
42 |
43 |
44 | com.github.fonimus
45 | ssh-shell-spring-boot-starter
46 | ${project.version}
47 |
48 |
49 |
50 | org.springframework.boot
51 | spring-boot-starter-test
52 | test
53 |
54 |
55 |
56 |
57 |
58 |
59 | ${basedir}/src/main/resources
60 |
61 | *.yml
62 | *.txt
63 |
64 | true
65 |
66 |
67 |
68 |
69 | org.springframework.boot
70 | spring-boot-maven-plugin
71 | ${spring-boot.version}
72 |
73 |
74 | repackage
75 |
76 | repackage
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/samples/basic/src/main/java/com/github/fonimus/ssh/shell/basic/BasicApplication.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.basic;
18 |
19 | import org.springframework.boot.Banner;
20 | import org.springframework.boot.autoconfigure.SpringBootApplication;
21 | import org.springframework.boot.builder.SpringApplicationBuilder;
22 |
23 | /**
24 | * Basic application example
25 | */
26 | @SpringBootApplication
27 | public class BasicApplication {
28 |
29 | /**
30 | * Start basic application
31 | *
32 | * @param args main args
33 | */
34 | public static void main(String[] args) {
35 | new SpringApplicationBuilder(BasicApplication.class).bannerMode(Banner.Mode.OFF).run(args);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/samples/basic/src/main/java/com/github/fonimus/ssh/shell/basic/BasicController.java:
--------------------------------------------------------------------------------
1 | package com.github.fonimus.ssh.shell.basic;
2 |
3 | import org.springframework.web.bind.annotation.GetMapping;
4 | import org.springframework.web.bind.annotation.RestController;
5 |
6 | @RestController
7 | public class BasicController {
8 |
9 | @GetMapping("/ping")
10 | public String ping() {
11 | return "pong";
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/samples/basic/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring.shell.interactive.enabled: false
2 |
3 | ssh:
4 | shell:
5 | prompt:
6 | color: cyan
7 | text: 'basic::>'
8 | password: password
9 | authorized-public-keys-file: samples/complete/src/main/resources/.ssh/authorized.keys
10 | commands:
11 | jmx:
12 | create: false
13 | script:
14 | create: false
15 | history:
16 | create: false
17 | postprocessors:
18 | create: false
19 | manage-sessions:
20 | create: false
21 | system:
22 | create: false
23 |
24 | logging:
25 | level:
26 | com.github.fonimus: debug
27 |
--------------------------------------------------------------------------------
/samples/basic/src/test/java/com/github/fonimus/ssh/shell/basic/DemoApplicationWebTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.basic;
18 |
19 | import com.github.fonimus.ssh.shell.PromptColor;
20 | import com.github.fonimus.ssh.shell.SshShellHelper;
21 | import org.junit.jupiter.api.Test;
22 | import org.springframework.beans.factory.annotation.Autowired;
23 | import org.springframework.boot.test.context.SpringBootTest;
24 |
25 | import static org.junit.jupiter.api.Assertions.assertEquals;
26 | import static org.junit.jupiter.api.Assertions.assertNotNull;
27 |
28 | @SpringBootTest(classes = BasicApplication.class, properties = {
29 | "spring.shell.interactive.enabled=false"
30 | })
31 | public class DemoApplicationWebTest {
32 |
33 | @Autowired
34 | private BasicCommands demo;
35 |
36 | @Test
37 | void testApplicationStartup() {
38 | assertEquals("message", demo.echo("message", null));
39 | assertEquals(SshShellHelper.getColoredMessage("message", PromptColor.CYAN),
40 | demo.echo("message", PromptColor.CYAN));
41 | assertNotNull(demo.pojo());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/samples/complete/README.md:
--------------------------------------------------------------------------------
1 | # Usage
2 |
3 | 1. Build sample application
4 |
5 | ```bash
6 | mvn clean install -f samples/complete [-DskipTests]
7 | ```
8 | 1. Start application
9 |
10 | ```bash
11 | java -jar samples/complete/target/ssh-shell-spring-boot-complete-sample[-version].jar
12 | ```
13 | 1. Connect to application via ssh (default password: pass)
14 |
15 | ```bash
16 | ~/home$ ssh -p 2222 [user|actuator|admin]@localhost
17 | Password authentication
18 | Password: [password]
19 |
20 | _ _ _ _
21 | _____| |_ __| |_ ___| | |
22 | (_-<_-< ' \ (_-< ' \/ -_) | |
23 | /__/__/_||_| /__/_||_\___|_|_| v1.5.0-SNAPSHOT
24 |
25 |
26 | Please type `help` to see available commands
27 | complete::>help
28 | AVAILABLE COMMANDS
29 |
30 | Actuator Commands
31 | * audit: Display audit endpoint.
32 | beans: Display beans endpoint.
33 | conditions: Display conditions endpoint.
34 | configprops: Display configprops endpoint.
35 | env: Display env endpoint.
36 | health: Display health endpoint.
37 | * httptrace: Display httptrace endpoint.
38 | info: Display info endpoint.
39 | loggers: Display or configure loggers.
40 | mappings: Display mappings endpoint.
41 | metrics: Display metrics endpoint.
42 | scheduledtasks: Display scheduledtasks endpoint.
43 | * sessions: Display sessions endpoint.
44 | shutdown: Shutdown application.
45 | * threaddump: Display threaddump endpoint.
46 |
47 | Built-In Commands
48 | clear: Clear the shell screen.
49 | exit, quit: Exit the shell.
50 | help: Display help about available commands.
51 | history: Display or save the history of previously run commands
52 | postprocessors: Display the available post processors
53 | script: Read and execute commands from a file.
54 | stacktrace: Display the full stacktrace of the last error.
55 |
56 | Datasource Commands
57 | datasource-list: List available datasources
58 | datasource-properties: Datasource properties command. Executes 'show variables'
59 | datasource-query: Datasource query command.
60 | datasource-update: Datasource update command.
61 |
62 | Demo Command
63 | admin: Admin command
64 | authentication: Authentication command
65 | conf: Confirmation command
66 | display-ssh-env: Displays ssh env information
67 | display-ssh-session: Displays ssh session information
68 | echo: Echo command
69 | ex: Ex command
70 | file: File command
71 | interactive: Interactive command
72 | progress: Progress command
73 | size: Terminal size command
74 | welcome: Welcome command
75 |
76 | Jmx Commands
77 | jmx-info: Displays information about jmx mbean. Use -a option to query attribute values.
78 | jmx-invoke: Invoke operation on object name.
79 | jmx-list: List jmx mbeans.
80 |
81 | Manage Sessions Commands
82 | manage-sessions-info: Displays session
83 | manage-sessions-list: Displays active sessions
84 | manage-sessions-stop: Stop session
85 |
86 | System Commands
87 | system-env: List system environment.
88 | system-properties: List system properties.
89 | system-threads: Thread command.
90 |
91 | Tasks Commands
92 | tasks-list: Display the available scheduled tasks
93 | tasks-restart: Restart all or specified task(s)
94 | tasks-stop: Stop all or specified task(s)
95 |
96 | Commands marked with (*) are currently unavailable.
97 | Type `help ` to learn more.
98 | ```
99 |
--------------------------------------------------------------------------------
/samples/complete/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 | ssh-shell-spring-boot-parent
21 | com.github.fonimus
22 | 3.1.1-SNAPSHOT
23 | ../../pom.xml
24 |
25 | 4.0.0
26 |
27 | ssh-shell-spring-boot-complete-sample
28 | Ssh shell spring boot complete sample
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter
38 |
39 |
40 | org.springframework.boot
41 | spring-boot-starter-web
42 |
43 |
44 | org.springframework.boot
45 | spring-boot-starter-security
46 |
47 |
48 | org.springframework.boot
49 | spring-boot-starter-actuator
50 |
51 |
52 | org.springframework.boot
53 | spring-boot-starter-jdbc
54 |
55 |
56 | com.github.fonimus
57 | ssh-shell-spring-boot-starter
58 | ${project.version}
59 |
60 |
61 | com.h2database
62 | h2
63 |
64 |
65 | com.mysql
66 | mysql-connector-j
67 |
68 |
69 |
70 | org.springframework.boot
71 | spring-boot-starter-test
72 | test
73 |
74 |
75 |
76 |
77 |
78 |
79 | ${basedir}/src/main/resources
80 |
81 | *.yml
82 | *.txt
83 |
84 | true
85 |
86 |
87 | ${basedir}/src/main/resources
88 |
89 | *.yml
90 | *.txt
91 |
92 | false
93 |
94 |
95 |
96 |
97 | org.springframework.boot
98 | spring-boot-maven-plugin
99 | ${spring-boot.version}
100 |
101 |
102 | repackage
103 |
104 | repackage
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/samples/complete/src/main/java/com/github/fonimus/ssh/shell/complete/CompleteApplication.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.complete;
18 |
19 | import org.springframework.boot.SpringApplication;
20 | import org.springframework.boot.autoconfigure.SpringBootApplication;
21 | import org.springframework.scheduling.annotation.EnableScheduling;
22 |
23 | /**
24 | * Complete application example
25 | */
26 | @SpringBootApplication
27 | @EnableScheduling
28 | public class CompleteApplication {
29 |
30 | /**
31 | * Start complete application
32 | *
33 | * @param args main args
34 | */
35 | public static void main(String[] args) {
36 | SpringApplication.run(CompleteApplication.class, args);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/samples/complete/src/main/java/com/github/fonimus/ssh/shell/complete/CompleteController.java:
--------------------------------------------------------------------------------
1 | package com.github.fonimus.ssh.shell.complete;
2 |
3 | import org.springframework.web.bind.annotation.GetMapping;
4 | import org.springframework.web.bind.annotation.RestController;
5 |
6 | @RestController
7 | public class CompleteController {
8 |
9 | @GetMapping("/ping")
10 | public String ping() {
11 | return "pong";
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/samples/complete/src/main/java/com/github/fonimus/ssh/shell/complete/CompletePromptProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.complete;
18 |
19 | import org.jline.utils.AttributedString;
20 | import org.springframework.shell.jline.PromptProvider;
21 | import org.springframework.stereotype.Component;
22 |
23 | import static org.jline.utils.AttributedStyle.CYAN;
24 | import static org.jline.utils.AttributedStyle.DEFAULT;
25 |
26 | @Component
27 | public class CompletePromptProvider implements PromptProvider {
28 |
29 | @Override
30 | public AttributedString getPrompt() {
31 | return new AttributedString("complete::>", DEFAULT.foreground(CYAN));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/samples/complete/src/main/java/com/github/fonimus/ssh/shell/complete/CompleteSecurity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.complete;
18 |
19 | import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
20 | import org.springframework.context.annotation.Bean;
21 | import org.springframework.context.annotation.Configuration;
22 | import org.springframework.security.authentication.AuthenticationManager;
23 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
24 | import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
25 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
26 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
27 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
28 | import org.springframework.security.crypto.password.PasswordEncoder;
29 | import org.springframework.security.web.SecurityFilterChain;
30 |
31 | /**
32 | * Security configuration
33 | */
34 | @Configuration
35 | @EnableWebSecurity
36 | @EnableMethodSecurity
37 | public class CompleteSecurity {
38 |
39 | @Bean
40 | public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager authManager) throws Exception {
41 | http.authorizeHttpRequests()
42 | .requestMatchers("/ping").permitAll()
43 | .requestMatchers(EndpointRequest.to("info")).permitAll()
44 | .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR")
45 | .and().authenticationManager(authManager);
46 | return http.build();
47 | }
48 |
49 | @Bean
50 | public PasswordEncoder passwordEncoder() {
51 | return new BCryptPasswordEncoder();
52 | }
53 |
54 | @Bean
55 | public AuthenticationManager authManager(HttpSecurity http) throws Exception {
56 | AuthenticationManagerBuilder authenticationManagerBuilder =
57 | http.getSharedObject(AuthenticationManagerBuilder.class);
58 | authenticationManagerBuilder.inMemoryAuthentication()
59 | .withUser("user")
60 | .password(passwordEncoder().encode("password"))
61 | .roles("USER")
62 | .and()
63 | .withUser("actuator")
64 | .password(passwordEncoder().encode("password"))
65 | .roles("ACTUATOR")
66 | .and()
67 | .withUser("admin")
68 | .password(passwordEncoder().encode("admin"))
69 | .roles("ADMIN", "ACTUATOR");
70 | return authenticationManagerBuilder.build();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/samples/complete/src/main/resources/.ssh/authorized.keys:
--------------------------------------------------------------------------------
1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDlkW3hZVrrOPjcswznq5WDbF8IV3iEZo4EP2A9Ofl2dqqfKhgObDQuMUQN1OebBpUCo9W13CUle1ca7AdXJY4ZU62+YJCDKy9+hSz+KWfl7kJx+StL57sv3ProC3n7gAH+uedY6pSlHr6zTjsiEyGZbaKaSShFBX0ta3j+vZ11T+bxyK3j7BFJ2YcUa4ys+gphm3by/LV6xEhr3tohnFhfO5COKrEYqYC97EjsgDhcaFtpzjOnFoYC+HDCAFCwKWJj+Puu9qNj+NlT0gX4DuVFbWEEwH2ZB+qhk5/b75pAGLikRTK9lPLrTX0MX283lAWrD84d6xKWZaqrcxoP1HBnTCaqs5XGrXN81UP2EK+3KYNcNoIwN9x5UABOc1vDfK91IyYNgheVl8NT+T7TYNNj27piXFlvSgDx5dmuqBopYri+IqS17KJXaVgRjbdAwkWHNCqqSW9RoGDxUWlYgOh7KhNlN2m4AcW9YMEd1z2WWaYQpevYG3p3GGEZ8Av5Hbk= francois@MacBook-Pro-de-Francois.local
2 |
--------------------------------------------------------------------------------
/samples/complete/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | jmx:
3 | enabled: false
4 | first-datasource:
5 | url: jdbc:h2:mem:testdb
6 | second-datasource:
7 | url: jdbc:h2:mem:testdb2
8 | main.lazy-initialization: true
9 |
10 | ssh:
11 | shell:
12 | authentication: security
13 | authorized-public-keys: classpath:.ssh/authorized.keys
14 | commands:
15 | actuator:
16 | excludes:
17 | - audit
18 | jvm:
19 | enable: false
20 | threads:
21 | enable: false
22 | manage-sessions:
23 | enable: true
24 | datasource:
25 | excludes:
26 | host: 0.0.0.0
27 | shared-history: false
28 | history-directory: ./target
29 |
30 | management:
31 | endpoints:
32 | web:
33 | exposure:
34 | include: '*'
35 | endpoint:
36 | shutdown:
37 | enabled: true
38 | health:
39 | group:
40 | nocommands:
41 | include: '*'
42 | exclude:
43 | - demo-command
44 | commands:
45 | include:
46 | - demo-command
47 | info:
48 | build.enabled: true
49 | env.enabled: true
50 | git.enabled: true
51 | java.enabled: true
52 | os.enabled: true
53 |
54 |
55 | logging:
56 | level:
57 | com.github.fonimus: debug
58 |
59 | info:
60 | build:
61 | groupId: ${project.artifactId}
62 | artifactId: ${project.groupId}
63 | version: ${project.version}
64 | dependencies:
65 | spring-boot: ${spring-boot.version}
66 | spring-shell: ${spring-shell.version}
67 |
--------------------------------------------------------------------------------
/samples/complete/src/main/resources/banner.txt:
--------------------------------------------------------------------------------
1 |
2 | _ _ _ _
3 | _____| |_ __| |_ ___| | |
4 | (_-<_-< ' \ (_-< ' \/ -_) | |
5 | /__/__/_||_| /__/_||_\___|_|_| v${project.version}
6 |
--------------------------------------------------------------------------------
/samples/complete/src/test/java/com/github/fonimus/ssh/shell/complete/AbstractDemoApplicationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.complete;
18 |
19 | import org.junit.jupiter.api.Test;
20 | import org.springframework.beans.factory.annotation.Autowired;
21 | import org.springframework.boot.actuate.info.InfoEndpoint;
22 |
23 | import java.util.Map;
24 |
25 | import static org.junit.jupiter.api.Assertions.assertFalse;
26 | import static org.junit.jupiter.api.Assertions.assertTrue;
27 |
28 | public abstract class AbstractDemoApplicationTest {
29 |
30 | @Autowired
31 | private InfoEndpoint info;
32 |
33 | @Test
34 | void testApplicationStartup() {
35 | Map i = info.info();
36 | assertFalse(i.isEmpty());
37 | assertTrue(i.containsKey("build"));
38 | assertTrue(i.containsKey("dependencies"));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/samples/complete/src/test/java/com/github/fonimus/ssh/shell/complete/DemoApplicationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.complete;
18 |
19 | import org.springframework.boot.test.context.SpringBootTest;
20 | import org.springframework.test.annotation.DirtiesContext;
21 |
22 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, properties = {
23 | "ssh.shell.port=2346",
24 | "spring.shell.interactive.enabled=false"
25 | })
26 | @DirtiesContext
27 | public class DemoApplicationTest
28 | extends AbstractDemoApplicationTest {
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/samples/complete/src/test/java/com/github/fonimus/ssh/shell/complete/DemoApplicationWebEnvTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.complete;
18 |
19 | import org.springframework.boot.test.context.SpringBootTest;
20 | import org.springframework.test.annotation.DirtiesContext;
21 |
22 | @SpringBootTest(properties = {
23 | "ssh.shell.port=2345",
24 | "spring.shell.interactive.enabled=false"
25 | })
26 | @DirtiesContext
27 | public class DemoApplicationWebEnvTest
28 | extends AbstractDemoApplicationTest {
29 |
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/samples/script.txt:
--------------------------------------------------------------------------------
1 | echo test1
2 | echo test2
3 | sleep 20
4 | echo test3
5 |
--------------------------------------------------------------------------------
/starter/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 | ssh-shell-spring-boot-parent
21 | com.github.fonimus
22 | 3.1.1-SNAPSHOT
23 | ../pom.xml
24 |
25 | 4.0.0
26 |
27 | ssh-shell-spring-boot-starter
28 | Ssh shell spring boot starter
29 |
30 |
31 |
32 | org.springframework.boot
33 | spring-boot-autoconfigure
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter-actuator
38 | true
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-security
43 | true
44 |
45 |
46 | org.springframework.session
47 | spring-session-core
48 | true
49 |
50 |
51 | org.springframework.boot
52 | spring-boot-configuration-processor
53 | true
54 |
55 |
56 | com.fasterxml.jackson.core
57 | jackson-databind
58 | true
59 |
60 |
61 | com.fasterxml.jackson.datatype
62 | jackson-datatype-jsr310
63 | true
64 |
65 |
66 | org.apache.sshd
67 | sshd-core
68 |
69 |
70 | org.springframework.shell
71 | spring-shell-starter
72 |
73 |
74 |
75 | org.springframework.boot
76 | spring-boot-starter-test
77 | test
78 |
79 |
80 | org.springframework.boot
81 | spring-boot-starter
82 | test
83 |
84 |
85 | org.springframework.boot
86 | spring-boot-starter-web
87 | test
88 |
89 |
90 | org.springframework.session
91 | spring-session-jdbc
92 | test
93 |
94 |
95 | com.h2database
96 | h2
97 | test
98 |
99 |
100 | com.jcraft
101 | jsch
102 | 0.1.55
103 | test
104 |
105 |
106 | org.awaitility
107 | awaitility
108 | 4.2.1
109 | test
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/ExtendedCompleterAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell;
18 |
19 | import lombok.AllArgsConstructor;
20 | import org.jline.reader.Candidate;
21 | import org.jline.reader.LineReader;
22 | import org.jline.reader.ParsedLine;
23 | import org.springframework.context.annotation.Primary;
24 | import org.springframework.shell.CompletingParsedLine;
25 | import org.springframework.shell.CompletionContext;
26 | import org.springframework.shell.CompletionProposal;
27 | import org.springframework.shell.Shell;
28 | import org.springframework.shell.boot.CompleterAutoConfiguration;
29 | import org.springframework.stereotype.Component;
30 |
31 | import java.util.List;
32 | import java.util.stream.Collectors;
33 |
34 | /**
35 | * Extended completer adapter to be able to set complete attribute of proposal
36 | *
37 | * Based on @{@link CompleterAutoConfiguration.CompleterAdapter}
38 | */
39 | @Component
40 | @Primary
41 | @AllArgsConstructor
42 | public class ExtendedCompleterAdapter extends CompleterAutoConfiguration.CompleterAdapter {
43 |
44 | private final Shell shell;
45 |
46 | @Override
47 | public void complete(LineReader reader, ParsedLine line, List candidates) {
48 | CompletingParsedLine cpl = (line instanceof CompletingParsedLine) ? ((CompletingParsedLine) line) : t -> t;
49 |
50 | CompletionContext context = new CompletionContext(sanitizeInput(line.words()), line.wordIndex(), line.wordCursor(), null, null);
51 |
52 | List proposals = shell.complete(context);
53 | proposals.stream()
54 | .map(p -> new Candidate(
55 | p.dontQuote() ? p.value() : cpl.emit(p.value()).toString(),
56 | p.displayText(),
57 | p.category(),
58 | p.description(),
59 | null,
60 | null,
61 | isComplete(p))
62 | )
63 | .forEach(candidates::add);
64 | }
65 |
66 | private static boolean isComplete(CompletionProposal p) {
67 | if (p instanceof ExtendedCompletionProposal) {
68 | return ((ExtendedCompletionProposal) p).isComplete();
69 | }
70 | return true;
71 | }
72 |
73 | /**
74 | * Sanitize the buffer input given the customizations applied to the JLine parser (e.g. support for
75 | * line continuations, etc.)
76 | * See @{@link CompleterAutoConfiguration}
77 | */
78 | private static List sanitizeInput(List words) {
79 | words = words.stream()
80 | .map(s -> s.replaceAll("^\\n+|\\n+$", "")) // CR at beginning/end of line introduced by backslash continuation
81 | .map(s -> s.replaceAll("\\n+", " ")) // CR in middle of word introduced by return inside a quoted string
82 | .collect(Collectors.toList());
83 | return words;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/ExtendedCompletionProposal.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell;
18 |
19 | import lombok.Getter;
20 | import lombok.Setter;
21 | import org.springframework.shell.CompletionProposal;
22 |
23 | /**
24 | * Extended completion proposal to be able to set complete attribute of proposal
25 | */
26 | public class ExtendedCompletionProposal extends CompletionProposal {
27 |
28 | /**
29 | * If should add space after proposed proposal
30 | */
31 | @Getter
32 | @Setter
33 | private boolean complete;
34 |
35 | /**
36 | * Default constructor
37 | *
38 | * @param value string value
39 | * @param complete true if should add space after proposed proposal (true is default value when not using
40 | * extended completion proposal)
41 | */
42 | public ExtendedCompletionProposal(String value, boolean complete) {
43 | super(value);
44 | this.complete = complete;
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/ExtendedInput.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell;
18 |
19 | import org.springframework.shell.Input;
20 |
21 | import java.util.ArrayList;
22 | import java.util.Arrays;
23 | import java.util.List;
24 |
25 | /**
26 | * Extended input which takes in account special characters
27 | */
28 | public class ExtendedInput implements Input {
29 |
30 | public static final String PIPE = "|";
31 |
32 | public static final String ARROW = ">";
33 |
34 | public static final List KEY_CHARS = Arrays.asList(PIPE, ARROW);
35 |
36 | private final Input base;
37 |
38 | /**
39 | * Default constructor
40 | *
41 | * @param base input base
42 | */
43 | public ExtendedInput(Input base) {
44 | this.base = base;
45 | }
46 |
47 | private static boolean isKeyCharInLine(String str) {
48 | for (String key : KEY_CHARS) {
49 | if (str.contains(key)) {
50 | return true;
51 | }
52 | }
53 | return false;
54 | }
55 |
56 | @Override
57 | public String rawText() {
58 | String raw = base.rawText();
59 | return raw != null && isKeyCharInLine(raw) ? raw.substring(0, firstIndexOfKeyChar(raw)) : raw;
60 | }
61 |
62 | @Override
63 | public List words() {
64 | List newList = new ArrayList<>();
65 | for (String word : base.words()) {
66 | if (KEY_CHARS.contains(word)) {
67 | return newList;
68 | }
69 | newList.add(word);
70 | }
71 | return newList;
72 | }
73 |
74 | private int firstIndexOfKeyChar(String str) {
75 | int firstIndex = Integer.MAX_VALUE;
76 | for (String key : KEY_CHARS) {
77 | int keyIndex = str.indexOf(key);
78 | if (keyIndex > -1 && keyIndex < firstIndex) {
79 | firstIndex = keyIndex;
80 | }
81 | }
82 | return firstIndex;
83 | }
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/ExtendedInteractiveShellRunner.java:
--------------------------------------------------------------------------------
1 | package com.github.fonimus.ssh.shell;
2 |
3 | import org.jline.reader.LineReader;
4 | import org.springframework.boot.ApplicationArguments;
5 | import org.springframework.context.annotation.Primary;
6 | import org.springframework.shell.Input;
7 | import org.springframework.shell.InputProvider;
8 | import org.springframework.shell.Shell;
9 | import org.springframework.shell.context.InteractionMode;
10 | import org.springframework.shell.context.ShellContext;
11 | import org.springframework.shell.jline.InteractiveShellRunner;
12 | import org.springframework.shell.jline.PromptProvider;
13 | import org.springframework.stereotype.Component;
14 |
15 | import static com.github.fonimus.ssh.shell.SshShellCommandFactory.SSH_THREAD_CONTEXT;
16 |
17 | /**
18 | * Used to clear thread context from post processors and also creates instance of InteractiveShellRunner
19 | * so that ThrowableResultHandler#shouldHandle() returns true
20 | */
21 | @Component
22 | @Primary
23 | public class ExtendedInteractiveShellRunner extends InteractiveShellRunner {
24 |
25 | private final LineReader lineReader;
26 | private final PromptProvider promptProvider;
27 | private final Shell shell;
28 | private final ShellContext shellContext;
29 |
30 | public ExtendedInteractiveShellRunner(LineReader lineReader, PromptProvider promptProvider, Shell shell, ShellContext shellContext) {
31 | super(lineReader, promptProvider, shell, shellContext);
32 | this.lineReader = lineReader;
33 | this.promptProvider = promptProvider;
34 | this.shell = shell;
35 | this.shellContext = shellContext;
36 | }
37 |
38 | @Override
39 | public void run(ApplicationArguments args) throws Exception {
40 | shellContext.setInteractionMode(InteractionMode.INTERACTIVE);
41 | InputProvider inputProvider = new SshShellInputProvider(lineReader, promptProvider);
42 | shell.run(inputProvider);
43 | }
44 |
45 | @Override
46 | public boolean canRun(ApplicationArguments args) {
47 | return false;
48 | }
49 |
50 | public static class SshShellInputProvider
51 | extends InteractiveShellRunner.JLineInputProvider {
52 |
53 | public SshShellInputProvider(LineReader lineReader, PromptProvider promptProvider) {
54 | super(lineReader, promptProvider);
55 | }
56 |
57 | @Override
58 | public Input readInput() {
59 | SshContext ctx = SSH_THREAD_CONTEXT.get();
60 | if (ctx != null) {
61 | ctx.getPostProcessorsList().clear();
62 | }
63 | return super.readInput();
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/PromptColor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell;
18 |
19 | /**
20 | * Map enum color to jline ones
21 | */
22 | public enum PromptColor {
23 |
24 | BLACK(0),
25 | RED(1),
26 | GREEN(2),
27 | YELLOW(3),
28 | BLUE(4),
29 | MAGENTA(5),
30 | CYAN(6),
31 | WHITE(7),
32 | BRIGHT(8);
33 |
34 | private final int value;
35 |
36 | PromptColor(int value) {
37 | this.value = value;
38 | }
39 |
40 | public int toJlineAttributedStyle() {
41 | return value;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/SimpleTable.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell;
18 |
19 | import lombok.Builder;
20 | import lombok.Data;
21 | import lombok.NonNull;
22 | import lombok.Singular;
23 | import org.springframework.shell.table.Aligner;
24 | import org.springframework.shell.table.BorderStyle;
25 | import org.springframework.shell.table.TableBuilder;
26 |
27 | import java.util.List;
28 |
29 | /**
30 | * Simple data builder, with header names, and list of lines, containing map with header names.
31 | * Optionally set aligner, and style
32 | */
33 | @Data
34 | @Builder
35 | public class SimpleTable {
36 |
37 | @Singular
38 | private List columns;
39 |
40 | @Builder.Default
41 | private boolean displayHeaders = true;
42 |
43 | @Singular
44 | private List headerAligners;
45 |
46 | @NonNull
47 | @Singular
48 | private List> lines;
49 |
50 | @Singular
51 | private List lineAligners;
52 |
53 | @Builder.Default
54 | private boolean useFullBorder = true;
55 |
56 | @Builder.Default
57 | private BorderStyle borderStyle = BorderStyle.fancy_light;
58 |
59 | private SimpleTableBuilderListener tableBuilderListener;
60 |
61 | /**
62 | * Listener to add some properties to table builder before it is rendered
63 | */
64 | @FunctionalInterface
65 | public interface SimpleTableBuilderListener {
66 |
67 | /**
68 | * Method called before render
69 | *
70 | * @param tableBuilder table builder
71 | */
72 | void onBuilt(TableBuilder tableBuilder);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/SshContext.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell;
18 |
19 | import com.github.fonimus.ssh.shell.auth.SshAuthentication;
20 | import com.github.fonimus.ssh.shell.postprocess.PostProcessorObject;
21 | import lombok.Getter;
22 | import lombok.Setter;
23 | import org.apache.sshd.server.Environment;
24 | import org.apache.sshd.server.session.ServerSession;
25 | import org.jline.reader.LineReader;
26 | import org.jline.terminal.Terminal;
27 |
28 | import java.util.ArrayList;
29 | import java.util.List;
30 |
31 | /**
32 | * Ssh context to hold terminal, exit callback and thread per thread
33 | */
34 | @Getter
35 | public class SshContext {
36 |
37 | private SshShellRunnable sshShellRunnable;
38 |
39 | private Terminal terminal;
40 |
41 | private LineReader lineReader;
42 |
43 | private SshAuthentication authentication;
44 |
45 | private final List postProcessorsList = new ArrayList<>();
46 |
47 | @Setter
48 | private boolean background;
49 |
50 | private long backgroundCount = 0;
51 |
52 | /**
53 | * Default empty constructor
54 | */
55 | public SshContext() {
56 | }
57 |
58 | /**
59 | * Constructor
60 | *
61 | * @param sshShellRunnable ssh runnable
62 | * @param terminal ssh terminal
63 | * @param lineReader ssh line reader
64 | * @param authentication (optional) spring authentication objects
65 | */
66 | public SshContext(SshShellRunnable sshShellRunnable, Terminal terminal, LineReader lineReader,
67 | SshAuthentication authentication) {
68 | this.sshShellRunnable = sshShellRunnable;
69 | this.terminal = terminal;
70 | this.lineReader = lineReader;
71 | this.authentication = authentication;
72 | }
73 |
74 | /**
75 | * Check if current prompt is the one started with application
76 | *
77 | * @return if local prompt or not
78 | */
79 | public boolean isLocalPrompt() {
80 | return sshShellRunnable == null;
81 | }
82 |
83 | /**
84 | * Return current ssh session
85 | *
86 | * @return ssh session, or null of is local prompt
87 | */
88 | public ServerSession getSshSession() {
89 | return isLocalPrompt() ? null : sshShellRunnable.getSshSession();
90 | }
91 |
92 | /**
93 | * Return current ssh env
94 | *
95 | * @return ssh env, or null of is local prompt
96 | */
97 | public Environment getSshEnv() {
98 | return isLocalPrompt() ? null : sshShellRunnable.getSshEnv();
99 | }
100 |
101 | /**
102 | * Increment background sessions count
103 | */
104 | public void incrementBackgroundCount() {
105 | this.backgroundCount++;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/SshIO.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell;
18 |
19 | import lombok.Getter;
20 | import lombok.Setter;
21 | import org.apache.sshd.server.ExitCallback;
22 |
23 | import java.io.InputStream;
24 | import java.io.OutputStream;
25 |
26 | /**
27 | * Ssh io
28 | */
29 | @Getter
30 | @Setter
31 | public class SshIO {
32 |
33 | private InputStream is;
34 |
35 | private OutputStream os;
36 |
37 | private ExitCallback ec;
38 | }
39 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/SshShellUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell;
18 |
19 | import org.apache.sshd.common.channel.PtyMode;
20 | import org.jline.terminal.Attributes;
21 |
22 | import java.util.Map;
23 |
24 | /**
25 | * Utility tools
26 | */
27 | public final class SshShellUtils {
28 |
29 | private SshShellUtils() {
30 | // private constructor
31 | }
32 |
33 | /**
34 | * Fill attributes with given modes
35 | *
36 | * @param attr attributes
37 | * @param ptyModes pty modes
38 | */
39 | public static void fill(Attributes attr, Map ptyModes) {
40 | for (Map.Entry e : ptyModes.entrySet()) {
41 | switch (e.getKey()) {
42 | case VINTR ->
43 | attr.setControlChar(Attributes.ControlChar.VINTR, e.getValue());
44 | case VQUIT ->
45 | attr.setControlChar(Attributes.ControlChar.VQUIT, e.getValue());
46 | case VERASE ->
47 | attr.setControlChar(Attributes.ControlChar.VERASE, e.getValue());
48 | case VKILL ->
49 | attr.setControlChar(Attributes.ControlChar.VKILL, e.getValue());
50 | case VEOF ->
51 | attr.setControlChar(Attributes.ControlChar.VEOF, e.getValue());
52 | case VEOL ->
53 | attr.setControlChar(Attributes.ControlChar.VEOL, e.getValue());
54 | case VEOL2 ->
55 | attr.setControlChar(Attributes.ControlChar.VEOL2, e.getValue());
56 | case VSTART ->
57 | attr.setControlChar(Attributes.ControlChar.VSTART, e.getValue());
58 | case VSTOP ->
59 | attr.setControlChar(Attributes.ControlChar.VSTOP, e.getValue());
60 | case VSUSP ->
61 | attr.setControlChar(Attributes.ControlChar.VSUSP, e.getValue());
62 | case VDSUSP ->
63 | attr.setControlChar(Attributes.ControlChar.VDSUSP, e.getValue());
64 | case VREPRINT ->
65 | attr.setControlChar(Attributes.ControlChar.VREPRINT, e.getValue());
66 | case VWERASE ->
67 | attr.setControlChar(Attributes.ControlChar.VWERASE, e.getValue());
68 | case VLNEXT ->
69 | attr.setControlChar(Attributes.ControlChar.VLNEXT, e.getValue());
70 | case VSTATUS ->
71 | attr.setControlChar(Attributes.ControlChar.VSTATUS, e.getValue());
72 | case VDISCARD ->
73 | attr.setControlChar(Attributes.ControlChar.VDISCARD, e.getValue());
74 | case ECHO ->
75 | attr.setLocalFlag(Attributes.LocalFlag.ECHO, e.getValue() != 0);
76 | case ICANON ->
77 | attr.setLocalFlag(Attributes.LocalFlag.ICANON, e.getValue() != 0);
78 | case ISIG ->
79 | attr.setLocalFlag(Attributes.LocalFlag.ISIG, e.getValue() != 0);
80 | case ICRNL ->
81 | attr.setInputFlag(Attributes.InputFlag.ICRNL, e.getValue() != 0);
82 | case INLCR ->
83 | attr.setInputFlag(Attributes.InputFlag.INLCR, e.getValue() != 0);
84 | case IGNCR ->
85 | attr.setInputFlag(Attributes.InputFlag.IGNCR, e.getValue() != 0);
86 | case OCRNL ->
87 | attr.setOutputFlag(Attributes.OutputFlag.OCRNL, e.getValue() != 0);
88 | case ONLCR ->
89 | attr.setOutputFlag(Attributes.OutputFlag.ONLCR, e.getValue() != 0);
90 | case ONLRET ->
91 | attr.setOutputFlag(Attributes.OutputFlag.ONLRET, e.getValue() != 0);
92 | case OPOST ->
93 | attr.setOutputFlag(Attributes.OutputFlag.OPOST, e.getValue() != 0);
94 | default -> {
95 | }
96 | // nothing to do
97 | }
98 | }
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/auth/SshAuthentication.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.auth;
18 |
19 | import lombok.AllArgsConstructor;
20 | import lombok.Getter;
21 | import lombok.NonNull;
22 | import lombok.RequiredArgsConstructor;
23 |
24 | import java.util.List;
25 |
26 | /**
27 | * Ssh authentication
28 | */
29 | @Getter
30 | @RequiredArgsConstructor
31 | @AllArgsConstructor
32 | public class SshAuthentication {
33 |
34 | @NonNull
35 | private final String name;
36 |
37 | @NonNull
38 | private final Object principal;
39 |
40 | private Object details;
41 |
42 | private Object credentials;
43 |
44 | private List authorities;
45 | }
46 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/auth/SshShellAuthenticationProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.auth;
18 |
19 | import org.apache.sshd.server.auth.password.PasswordAuthenticator;
20 |
21 | /**
22 | * Interface to implements custom authentication provider
23 | */
24 | @FunctionalInterface
25 | public interface SshShellAuthenticationProvider
26 | extends PasswordAuthenticator {
27 |
28 | String AUTHENTICATION_ATTRIBUTE = "authentication";
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/auth/SshShellPasswordAuthenticationProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.auth;
18 |
19 | import lombok.extern.slf4j.Slf4j;
20 | import org.apache.sshd.server.auth.password.PasswordChangeRequiredException;
21 | import org.apache.sshd.server.session.ServerSession;
22 |
23 | import java.util.UUID;
24 |
25 | /**
26 | * Password implementation
27 | */
28 | @Slf4j
29 | public class SshShellPasswordAuthenticationProvider
30 | implements SshShellAuthenticationProvider {
31 |
32 | private final String user;
33 |
34 | private final String password;
35 |
36 | public SshShellPasswordAuthenticationProvider(String user, String password) {
37 | this.user = user;
38 | String pass = password;
39 | if (pass == null) {
40 | pass = UUID.randomUUID().toString();
41 | LOGGER.info(" --- Generating password for ssh connection: {}", pass);
42 | }
43 | this.password = pass;
44 | }
45 |
46 | @Override
47 | public boolean authenticate(String username, String pass,
48 | ServerSession serverSession) throws PasswordChangeRequiredException {
49 |
50 | serverSession.getIoSession().setAttribute(AUTHENTICATION_ATTRIBUTE, new SshAuthentication(username, username));
51 |
52 | return username.equals(this.user) && pass.equals(this.password);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/auth/SshShellPublicKeyAuthenticationProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.auth;
18 |
19 | import lombok.extern.slf4j.Slf4j;
20 | import org.apache.sshd.server.config.keys.AuthorizedKeysAuthenticator;
21 | import org.apache.sshd.server.session.ServerSession;
22 |
23 | import java.io.File;
24 | import java.security.PublicKey;
25 |
26 | import static com.github.fonimus.ssh.shell.auth.SshShellAuthenticationProvider.AUTHENTICATION_ATTRIBUTE;
27 |
28 | /**
29 | * Authorized keys authenticator extension to set authentication attribute
30 | */
31 | @Slf4j
32 | public class SshShellPublicKeyAuthenticationProvider
33 | extends AuthorizedKeysAuthenticator {
34 |
35 | /**
36 | * Default constructor
37 | *
38 | * @param publicKeysFile public keys file
39 | */
40 | public SshShellPublicKeyAuthenticationProvider(File publicKeysFile) {
41 | super(publicKeysFile.toPath());
42 | }
43 |
44 | @Override
45 | public boolean authenticate(String username, PublicKey key, ServerSession session) {
46 | boolean authenticated = super.authenticate(username, key, session);
47 | session.getIoSession().setAttribute(AUTHENTICATION_ATTRIBUTE, new SshAuthentication(username, username));
48 | return authenticated;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/commands/AbstractCommand.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.commands;
18 |
19 | import com.github.fonimus.ssh.shell.SshShellCommandFactory;
20 | import com.github.fonimus.ssh.shell.SshShellHelper;
21 | import com.github.fonimus.ssh.shell.SshShellProperties;
22 | import com.github.fonimus.ssh.shell.auth.SshAuthentication;
23 | import lombok.extern.slf4j.Slf4j;
24 | import org.springframework.shell.Availability;
25 | import org.springframework.shell.standard.AbstractShellComponent;
26 |
27 | import java.util.List;
28 |
29 | /**
30 | * Abstract command with availability
31 | */
32 | @Slf4j
33 | public class AbstractCommand extends AbstractShellComponent {
34 |
35 | protected final SshShellHelper helper;
36 |
37 | protected final SshShellProperties properties;
38 |
39 | protected final CommandProperties commandProperties;
40 |
41 | public AbstractCommand(SshShellHelper helper, SshShellProperties properties, CommandProperties commandProperties) {
42 | this.helper = helper;
43 | this.properties = properties;
44 | this.commandProperties = commandProperties;
45 | }
46 |
47 | /**
48 | * Compute availability depending on command group and name
49 | *
50 | * @param commandGroup command group
51 | * @param commandName command name
52 | * @return command availability
53 | */
54 | protected Availability availability(String commandGroup, String commandName) {
55 | try {
56 | preAvailability();
57 | if (!commandProperties.isEnable()) {
58 | return Availability.unavailable("command deactivated (please check property '" +
59 | SshShellProperties.SSH_SHELL_PREFIX + ".commands." + commandGroup + ".enable" + "')");
60 | }
61 | if (commandProperties.getExcludes() != null && commandProperties.getExcludes().contains(commandName)) {
62 | return Availability.unavailable("command is excluded (please check property '" +
63 | SshShellProperties.SSH_SHELL_PREFIX + ".commands." + commandGroup + ".excludes" + "')");
64 | }
65 | if (commandProperties.getIncludes() != null && !commandProperties.getIncludes().contains(commandName)) {
66 | return Availability.unavailable("command not included (please check property '" +
67 | SshShellProperties.SSH_SHELL_PREFIX + ".commands." + commandGroup + ".includes" + "')");
68 | }
69 | if (helper.isLocalPrompt()) {
70 | LOGGER.debug("Not an ssh session -> local prompt -> giving all rights");
71 | return Availability.available();
72 | }
73 | SshAuthentication auth = SshShellCommandFactory.SSH_THREAD_CONTEXT.get().getAuthentication();
74 | List authorities = auth != null ? auth.getAuthorities() : null;
75 | if (commandProperties.isRestricted() && !helper.checkAuthorities(commandProperties.getAuthorizedRoles(),
76 | authorities, properties.getAuthentication() == SshShellProperties.AuthenticationType.simple)) {
77 | return Availability.unavailable("command is forbidden for current user");
78 | }
79 | postAvailability();
80 | return Availability.available();
81 | } catch (AvailabilityException e) {
82 | return Availability.unavailable(e.getMessage());
83 | }
84 | }
85 |
86 | /**
87 | * Extends this to add behavior before the one in abstract
88 | *
89 | * @throws AvailabilityException if unavailable
90 | */
91 | protected void preAvailability() throws AvailabilityException {
92 | // nothing by default
93 | }
94 |
95 |
96 | /**
97 | * Extends this to add behavior after the one in abstract
98 | *
99 | * @throws AvailabilityException if unavailable
100 | */
101 | protected void postAvailability() throws AvailabilityException {
102 | // nothing by default
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/commands/AvailabilityException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.commands;
18 |
19 | import java.io.Serial;
20 |
21 | /**
22 | * Availability
23 | */
24 | public class AvailabilityException
25 | extends Exception {
26 |
27 | @Serial
28 | private static final long serialVersionUID = -8323343028474225670L;
29 |
30 | public AvailabilityException(String message) {
31 | super(message);
32 | }
33 |
34 | public AvailabilityException(String message, Throwable cause) {
35 | super(message, cause);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/commands/ColorAligner.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.commands;
18 |
19 | import com.github.fonimus.ssh.shell.PromptColor;
20 | import com.github.fonimus.ssh.shell.SshShellHelper;
21 | import org.springframework.shell.table.Aligner;
22 |
23 | /**
24 | * Add this aligner to color cell
25 | */
26 | public class ColorAligner implements Aligner {
27 |
28 | private final PromptColor color;
29 |
30 | /**
31 | * Default constructor
32 | *
33 | * @param color the cell text color
34 | */
35 | public ColorAligner(PromptColor color) {
36 | this.color = color;
37 | }
38 |
39 | @Override
40 | public String[] align(String[] text, int cellWidth, int cellHeight) {
41 | String[] result = new String[text.length];
42 | for (int i = 0; i < text.length; i++) {
43 | result[i] = SshShellHelper.getColoredMessage(text[i], color);
44 | }
45 | return result;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/commands/CommandProperties.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.commands;
18 |
19 | import lombok.AllArgsConstructor;
20 | import lombok.Data;
21 | import lombok.NoArgsConstructor;
22 |
23 | import java.util.ArrayList;
24 | import java.util.Collections;
25 | import java.util.List;
26 |
27 | import static com.github.fonimus.ssh.shell.SshShellProperties.ADMIN_ROLE;
28 |
29 | /**
30 | * Command specific properties
31 | */
32 | @Data
33 | @NoArgsConstructor
34 | @AllArgsConstructor
35 | public class CommandProperties {
36 |
37 | /**
38 | * Create command at startup
39 | */
40 | private boolean create = true;
41 |
42 | /**
43 | * Enable command at startup
44 | */
45 | private boolean enable = true;
46 |
47 | /**
48 | * Is command restricted
49 | */
50 | private boolean restricted = true;
51 |
52 | private List authorizedRoles = new ArrayList<>(Collections.singletonList(ADMIN_ROLE));
53 |
54 | /**
55 | * Possibility to include some sub commands only
56 | */
57 | private List includes;
58 |
59 | /**
60 | * Possibility to exclude some sub commands only
61 | */
62 | private List excludes;
63 |
64 | /**
65 | * Create properties for disabled command
66 | *
67 | * @return disabled command
68 | */
69 | public static CommandProperties disabledByDefault() {
70 | CommandProperties properties = new CommandProperties();
71 | properties.setEnable(false);
72 | return properties;
73 | }
74 |
75 | /**
76 | * Create properties for command with authorized roles
77 | *
78 | * @return command with authorized roles
79 | */
80 | public static CommandProperties withAuthorizedRoles(List authorizedRoles) {
81 | CommandProperties properties = new CommandProperties();
82 | properties.setAuthorizedRoles(authorizedRoles);
83 | return properties;
84 | }
85 |
86 | /**
87 | * Create properties for not restructed command
88 | *
89 | * @return not restructed command
90 | */
91 | public static CommandProperties notRestrictedByDefault() {
92 | CommandProperties properties = new CommandProperties();
93 | properties.setRestricted(false);
94 | properties.setAuthorizedRoles(null);
95 | return properties;
96 | }
97 |
98 | /**
99 | * Create properties for excluded by default
100 | *
101 | * @return excluded by default
102 | */
103 | public static CommandProperties withExcludedByDefault(List excludes) {
104 | CommandProperties properties = new CommandProperties();
105 | properties.setExcludes(excludes);
106 | return properties;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/commands/HistoryCommand.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.commands;
18 |
19 | import com.github.fonimus.ssh.shell.SshShellHelper;
20 | import com.github.fonimus.ssh.shell.SshShellProperties;
21 | import com.github.fonimus.ssh.shell.providers.ExtendedFileValueProvider;
22 | import lombok.extern.slf4j.Slf4j;
23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
24 | import org.springframework.shell.Availability;
25 | import org.springframework.shell.standard.ShellCommandGroup;
26 | import org.springframework.shell.standard.ShellMethod;
27 | import org.springframework.shell.standard.ShellMethodAvailability;
28 | import org.springframework.shell.standard.ShellOption;
29 | import org.springframework.shell.standard.commands.History;
30 | import org.springframework.util.CollectionUtils;
31 |
32 | import java.io.File;
33 | import java.io.IOException;
34 | import java.util.List;
35 |
36 | /**
37 | * Override history command to get history per user if not shared
38 | */
39 | @Slf4j
40 | @SshShellComponent
41 | @ShellCommandGroup("Built-In Commands")
42 | @ConditionalOnProperty(
43 | name = SshShellProperties.SSH_SHELL_PREFIX + ".commands." + HistoryCommand.GROUP + ".create",
44 | havingValue = "true", matchIfMissing = true
45 | )
46 | public class HistoryCommand extends AbstractCommand implements History.Command {
47 |
48 | public static final String GROUP = "history";
49 | public static final String COMMAND_HISTORY = GROUP;
50 |
51 | public HistoryCommand(SshShellProperties properties, SshShellHelper helper) {
52 | super(helper, properties, properties.getCommands().getHistory());
53 | }
54 |
55 | @ShellMethod(key = COMMAND_HISTORY, value = "Display or save the history of previously run commands")
56 | @ShellMethodAvailability("historyAvailability")
57 | public Object history(
58 | @ShellOption(help = "A file to save history to.", defaultValue = ShellOption.NULL, valueProvider = ExtendedFileValueProvider.class) File file,
59 | @ShellOption(help = "To display standard spring shell way (array.tostring). Default value: false", defaultValue = "false") boolean displayArray
60 | ) throws IOException {
61 | List result = new History(helper.getHistory()).history(file);
62 | if (file != null && result.size() == 1) {
63 | return result.get(0);
64 | } else if (displayArray) {
65 | return result;
66 | }
67 | StringBuilder sb = new StringBuilder();
68 | if (!CollectionUtils.isEmpty(result)) {
69 | result.forEach(h -> sb.append(h).append(System.lineSeparator()));
70 | }
71 | return sb.toString();
72 | }
73 |
74 | private Availability historyAvailability() {
75 | return availability(GROUP, COMMAND_HISTORY);
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/commands/PostProcessorsCommand.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.commands;
18 |
19 | import com.github.fonimus.ssh.shell.SshShellHelper;
20 | import com.github.fonimus.ssh.shell.SshShellProperties;
21 | import com.github.fonimus.ssh.shell.postprocess.PostProcessor;
22 | import org.jline.utils.AttributedStringBuilder;
23 | import org.jline.utils.AttributedStyle;
24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
25 | import org.springframework.shell.Availability;
26 | import org.springframework.shell.standard.ShellCommandGroup;
27 | import org.springframework.shell.standard.ShellMethod;
28 | import org.springframework.shell.standard.ShellMethodAvailability;
29 |
30 | import java.lang.reflect.ParameterizedType;
31 | import java.util.ArrayList;
32 | import java.util.Comparator;
33 | import java.util.List;
34 |
35 | /**
36 | * Command to list available post processors
37 | */
38 | @SshShellComponent
39 | @ShellCommandGroup("Built-In Commands")
40 | @ConditionalOnProperty(
41 | name = SshShellProperties.SSH_SHELL_PREFIX + ".commands." + PostProcessorsCommand.GROUP + ".create",
42 | havingValue = "true", matchIfMissing = true
43 | )
44 | public class PostProcessorsCommand extends AbstractCommand {
45 |
46 | public static final String GROUP = "postprocessors";
47 | public static final String COMMAND_POST_PROCESSORS = "postprocessors";
48 |
49 | private final List> postProcessors;
50 |
51 | public PostProcessorsCommand(SshShellHelper helper, SshShellProperties properties,
52 | List> postProcessors) {
53 | super(helper, properties, properties.getCommands().getPostprocessors());
54 | this.postProcessors = new ArrayList<>(postProcessors);
55 | this.postProcessors.sort(Comparator.comparing(PostProcessor::getName));
56 | }
57 |
58 | @ShellMethod(key = COMMAND_POST_PROCESSORS, value = "Display the available post processors")
59 | @ShellMethodAvailability("postprocessorsAvailability")
60 | public CharSequence postprocessors() {
61 | AttributedStringBuilder result = new AttributedStringBuilder();
62 | result.append("Available Post-Processors\n\n", AttributedStyle.BOLD);
63 | for (PostProcessor, ?> postProcessor : postProcessors) {
64 | result.append("\t" + postProcessor.getName() + ":\n", AttributedStyle.BOLD);
65 | Class> input =
66 | ((Class>) ((ParameterizedType) (postProcessor.getClass().getGenericInterfaces())[0]).getActualTypeArguments()[0]);
67 | Class> output =
68 | ((Class>) ((ParameterizedType) (postProcessor.getClass().getGenericInterfaces())[0]).getActualTypeArguments()[1]);
69 | result.append("\t\thelp : " + postProcessor.getDescription() + "\n", AttributedStyle.DEFAULT);
70 | result.append("\t\tinput : " + input.getName() + "\n", AttributedStyle.DEFAULT);
71 | result.append("\t\toutput : " + output.getName() + "\n", AttributedStyle.DEFAULT);
72 | }
73 |
74 | return result;
75 | }
76 |
77 | private Availability postprocessorsAvailability() {
78 | return availability(GROUP, COMMAND_POST_PROCESSORS);
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/commands/SshShellComponent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.commands;
18 |
19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
20 | import org.springframework.shell.standard.ShellComponent;
21 |
22 | import java.lang.annotation.*;
23 |
24 | import static com.github.fonimus.ssh.shell.SshShellProperties.SSH_SHELL_ENABLE;
25 |
26 | /**
27 | * Conditional {@link org.springframework.shell.standard.ShellComponent}
28 | */
29 | @Retention(RetentionPolicy.RUNTIME)
30 | @Target(ElementType.TYPE)
31 | @Documented
32 | @ShellComponent
33 | @ConditionalOnProperty(name = SSH_SHELL_ENABLE, havingValue = "true", matchIfMissing = true)
34 | public @interface SshShellComponent {
35 |
36 | /**
37 | * Used to indicate a suggestion for a logical name for the component.
38 | *
39 | * @return the suggested component name, if any
40 | */
41 | String value() default "";
42 | }
43 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/commands/StacktraceCommand.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.commands;
18 |
19 | import com.github.fonimus.ssh.shell.SshShellHelper;
20 | import com.github.fonimus.ssh.shell.SshShellProperties;
21 | import com.github.fonimus.ssh.shell.postprocess.ExtendedResultHandlerService;
22 | import org.jline.terminal.Terminal;
23 | import org.springframework.beans.factory.annotation.Autowired;
24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
25 | import org.springframework.context.annotation.Lazy;
26 | import org.springframework.shell.Availability;
27 | import org.springframework.shell.standard.ShellCommandGroup;
28 | import org.springframework.shell.standard.ShellMethod;
29 | import org.springframework.shell.standard.ShellMethodAvailability;
30 | import org.springframework.shell.standard.commands.Stacktrace;
31 |
32 | /**
33 | * Override stacktrace command to get error per thread
34 | */
35 | @SshShellComponent
36 | @ShellCommandGroup("Built-In Commands")
37 | @ConditionalOnProperty(
38 | name = SshShellProperties.SSH_SHELL_PREFIX + ".commands." + StacktraceCommand.GROUP + ".create",
39 | havingValue = "true", matchIfMissing = true
40 | )
41 | public class StacktraceCommand extends AbstractCommand implements Stacktrace.Command {
42 |
43 | public static final String GROUP = "stacktrace";
44 | public static final String COMMAND_STACKTRACE = GROUP;
45 |
46 | private Terminal terminal;
47 |
48 | public StacktraceCommand(SshShellHelper helper, SshShellProperties properties) {
49 | super(helper, properties, properties.getCommands().getStacktrace());
50 | }
51 |
52 | @ShellMethod(key = COMMAND_STACKTRACE, value = "Display the full stacktrace of the last error.")
53 | @ShellMethodAvailability("stacktraceAvailability")
54 | public void stacktrace() {
55 | Throwable lastError = ExtendedResultHandlerService.THREAD_CONTEXT.get();
56 | if (lastError != null) {
57 | lastError.printStackTrace(this.terminal.writer());
58 | }
59 | }
60 |
61 | @Autowired
62 | @Lazy
63 | public void setTerminal(Terminal terminal) {
64 | this.terminal = terminal;
65 | }
66 |
67 | private Availability stacktraceAvailability() {
68 | return availability(GROUP, COMMAND_STACKTRACE);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/interactive/Interactive.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.interactive;
18 |
19 | import lombok.Builder;
20 | import lombok.Getter;
21 | import lombok.NonNull;
22 | import lombok.Singular;
23 | import org.jline.terminal.Size;
24 |
25 | import java.util.List;
26 |
27 | /**
28 | * Interactive bean
29 | */
30 | @Builder
31 | @Getter
32 | public class Interactive {
33 |
34 | @NonNull
35 | private InteractiveInput input;
36 |
37 | @Builder.Default
38 | private long refreshDelay = 3000;
39 |
40 | @Builder.Default
41 | private boolean fullScreen = true;
42 |
43 | @Builder.Default
44 | private boolean exit = true;
45 |
46 | @Builder.Default
47 | private boolean increase = true;
48 |
49 | @Builder.Default
50 | private boolean decrease = true;
51 |
52 | @Singular
53 | private List bindings;
54 |
55 | private Size size;
56 | }
57 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/interactive/InteractiveInput.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.interactive;
18 |
19 | import org.jline.terminal.Size;
20 | import org.jline.utils.AttributedString;
21 |
22 | import java.util.List;
23 |
24 | /**
25 | * Interface to give to interactive command to provide lines
26 | */
27 | @FunctionalInterface
28 | public interface InteractiveInput {
29 |
30 | /**
31 | * Get lines to write
32 | *
33 | * @param size terminal size
34 | * @param currentDelay current refresh delay
35 | * @return lines
36 | */
37 | List getLines(Size size, long currentDelay);
38 | }
39 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/interactive/InteractiveInputIO.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.interactive;
18 |
19 | import lombok.AllArgsConstructor;
20 | import lombok.Data;
21 | import lombok.NonNull;
22 | import lombok.RequiredArgsConstructor;
23 | import org.jline.utils.AttributedString;
24 |
25 | import java.util.List;
26 |
27 | /**
28 | * Interface to give to interactive command to provide lines
29 | */
30 | @Data
31 | @RequiredArgsConstructor
32 | @AllArgsConstructor
33 | public class InteractiveInputIO {
34 |
35 | private boolean stop;
36 |
37 | @NonNull
38 | private List lines;
39 | }
40 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/interactive/KeyBinding.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.interactive;
18 |
19 | import lombok.Builder;
20 | import lombok.Getter;
21 | import lombok.NonNull;
22 | import lombok.Singular;
23 |
24 | import java.util.List;
25 |
26 | /**
27 | * Key binding bean
28 | */
29 | @Builder
30 | @Getter
31 | public class KeyBinding {
32 |
33 | @NonNull
34 | private String description;
35 |
36 | @NonNull
37 | private KeyBindingInput input;
38 |
39 | @NonNull
40 | @Singular
41 | private List keys;
42 | }
43 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/interactive/KeyBindingInput.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.interactive;
18 |
19 | /**
20 | * Key binding input interface
21 | */
22 | @FunctionalInterface
23 | public interface KeyBindingInput {
24 |
25 | /**
26 | * Perform action
27 | */
28 | void action();
29 | }
30 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/interactive/StoppableInteractiveInput.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.interactive;
18 |
19 | import org.jline.terminal.Size;
20 | import org.jline.utils.AttributedString;
21 |
22 | import java.util.List;
23 |
24 | /**
25 | * Interface to give to interactive command to provide lines
26 | */
27 | @FunctionalInterface
28 | public interface StoppableInteractiveInput extends InteractiveInput {
29 |
30 | /**
31 | * Get lines to write
32 | *
33 | * @param size terminal size
34 | * @param currentDelay current refresh delay
35 | * @return bean containing lines
36 | */
37 | InteractiveInputIO getIO(Size size, long currentDelay);
38 |
39 | @Override
40 | default List getLines(Size size, long currentDelay) {
41 | return getIO(size, currentDelay).getLines();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/listeners/SshShellEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.listeners;
18 |
19 | import lombok.AllArgsConstructor;
20 | import lombok.Data;
21 | import org.apache.sshd.server.channel.ChannelSession;
22 |
23 | /**
24 | * Ssh shell event
25 | */
26 | @Data
27 | @AllArgsConstructor
28 | public class SshShellEvent {
29 |
30 | private SshShellEventType type;
31 |
32 | private ChannelSession session;
33 |
34 | public long getSessionId() {
35 | return session.getServerSession().getIoSession().getId();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/listeners/SshShellEventType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.listeners;
18 |
19 | /**
20 | * Event types
21 | */
22 | public enum SshShellEventType {
23 | SESSION_STARTED, SESSION_STOPPED, SESSION_STOPPED_UNEXPECTEDLY
24 | }
25 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/listeners/SshShellListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.listeners;
18 |
19 | /**
20 | * Listen for ssh shell events
21 | */
22 | public interface SshShellListener {
23 |
24 | /**
25 | * Event occurred
26 | *
27 | * @param event ssh shell event
28 | */
29 | void onEvent(SshShellEvent event);
30 | }
31 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/listeners/SshShellListenerService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.listeners;
18 |
19 | import lombok.extern.slf4j.Slf4j;
20 | import org.apache.sshd.server.channel.ChannelSession;
21 |
22 | import java.util.ArrayList;
23 | import java.util.List;
24 |
25 | /**
26 | * Service calling listeners
27 | */
28 | @Slf4j
29 | public class SshShellListenerService {
30 |
31 | private final List listeners;
32 |
33 | public SshShellListenerService(List listeners) {
34 | this.listeners = listeners == null ? new ArrayList<>() : listeners;
35 | LOGGER.info("Ssh shell listener service initialized with {} listeners", this.listeners.size());
36 | }
37 |
38 | /**
39 | * Session started
40 | *
41 | * @param channelSession ssh channel session
42 | */
43 | public void onSessionStarted(ChannelSession channelSession) {
44 | notify(new SshShellEvent(SshShellEventType.SESSION_STARTED, channelSession));
45 | }
46 |
47 | /**
48 | * Session stopped
49 | *
50 | * @param channelSession ssh channel session
51 | */
52 | public void onSessionStopped(ChannelSession channelSession) {
53 | notify(new SshShellEvent(SshShellEventType.SESSION_STOPPED, channelSession));
54 | }
55 |
56 | /**
57 | * Session stopped with error
58 | *
59 | * @param channelSession ssh channel session
60 | */
61 | public void onSessionError(ChannelSession channelSession) {
62 | notify(new SshShellEvent(SshShellEventType.SESSION_STOPPED_UNEXPECTEDLY, channelSession));
63 | }
64 |
65 | private void notify(SshShellEvent event) {
66 | for (SshShellListener listener : this.listeners) {
67 | try {
68 | listener.onEvent(event);
69 | } catch (RuntimeException e) {
70 | LOGGER.error("Unable to execute onSessionStarted on listener : {}", listener.getClass().getName(), e);
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/manage/SshShellSessionManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.manage;
18 |
19 | import com.github.fonimus.ssh.shell.SshShellCommandFactory;
20 | import com.github.fonimus.ssh.shell.auth.SshAuthentication;
21 | import org.apache.sshd.server.channel.ChannelSession;
22 | import org.springframework.stereotype.Component;
23 |
24 | import java.util.Map;
25 |
26 | /**
27 | * Session manager
28 | */
29 | @Component
30 | public class SshShellSessionManager {
31 |
32 | private final SshShellCommandFactory commandFactory;
33 |
34 | /**
35 | * Ssh shell session manager
36 | *
37 | * @param commandFactory ssh shell command factory
38 | */
39 | public SshShellSessionManager(SshShellCommandFactory commandFactory) {
40 | this.commandFactory = commandFactory;
41 | }
42 |
43 | /**
44 | * List active sessions
45 | *
46 | * @return active sessions
47 | */
48 | public Map listSessions() {
49 | return this.commandFactory.listSessions();
50 | }
51 |
52 | /**
53 | * @param id session id
54 | * @return found session, or null if not existing
55 | */
56 | public ChannelSession getSession(long id) {
57 | return listSessions().get(id);
58 | }
59 |
60 | /**
61 | * Stop active session
62 | *
63 | * @param id session id
64 | * @return true if session found and stopped, false otherwise
65 | */
66 | public boolean stopSession(long id) {
67 | ChannelSession session = getSession(id);
68 | if (session != null) {
69 | this.commandFactory.destroy(session);
70 | return true;
71 | }
72 | return false;
73 | }
74 |
75 | /**
76 | * Search for authenticated user in session
77 | *
78 | * @param session ssh session
79 | * @return user name
80 | */
81 | public static String sessionUserName(ChannelSession session) {
82 | SshAuthentication authentication =
83 | (SshAuthentication) session.getServerSession().getIoSession().getAttribute("authentication");
84 | if (authentication == null) {
85 | return null;
86 | }
87 | return authentication.getName();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/postprocess/PostProcessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.postprocess;
18 |
19 | import java.util.List;
20 |
21 | /**
22 | * Post processor interface
23 | */
24 | public interface PostProcessor {
25 |
26 | String getName();
27 |
28 | String getDescription();
29 |
30 | O process(I result, List parameters) throws PostProcessorException;
31 | }
32 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/postprocess/PostProcessorException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.postprocess;
18 |
19 | import java.io.Serial;
20 |
21 | /**
22 | * Post processor exception
23 | */
24 | public class PostProcessorException
25 | extends Exception {
26 |
27 | @Serial
28 | private static final long serialVersionUID = 150227794242813079L;
29 |
30 | public PostProcessorException(String message) {
31 | super(message);
32 | }
33 |
34 | public PostProcessorException(String message, Throwable cause) {
35 | super(message, cause);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/postprocess/PostProcessorObject.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.postprocess;
18 |
19 | import lombok.AllArgsConstructor;
20 | import lombok.Getter;
21 | import lombok.NonNull;
22 | import lombok.RequiredArgsConstructor;
23 |
24 | import java.util.List;
25 |
26 | /**
27 | * Post processor object
28 | */
29 | @RequiredArgsConstructor
30 | @AllArgsConstructor
31 | @Getter
32 | public class PostProcessorObject {
33 |
34 | @NonNull
35 | private String name;
36 |
37 | private List parameters;
38 | }
39 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/postprocess/provided/GrepPostProcessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.postprocess.provided;
18 |
19 | import com.github.fonimus.ssh.shell.postprocess.PostProcessor;
20 | import lombok.extern.slf4j.Slf4j;
21 |
22 | import java.util.List;
23 |
24 | /**
25 | * Grep post processor
26 | */
27 | @Slf4j
28 | public class GrepPostProcessor
29 | implements PostProcessor {
30 |
31 | @Override
32 | public String getName() {
33 | return "grep";
34 | }
35 |
36 | @Override
37 | public String getDescription() {
38 | return "Find pattern in result lines";
39 | }
40 |
41 | @Override
42 | public String process(String result, List parameters) {
43 | if (parameters == null || parameters.isEmpty()) {
44 | LOGGER.debug("Cannot use [{}] post processor without any parameters", getName());
45 | return result;
46 | } else {
47 | StringBuilder sb = new StringBuilder();
48 | for (String line : result.split("\n")) {
49 | if (contains(line, parameters)) {
50 | sb.append(line).append("\n");
51 | }
52 | }
53 | return sb.toString().isEmpty() ? sb.toString() : sb.substring(0, sb.toString().length() - 1);
54 | }
55 | }
56 |
57 | private boolean contains(String line, List parameters) {
58 | for (String parameter : parameters) {
59 | if (parameter == null || parameter.isEmpty() || line.contains(parameter)) {
60 | return true;
61 | }
62 | }
63 | return false;
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/postprocess/provided/HighlightPostProcessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.postprocess.provided;
18 |
19 | import com.github.fonimus.ssh.shell.PromptColor;
20 | import com.github.fonimus.ssh.shell.SshShellHelper;
21 | import com.github.fonimus.ssh.shell.postprocess.PostProcessor;
22 | import lombok.extern.slf4j.Slf4j;
23 |
24 | import java.util.List;
25 |
26 | /**
27 | * Grep post processor
28 | */
29 | @Slf4j
30 | public class HighlightPostProcessor
31 | implements PostProcessor {
32 |
33 | @Override
34 | public String getName() {
35 | return "highlight";
36 | }
37 |
38 | @Override
39 | public String getDescription() {
40 | return "Highlight some words in result";
41 | }
42 |
43 | @Override
44 | public String process(String result, List parameters) {
45 | if (parameters == null || parameters.isEmpty()) {
46 | LOGGER.debug("Cannot use [{}] post processor without any parameters", getName());
47 | return result;
48 | } else {
49 | String finalResult = result;
50 | for (String toHighlight : parameters) {
51 | finalResult = finalResult.replaceAll(toHighlight,
52 | SshShellHelper.getBackgroundColoredMessage(toHighlight,
53 | PromptColor.YELLOW));
54 | }
55 | return finalResult;
56 | }
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/postprocess/provided/JsonPointerPostProcessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.postprocess.provided;
18 |
19 | import com.fasterxml.jackson.databind.JsonNode;
20 | import com.fasterxml.jackson.databind.ObjectMapper;
21 | import com.github.fonimus.ssh.shell.postprocess.PostProcessor;
22 | import lombok.AllArgsConstructor;
23 | import lombok.extern.slf4j.Slf4j;
24 |
25 | import java.io.IOException;
26 | import java.util.List;
27 |
28 | /**
29 | * Json pointer post processor
30 | */
31 | @Slf4j
32 | @AllArgsConstructor
33 | public class JsonPointerPostProcessor
34 | implements PostProcessor {
35 |
36 | private final ObjectMapper mapper;
37 |
38 | @Override
39 | public String getName() {
40 | return "json";
41 | }
42 |
43 | @Override
44 | public String getDescription() {
45 | return "Json path (use pretty postprocessor first to get json from object)";
46 | }
47 |
48 | @Override
49 | public String process(String result, List parameters) {
50 | if (parameters == null || parameters.isEmpty()) {
51 | LOGGER.debug("Cannot use [{}] post processor without any parameters", getName());
52 | } else {
53 | if (parameters.size() != 1) {
54 | LOGGER.debug("[{}] post processor only need one parameter, rest will be ignored", getName());
55 | }
56 | String path = parameters.get(0);
57 | try {
58 | JsonNode node = mapper.readTree(result).at(path);
59 | if (node.isMissingNode()) {
60 | return "No node found with json path expression: " + path;
61 | } else {
62 | if (node.isTextual()) {
63 | return node.asText();
64 | } else {
65 | return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(node);
66 | }
67 | }
68 | } catch (IOException e) {
69 | LOGGER.warn("Unable to read tree", e);
70 | } catch (IllegalArgumentException e) {
71 | LOGGER.warn("Illegal argument: " + path, e);
72 | return e.getMessage();
73 | }
74 | }
75 | return result;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/starter/src/main/java/com/github/fonimus/ssh/shell/postprocess/provided/PrettyJsonPostProcessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020 François Onimus
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 |
17 | package com.github.fonimus.ssh.shell.postprocess.provided;
18 |
19 | import com.fasterxml.jackson.core.JsonProcessingException;
20 | import com.fasterxml.jackson.databind.ObjectMapper;
21 | import com.github.fonimus.ssh.shell.postprocess.PostProcessor;
22 | import com.github.fonimus.ssh.shell.postprocess.PostProcessorException;
23 | import lombok.AllArgsConstructor;
24 | import lombok.extern.slf4j.Slf4j;
25 |
26 | import java.util.List;
27 |
28 | /**
29 | * Pretty json post processor
30 | */
31 | @Slf4j
32 | @AllArgsConstructor
33 | public class PrettyJsonPostProcessor
34 | implements PostProcessor