1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-22 03:03:43 +00:00

[PM-7604] Require target UserID for KdfConfigService (#14380)

* Require userId for KdfConfigService

* Update auth team callers

* Update tools team callers
This commit is contained in:
Thomas Avery
2025-04-29 17:25:27 -05:00
committed by GitHub
parent f39e37002b
commit d43e4757df
14 changed files with 171 additions and 142 deletions

View File

@@ -4,6 +4,7 @@ import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { UserId } from "@bitwarden/common/types/guid";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { KdfConfig, KdfConfigService, KdfType } from "@bitwarden/key-management";
@@ -17,8 +18,12 @@ export class BaseVaultExportService {
private kdfConfigService: KdfConfigService,
) {}
protected async buildPasswordExport(clearText: string, password: string): Promise<string> {
const kdfConfig: KdfConfig = await this.kdfConfigService.getKdfConfig();
protected async buildPasswordExport(
userId: UserId,
clearText: string,
password: string,
): Promise<string> {
const kdfConfig: KdfConfig = await this.kdfConfigService.getKdfConfig(userId);
const salt = Utils.fromBufferToB64(await this.cryptoFunctionService.randomBytes(16));
const key = await this.pinService.makePinKey(password, salt, kdfConfig);

View File

@@ -13,6 +13,7 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract
import { CipherWithIdExport, FolderWithIdExport } from "@bitwarden/common/models/export";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { 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";
@@ -59,19 +60,21 @@ export class IndividualVaultExportService
* @param format The format of the export
*/
async getExport(format: ExportFormat = "csv"): Promise<ExportedVault> {
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
if (format === "encrypted_json") {
return this.getEncryptedExport();
return this.getEncryptedExport(userId);
} else if (format === "zip") {
return this.getDecryptedExportZip();
return this.getDecryptedExportZip(userId);
}
return this.getDecryptedExport(format);
return this.getDecryptedExport(userId, format);
}
/** Creates a password protected export of an individiual vault (My Vault) as a JSON file
/** Creates a password protected export of an individual vault (My Vault) as a JSON file
* @param password The password to encrypt the export with
* @returns A password-protected encrypted individual vault export
*/
async getPasswordProtectedExport(password: string): Promise<ExportedVaultAsString> {
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const exportVault = await this.getExport("json");
if (exportVault.type !== "text/plain") {
@@ -80,19 +83,20 @@ export class IndividualVaultExportService
return {
type: "text/plain",
data: await this.buildPasswordExport(exportVault.data, password),
data: await this.buildPasswordExport(userId, exportVault.data, password),
fileName: ExportHelper.getFileName("", "encrypted_json"),
} as ExportedVaultAsString;
}
/** Creates a unencrypted export of an individual vault including attachments
* @param activeUserId The user ID of the user requesting the export
* @returns A unencrypted export including attachments
*/
async getDecryptedExportZip(): Promise<ExportedVaultAsBlob> {
async getDecryptedExportZip(activeUserId: UserId): Promise<ExportedVaultAsBlob> {
const zip = new JSZip();
// ciphers
const exportedVault = await this.getDecryptedExport("json");
const exportedVault = await this.getDecryptedExport(activeUserId, "json");
zip.file("data.json", exportedVault.data);
const attachmentsFolder = zip.folder("attachments");
@@ -100,8 +104,6 @@ export class IndividualVaultExportService
throw new Error("Error creating attachments folder");
}
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
// attachments
for (const cipher of await this.cipherService.getAllDecrypted(activeUserId)) {
if (
@@ -161,11 +163,13 @@ export class IndividualVaultExportService
}
}
private async getDecryptedExport(format: "json" | "csv"): Promise<ExportedVaultAsString> {
private async getDecryptedExport(
activeUserId: UserId,
format: "json" | "csv",
): Promise<ExportedVaultAsString> {
let decFolders: FolderView[] = [];
let decCiphers: CipherView[] = [];
const promises = [];
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
promises.push(
firstValueFrom(this.folderService.folderViews$(activeUserId)).then((folders) => {
@@ -196,11 +200,10 @@ export class IndividualVaultExportService
} as ExportedVaultAsString;
}
private async getEncryptedExport(): Promise<ExportedVaultAsString> {
private async getEncryptedExport(activeUserId: UserId): Promise<ExportedVaultAsString> {
let folders: Folder[] = [];
let ciphers: Cipher[] = [];
const promises = [];
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
promises.push(
firstValueFrom(this.folderService.folders$(activeUserId)).then((f) => {
@@ -216,9 +219,7 @@ export class IndividualVaultExportService
await Promise.all(promises);
const userKey = await this.keyService.getUserKeyWithLegacySupport(
await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)),
);
const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId);
const encKeyValidation = await this.encryptService.encrypt(Utils.newGuid(), userKey);
const jsonDoc: BitwardenEncryptedIndividualJsonExport = {

View File

@@ -18,7 +18,7 @@ import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/a
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { CipherWithIdExport, CollectionWithIdExport } from "@bitwarden/common/models/export";
import { Utils } from "@bitwarden/common/platform/misc/utils";
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 { CipherType } from "@bitwarden/common/vault/enums";
import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data";
@@ -67,6 +67,7 @@ export class OrganizationVaultExportService
password: string,
onlyManagedCollections: boolean,
): Promise<ExportedVaultAsString> {
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const exportVault = await this.getOrganizationExport(
organizationId,
"json",
@@ -75,7 +76,7 @@ export class OrganizationVaultExportService
return {
type: "text/plain",
data: await this.buildPasswordExport(exportVault.data, password),
data: await this.buildPasswordExport(userId, exportVault.data, password),
fileName: ExportHelper.getFileName("org", "encrypted_json"),
} as ExportedVaultAsString;
}
@@ -102,12 +103,13 @@ export class OrganizationVaultExportService
if (format === "zip") {
throw new Error("Zip export not supported for organization");
}
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
if (format === "encrypted_json") {
return {
type: "text/plain",
data: onlyManagedCollections
? await this.getEncryptedManagedExport(organizationId)
? await this.getEncryptedManagedExport(userId, organizationId)
: await this.getOrganizationEncryptedExport(organizationId),
fileName: ExportHelper.getFileName("org", "encrypted_json"),
} as ExportedVaultAsString;
@@ -116,20 +118,20 @@ export class OrganizationVaultExportService
return {
type: "text/plain",
data: onlyManagedCollections
? await this.getDecryptedManagedExport(organizationId, format)
: await this.getOrganizationDecryptedExport(organizationId, format),
? await this.getDecryptedManagedExport(userId, organizationId, format)
: await this.getOrganizationDecryptedExport(userId, organizationId, format),
fileName: ExportHelper.getFileName("org", format),
} as ExportedVaultAsString;
}
private async getOrganizationDecryptedExport(
activeUserId: UserId,
organizationId: string,
format: "json" | "csv",
): Promise<string> {
const decCollections: CollectionView[] = [];
const decCiphers: CipherView[] = [];
const promises = [];
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
promises.push(
this.apiService.getOrganizationExport(organizationId).then((exportData) => {
@@ -210,6 +212,7 @@ export class OrganizationVaultExportService
}
private async getDecryptedManagedExport(
activeUserId: UserId,
organizationId: string,
format: "json" | "csv",
): Promise<string> {
@@ -217,7 +220,6 @@ export class OrganizationVaultExportService
let allDecCiphers: CipherView[] = [];
let decCollections: CollectionView[] = [];
const promises = [];
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
promises.push(
this.collectionService.getAllDecrypted().then(async (collections) => {
@@ -245,12 +247,14 @@ export class OrganizationVaultExportService
return this.buildJsonExport(decCollections, decCiphers);
}
private async getEncryptedManagedExport(organizationId: string): Promise<string> {
private async getEncryptedManagedExport(
activeUserId: UserId,
organizationId: string,
): Promise<string> {
let encCiphers: Cipher[] = [];
let allCiphers: Cipher[] = [];
let encCollections: Collection[] = [];
const promises = [];
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
promises.push(
this.collectionService.getAll().then((collections) => {