Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance sftp interactiveAuth mode #94

Merged
merged 1 commit into from
Feb 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ boolean: Upload on every save operation of VS code
**default**: false

### useTempFile
boolean: Upload temp file on every save operation of VSCode to avoid breaking a webpage when a user acceses it
boolean: Upload temp file on every save operation of VSCode to avoid breaking a webpage when a user acceses it
while the file is still being uploaded (is incomplete).

**Default**: false
Expand Down Expand Up @@ -113,7 +113,7 @@ number: the maximum connection time

### limitOpenFilesOnRemote
mixed: Limit open file descriptors to the specific number in a remote server. Set to true for using default limit(222). Do not set this unless you have to.

**default**: false


Expand All @@ -129,9 +129,9 @@ mixed: Limit open file descriptors to the specific number in a remote server. Se
mixed: For an encrypted private key, this is the passphrase string used to decrypt it. Set to true for enable passphrase dialog. This will prevent from using cleartext passphrase in this config.

### interactiveAuth
boolean: Set to true for enable verifyCode dialog. Keyboard interaction authentication mechanism. For example using Google Authentication (Multi factor)
*boolean*|*string*[]: Enable keyboard interaction authentication mechanism. Set to true to enable `verifyCode` dialog. For example using Google Authentication (multi-factor). Or pass array of predefined phrases to automatically enter them without user prompting.

Note: _(requires the server to have keyboard-interactive authentication enabled)_
Note: *Requires the server to have keyboard-interactive authentication enabled.*

**default**: false

Expand Down
3 changes: 1 addition & 2 deletions docs/sftp_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ more stability because some clients/servers have some sort of configured/hard co
for enabling passphrase dialog. This will prevent from using cleartext passphrase in this config.

## interactiveAuth
*boolean*: Set to true to enable `verifyCode` dialog. Keyboard interation mechanism. For example, using
Google Authentication (multi-factor).
*boolean*|*string*[]: Enable keyboard interaction authentication mechanism. Set to true to enable `verifyCode` dialog. For example using Google Authentication (multi-factor). Or array of predefined phrases to automatically pass them without user prompting.

Note: *Requires the server to have keyboard-interactive authentication enabled.*

Expand Down
18 changes: 15 additions & 3 deletions schema/definitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,21 @@
"description": "For an encrypted private key, this is the passphrase string used to decrypt it. Set to true for enable passphrase dialog. This will prevent from using cleartext passphrase in this config."
},
"interactiveAuth": {
"type": "boolean",
"description": "True to enable verifyCode dialog. Keyboard interaction authentication mechanism. For example using Google Authentication.",
"default": true
"oneOf": [
{
"type": "boolean",
"description": "True to enable verifyCode dialog.",
"default": false
},
{
"type": "array",
"items": {
"type": "string"
},
"description": "Array of predefined phrases to automatically pass them without user prompting."
}
],
"description": "Keyboard interaction authentication mechanism. For example using Google Authentication."
},
"algorithms": {
"type": "object",
Expand Down
4 changes: 2 additions & 2 deletions src/core/fileService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ interface SftpOption {
agent?: string;
privateKeyPath?: string;
passphrase: string | true;
interactiveAuth: boolean;
interactiveAuth: boolean | string[];
algorithms: any;
sshConfigPath?: string;
concurrency: number;
Expand Down Expand Up @@ -265,7 +265,7 @@ function mergeConfigWithExternalRefer(
}
}
});

