diff --git a/jslib b/jslib index b5b4222b32..d4c2b20a25 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit b5b4222b325a5fab7fabfd53f23de1876ffccec8 +Subproject commit d4c2b20a2594fcac1fdabf312b7289657b4af0c8 diff --git a/src/commands/login.command.ts b/src/commands/login.command.ts index 1a9f00e65d..0a08402d55 100644 --- a/src/commands/login.command.ts +++ b/src/commands/login.command.ts @@ -1,138 +1,26 @@ -import * as program from 'commander'; -import * as inquirer from 'inquirer'; - -import { TwoFactorProviderType } from 'jslib/enums/twoFactorProviderType'; - -import { AuthResult } from 'jslib/models/domain/authResult'; -import { TwoFactorEmailRequest } from 'jslib/models/request/twoFactorEmailRequest'; - import { ApiService } from 'jslib/abstractions/api.service'; import { AuthService } from 'jslib/abstractions/auth.service'; import { CryptoFunctionService } from 'jslib/abstractions/cryptoFunction.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; import { SyncService } from 'jslib/abstractions/sync.service'; -import { Response } from 'jslib/cli/models/response'; import { MessageResponse } from 'jslib/cli/models/response/messageResponse'; import { Utils } from 'jslib/misc/utils'; -export class LoginCommand { - constructor(private authService: AuthService, private apiService: ApiService, - private cryptoFunctionService: CryptoFunctionService, private syncService: SyncService) { } +import { LoginCommand as BaseLoginCommand } from 'jslib/cli/commands/login.command'; - async run(email: string, password: string, cmd: program.Command) { - if (email == null || email === '') { - const answer: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({ - type: 'input', - name: 'email', - message: 'Email address:', - }); - email = answer.email; - } - if (email == null || email.trim() === '') { - return Response.badRequest('Email address is required.'); - } - if (email.indexOf('@') === -1) { - return Response.badRequest('Email address is invalid.'); - } - - if (password == null || password === '') { - const answer: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({ - type: 'password', - name: 'password', - message: 'Master password:', - }); - password = answer.password; - } - if (password == null || password === '') { - return Response.badRequest('Master password is required.'); - } - - let twoFactorToken: string = cmd.code; - let twoFactorMethod: TwoFactorProviderType = null; - try { - if (cmd.method != null) { - twoFactorMethod = parseInt(cmd.method, null); - } - } catch (e) { - return Response.error('Invalid two-step login method.'); - } - - try { - await this.setNewSessionKey(); - let response: AuthResult = null; - if (twoFactorToken != null && twoFactorMethod != null) { - response = await this.authService.logInComplete(email, password, twoFactorMethod, - twoFactorToken, false); - } else { - response = await this.authService.logIn(email, password); - if (response.twoFactor) { - let selectedProvider: any = null; - const twoFactorProviders = this.authService.getSupportedTwoFactorProviders(null); - if (twoFactorProviders.length === 0) { - return Response.badRequest('No providers available for this client.'); - } - - if (twoFactorMethod != null) { - try { - selectedProvider = twoFactorProviders.filter((p) => p.type === twoFactorMethod)[0]; - } catch (e) { - return Response.error('Invalid two-step login method.'); - } - } - - if (selectedProvider == null) { - if (twoFactorProviders.length === 1) { - selectedProvider = twoFactorProviders[0]; - } else { - const options = twoFactorProviders.map((p) => p.name); - options.push(new inquirer.Separator()); - options.push('Cancel'); - const answer: inquirer.Answers = - await inquirer.createPromptModule({ output: process.stderr })({ - type: 'list', - name: 'method', - message: 'Two-step login method:', - choices: options, - }); - const i = options.indexOf(answer.method); - if (i === (options.length - 1)) { - return Response.error('Login failed.'); - } - selectedProvider = twoFactorProviders[i]; - } - } - - if (twoFactorToken == null && response.twoFactorProviders.size > 1 && - selectedProvider.type === TwoFactorProviderType.Email) { - const emailReq = new TwoFactorEmailRequest(this.authService.email, - this.authService.masterPasswordHash); - await this.apiService.postTwoFactorEmail(emailReq); - } - - if (twoFactorToken == null) { - const answer: inquirer.Answers = - await inquirer.createPromptModule({ output: process.stderr })({ - type: 'input', - name: 'token', - message: 'Two-step login code:', - }); - twoFactorToken = answer.token; - if (twoFactorToken == null || twoFactorToken === '') { - return Response.badRequest('Code is required.'); - } - } - - response = await this.authService.logInTwoFactor(selectedProvider.type, - twoFactorToken, false); - } - } - - if (response.twoFactor) { - return Response.error('Login failed.'); - } - - await this.syncService.fullSync(true); +export class LoginCommand extends BaseLoginCommand { + constructor(authService: AuthService, apiService: ApiService, + cryptoFunctionService: CryptoFunctionService, syncService: SyncService, + i18nService: I18nService) { + super(authService, apiService, i18nService); + this.validatedParams = async () => { + const key = await cryptoFunctionService.randomBytes(64); + process.env.BW_SESSION = Utils.fromBufferToB64(key); + }; + this.success = async () => { + await syncService.fullSync(true); const res = new MessageResponse('You are logged in!', '\n' + 'To unlock your vault, set your session key to the `BW_SESSION` environment variable. ex:\n' + '$ export BW_SESSION="' + process.env.BW_SESSION + '"\n' + @@ -140,14 +28,7 @@ export class LoginCommand { 'You can also pass the session key to any command with the `--session` option. ex:\n' + '$ bw list items --session ' + process.env.BW_SESSION); res.raw = process.env.BW_SESSION; - return Response.success(res); - } catch (e) { - return Response.error(e); - } - } - - private async setNewSessionKey() { - const key = await this.cryptoFunctionService.randomBytes(64); - process.env.BW_SESSION = Utils.fromBufferToB64(key); + return res; + }; } } diff --git a/src/commands/logout.command.ts b/src/commands/logout.command.ts deleted file mode 100644 index 1d3e7e7ae7..0000000000 --- a/src/commands/logout.command.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as program from 'commander'; - -import { AuthService } from 'jslib/abstractions/auth.service'; - -import { Response } from 'jslib/cli/models/response'; -import { MessageResponse } from 'jslib/cli/models/response/messageResponse'; - -export class LogoutCommand { - constructor(private authService: AuthService, private logoutCallback: () => Promise) { } - - async run(cmd: program.Command) { - await this.logoutCallback(); - this.authService.logOut(() => { /* Do nothing */ }); - const res = new MessageResponse('You have logged out.', null); - return Response.success(res); - } -} diff --git a/src/program.ts b/src/program.ts index 9e66233ae9..91f01e91ee 100644 --- a/src/program.ts +++ b/src/program.ts @@ -15,11 +15,11 @@ import { ImportCommand } from './commands/import.command'; import { ListCommand } from './commands/list.command'; import { LockCommand } from './commands/lock.command'; import { LoginCommand } from './commands/login.command'; -import { LogoutCommand } from './commands/logout.command'; import { ShareCommand } from './commands/share.command'; import { SyncCommand } from './commands/sync.command'; import { UnlockCommand } from './commands/unlock.command'; +import { LogoutCommand } from 'jslib/cli/commands/logout.command'; import { UpdateCommand } from 'jslib/cli/commands/update.command'; import { Response } from 'jslib/cli/models/response'; @@ -121,7 +121,7 @@ export class Program extends BaseProgram { .action(async (email: string, password: string, cmd: program.Command) => { await this.exitIfAuthed(); const command = new LoginCommand(this.main.authService, this.main.apiService, - this.main.cryptoFunctionService, this.main.syncService); + this.main.cryptoFunctionService, this.main.syncService, this.main.i18nService); const response = await command.run(email, password, cmd); this.processResponse(response); }); @@ -137,7 +137,8 @@ export class Program extends BaseProgram { }) .action(async (cmd) => { await this.exitIfNotAuthed(); - const command = new LogoutCommand(this.main.authService, async () => await this.main.logout()); + const command = new LogoutCommand(this.main.authService, this.main.i18nService, + async () => await this.main.logout()); const response = await command.run(cmd); this.processResponse(response); }); @@ -557,7 +558,8 @@ export class Program extends BaseProgram { writeLn('', true); }) .action(async (cmd) => { - const command = new UpdateCommand(this.main.platformUtilsService, 'cli', 'bw'); + const command = new UpdateCommand(this.main.platformUtilsService, this.main.i18nService, + 'cli', 'bw', true); const response = await command.run(cmd); this.processResponse(response); });