CLI: Handling of password prompt

pull/138/head^2
Laurent Cozic 2017-12-12 18:17:30 +00:00
parent 92b857d83b
commit e44975622a
5 changed files with 58 additions and 22 deletions

View File

@ -80,8 +80,8 @@ class AppGui {
await this.renderer_.renderRoot(); await this.renderer_.renderRoot();
} }
prompt(initialText = '', promptString = ':') { prompt(initialText = '', promptString = ':', options = null) {
return this.widget('statusBar').prompt(initialText, promptString); return this.widget('statusBar').prompt(initialText, promptString, options);
} }
stdoutMaxWidth() { stdoutMaxWidth() {

View File

@ -144,13 +144,15 @@ class Application extends BaseApplication {
message += ' (' + options.answers.join('/') + ')'; message += ' (' + options.answers.join('/') + ')';
} }
let answer = await this.gui().prompt('', message + ' '); let answer = await this.gui().prompt('', message + ' ', options);
if (options.type === 'boolean') { if (options.type === 'boolean') {
if (answer === null) return false; // Pressed ESCAPE if (answer === null) return false; // Pressed ESCAPE
if (!answer) answer = options.answers[0]; if (!answer) answer = options.answers[0];
let positiveIndex = options.booleanAnswerDefault == 'y' ? 0 : 1; let positiveIndex = options.booleanAnswerDefault == 'y' ? 0 : 1;
return answer.toLowerCase() === options.answers[positiveIndex].toLowerCase(); return answer.toLowerCase() === options.answers[positiveIndex].toLowerCase();
} else {
return answer;
} }
}); });
@ -275,7 +277,7 @@ class Application extends BaseApplication {
dummyGui() { dummyGui() {
return { return {
isDummy: () => { return true; }, isDummy: () => { return true; },
prompt: (initialText = '', promptString = '') => { return cliUtils.prompt(initialText, promptString); }, prompt: (initialText = '', promptString = '', options = null) => { return cliUtils.prompt(initialText, promptString, options); },
showConsole: () => {}, showConsole: () => {},
maximizeConsole: () => {}, maximizeConsole: () => {},
stdout: (text) => { console.info(text); }, stdout: (text) => { console.info(text); },

View File

@ -178,38 +178,39 @@ cliUtils.promptConfirm = function(message, answers = null) {
}); });
} }
cliUtils.promptInput = function(message) {
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise((resolve, reject) => {
rl.question(message + ' ', (answer) => {
rl.close();
resolve(answer);
});
});
}
// Note: initialText is there to have the same signature as statusBar.prompt() so that // Note: initialText is there to have the same signature as statusBar.prompt() so that
// it can be a drop-in replacement, however initialText is not used (and cannot be // it can be a drop-in replacement, however initialText is not used (and cannot be
// with readline.question?). // with readline.question?).
cliUtils.prompt = function(initialText = '', promptString = ':') { cliUtils.prompt = function(initialText = '', promptString = ':', options = null) {
if (!options) options = {};
const readline = require('readline'); const readline = require('readline');
const Writable = require('stream').Writable;
const mutableStdout = new Writable({
write: function(chunk, encoding, callback) {
if (!this.muted)
process.stdout.write(chunk, encoding);
callback();
}
});
const rl = readline.createInterface({ const rl = readline.createInterface({
input: process.stdin, input: process.stdin,
output: process.stdout output: mutableStdout,
terminal: true,
}); });
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
mutableStdout.muted = false;
rl.question(promptString, (answer) => { rl.question(promptString, (answer) => {
rl.close(); rl.close();
if (!!options.secure) this.stdout_('');
resolve(answer); resolve(answer);
}); });
mutableStdout.muted = !!options.secure;
}); });
} }

View File

@ -0,0 +1,31 @@
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('lib/locale.js');
const { Folder } = require('lib/models/folder.js');
const { importEnex } = require('lib/import-enex');
const { filename, basename } = require('lib/path-utils.js');
const { cliUtils } = require('./cli-utils.js');
class Command extends BaseCommand {
usage() {
return 'encrypt-config <command>';
}
description() {
return _('Manages encryption configuration.');
}
async action(args) {
// init
// change-password
if (args.command === 'init') {
const password = await this.prompt(_('Enter master password:'), { type: 'string', secure: true });
this.logger().info(password);
}
}
}
module.exports = Command;

View File

@ -41,6 +41,7 @@ class StatusBarWidget extends BaseWidget {
}; };
if ('cursorPosition' in options) this.promptState_.cursorPosition = options.cursorPosition; if ('cursorPosition' in options) this.promptState_.cursorPosition = options.cursorPosition;
if ('secure' in options) this.promptState_.secure = options.secure;
this.promptState_.promise = new Promise((resolve, reject) => { this.promptState_.promise = new Promise((resolve, reject) => {
this.promptState_.resolve = resolve; this.promptState_.resolve = resolve;
@ -111,6 +112,7 @@ class StatusBarWidget extends BaseWidget {
}; };
if ('cursorPosition' in this.promptState_) options.cursorPosition = this.promptState_.cursorPosition; if ('cursorPosition' in this.promptState_) options.cursorPosition = this.promptState_.cursorPosition;
if (this.promptState_.secure) options.echoChar = true;
this.inputEventEmitter_ = this.term.inputField(options, (error, input) => { this.inputEventEmitter_ = this.term.inputField(options, (error, input) => {
let resolveResult = null; let resolveResult = null;