moved login scripts processing into tabby-terminal

This commit is contained in:
Eugene Pankov
2021-07-05 23:56:38 +02:00
parent 461cd2bec7
commit bf762cc4c7
16 changed files with 270 additions and 344 deletions

View File

@@ -10,8 +10,8 @@ import stripAnsi from 'strip-ansi'
import socksv5 from 'socksv5'
import { Injector, NgZone } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigService, FileProvidersService, HostAppService, Logger, NotificationsService, Platform, PlatformService, wrapPromise, PromptModalComponent, Profile, LogService } from 'tabby-core'
import { BaseSession } from 'tabby-terminal'
import { ConfigService, FileProvidersService, HostAppService, NotificationsService, Platform, PlatformService, wrapPromise, PromptModalComponent, Profile, LogService } from 'tabby-core'
import { BaseSession, LoginScriptsOptions } from 'tabby-terminal'
import { Server, Socket, createServer, createConnection } from 'net'
import { Client, ClientChannel, SFTPWrapper } from 'ssh2'
import type { FileEntry, Stats } from 'ssh2-streams'
@@ -22,13 +22,6 @@ import { promisify } from 'util'
const WINDOWS_OPENSSH_AGENT_PIPE = '\\\\.\\pipe\\openssh-ssh-agent'
export interface LoginScript {
expect: string
send: string
isRegex?: boolean
optional?: boolean
}
export enum SSHAlgorithmType {
HMAC = 'hmac',
KEX = 'kex',
@@ -40,14 +33,13 @@ export interface SSHProfile extends Profile {
options: SSHProfileOptions
}
export interface SSHProfileOptions {
export interface SSHProfileOptions extends LoginScriptsOptions {
host: string
port?: number
user: string
auth?: null|'password'|'publicKey'|'agent'|'keyboardInteractive'
password?: string
privateKeys?: string[]
scripts?: LoginScript[]
keepaliveInterval?: number
keepaliveCountMax?: number
readyTimeout?: number
@@ -255,12 +247,10 @@ export class SFTPSession {
}
export class SSHSession extends BaseSession {
scripts?: LoginScript[]
shell?: ClientChannel
ssh: Client
sftp?: SFTPWrapper
forwardedPorts: ForwardedPort[] = []
logger: Logger
jumpStream: any
proxyCommandStream: ProxyCommandStream|null = null
savedPassword?: string
@@ -286,8 +276,7 @@ export class SSHSession extends BaseSession {
injector: Injector,
public profile: SSHProfile,
) {
super()
this.logger = injector.get(LogService).create(`ssh-${profile.options.host}-${profile.options.port}`)
super(injector.get(LogService).create(`ssh-${profile.options.host}-${profile.options.port}`))
this.passwordStorage = injector.get(PasswordStorageService)
this.ngbModal = injector.get(NgbModal)
@@ -298,7 +287,6 @@ export class SSHSession extends BaseSession {
this.fileProviders = injector.get(FileProvidersService)
this.config = injector.get(ConfigService)
this.scripts = profile.options.scripts ?? []
this.destroyed$.subscribe(() => {
for (const port of this.forwardedPorts) {
if (port.type === PortForwardType.Local) {
@@ -306,6 +294,8 @@ export class SSHSession extends BaseSession {
}
}
})
this.setLoginScriptsOptions(profile.options)
}
async init (): Promise<void> {
@@ -389,6 +379,8 @@ export class SSHSession extends BaseSession {
return
}
this.loginScriptProcessor?.executeUnconditionalScripts()
this.shell.on('greeting', greeting => {
this.emitServiceMessage(`Shell greeting: ${greeting}`)
})
@@ -398,48 +390,7 @@ export class SSHSession extends BaseSession {
})
this.shell.on('data', data => {
const dataString = data.toString()
this.emitOutput(data)
if (this.scripts) {
let found = false
for (const script of this.scripts) {
let match = false
let cmd = ''
if (script.isRegex) {
const re = new RegExp(script.expect, 'g')
if (dataString.match(re)) {
cmd = dataString.replace(re, script.send)
match = true
found = true
}
} else {
if (dataString.includes(script.expect)) {
cmd = script.send
match = true
found = true
}
}
if (match) {
this.logger.info('Executing script: "' + cmd + '"')
this.shell?.write(cmd + '\n')
this.scripts = this.scripts.filter(x => x !== script)
} else {
if (script.optional) {
this.logger.debug('Skip optional script: ' + script.expect)
found = true
this.scripts = this.scripts.filter(x => x !== script)
} else {
break
}
}
}
if (found) {
this.executeUnconditionalScripts()
}
}
})
this.shell.on('end', () => {
@@ -513,8 +464,6 @@ export class SSHSession extends BaseSession {
})
})
})
this.executeUnconditionalScripts()
}
emitServiceMessage (msg: string): void {
@@ -714,20 +663,6 @@ export class SSHSession extends BaseSession {
})
}
private executeUnconditionalScripts () {
if (this.scripts) {
for (const script of this.scripts) {
if (!script.expect) {
console.log('Executing script:', script.send)
this.shell?.write(script.send + '\n')
this.scripts = this.scripts.filter(x => x !== script)
} else {
break
}
}
}
}
async loadPrivateKey (privateKeyContents?: Buffer): Promise<string|null> {
if (!privateKeyContents) {
const userKeyPath = path.join(process.env.HOME!, '.ssh', 'id_rsa')

View File

@@ -189,43 +189,6 @@ ul.nav-tabs(ngbNav, #nav='ngbNav')
li(ngbNavItem)
a(ngbNavLink) Login scripts
ng-template(ngbNavContent)
table(*ngIf='profile.options.scripts.length > 0')
tr
th String to expect
th String to be sent
th.pl-2 Regex
th.pl-2 Optional
th.pl-2 Actions
tr(*ngFor='let script of profile.options.scripts')
td.pr-2
input.form-control(
type='text',
[(ngModel)]='script.expect'
)
td
input.form-control(
type='text',
[(ngModel)]='script.send'
)
td.pl-2
checkbox(
[(ngModel)]='script.isRegex',
)
td.pl-2
checkbox(
[(ngModel)]='script.optional',
)
td.pl-2
.input-group.flex-nowrap
button.btn.btn-outline-info.ml-0((click)='moveScriptUp(script)')
i.fas.fa-arrow-up
button.btn.btn-outline-info.ml-0((click)='moveScriptDown(script)')
i.fas.fa-arrow-down
button.btn.btn-outline-danger.ml-0((click)='deleteScript(script)')
i.fas.fa-trash
button.btn.btn-outline-info.mt-2((click)='addScript()')
i.fas.fa-plus
span New item
login-scripts-settings([options]='profile.options')
div([ngbNavOutlet]='nav')

View File

@@ -2,9 +2,9 @@
import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigService, PlatformService, FileProvidersService, Platform, HostAppService, PromptModalComponent } from 'tabby-core'
import { ConfigService, FileProvidersService, Platform, HostAppService, PromptModalComponent } from 'tabby-core'
import { PasswordStorageService } from '../services/passwordStorage.service'
import { LoginScript, ForwardedPortConfig, SSHAlgorithmType, ALGORITHM_BLACKLIST, SSHProfile } from '../api'
import { ForwardedPortConfig, SSHAlgorithmType, ALGORITHM_BLACKLIST, SSHProfile } from '../api'
import * as ALGORITHMS from 'ssh2/lib/protocol/constants'
/** @hidden */
@@ -23,9 +23,8 @@ export class SSHProfileSettingsComponent {
jumpHosts: SSHProfile[]
constructor (
public config: ConfigService,
public hostApp: HostAppService,
private platform: PlatformService,
private config: ConfigService,
private passwordStorage: PasswordStorageService,
private ngbModal: NgbModal,
private fileProviders: FileProvidersService,
@@ -63,7 +62,6 @@ export class SSHProfileSettingsComponent {
}
}
this.profile.options.scripts = this.profile.options.scripts ?? []
this.profile.options.auth = this.profile.options.auth ?? null
this.profile.options.privateKeys ??= []
@@ -116,49 +114,6 @@ export class SSHProfileSettingsComponent {
}
}
moveScriptUp (script: LoginScript) {
if (!this.profile.options.scripts) {
this.profile.options.scripts = []
}
const index = this.profile.options.scripts.indexOf(script)
if (index > 0) {
this.profile.options.scripts.splice(index, 1)
this.profile.options.scripts.splice(index - 1, 0, script)
}
}
moveScriptDown (script: LoginScript) {
if (!this.profile.options.scripts) {
this.profile.options.scripts = []
}
const index = this.profile.options.scripts.indexOf(script)
if (index >= 0 && index < this.profile.options.scripts.length - 1) {
this.profile.options.scripts.splice(index, 1)
this.profile.options.scripts.splice(index + 1, 0, script)
}
}
async deleteScript (script: LoginScript) {
if (this.profile.options.scripts && (await this.platform.showMessageBox(
{
type: 'warning',
message: 'Delete this script?',
detail: script.expect,
buttons: ['Keep', 'Delete'],
defaultId: 1,
}
)).response === 1) {
this.profile.options.scripts = this.profile.options.scripts.filter(x => x !== script)
}
}
addScript () {
if (!this.profile.options.scripts) {
this.profile.options.scripts = []
}
this.profile.options.scripts.push({ expect: '', send: '' })
}
onForwardAdded (fw: ForwardedPortConfig) {
this.profile.options.forwardedPorts = this.profile.options.forwardedPorts ?? []
this.profile.options.forwardedPorts.push(fw)