// FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { firstValueFrom, map } from "rxjs"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; import { MasterPasswordVerification } from "@bitwarden/common/auth/types/verification"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { MasterKey } from "@bitwarden/common/types/key"; import { KeyService } from "@bitwarden/key-management"; import { ConvertToKeyConnectorCommand } from "../../key-management/convert-to-key-connector.command"; import { Response } from "../../models/response"; import { MessageResponse } from "../../models/response/message.response"; import { I18nService } from "../../platform/services/i18n.service"; import { CliUtils } from "../../utils"; export class UnlockCommand { constructor( private accountService: AccountService, private masterPasswordService: InternalMasterPasswordServiceAbstraction, private keyService: KeyService, private userVerificationService: UserVerificationService, private cryptoFunctionService: CryptoFunctionService, private logService: ConsoleLogService, private keyConnectorService: KeyConnectorService, private environmentService: EnvironmentService, private organizationApiService: OrganizationApiServiceAbstraction, private logout: () => Promise, private i18nService: I18nService, ) {} async run(password: string, cmdOptions: Record) { const normalizedOptions = new Options(cmdOptions); const passwordResult = await CliUtils.getPassword(password, normalizedOptions, this.logService); if (passwordResult instanceof Response) { return passwordResult; } else { password = passwordResult; } await this.setNewSessionKey(); const [userId, email] = await firstValueFrom( this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])), ); const verification = { type: VerificationType.MasterPassword, secret: password, } as MasterPasswordVerification; let masterKey: MasterKey; try { const response = await this.userVerificationService.verifyUserByMasterPassword( verification, userId, email, ); masterKey = response.masterKey; } catch (e) { // verification failure throws return Response.error(e.message); } const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey, userId); await this.keyService.setUserKey(userKey, userId); if (await firstValueFrom(this.keyConnectorService.convertAccountRequired$)) { const convertToKeyConnectorCommand = new ConvertToKeyConnectorCommand( userId, this.keyConnectorService, this.environmentService, this.organizationApiService, this.logout, this.i18nService, ); const convertResponse = await convertToKeyConnectorCommand.run(); if (!convertResponse.success) { return convertResponse; } } return this.successResponse(); } private async setNewSessionKey() { const key = await this.cryptoFunctionService.randomBytes(64); process.env.BW_SESSION = Utils.fromBufferToB64(key); } private async successResponse() { const res = new MessageResponse( "Your vault is now unlocked!", "\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' + '> $env:BW_SESSION="' + process.env.BW_SESSION + '"\n\n' + "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); } } class Options { passwordEnv: string; passwordFile: string; constructor(passedOptions: Record) { this.passwordEnv = passedOptions?.passwordenv || passedOptions?.passwordEnv; this.passwordFile = passedOptions?.passwordfile || passedOptions?.passwordFile; } }