1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 08:13:42 +00:00

[PM-24099] Tools - Remove getOrgKey from the key service (#16351)

* replace deprecated getOrgKey() method
• obtain account using `accountService.activeAccount$` and then use id property to guarentee validity of UserId
This commit is contained in:
John Harrington
2025-10-01 13:26:30 -07:00
committed by GitHub
parent 688647b2c6
commit 75253c7709
3 changed files with 75 additions and 14 deletions

View File

@@ -64,12 +64,13 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
private async parseEncrypted( private async parseEncrypted(
results: BitwardenEncryptedIndividualJsonExport | BitwardenEncryptedOrgJsonExport, results: BitwardenEncryptedIndividualJsonExport | BitwardenEncryptedOrgJsonExport,
) { ) {
const account = await firstValueFrom(this.accountService.activeAccount$);
if (results.encKeyValidation_DO_NOT_EDIT != null) { if (results.encKeyValidation_DO_NOT_EDIT != null) {
let keyForDecryption: SymmetricCryptoKey = await this.keyService.getOrgKey( const orgKeys = await firstValueFrom(this.keyService.orgKeys$(account.id));
this.organizationId, let keyForDecryption: SymmetricCryptoKey = orgKeys?.[this.organizationId];
);
if (keyForDecryption == null) { if (keyForDecryption == null) {
keyForDecryption = await this.keyService.getUserKey(); keyForDecryption = await firstValueFrom(this.keyService.userKey$(account.id));
} }
const encKeyValidation = new EncString(results.encKeyValidation_DO_NOT_EDIT); const encKeyValidation = new EncString(results.encKeyValidation_DO_NOT_EDIT);
try { try {
@@ -113,10 +114,7 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
}); });
} }
const activeUserId = await firstValueFrom( const view = await this.cipherService.decrypt(cipher, account.id);
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
const view = await this.cipherService.decrypt(cipher, activeUserId);
this.cleanupCipher(view); this.cleanupCipher(view);
this.result.ciphers.push(view); this.result.ciphers.push(view);
} }

View File

