diff --git a/jslib b/jslib index a5476f12aa..8b26d90e74 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit a5476f12aa156f8375d137ca3ac3ddce6d4ca7cc +Subproject commit 8b26d90e742966bff3dc9e57ecb104b59e48371b diff --git a/src/commands/import.command.ts b/src/commands/import.command.ts index 3612bca8b9..d78469e841 100644 --- a/src/commands/import.command.ts +++ b/src/commands/import.command.ts @@ -1,33 +1,33 @@ import * as program from 'commander'; import * as inquirer from 'inquirer'; import { CryptoService } from 'jslib/abstractions/crypto.service'; -import { ImportOptions, ImportService } from 'jslib/abstractions/import.service'; +import { ImportService } from 'jslib/abstractions/import.service'; import { UserService } from 'jslib/abstractions/user.service'; import { Response } from '../models/response'; +import { MessageResponse } from '../models/response/messageResponse'; import { CliUtils } from '../utils'; -const writeLn = CliUtils.writeLn; - export class ImportCommand { constructor(private cryptoService: CryptoService, private userService: UserService, private importService: ImportService) { } - async list() { - const options: ImportOptions = this.importService.getOptions().sort((a, b) => { - if (a.id < b.id) { return -1; } - if (a.id > b.id) { return 1; } - return 0; - }); - writeLn('\nSupported input formats:\n'); - options.forEach((option) => { - writeLn(' ' + option.id); - }); - return Response.success(); + async run(format: string, filepath: string, password: string, cmd: program.Command): Promise { + if (cmd.formats || false) { + return this.list(); + } else { + return this.import(format, filepath, password); + } } - async run(type: string, path: string, password: string, cmd: program.Command): Promise { + private async import(format: string, filepath: string, password: string) { + if (format == null || format === '') { + return Response.badRequest('`format` was not provided.'); + } + if (filepath == null || filepath === '') { + return Response.badRequest('`filepath` was not provided.'); + } if (password == null || password === '') { const answer: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({ type: 'password', @@ -45,23 +45,39 @@ export class ImportCommand { const key = await this.cryptoService.makeKey(password, email); const keyHash = await this.cryptoService.hashPassword(password, key); const storedKeyHash = await this.cryptoService.getKeyHash(); - const importer = await this.importService.getImporter(type); + if (storedKeyHash == null || keyHash == null || storedKeyHash !== keyHash) { + return Response.badRequest('Invalid master password.'); + } + + const importer = await this.importService.getImporter(format); if (importer === null) { return Response.badRequest('Proper importer type required.'); } - if (storedKeyHash != null && keyHash != null && storedKeyHash === keyHash) { - return CliUtils.readFile(path).then(async (contents) => { - const submitResult = await this.importService.submit(importer, contents); - if (submitResult !== null) { - return Response.success(); - } else { - return Response.badRequest(submitResult.message); - } - }).catch((err) => { - return Response.badRequest(err); - }); - } else { - return Response.badRequest('Invalid master password.'); + + try { + const contents = await CliUtils.readFile(filepath); + if (contents === null || contents === '') { + return Response.badRequest('Import file was empty.'); + } + + const submitResult = await this.importService.import(importer, contents); + if (submitResult !== null) { + const res = new MessageResponse('Imported ' + filepath, null); + return Response.success(res); + } else { + return Response.badRequest(submitResult.message); + } + } catch (err) { + return Response.badRequest(err); } } + + private async list() { + const options = this.importService.importOptions.sort((a, b) => { + return a.id < b.id ? -1 : a.id > b.id ? 1 : 0; + }).map((option) => option.id).join('\n'); + const res = new MessageResponse('Supported input formats:', options); + res.raw = options; + return Response.success(res); + } } diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index f5e8edc80f..e14427ba1c 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -13,5 +13,11 @@ }, "noneFolder": { "message": "No Folder" + }, + "importFormatError": { + "message": "Data is not formatted correctly. Please check your import file and try again." + }, + "importNothingError": { + "message": "Nothing was imported." } } diff --git a/src/program.ts b/src/program.ts index a7cd3006bd..d6a362f457 100644 --- a/src/program.ts +++ b/src/program.ts @@ -391,19 +391,13 @@ export class Program { }); program - .command('import [password]') + .command('import [format] [input] [password]') .description('Import vault data from a file.') - .option('-l, --list-formats', 'List valid formats') - .on('option:list-formats', async () => { - const command = new ImportCommand(this.main.cryptoService, - this.main.userService, this.main.importService); - const response = await command.list(); - this.processResponse(response); - }) + .option('--formats', 'List formats') .on('--help', () => { writeLn('\n Examples:'); writeLn(''); - writeLn(' bw import --list-formats'); + writeLn(' bw import --formats'); writeLn(' bw import bitwardencsv ./from/source.csv'); writeLn(' bw import keepass2xml keepass_backup.xml myPassword123'); }) diff --git a/src/utils.ts b/src/utils.ts index 6063deecde..48e282482b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -28,7 +28,7 @@ export class CliUtils { p = osInput; } } else { - reject('you must specify a path'); + reject('You must specify a file path.'); } fs.readFile(p, 'utf8', (err, data) => { if (err != null) {