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

Use stderr for interactive prompts #63

Merged
merged 1 commit into from
Mar 1, 2023
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
7 changes: 1 addition & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#!/usr/bin/env node
import { program } from 'commander';
import inquirer from 'inquirer';
import inquirerSearchList from 'inquirer-search-list';
import winston from 'winston';
import { Database } from 'better-sqlite3';
import { connectAndPrepare } from './database/index';
Expand All @@ -21,8 +19,6 @@ import {
reset,
} from './middleware';

import PromptConstructor = inquirer.prompts.PromptConstructor;

const debugLevel = process.argv.indexOf('--debug') !== -1 ? 'debug' : 'info';

winston.configure({
Expand All @@ -31,8 +27,6 @@ winston.configure({
transports: [new winston.transports.Console({ stderrLevels: ['error', 'debug', 'info'] })],
});

inquirer.registerPrompt('search-list', inquirerSearchList as PromptConstructor);

program.name('dcli').description('Dashlane CLI').version('1.1.0');

program.option('--debug', 'Print debug messages');
Expand All @@ -44,6 +38,7 @@ program
.action(async () => {
const { db, secrets } = await connectAndPrepare({ autoSync: false });
await sync({ db, secrets });
console.log('Successfully synced');
db.close();
});

Expand Down
28 changes: 3 additions & 25 deletions src/middleware/getPasswords.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import Database from 'better-sqlite3';
import * as clipboard from 'clipboardy';
import inquirer from 'inquirer';
import { authenticator } from 'otplib';
import winston from 'winston';
import {
AuthentifiantTransactionContent,
BackupEditTransaction,
PrintableVaultCredential,
Secrets,
VaultCredential,
} from '../types';
import { AuthentifiantTransactionContent, BackupEditTransaction, Secrets, VaultCredential } from '../types';
import { decryptTransaction } from '../crypto';
import { askCredentialChoice } from '../utils';

interface GetCredential {
filters: string[] | null;
Expand Down Expand Up @@ -105,23 +99,7 @@ export const selectCredential = async (params: GetCredential, onlyOtpCredentials
return matchedCredentials[0];
}

const message = params.filters
? 'There are multiple results for your query, pick one:'
: 'What password would you like to get?';

const { printableCredential } = await inquirer.prompt<{ printableCredential: PrintableVaultCredential }>([
{
type: 'search-list',
name: 'printableCredential',
message,
choices: matchedCredentials.map((item) => {
const printableItem = new PrintableVaultCredential(item);
return { name: printableItem.toString(), value: printableItem };
}),
},
]);

return printableCredential.vaultCredential;
return askCredentialChoice({ matchedCredentials, hasFilters: Boolean(params.filters) });
};

export const getPassword = async (params: GetCredential): Promise<void> => {
Expand Down
22 changes: 3 additions & 19 deletions src/middleware/getSecureNotes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import Database from 'better-sqlite3';
import inquirer from 'inquirer';
import winston from 'winston';
import { BackupEditTransaction, PrintableVaultNote, Secrets, SecureNoteTransactionContent, VaultNote } from '../types';
import { BackupEditTransaction, Secrets, SecureNoteTransactionContent, VaultNote } from '../types';
import { decryptTransaction } from '../crypto';
import { askSecureNoteChoice } from '../utils';

interface GetSecureNote {
titleFilter: string | null;
Expand Down Expand Up @@ -62,23 +62,7 @@ export const getNote = async (params: GetSecureNote): Promise<void> => {
} else if (matchedNotes.length === 1) {
selectedNote = matchedNotes[0];
} else {
const message = titleFilter
? 'There are multiple results for your query, pick one:'
: 'What note would you like to get?';

const { printableNote } = await inquirer.prompt<{ printableNote: PrintableVaultNote }>([
{
type: 'search-list',
name: 'printableNote',
message,
choices: matchedNotes.map((item) => {
const printableItem = new PrintableVaultNote(item);
return { name: printableItem.toString(), value: printableItem };
}),
},
]);

selectedNote = printableNote.vaultNote;
selectedNote = await askSecureNoteChoice({ matchedNotes, hasFilters: Boolean(titleFilter) });
}

console.log(selectedNote.content);
Expand Down
18 changes: 3 additions & 15 deletions src/middleware/registerDevice.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import inquirer from 'inquirer';
import winston from 'winston';
import {
CompleteDeviceRegistrationOutput,
Expand All @@ -9,6 +8,7 @@ import {
performTotpVerification,
requestDeviceRegistration,
} from '../endpoints';
import { askOtp, askToken } from '../utils';

interface RegisterDevice {
login: string;
Expand All @@ -27,25 +27,13 @@ export const registerDevice = async (params: RegisterDevice): Promise<CompleteDe
} else if (verification.find((method) => method.type === 'dashlane_authenticator')) {
({ authTicket } = await performDashlaneAuthenticatorVerification({ login }));
} else if (verification.find((method) => method.type === 'totp')) {
const { otp } = await inquirer.prompt<{ otp: number }>([
{
type: 'number',
name: 'otp',
message: 'Please enter your OTP code:',
},
]);
const otp = askOtp();
({ authTicket } = await performTotpVerification({
login,
otp: String(otp).padStart(5, '0'),
}));
} else if (verification.find((method) => method.type === 'email_token')) {
const { token } = await inquirer.prompt<{ token: number }>([
{
type: 'number',
name: 'token',
message: 'Please enter the code you received by email:',
},
]);
const token = askToken();
({ authTicket } = await performEmailTokenVerification({
login,
token: String(token).padStart(5, '0'),
Expand Down
76 changes: 72 additions & 4 deletions src/utils/dialogs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import inquirer from 'inquirer';
import inquirerSearchList from 'inquirer-search-list';
import { PrintableVaultCredential, PrintableVaultNote, VaultCredential, VaultNote } from '../types';
import PromptConstructor = inquirer.prompts.PromptConstructor;

export const prompt = inquirer.createPromptModule({ output: process.stderr });
prompt.registerPrompt('search-list', inquirerSearchList as PromptConstructor);

export const askMasterPassword = async (): Promise<string> => {
const { masterPassword } = await inquirer.prompt<{ masterPassword: string }>([
const { masterPassword } = await prompt<{ masterPassword: string }>([
{
type: 'password',
name: 'masterPassword',
Expand All @@ -12,7 +18,7 @@ export const askMasterPassword = async (): Promise<string> => {
};

export const askReplaceIncorrectMasterPassword = async () => {
const { replaceMasterPassword } = await inquirer.prompt<{ replaceMasterPassword: string }>([
const { replaceMasterPassword } = await prompt<{ replaceMasterPassword: string }>([
{
type: 'list',
name: 'replaceMasterPassword',
Expand All @@ -24,7 +30,7 @@ export const askReplaceIncorrectMasterPassword = async () => {
};

export const askIgnoreBreakingChanges = async () => {
const { ignoreBreakingChanges } = await inquirer.prompt<{ ignoreBreakingChanges: string }>([
const { ignoreBreakingChanges } = await prompt<{ ignoreBreakingChanges: string }>([
{
type: 'list',
name: 'ignoreBreakingChanges',
Expand All @@ -37,7 +43,7 @@ export const askIgnoreBreakingChanges = async () => {
};

export const askEmailAddress = async (): Promise<string> => {
const { login } = await inquirer.prompt<{ login: string }>([
const { login } = await prompt<{ login: string }>([
{
type: 'input',
name: 'login',
Expand All @@ -58,3 +64,65 @@ export const askConfirmReset = async () => {
]);
return confirmReset === 'Yes';
};

export const askCredentialChoice = async (params: { matchedCredentials: VaultCredential[]; hasFilters: boolean }) => {
const message = params.hasFilters
? 'There are multiple results for your query, pick one:'
: 'What password would you like to get?';

const response = await prompt<{ printableCredential: PrintableVaultCredential }>([
{
type: 'search-list',
name: 'printableCredential',
message,
choices: params.matchedCredentials.map((item) => {
const printableItem = new PrintableVaultCredential(item);
return { name: printableItem.toString(), value: printableItem };
}),
},
]);

return response.printableCredential.vaultCredential;
};

export const askSecureNoteChoice = async (params: { matchedNotes: VaultNote[]; hasFilters: boolean }) => {
const message = params.hasFilters
? 'There are multiple results for your query, pick one:'
: 'What note would you like to get?';

const response = await prompt<{ printableNote: PrintableVaultNote }>([
{
type: 'search-list',
name: 'printableNote',
message,
choices: params.matchedNotes.map((item) => {
const printableItem = new PrintableVaultNote(item);
return { name: printableItem.toString(), value: printableItem };
}),
},
]);

return response.printableNote.vaultNote;
};

export const askOtp = async () => {
const { otp } = await prompt<{ otp: number }>([
{
type: 'number',
name: 'otp',
message: 'Please enter your OTP code:',
},
]);
return otp;
};

export const askToken = async () => {
const { token } = await prompt<{ token: number }>([
{
type: 'number',
name: 'token',
message: 'Please enter the code you received by email:',
},
]);
return token;
};
2 changes: 2 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from './dialogs';
export * from './gotImplementation';
export * from './strings';
3 changes: 1 addition & 2 deletions src/utils.ts → src/utils/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export const parseBooleanString = (booleanString: string): boolean => {
return true;
} else if (booleanString === 'false') {
return false;
} else {
throw new Error("The provided boolean variable should be either 'true' or 'false'");
}
throw new Error("The provided boolean variable should be either 'true' or 'false'");
};