Files
smartssh/ts/smartssh.classes.sshconfig.ts
T

125 lines
3.5 KiB
TypeScript

import * as plugins from './smartssh.plugins.js';
import * as helpers from './smartssh.classes.helpers.js';
import { SshKey } from './smartssh.classes.sshkey.js';
export type TSshStrictHostKeyChecking = boolean | 'accept-new';
export interface ISshConfigOptions {
strictHostKeyChecking?: TSshStrictHostKeyChecking;
}
export interface ISshConfigHostBlock {
host: string;
values: Record<string, string>;
}
export class SshConfig {
private _sshKeyArray: SshKey[];
private _options: ISshConfigOptions;
constructor(sshKeyArrayArg: SshKey[], optionsArg: ISshConfigOptions = {}) {
this._sshKeyArray = sshKeyArrayArg;
this._options = {
...optionsArg,
strictHostKeyChecking: optionsArg.strictHostKeyChecking ?? true,
};
}
/**
* stores a config file
*/
store(dirPathArg: string) {
const resolvedDir = helpers.resolveSshDirPath(dirPathArg);
helpers.ensureSshDirSync(resolvedDir);
const configArray: configObject[] = [];
for (const sshKey of this._sshKeyArray) {
let configString = '';
if (sshKey.host) {
helpers.assertSafeHost(sshKey.host);
const identityFilePath = plugins.path.join(resolvedDir, sshKey.host);
configString =
'Host ' +
sshKey.host +
'\n' +
' HostName ' +
sshKey.host +
'\n' +
' IdentityFile ' +
helpers.quoteSshConfigValue(identityFilePath) +
'\n';
if (this._options.strictHostKeyChecking !== undefined) {
const strictHostKeyChecking = this._options.strictHostKeyChecking;
configString +=
' StrictHostKeyChecking ' +
(strictHostKeyChecking === 'accept-new'
? 'accept-new'
: strictHostKeyChecking
? 'yes'
: 'no') +
'\n';
}
}
configArray.push({
configString: configString,
authorized: sshKey.authorized,
sshKey: sshKey,
});
}
let configFile: string = '';
for (const config of configArray) {
configFile = configFile + config.configString + '\n';
}
plugins.fs.writeFileSync(plugins.path.join(resolvedDir, 'config'), configFile);
plugins.fs.chmodSync(plugins.path.join(resolvedDir, 'config'), 0o600);
}
read(dirPathArg: string) {
const configPath = plugins.path.join(helpers.resolveSshDirPath(dirPathArg), 'config');
return plugins.fs.readFileSync(configPath, 'utf8');
}
parse(dirPathArg: string) {
return SshConfig.parse(this.read(dirPathArg));
}
static parse(configStringArg: string): ISshConfigHostBlock[] {
const blocks: ISshConfigHostBlock[] = [];
let currentBlock: ISshConfigHostBlock | undefined;
for (const rawLine of configStringArg.split(/\r?\n/)) {
const line = rawLine.trim();
if (!line || line.startsWith('#')) {
continue;
}
const [keyword, ...valueParts] = line.split(/\s+/);
const value = valueParts.join(' ');
if (!keyword || !value) {
continue;
}
if (keyword.toLowerCase() === 'host') {
if (!helpers.isSafeHost(value)) {
continue;
}
currentBlock = {
host: value,
values: {},
};
blocks.push(currentBlock);
continue;
}
if (currentBlock) {
currentBlock.values[keyword.toLowerCase()] = value;
}
}
return blocks;
}
}
export interface configObject {
configString: string;
authorized: boolean;
sshKey: SshKey;
}