diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 21609432a4..4cd61ebead 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -990,6 +990,7 @@ export default class MainBackground { this.sendStateProvider = new SendStateProvider(this.stateProvider); this.sendService = new SendService( + this.accountService, this.keyService, this.i18nService, this.keyGenerationService, diff --git a/apps/cli/src/oss-serve-configurator.ts b/apps/cli/src/oss-serve-configurator.ts index 3c80d12af2..ccc2f3705b 100644 --- a/apps/cli/src/oss-serve-configurator.ts +++ b/apps/cli/src/oss-serve-configurator.ts @@ -211,6 +211,7 @@ export class OssServeConfigurator { this.serviceContainer.sendService, this.serviceContainer.sendApiService, this.serviceContainer.environmentService, + this.serviceContainer.accountService, ); } diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index d13d251bce..26d07b774b 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -552,6 +552,7 @@ export class ServiceContainer { this.sendStateProvider = new SendStateProvider(this.stateProvider); this.sendService = new SendService( + this.accountService, this.keyService, this.i18nService, this.keyGenerationService, diff --git a/apps/cli/src/tools/send/commands/create.command.ts b/apps/cli/src/tools/send/commands/create.command.ts index d4f544d39b..7803f6f94d 100644 --- a/apps/cli/src/tools/send/commands/create.command.ts +++ b/apps/cli/src/tools/send/commands/create.command.ts @@ -6,6 +6,7 @@ import * as path from "path"; import { firstValueFrom, switchMap } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; @@ -142,7 +143,8 @@ export class SendCreateCommand { await this.sendApiService.save([encSend, fileData]); const newSend = await this.sendService.getFromState(encSend.id); - const decSend = await newSend.decrypt(); + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + const decSend = await newSend.decrypt(activeUserId); const env = await firstValueFrom(this.environmentService.environment$); const res = new SendResponse(decSend, env.getWebVaultUrl()); return Response.success(res); diff --git a/apps/cli/src/tools/send/commands/edit.command.ts b/apps/cli/src/tools/send/commands/edit.command.ts index 09f89041cc..bf53c8a5cb 100644 --- a/apps/cli/src/tools/send/commands/edit.command.ts +++ b/apps/cli/src/tools/send/commands/edit.command.ts @@ -3,6 +3,7 @@ import { firstValueFrom } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; @@ -83,7 +84,8 @@ export class SendEditCommand { return Response.error("Premium status is required to use this feature."); } - let sendView = await send.decrypt(); + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + let sendView = await send.decrypt(activeUserId); sendView = SendResponse.toView(req, sendView); try { diff --git a/apps/cli/src/tools/send/commands/get.command.ts b/apps/cli/src/tools/send/commands/get.command.ts index 2d6cc93c78..d524873349 100644 --- a/apps/cli/src/tools/send/commands/get.command.ts +++ b/apps/cli/src/tools/send/commands/get.command.ts @@ -12,6 +12,7 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; +import { isGuid } from "@bitwarden/guid"; import { DownloadCommand } from "../../../commands/download.command"; import { Response } from "../../../models/response"; @@ -74,13 +75,13 @@ export class SendGetCommand extends DownloadCommand { } private async getSendView(id: string): Promise { - if (Utils.isGuid(id)) { + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + if (isGuid(id)) { const send = await this.sendService.getFromState(id); if (send != null) { - return await send.decrypt(); + return await send.decrypt(activeUserId); } } else if (id.trim() !== "") { - const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); let sends = await this.sendService.getAllDecryptedFromState(activeUserId); sends = this.searchService.searchSends(sends, id); if (sends.length > 1) { diff --git a/apps/cli/src/tools/send/commands/remove-password.command.ts b/apps/cli/src/tools/send/commands/remove-password.command.ts index 4f7add366b..74676d84a7 100644 --- a/apps/cli/src/tools/send/commands/remove-password.command.ts +++ b/apps/cli/src/tools/send/commands/remove-password.command.ts @@ -2,6 +2,8 @@ // @ts-strict-ignore import { firstValueFrom } from "rxjs"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { SendService } from "@bitwarden/common/tools/send/services//send.service.abstraction"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; @@ -14,6 +16,7 @@ export class SendRemovePasswordCommand { private sendService: SendService, private sendApiService: SendApiService, private environmentService: EnvironmentService, + private accountService: AccountService, ) {} async run(id: string) { @@ -21,7 +24,8 @@ export class SendRemovePasswordCommand { await this.sendApiService.removePassword(id); const updatedSend = await firstValueFrom(this.sendService.get$(id)); - const decSend = await updatedSend.decrypt(); + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + const decSend = await updatedSend.decrypt(activeUserId); const env = await firstValueFrom(this.environmentService.environment$); const webVaultUrl = env.getWebVaultUrl(); const res = new SendResponse(decSend, webVaultUrl); diff --git a/apps/cli/src/tools/send/send.program.ts b/apps/cli/src/tools/send/send.program.ts index 2ea73f8c5c..6c643e04cd 100644 --- a/apps/cli/src/tools/send/send.program.ts +++ b/apps/cli/src/tools/send/send.program.ts @@ -297,6 +297,7 @@ export class SendProgram extends BaseProgram { this.serviceContainer.sendService, this.serviceContainer.sendApiService, this.serviceContainer.environmentService, + this.serviceContainer.accountService, ); const response = await cmd.run(id); this.processResponse(response); diff --git a/apps/desktop/src/app/tools/send/add-edit.component.ts b/apps/desktop/src/app/tools/send/add-edit.component.ts index 025bab6653..bee4f920ed 100644 --- a/apps/desktop/src/app/tools/send/add-edit.component.ts +++ b/apps/desktop/src/app/tools/send/add-edit.component.ts @@ -3,11 +3,13 @@ import { CommonModule, DatePipe } from "@angular/common"; import { Component } from "@angular/core"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms"; +import { firstValueFrom } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/tools/send/add-edit.component"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -63,7 +65,8 @@ export class AddEditComponent extends BaseAddEditComponent { async refresh() { const send = await this.loadSend(); - this.send = await send.decrypt(); + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + this.send = await send.decrypt(userId); this.updateFormValues(); this.hasPassword = this.send.password != null && this.send.password.trim() !== ""; } diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index c66c74a3ea..d263e493b8 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -791,6 +791,7 @@ const safeProviders: SafeProvider[] = [ provide: InternalSendService, useClass: SendService, deps: [ + AccountServiceAbstraction, KeyService, I18nServiceAbstraction, KeyGenerationService, diff --git a/libs/angular/src/tools/send/add-edit.component.ts b/libs/angular/src/tools/send/add-edit.component.ts index 221b751528..f87b5f9bf8 100644 --- a/libs/angular/src/tools/send/add-edit.component.ts +++ b/libs/angular/src/tools/send/add-edit.component.ts @@ -260,12 +260,19 @@ export class AddEditComponent implements OnInit, OnDestroy { }); if (this.editMode) { - this.sendService - .get$(this.sendId) + this.accountService.activeAccount$ .pipe( - //Promise.reject will complete the BehaviourSubject, if desktop starts relying only on BehaviourSubject, this should be changed. - concatMap((s) => - s instanceof Send ? s.decrypt() : Promise.reject(new Error("Failed to load send.")), + getUserId, + switchMap((userId) => + this.sendService + .get$(this.sendId) + .pipe( + concatMap((s) => + s instanceof Send + ? s.decrypt(userId) + : Promise.reject(new Error("Failed to load send.")), + ), + ), ), takeUntil(this.destroy$), ) diff --git a/libs/common/src/tools/send/models/domain/send.spec.ts b/libs/common/src/tools/send/models/domain/send.spec.ts index e9b0ae7b3b..d465aa9792 100644 --- a/libs/common/src/tools/send/models/domain/send.spec.ts +++ b/libs/common/src/tools/send/models/domain/send.spec.ts @@ -1,5 +1,7 @@ import { mock } from "jest-mock-extended"; +import { of } from "rxjs"; +import { emptyGuid, UserId } from "@bitwarden/common/types/guid"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports import { KeyService } from "@bitwarden/key-management"; @@ -97,6 +99,7 @@ describe("Send", () => { const text = mock(); text.decrypt.mockResolvedValue("textView" as any); const userKey = new SymmetricCryptoKey(new Uint8Array(32)) as UserKey; + const userId = emptyGuid as UserId; const send = new Send(); send.id = "id"; @@ -120,11 +123,11 @@ describe("Send", () => { .calledWith(send.key, userKey) .mockResolvedValue(makeStaticByteArray(32)); keyService.makeSendKey.mockResolvedValue("cryptoKey" as any); - keyService.getUserKey.mockResolvedValue(userKey); + keyService.userKey$.calledWith(userId).mockReturnValue(of(userKey)); (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); - const view = await send.decrypt(); + const view = await send.decrypt(userId); expect(text.decrypt).toHaveBeenNthCalledWith(1, "cryptoKey"); expect(send.name.decrypt).toHaveBeenNthCalledWith( diff --git a/libs/common/src/tools/send/models/domain/send.ts b/libs/common/src/tools/send/models/domain/send.ts index 48057aedd2..48129d4314 100644 --- a/libs/common/src/tools/send/models/domain/send.ts +++ b/libs/common/src/tools/send/models/domain/send.ts @@ -1,7 +1,10 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { firstValueFrom } from "rxjs"; import { Jsonify } from "type-fest"; +import { UserId } from "@bitwarden/common/types/guid"; + import { EncString } from "../../../../key-management/crypto/models/enc-string"; import { Utils } from "../../../../platform/misc/utils"; import Domain from "../../../../platform/models/domain/domain-base"; @@ -73,22 +76,18 @@ export class Send extends Domain { } } - async decrypt(): Promise { - const model = new SendView(this); + async decrypt(userId: UserId): Promise { + if (!userId) { + throw new Error("User ID must not be null or undefined"); + } + const model = new SendView(this); const keyService = Utils.getContainerService().getKeyService(); const encryptService = Utils.getContainerService().getEncryptService(); - - try { - const sendKeyEncryptionKey = await keyService.getUserKey(); - // model.key is a seed used to derive a key, not a SymmetricCryptoKey - model.key = await encryptService.decryptBytes(this.key, sendKeyEncryptionKey); - model.cryptoKey = await keyService.makeSendKey(model.key); - // FIXME: Remove when updating file. Eslint update - // eslint-disable-next-line @typescript-eslint/no-unused-vars - } catch (e) { - // TODO: error? - } + const sendKeyEncryptionKey = await firstValueFrom(keyService.userKey$(userId)); + // model.key is a seed used to derive a key, not a SymmetricCryptoKey + model.key = await encryptService.decryptBytes(this.key, sendKeyEncryptionKey); + model.cryptoKey = await keyService.makeSendKey(model.key); await this.decryptObj(this, model, ["name", "notes"], null, model.cryptoKey); diff --git a/libs/common/src/tools/send/services/send.service.spec.ts b/libs/common/src/tools/send/services/send.service.spec.ts index 8b080089c3..96fb2f43c8 100644 --- a/libs/common/src/tools/send/services/send.service.spec.ts +++ b/libs/common/src/tools/send/services/send.service.spec.ts @@ -86,6 +86,7 @@ describe("SendService", () => { decryptedState.nextState([testSendViewData("1", "Test Send")]); sendService = new SendService( + accountService, keyService, i18nService, keyGenerationService, diff --git a/libs/common/src/tools/send/services/send.service.ts b/libs/common/src/tools/send/services/send.service.ts index 2664b0d435..810dbc05a2 100644 --- a/libs/common/src/tools/send/services/send.service.ts +++ b/libs/common/src/tools/send/services/send.service.ts @@ -2,6 +2,7 @@ // @ts-strict-ignore import { Observable, concatMap, distinctUntilChanged, firstValueFrom, map } from "rxjs"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports import { PBKDF2KdfConfig, KeyService } from "@bitwarden/key-management"; @@ -35,12 +36,16 @@ export class SendService implements InternalSendServiceAbstraction { map(([, record]) => Object.values(record || {}).map((data) => new Send(data))), ); sendViews$ = this.stateProvider.encryptedState$.pipe( - concatMap(([, record]) => - this.decryptSends(Object.values(record || {}).map((data) => new Send(data))), + concatMap(([userId, record]) => + this.decryptSends( + Object.values(record || {}).map((data) => new Send(data)), + userId, + ), ), ); constructor( + private accountService: AccountService, private keyService: KeyService, private i18nService: I18nService, private keyGenerationService: KeyGenerationService, @@ -89,8 +94,9 @@ export class SendService implements InternalSendServiceAbstraction { ); send.password = passwordKey.keyB64; } + const userId = (await firstValueFrom(this.accountService.activeAccount$)).id; if (userKey == null) { - userKey = await this.keyService.getUserKey(); + userKey = await firstValueFrom(this.keyService.userKey$(userId)); } // Key is not a SymmetricCryptoKey, but key material used to derive the cryptoKey send.key = await this.encryptService.encryptBytes(model.key, userKey); @@ -111,11 +117,12 @@ export class SendService implements InternalSendServiceAbstraction { model.file.fileName, file, model.cryptoKey, + userId, ); send.file.fileName = name; fileData = data; } else { - fileData = await this.parseFile(send, file, model.cryptoKey); + fileData = await this.parseFile(send, file, model.cryptoKey, userId); } } } @@ -208,6 +215,9 @@ export class SendService implements InternalSendServiceAbstraction { } async getAllDecryptedFromState(userId: UserId): Promise { + if (!userId) { + throw new Error("User ID must not be null or undefined"); + } let decSends = await this.stateProvider.getDecryptedSends(); if (decSends != null) { return decSends; @@ -222,7 +232,7 @@ export class SendService implements InternalSendServiceAbstraction { const promises: Promise[] = []; const sends = await this.getAll(); sends.forEach((send) => { - promises.push(send.decrypt().then((f) => decSends.push(f))); + promises.push(send.decrypt(userId).then((f) => decSends.push(f))); }); await Promise.all(promises); @@ -311,7 +321,12 @@ export class SendService implements InternalSendServiceAbstraction { return requests; } - private parseFile(send: Send, file: File, key: SymmetricCryptoKey): Promise { + private parseFile( + send: Send, + file: File, + key: SymmetricCryptoKey, + userId: UserId, + ): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsArrayBuffer(file); @@ -321,6 +336,7 @@ export class SendService implements InternalSendServiceAbstraction { file.name, evt.target.result as ArrayBuffer, key, + userId, ); send.file.fileName = name; resolve(data); @@ -338,17 +354,18 @@ export class SendService implements InternalSendServiceAbstraction { fileName: string, data: ArrayBuffer, key: SymmetricCryptoKey, + userId: UserId, ): Promise<[EncString, EncArrayBuffer]> { if (key == null) { - key = await this.keyService.getUserKey(); + key = await firstValueFrom(this.keyService.userKey$(userId)); } const encFileName = await this.encryptService.encryptString(fileName, key); const encFileData = await this.encryptService.encryptFileData(new Uint8Array(data), key); return [encFileName, encFileData]; } - private async decryptSends(sends: Send[]) { - const decryptSendPromises = sends.map((s) => s.decrypt()); + private async decryptSends(sends: Send[], userId: UserId) { + const decryptSendPromises = sends.map((s) => s.decrypt(userId)); const decryptedSends = await Promise.all(decryptSendPromises); decryptedSends.sort(Utils.getSortFunction(this.i18nService, "name")); diff --git a/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts b/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts index 14a16211de..76965a364e 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts @@ -88,7 +88,7 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { for (const c of results.items) { const cipher = CipherWithIdExport.toDomain(c); - // reset ids incase they were set for some reason + // reset ids in case they were set for some reason cipher.id = null; cipher.organizationId = this.organizationId; cipher.collectionIds = null; @@ -131,7 +131,7 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer { results.items.forEach((c) => { const cipher = CipherWithIdExport.toView(c); - // reset ids incase they were set for some reason + // reset ids in case they were set for some reason cipher.id = null; cipher.organizationId = null; cipher.collectionIds = null; diff --git a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.spec.ts b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.spec.ts index dfdcef5173..46c8ef7976 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.spec.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.spec.ts @@ -9,7 +9,6 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { emptyGuid, OrganizationId } from "@bitwarden/common/types/guid"; import { OrgKey, UserKey } from "@bitwarden/common/types/key"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { newGuid } from "@bitwarden/guid"; import { KdfType, KeyService } from "@bitwarden/key-management"; import { UserId } from "@bitwarden/user-core"; @@ -41,7 +40,7 @@ describe("BitwardenPasswordProtectedImporter", () => { accountService = mock(); accountService.activeAccount$ = of({ - id: newGuid() as UserId, + id: emptyGuid as UserId, email: "test@example.com", emailVerified: true, name: "Test User", @@ -52,8 +51,8 @@ describe("BitwardenPasswordProtectedImporter", () => { The key values below are never read, empty objects are cast as types for compilation type checking only. Tests specific to key contents are in key-service.spec.ts */ - const mockOrgKey = {} as unknown as OrgKey; - const mockUserKey = {} as unknown as UserKey; + const mockOrgKey = {} as OrgKey; + const mockUserKey = {} as UserKey; keyService.orgKeys$.mockImplementation(() => of({ [mockOrgId]: mockOrgKey } as Record), @@ -99,7 +98,7 @@ describe("BitwardenPasswordProtectedImporter", () => { beforeEach(() => { accountService.activeAccount$ = of({ - id: newGuid() as UserId, + id: emptyGuid as UserId, email: "test@example.com", emailVerified: true, name: "Test User", diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts index 4050ae9fb4..c17490ed4a 100644 --- a/libs/importer/src/services/import.service.ts +++ b/libs/importer/src/services/import.service.ts @@ -10,6 +10,7 @@ import { CollectionView, } from "@bitwarden/admin-console/common"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { DeviceType } from "@bitwarden/common/enums"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; @@ -21,7 +22,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SemanticLogger } from "@bitwarden/common/tools/log"; import { SystemServiceProvider } from "@bitwarden/common/tools/providers"; -import { OrganizationId } from "@bitwarden/common/types/guid"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType, toCipherTypeName } from "@bitwarden/common/vault/enums"; @@ -238,10 +239,11 @@ export class ImportService implements ImportServiceAbstraction { try { await this.setImportTarget(importResult, organizationId, selectedImportTarget); + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); if (organizationId != null) { - await this.handleOrganizationalImport(importResult, organizationId); + await this.handleOrganizationalImport(importResult, organizationId, userId); } else { - await this.handleIndividualImport(importResult); + await this.handleIndividualImport(importResult, userId); } } catch (error) { const errorResponse = new ErrorResponse(error, 400); @@ -419,16 +421,14 @@ export class ImportService implements ImportServiceAbstraction { } } - private async handleIndividualImport(importResult: ImportResult) { + private async handleIndividualImport(importResult: ImportResult, userId: UserId) { const request = new ImportCiphersRequest(); - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); for (let i = 0; i < importResult.ciphers.length; i++) { - const c = await this.cipherService.encrypt(importResult.ciphers[i], activeUserId); + const c = await this.cipherService.encrypt(importResult.ciphers[i], userId); request.ciphers.push(new CipherRequest(c)); } - const userKey = await this.keyService.getUserKey(activeUserId); + const userKey = await firstValueFrom(this.keyService.userKey$(userId)); + if (importResult.folders != null) { for (let i = 0; i < importResult.folders.length; i++) { const f = await this.folderService.encrypt(importResult.folders[i], userKey); @@ -446,20 +446,18 @@ export class ImportService implements ImportServiceAbstraction { private async handleOrganizationalImport( importResult: ImportResult, organizationId: OrganizationId, + userId: UserId, ) { const request = new ImportOrganizationCiphersRequest(); - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); for (let i = 0; i < importResult.ciphers.length; i++) { importResult.ciphers[i].organizationId = organizationId; - const c = await this.cipherService.encrypt(importResult.ciphers[i], activeUserId); + const c = await this.cipherService.encrypt(importResult.ciphers[i], userId); request.ciphers.push(new CipherRequest(c)); } if (importResult.collections != null) { for (let i = 0; i < importResult.collections.length; i++) { importResult.collections[i].organizationId = organizationId; - const c = await this.collectionService.encrypt(importResult.collections[i], activeUserId); + const c = await this.collectionService.encrypt(importResult.collections[i], userId); request.collections.push(new CollectionWithIdRequest(c)); } } diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts index a2df4ec27d..df31783539 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts @@ -12,7 +12,7 @@ import { import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { CipherWithIdExport } from "@bitwarden/common/models/export/cipher-with-ids.export"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { CipherId, UserId } from "@bitwarden/common/types/guid"; +import { CipherId, emptyGuid, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums"; @@ -179,7 +179,7 @@ describe("VaultExportService", () => { let restrictedItemTypesService: Partial; let fetchMock: jest.Mock; - const userId = "" as UserId; + const userId = emptyGuid as UserId; beforeEach(() => { cryptoFunctionService = mock(); diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts index e51c9543bb..e7a97801e0 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts @@ -201,6 +201,10 @@ export class IndividualVaultExportService } private async getEncryptedExport(activeUserId: UserId): Promise { + if (!activeUserId) { + throw new Error("User ID must not be null or undefined"); + } + let folders: Folder[] = []; let ciphers: Cipher[] = []; const promises = []; @@ -225,7 +229,7 @@ export class IndividualVaultExportService await Promise.all(promises); - const userKey = await this.keyService.getUserKey(activeUserId); + const userKey = await firstValueFrom(this.keyService.userKey$(activeUserId)); const encKeyValidation = await this.encryptService.encryptString(Utils.newGuid(), userKey); const jsonDoc: BitwardenEncryptedIndividualJsonExport = { diff --git a/libs/tools/generator/extensions/history/src/legacy-password-history-decryptor.ts b/libs/tools/generator/extensions/history/src/legacy-password-history-decryptor.ts index 91998589bd..c0c32824ce 100644 --- a/libs/tools/generator/extensions/history/src/legacy-password-history-decryptor.ts +++ b/libs/tools/generator/extensions/history/src/legacy-password-history-decryptor.ts @@ -1,3 +1,5 @@ +import { firstValueFrom } from "rxjs"; + import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { UserId } from "@bitwarden/common/types/guid"; @@ -15,7 +17,11 @@ export class LegacyPasswordHistoryDecryptor { /** Decrypts a password history. */ async decrypt(history: GeneratedPasswordHistory[]): Promise { - const key = await this.keyService.getUserKey(this.userId); + const key = await firstValueFrom(this.keyService.userKey$(this.userId)); + + if (key == undefined) { + throw new Error("No user key found for decryption"); + } const promises = (history ?? []).map(async (item) => { const encrypted = new EncString(item.password); diff --git a/libs/tools/send/send-ui/src/send-form/services/default-send-form.service.ts b/libs/tools/send/send-ui/src/send-form/services/default-send-form.service.ts index ab8d7bae9f..1eb9e864fb 100644 --- a/libs/tools/send/send-ui/src/send-form/services/default-send-form.service.ts +++ b/libs/tools/send/send-ui/src/send-form/services/default-send-form.service.ts @@ -1,7 +1,10 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { inject, Injectable } from "@angular/core"; +import { firstValueFrom } from "rxjs"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { Send } from "@bitwarden/common/tools/send/models/domain/send"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; @@ -12,11 +15,13 @@ import { SendFormService } from "../abstractions/send-form.service"; @Injectable() export class DefaultSendFormService implements SendFormService { + private accountService = inject(AccountService); private sendApiService: SendApiService = inject(SendApiService); private sendService = inject(SendService); async decryptSend(send: Send): Promise { - return await send.decrypt(); + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + return await send.decrypt(userId); } async saveSend(send: SendView, file: File | ArrayBuffer, config: SendFormConfig) {