@@ -1,12 +1,17 @@
import { mock, MockProxy } from "jest-mock-extended"; import { mock, MockProxy } from "jest-mock-extended";
import { of } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils"; 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 { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { newGuid } from "@bitwarden/guid";
import { KdfType, KeyService } from "@bitwarden/key-management"; import { KdfType, KeyService } from "@bitwarden/key-management";
import { UserId } from "@bitwarden/user-core";
import { emptyAccountEncrypted } from "../spec-data/bitwarden-json/account-encrypted.json"; import { emptyAccountEncrypted } from "../spec-data/bitwarden-json/account-encrypted.json";
import { emptyUnencryptedExport } from "../spec-data/bitwarden-json/unencrypted.json"; import { emptyUnencryptedExport } from "../spec-data/bitwarden-json/unencrypted.json";
@@ -35,6 +40,36 @@ describe("BitwardenPasswordProtectedImporter", () => {
pinService = mock<PinServiceAbstraction>(); pinService = mock<PinServiceAbstraction>();
accountService = mock<AccountService>(); accountService = mock<AccountService>();
accountService.activeAccount$ = of({
id: newGuid() as UserId,
email: "test@example.com",
emailVerified: true,
name: "Test User",
});
const mockOrgId = emptyGuid as OrganizationId;
/*
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;
keyService.orgKeys$.mockImplementation(() =>
of({ [mockOrgId]: mockOrgKey } as Record<OrganizationId, OrgKey>),
);
keyService.userKey$.mockImplementation(() => of(mockUserKey));
(keyService as any).activeUserOrgKeys$ = of({
[mockOrgId]: mockOrgKey,
} as Record<OrganizationId, OrgKey>);
/*
Crypto isnt under test here; keys are just placeholders.
Decryption methods are stubbed to always return empty CipherView or string allowing OK import flow.
*/
cipherService.decrypt.mockResolvedValue({} as any);
encryptService.decryptString.mockResolvedValue("ok");
importer = new BitwardenPasswordProtectedImporter( importer = new BitwardenPasswordProtectedImporter(
keyService, keyService,
encryptService, encryptService,
@@ -62,6 +97,24 @@ describe("BitwardenPasswordProtectedImporter", () => {
jest.spyOn(BitwardenJsonImporter.prototype, "parse"); jest.spyOn(BitwardenJsonImporter.prototype, "parse");
}); });
beforeEach(() => {
accountService.activeAccount$ = of({
id: newGuid() as UserId,
email: "test@example.com",
emailVerified: true,
name: "Test User",
});
importer = new BitwardenPasswordProtectedImporter(
keyService,
encryptService,
i18nService,
cipherService,
pinService,
accountService,
promptForPassword_callback,
);
});
it("Should call BitwardenJsonImporter", async () => { it("Should call BitwardenJsonImporter", async () => {
expect((await importer.parse(emptyAccountEncrypted)).success).toEqual(true); expect((await importer.parse(emptyAccountEncrypted)).success).toEqual(true);
expect(BitwardenJsonImporter.prototype.parse).toHaveBeenCalledWith(emptyAccountEncrypted); expect(BitwardenJsonImporter.prototype.parse).toHaveBeenCalledWith(emptyAccountEncrypted);

View File

@@ -15,6 +15,7 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract
import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
import { CipherWithIdExport, CollectionWithIdExport } from "@bitwarden/common/models/export"; import { CipherWithIdExport, CollectionWithIdExport } from "@bitwarden/common/models/export";
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherType } from "@bitwarden/common/vault/enums";
@@ -22,6 +23,7 @@ import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service";
import { newGuid } from "@bitwarden/guid";
import { KdfConfigService, KeyService } from "@bitwarden/key-management"; import { KdfConfigService, KeyService } from "@bitwarden/key-management";
import { import {
@@ -112,7 +114,7 @@ export class OrganizationVaultExportService
type: "text/plain", type: "text/plain",
data: onlyManagedCollections data: onlyManagedCollections
? await this.getEncryptedManagedExport(userId, organizationId) ? await this.getEncryptedManagedExport(userId, organizationId)
: await this.getOrganizationEncryptedExport(organizationId), : await this.getOrganizationEncryptedExport(userId, organizationId),
fileName: ExportHelper.getFileName("org", "encrypted_json"), fileName: ExportHelper.getFileName("org", "encrypted_json"),
} as ExportedVaultAsString; } as ExportedVaultAsString;
} }
@@ -184,7 +186,10 @@ export class OrganizationVaultExportService
return this.buildJsonExport(decCollections, decCiphers); return this.buildJsonExport(decCollections, decCiphers);
} }
private async getOrganizationEncryptedExport(organizationId: OrganizationId): Promise<string> { private async getOrganizationEncryptedExport(
userId: UserId,
organizationId: OrganizationId,
): Promise<string> {
const collections: Collection[] = []; const collections: Collection[] = [];
const ciphers: Cipher[] = []; const ciphers: Cipher[] = [];
@@ -215,7 +220,7 @@ export class OrganizationVaultExportService
} }
}); });
} }
return this.BuildEncryptedExport(organizationId, collections, ciphers); return this.BuildEncryptedExport(userId, organizationId, collections, ciphers);
} }
private async getDecryptedManagedExport( private async getDecryptedManagedExport(
@@ -295,16 +300,21 @@ export class OrganizationVaultExportService
!this.restrictedItemTypesService.isCipherRestricted(f, restrictions), !this.restrictedItemTypesService.isCipherRestricted(f, restrictions),
); );
return this.BuildEncryptedExport(organizationId, encCollections, encCiphers); return this.BuildEncryptedExport(activeUserId, organizationId, encCollections, encCiphers);
} }
private async BuildEncryptedExport( private async BuildEncryptedExport(
activeUserId: UserId,
organizationId: OrganizationId, organizationId: OrganizationId,
collections: Collection[], collections: Collection[],
ciphers: Cipher[], ciphers: Cipher[],
): Promise<string> { ): Promise<string> {
const orgKey = await this.keyService.getOrgKey(organizationId); const orgKeys = await firstValueFrom(this.keyService.orgKeys$(activeUserId));
const encKeyValidation = await this.encryptService.encryptString(Utils.newGuid(), orgKey); const keyForEncryption: SymmetricCryptoKey = orgKeys?.[organizationId];
if (keyForEncryption == null) {
throw new Error("No encryption key found for organization");
}
const encKeyValidation = await this.encryptService.encryptString(newGuid(), keyForEncryption);
const jsonDoc: BitwardenEncryptedOrgJsonExport = { const jsonDoc: BitwardenEncryptedOrgJsonExport = {
encrypted: true, encrypted: true,