Skip to content

Commit

Permalink
fix(cli-repl): prompt for password when redirecting output MONGOSH-1136
Browse files Browse the repository at this point in the history
… (#2116)

* prompt for password when redirecting output

* inject promptOutput

* add tests

* feedback
  • Loading branch information
mabaasit authored Aug 13, 2024
1 parent 8eea5b3 commit f4448b0
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 6 deletions.
17 changes: 11 additions & 6 deletions packages/cli-repl/src/cli-repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ export type CliReplOptions = {
input: Readable;
/** The stream to write shell output to. */
output: Writable;
/**
* The stream to write prompt output to when requesting data from user, like password.
* Helpful when user wants to redirect the output to a file or null device.
* If not provided, the `output` stream will be used.
*/
promptOutput?: Writable;
/** The set of home directory paths used by this shell instance. */
shellHomePaths: ShellHomePaths;
/** The ordered list of paths in which to look for a global configuration file. */
Expand Down Expand Up @@ -112,6 +118,7 @@ export class CliRepl implements MongoshIOProvider {
logWriter?: MongoLogWriter;
input: Readable;
output: Writable;
promptOutput: Writable;
analyticsOptions?: AnalyticsOptions;
segmentAnalytics?: SegmentAnalytics;
toggleableAnalytics: ToggleableAnalytics = new ToggleableAnalytics();
Expand All @@ -132,6 +139,7 @@ export class CliRepl implements MongoshIOProvider {
this.cliOptions = options.shellCliOptions;
this.input = options.input;
this.output = options.output;
this.promptOutput = options.promptOutput ?? options.output;
this.analyticsOptions = options.analyticsOptions;
this.onExit = options.onExit;

Expand Down Expand Up @@ -1003,22 +1011,19 @@ export class CliRepl implements MongoshIOProvider {

/**
* Require the user to enter a password.
*
* @param {string} driverUrl - The driver URI.
* @param {DevtoolsConnectOptions} driverOptions - The driver options.
*/
async requirePassword(): Promise<string> {
const passwordPromise = askpassword({
input: this.input,
output: this.output,
output: this.promptOutput,
replacementCharacter: '*',
});
this.output.write('Enter password: ');
this.promptOutput.write('Enter password: ');
try {
try {
return (await passwordPromise).toString();
} finally {
this.output.write('\n');
this.promptOutput.write('\n');
}
} catch (error: any) {
await this._fatalError(error);
Expand Down
44 changes: 44 additions & 0 deletions packages/cli-repl/src/run.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,48 @@ describe('CLI entry point', function () {
'MongoshInvalidInputError: [COMMON-10001] Invalid URI: /'
);
});

context('prompts for password', function () {
it('requests password when user is not redirecting output', async function () {
const args = [...pathToRun, 'mongodb://amy@localhost:27017'];
const proc = childProcess.spawn(process.execPath, args, {
stdio: ['pipe'],
env: { ...process.env, TEST_USE_STDOUT_FOR_PASSWORD: '1' },
});
let stdout = '';
let promptedForPassword = false;
proc.stdout?.setEncoding('utf8').on('data', (chunk) => {
stdout += chunk;
if (stdout.includes('Enter password')) {
promptedForPassword = true;
proc.stdin?.write('\n');
}
});

const [code] = await once(proc, 'exit');
expect(code).to.equal(1);
expect(promptedForPassword).to.be.true;
});

it('requests password when user is redirecting output', async function () {
const args = [...pathToRun, 'mongodb://amy@localhost:27017'];
const proc = childProcess.spawn(process.execPath, args, {
stdio: ['pipe', 'ignore', 'pipe'],
env: { ...process.env },
});
let stderr = '';
let promptedForPassword = false;
proc.stderr?.setEncoding('utf8').on('data', (chunk) => {
stderr += chunk;
if (stderr.includes('Enter password')) {
promptedForPassword = true;
proc.stdin?.write('\n');
}
});

const [code] = await once(proc, 'exit');
expect(code).to.equal(1);
expect(promptedForPassword).to.be.true;
});
});
});
4 changes: 4 additions & 0 deletions packages/cli-repl/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ async function main() {
getCryptLibraryPaths,
input: process.stdin,
output: process.stdout,
promptOutput:
process.env.TEST_USE_STDOUT_FOR_PASSWORD || process.stdout.isTTY
? process.stdout
: process.stderr,
// Node.js 20.0.0 made p.exit(undefined) behave as p.exit(0) rather than p.exit()
onExit: (code?: number | undefined) =>
code === undefined ? process.exit() : process.exit(code),
Expand Down

0 comments on commit f4448b0

Please sign in to comment.