mirror of
https://github.com/Eugeny/tabby.git
synced 2025-10-05 14:34:54 +00:00
moved login scripts processing into tabby-terminal
This commit is contained in:
@@ -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')
|
||||
|
@@ -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')
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user