// Bug introduced in pull request #69 : Fix ssh config resolution
/* const parsedSSHConfig = sshConfig.parse(sshConfigContent);
const computed = parsedSSHConfig.compute(copyed.host);
Expand Down
2 changes: 1 addition & 1 deletion src/core/remote-client/remoteClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface ConnectOption {
privateKeyPath?: string;
privateKey?: string;
passphrase?: string | boolean;
interactiveAuth?: boolean;
interactiveAuth?: boolean | string[];
agent?: string;
sock?: any;
hop?: ConnectOption | ConnectOption[];
Expand Down
13 changes: 10 additions & 3 deletions src/core/remote-client/sshClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ export default class SSHClient extends RemoteClient {

_hasProvideAuth(connectOption: ConnectOption) {
return (
// interactiveAuth : boolean
connectOption.interactiveAuth === true ||
// or interactiveAuth : array of phrases
(Array.isArray(connectOption.interactiveAuth) && !!connectOption.interactiveAuth.length) ||
// or key defined
['password', 'agent', 'privateKeyPath'].some(
// tslint:disable-next-line triple-equals
key => connectOption[key] != undefined
Expand Down Expand Up @@ -260,7 +264,10 @@ export default class SSHClient extends RemoteClient {
finish,
stackedAnswers
) {
const answers = stackedAnswers || [];
const answers = stackedAnswers ||
// load predefined answeres if any
(Array.isArray(interactiveAuth) ? interactiveAuth : undefined) ||
[];
if (answers.length < prompts.length) {
config
.askForPasswd(
Expand Down Expand Up @@ -299,15 +306,15 @@ export default class SSHClient extends RemoteClient {
// keepaliveInterval: 1000 * 600, // 10 mins
// keepaliveInterval: 1000 * 1800, // 30 mins
keepaliveCountMax: 2, // x2 original
// keepaliveCountMax: 3, // x3
// keepaliveCountMax: 3, // x3
// keepaliveCountMax: 6, // x6
readyTimeout: interactiveAuth
? Math.max(60 * 1000, connectTimeout || 0) // 60 secs, original
// ? Math.max(1800 * 1000, connectTimeout || 0) // 30 mins
// ? Math.max(10800 * 1000, connectTimeout || 0) // 180 mins
: connectTimeout,
...option,
tryKeyboard: interactiveAuth,
tryKeyboard: !!interactiveAuth,
});
});
}
Expand Down
6 changes: 5 additions & 1 deletion src/modules/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ const configScheme = {
agent: nullable(Joi.string()),
privateKeyPath: nullable(Joi.string()),
passphrase: nullable(Joi.string().allow(true)),
interactiveAuth: Joi.boolean(),
interactiveAuth: Joi.alternatives([
Joi.boolean(),
Joi.array()
.items(Joi.string()),
]).optional(),
algorithms: Joi.any(),
sshConfigPath: Joi.string(),
sshCustomParams: Joi.string(),
Expand Down
22 changes: 16 additions & 6 deletions src/modules/serviceManager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,24 @@ const serviceManager = new Trie<FileService>(

function maskConfig(config) {
const copy = {};
const privated = ['username', 'password', 'passphrase'];
const MASK = '******';
Object.keys(config).forEach(key => {
const configValue = config[key];
// tslint:disable-next-line triple-equals
if (privated.indexOf(key) !== -1 && configValue != undefined) {
copy[key] = '******';
} else {
copy[key] = configValue;
switch (key) {
case 'username':
case 'password':
case 'passphrase':
copy[key] = MASK;
break;
case 'interactiveAuth':
if (Array.isArray(configValue)) {
copy[key] = configValue.map(phrase => MASK);
} else {
copy[key] = configValue;
}
break;
default:
copy[key] = configValue;
}
});
return copy;
Expand Down
62 changes: 33 additions & 29 deletions test/config.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ const configScheme = {
agent: nullable(Joi.string()),
privateKeyPath: nullable(Joi.string()),
passphrase: nullable(Joi.string().allow(true)),
interactiveAuth: Joi.boolean().optional(),
interactiveAuth: Joi.alternatives([
Joi.boolean(),
Joi.array()
.items(Joi.string()),
]).optional(),

secure: Joi.any().valid(true, false, 'control', 'implicit').optional(),
secureOptions: nullable(Joi.object()),
Expand Down Expand Up @@ -49,21 +53,21 @@ describe("validation config", () => {
privateKeyPath: null,
passive: false,
interactiveAuth: false,

remotePath: '/',
uploadOnSave: false,

useTempFile: false,
openSsh: false,

syncMode: 'update',

watcher: {
files: false,
autoUpload: false,
autoDelete: false,
},

ignore: [
'**/.vscode',
'**/.git',
Expand All @@ -83,13 +87,13 @@ describe("validation config", () => {
port: 22,
username: 'username',
protocol: 'sftp',

remotePath: '/',

syncMode: 'update',

watcher: {},

ignore: [
'**/.vscode',
'**/.git',
Expand Down Expand Up @@ -118,21 +122,21 @@ describe("validation config", () => {
protocol: 'unknown',
passive: false,
interactiveAuth: false,

remotePath: '/',
uploadOnSave: false,

useTempFile: false,
openSsh: false,

syncMode: 'update',

watcher: {
files: false,
autoUpload: false,
autoDelete: false,
},

ignore: [
'**/.vscode',
'**/.git',
Expand All @@ -154,21 +158,21 @@ describe("validation config", () => {
protocol: 'sftp',
passive: false,
interactiveAuth: false,

remotePath: '/',
uploadOnSave: false,

useTempFile: false,
openSsh: false,

syncMode: 'update',

watcher: {
files: false,
autoUpload: false,
autoDelete: false,
},

ignore: [
'**/.vscode',
'**/.git',
Expand Down Expand Up @@ -214,21 +218,21 @@ describe("validation config", () => {
protocol: 'sftp',
passive: false,
interactiveAuth: false,

remotePath: '/',
uploadOnSave: false,

useTempFile: false,
openSsh: false,

syncMode: 'update',

watcher: {
files: false,
autoUpload: false,
autoDelete: false,
},

ignore: [
1,
'**/.git',
Expand Down Expand Up @@ -260,18 +264,18 @@ describe("validation config", () => {

remotePath: '/',
uploadOnSave: false,

useTempFile: false,
openSsh: false,

syncMode: 'update',

watcher: {
files: false,
autoUpload: false,
autoDelete: false,
},

ignore: [
'**/.git',
'**/.DS_Store',
Expand Down