From 88cbd7b1060f71a74f2a322670516265e9205ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Boillot?= Date: Wed, 20 Apr 2022 09:34:06 +0200 Subject: [PATCH] Feature/output different format (#18) * Add different output formats * Run lint * Remove debug log --- src/index.ts | 40 +++++++++++++++++------ src/middleware/getPasswords.ts | 56 ++++++++++++++++++++------------ src/middleware/getSecureNotes.ts | 6 ++-- 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/src/index.ts b/src/index.ts index 586f64eb..f1fe7ddf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,7 @@ import { program } from 'commander'; import winston from 'winston'; import { sync } from './middleware/sync.js'; import { getNote } from './middleware/getSecureNotes.js'; -import { getOtp, getPassword } from './middleware/getPasswords.js'; +import { getOtp, getPassword, selectCredentials } from './middleware/getPasswords.js'; import { connectAndPrepare } from './database/index.js'; const debugLevel = process.argv.indexOf('--debug') !== -1 ? 'debug' : 'info'; @@ -31,16 +31,36 @@ program .command('password') .alias('p') .description('Retrieve passwords from local vault and save it in the clipboard.') - .option('--print, -p', 'Prints just the password, instead of copying it inside the clipboard') + .option( + '--output ', + 'How to print the passwords among `clipboard, password, json`. The JSON option outputs all the matching credentials.', + 'clipboard' + ) .argument('[filter]', 'Filter passwords based on their title (usually the website)') - .action(async (filter: string | null, options: { print: boolean }) => { + .action(async (filter: string | null, options: { output: string | null }) => { const { db, deviceKeys } = await connectAndPrepare(); - await getPassword({ - titleFilter: filter, - login: deviceKeys.login, - print: options.print, - db, - }); + + if (options.output === 'json') { + console.log( + JSON.stringify( + await selectCredentials({ + titleFilter: filter, + login: deviceKeys.login, + output: options.output, + db, + }), + null, + 4 + ) + ); + } else { + await getPassword({ + titleFilter: filter, + login: deviceKeys.login, + output: options.output, + db, + }); + } db.close(); }); @@ -55,7 +75,7 @@ program await getOtp({ titleFilter: filter, login: deviceKeys.login, - print: options.print, + output: options.print ? 'otp' : 'clipboard', db, }); db.close(); diff --git a/src/middleware/getPasswords.ts b/src/middleware/getPasswords.ts index be08105b..5c1e6043 100644 --- a/src/middleware/getPasswords.ts +++ b/src/middleware/getPasswords.ts @@ -8,12 +8,12 @@ import winston from 'winston'; import { decryptTransaction, getDerivate } from '../crypto/decrypt.js'; import { BackupEditTransaction, VaultCredential, AuthentifiantTransactionContent } from '../types.js'; import { askReplaceMasterPassword, getMasterPassword, setMasterPassword } from '../steps/keychainManager.js'; -import { notEmpty } from '../utils'; +import { notEmpty } from '../utils.js'; interface GetCredential { titleFilter: string | null; login: string; - print: boolean; + output: string | null; db: Database.Database; } @@ -55,7 +55,7 @@ const decryptPasswordTransactions = async ( } }; -export const selectCredential = async (params: GetCredential, onlyOtpCredentials = false): Promise => { +export const selectCredentials = async (params: GetCredential): Promise => { const { login, titleFilter, db } = params; const masterPassword = await getMasterPassword(login); @@ -91,6 +91,12 @@ export const selectCredential = async (params: GetCredential, onlyOtpCredentials ); } + return matchedCredentials; +}; + +export const selectCredential = async (params: GetCredential, onlyOtpCredentials = false): Promise => { + let matchedCredentials = await selectCredentials(params); + if (onlyOtpCredentials) { matchedCredentials = matchedCredentials.filter((credential) => credential.otpSecret); } @@ -101,7 +107,7 @@ export const selectCredential = async (params: GetCredential, onlyOtpCredentials return matchedCredentials[0]; } - const message = titleFilter + const message = params.titleFilter ? 'There are multiple results for your query, pick one:' : 'What password would you like to get?'; @@ -141,18 +147,22 @@ export const selectCredential = async (params: GetCredential, onlyOtpCredentials export const getPassword = async (params: GetCredential): Promise => { const selectedCredential = await selectCredential(params); - if (params.print) { - console.log(selectedCredential.password); - return; - } + switch (params.output || 'clipboard') { + case 'clipboard': + clipboard.default.writeSync(selectedCredential.password); + console.log(`🔓 Password for "${selectedCredential.title}" copied to clipboard!`); - clipboard.default.writeSync(selectedCredential.password); - console.log(`🔓 Password for "${selectedCredential.title}" copied to clipboard!`); - - if (selectedCredential.otpSecret) { - const token = authenticator.generate(selectedCredential.otpSecret); - const timeRemaining = authenticator.timeRemaining(); - console.log(`🔢 OTP code: ${token} \u001B[3m(expires in ${timeRemaining} seconds)\u001B[0m`); + if (selectedCredential.otpSecret) { + const token = authenticator.generate(selectedCredential.otpSecret); + const timeRemaining = authenticator.timeRemaining(); + console.log(`🔢 OTP code: ${token} \u001B[3m(expires in ${timeRemaining} seconds)\u001B[0m`); + } + break; + case 'password': + console.log(selectedCredential.password); + break; + default: + throw new Error('Unable to recognize the output mode.'); } }; @@ -162,11 +172,15 @@ export const getOtp = async (params: GetCredential): Promise => { // otpSecret can't be null because onlyOtpCredentials is set to true above // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const token = authenticator.generate(selectedCredential.otpSecret!); - if (params.print) { - console.log(token); - return; - } - const timeRemaining = authenticator.timeRemaining(); - console.log(`🔢 OTP code: ${token} \u001B[3m(expires in ${timeRemaining} seconds)\u001B[0m`); + switch (params.output || 'clipboard') { + case 'clipboard': + console.log(`🔢 OTP code: ${token} \u001B[3m(expires in ${timeRemaining} seconds)\u001B[0m`); + break; + case 'otp': + console.log(token); + break; + default: + throw new Error('Unable to recognize the output mode.'); + } }; diff --git a/src/middleware/getSecureNotes.ts b/src/middleware/getSecureNotes.ts index 9117c2ec..16097a04 100644 --- a/src/middleware/getSecureNotes.ts +++ b/src/middleware/getSecureNotes.ts @@ -1,12 +1,12 @@ import winston from 'winston'; import Database from 'better-sqlite3'; +import inquirer from 'inquirer'; +import inquirerAutocomplete from 'inquirer-autocomplete-prompt'; import { BackupEditTransaction, SecureNoteTransactionContent, VaultNote } from '../types.js'; import { decryptTransaction, getDerivate } from '../crypto/decrypt.js'; import { askReplaceMasterPassword, getMasterPassword, setMasterPassword } from '../steps/index.js'; -import inquirer from 'inquirer'; -import inquirerAutocomplete from 'inquirer-autocomplete-prompt'; -import { notEmpty } from '../utils'; +import { notEmpty } from '../utils.js'; interface GetSecureNote { titleFilter: string | null;