mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +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:
@@ -5,7 +5,7 @@ import * as http from "http";
|
|||||||
import { OptionValues } from "commander";
|
import { OptionValues } from "commander";
|
||||||
import * as inquirer from "inquirer";
|
import * as inquirer from "inquirer";
|
||||||
import Separator from "inquirer/lib/objects/separator";
|
import Separator from "inquirer/lib/objects/separator";
|
||||||
import { firstValueFrom, map, switchMap } from "rxjs";
|
import { firstValueFrom, map } from "rxjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
LoginStrategyServiceAbstraction,
|
LoginStrategyServiceAbstraction,
|
||||||
@@ -29,7 +29,6 @@ import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/ide
|
|||||||
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
|
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
|
||||||
import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request";
|
import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request";
|
||||||
import { UpdateTempPasswordRequest } from "@bitwarden/common/auth/models/request/update-temp-password.request";
|
import { UpdateTempPasswordRequest } from "@bitwarden/common/auth/models/request/update-temp-password.request";
|
||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
|
||||||
import { ClientType } from "@bitwarden/common/enums";
|
import { ClientType } from "@bitwarden/common/enums";
|
||||||
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
|
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 { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
||||||
@@ -40,6 +39,7 @@ import { Utils } from "@bitwarden/common/platform/misc/utils";
|
|||||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||||
import { KdfConfigService, KeyService } from "@bitwarden/key-management";
|
import { KdfConfigService, KeyService } from "@bitwarden/key-management";
|
||||||
@@ -367,9 +367,9 @@ export class LoginCommand {
|
|||||||
clientSecret == null
|
clientSecret == null
|
||||||
) {
|
) {
|
||||||
if (response.forcePasswordReset === ForceSetPasswordReason.AdminForcePasswordReset) {
|
if (response.forcePasswordReset === ForceSetPasswordReason.AdminForcePasswordReset) {
|
||||||
return await this.updateTempPassword();
|
return await this.updateTempPassword(response.userId);
|
||||||
} else if (response.forcePasswordReset === ForceSetPasswordReason.WeakMasterPassword) {
|
} else if (response.forcePasswordReset === ForceSetPasswordReason.WeakMasterPassword) {
|
||||||
return await this.updateWeakPassword(password);
|
return await this.updateWeakPassword(response.userId, password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,7 +431,7 @@ export class LoginCommand {
|
|||||||
return Response.success(res);
|
return Response.success(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateWeakPassword(currentPassword: string) {
|
private async updateWeakPassword(userId: UserId, currentPassword: string) {
|
||||||
// If no interaction available, alert user to use web vault
|
// If no interaction available, alert user to use web vault
|
||||||
if (!this.canInteract) {
|
if (!this.canInteract) {
|
||||||
await this.logoutCallback();
|
await this.logoutCallback();
|
||||||
@@ -448,6 +448,7 @@ export class LoginCommand {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const { newPasswordHash, newUserKey, hint } = await this.collectNewMasterPasswordDetails(
|
const { newPasswordHash, newUserKey, hint } = await this.collectNewMasterPasswordDetails(
|
||||||
|
userId,
|
||||||
"Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now.",
|
"Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now.",
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -469,7 +470,7 @@ export class LoginCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateTempPassword() {
|
private async updateTempPassword(userId: UserId) {
|
||||||
// If no interaction available, alert user to use web vault
|
// If no interaction available, alert user to use web vault
|
||||||
if (!this.canInteract) {
|
if (!this.canInteract) {
|
||||||
await this.logoutCallback();
|
await this.logoutCallback();
|
||||||
@@ -486,6 +487,7 @@ export class LoginCommand {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const { newPasswordHash, newUserKey, hint } = await this.collectNewMasterPasswordDetails(
|
const { newPasswordHash, newUserKey, hint } = await this.collectNewMasterPasswordDetails(
|
||||||
|
userId,
|
||||||
"An organization administrator recently changed your master password. In order to access the vault, you must update your master password now.",
|
"An organization administrator recently changed your master password. In order to access the vault, you must update your master password now.",
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -510,10 +512,12 @@ export class LoginCommand {
|
|||||||
* Collect new master password and hint from the CLI. The collected password
|
* Collect new master password and hint from the CLI. The collected password
|
||||||
* is validated against any applicable master password policies, a new master
|
* is validated against any applicable master password policies, a new master
|
||||||
* key is generated, and we use it to re-encrypt the user key
|
* key is generated, and we use it to re-encrypt the user key
|
||||||
|
* @param userId - User ID of the account
|
||||||
* @param prompt - Message that is displayed during the initial prompt
|
* @param prompt - Message that is displayed during the initial prompt
|
||||||
* @param error
|
* @param error
|
||||||
*/
|
*/
|
||||||
private async collectNewMasterPasswordDetails(
|
private async collectNewMasterPasswordDetails(
|
||||||
|
userId: UserId,
|
||||||
prompt: string,
|
prompt: string,
|
||||||
error?: string,
|
error?: string,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
@@ -539,11 +543,12 @@ export class LoginCommand {
|
|||||||
|
|
||||||
// Master Password Validation
|
// Master Password Validation
|
||||||
if (masterPassword == null || masterPassword === "") {
|
if (masterPassword == null || masterPassword === "") {
|
||||||
return this.collectNewMasterPasswordDetails(prompt, "Master password is required.\n");
|
return this.collectNewMasterPasswordDetails(userId, prompt, "Master password is required.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (masterPassword.length < Utils.minimumPasswordLength) {
|
if (masterPassword.length < Utils.minimumPasswordLength) {
|
||||||
return this.collectNewMasterPasswordDetails(
|
return this.collectNewMasterPasswordDetails(
|
||||||
|
userId,
|
||||||
prompt,
|
prompt,
|
||||||
`Master password must be at least ${Utils.minimumPasswordLength} characters long.\n`,
|
`Master password must be at least ${Utils.minimumPasswordLength} characters long.\n`,
|
||||||
);
|
);
|
||||||
@@ -556,10 +561,7 @@ export class LoginCommand {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const enforcedPolicyOptions = await firstValueFrom(
|
const enforcedPolicyOptions = await firstValueFrom(
|
||||||
this.accountService.activeAccount$.pipe(
|
this.policyService.masterPasswordPolicyOptions$(userId),
|
||||||
getUserId,
|
|
||||||
switchMap((userId) => this.policyService.masterPasswordPolicyOptions$(userId)),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Verify master password meets policy requirements
|
// Verify master password meets policy requirements
|
||||||
@@ -572,6 +574,7 @@ export class LoginCommand {
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return this.collectNewMasterPasswordDetails(
|
return this.collectNewMasterPasswordDetails(
|
||||||
|
userId,
|
||||||
prompt,
|
prompt,
|
||||||
"Your new master password does not meet the policy requirements.\n",
|
"Your new master password does not meet the policy requirements.\n",
|
||||||
);
|
);
|
||||||
@@ -589,6 +592,7 @@ export class LoginCommand {
|
|||||||
// Re-type Validation
|
// Re-type Validation
|
||||||
if (masterPassword !== masterPasswordRetype) {
|
if (masterPassword !== masterPasswordRetype) {
|
||||||
return this.collectNewMasterPasswordDetails(
|
return this.collectNewMasterPasswordDetails(
|
||||||
|
userId,
|
||||||
prompt,
|
prompt,
|
||||||
"Master password confirmation does not match.\n",
|
"Master password confirmation does not match.\n",
|
||||||
);
|
);
|
||||||
@@ -601,7 +605,7 @@ export class LoginCommand {
|
|||||||
message: "Master Password Hint (optional):",
|
message: "Master Password Hint (optional):",
|
||||||
});
|
});
|
||||||
const masterPasswordHint = hint.input;
|
const masterPasswordHint = hint.input;
|
||||||
const kdfConfig = await this.kdfConfigService.getKdfConfig();
|
const kdfConfig = await this.kdfConfigService.getKdfConfig(userId);
|
||||||
|
|
||||||
// Create new key and hash new password
|
// Create new key and hash new password
|
||||||
const newMasterKey = await this.keyService.makeMasterKey(
|
const newMasterKey = await this.keyService.makeMasterKey(
|
||||||
|
|||||||
@@ -310,13 +310,16 @@ export class ChangePasswordComponent
|
|||||||
newMasterKey: MasterKey,
|
newMasterKey: MasterKey,
|
||||||
newUserKey: [UserKey, EncString],
|
newUserKey: [UserKey, EncString],
|
||||||
) {
|
) {
|
||||||
const masterKey = await this.keyService.makeMasterKey(
|
const [userId, email] = await firstValueFrom(
|
||||||
this.currentMasterPassword,
|
this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])),
|
||||||
await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.email))),
|
);
|
||||||
await this.kdfConfigService.getKdfConfig(),
|
|
||||||
|
const masterKey = await this.keyService.makeMasterKey(
|
||||||
|
this.currentMasterPassword,
|
||||||
|
email,
|
||||||
|
await this.kdfConfigService.getKdfConfig(userId),
|
||||||
);
|
);
|
||||||
|
|
||||||
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
|
||||||
const newLocalKeyHash = await this.keyService.hashMasterKey(
|
const newLocalKeyHash = await this.keyService.hashMasterKey(
|
||||||
this.masterPassword,
|
this.masterPassword,
|
||||||
newMasterKey,
|
newMasterKey,
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { FormBuilder, FormControl, ValidatorFn, Validators } from "@angular/forms";
|
import { FormBuilder, FormControl, ValidatorFn, Validators } from "@angular/forms";
|
||||||
import { Subject, takeUntil } from "rxjs";
|
import { Subject, firstValueFrom, takeUntil } from "rxjs";
|
||||||
|
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
import {
|
import {
|
||||||
KdfConfigService,
|
KdfConfigService,
|
||||||
@@ -43,6 +45,7 @@ export class ChangeKdfComponent implements OnInit, OnDestroy {
|
|||||||
constructor(
|
constructor(
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private kdfConfigService: KdfConfigService,
|
private kdfConfigService: KdfConfigService,
|
||||||
|
private accountService: AccountService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
) {
|
) {
|
||||||
this.kdfOptions = [
|
this.kdfOptions = [
|
||||||
@@ -52,7 +55,8 @@ export class ChangeKdfComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
|
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
||||||
|
this.kdfConfig = await this.kdfConfigService.getKdfConfig(userId);
|
||||||
this.formGroup.get("kdf").setValue(this.kdfConfig.kdfType);
|
this.formGroup.get("kdf").setValue(this.kdfConfig.kdfType);
|
||||||
this.setFormControlValues(this.kdfConfig);
|
this.setFormControlValues(this.kdfConfig);
|
||||||
|
|
||||||
|
|||||||
@@ -83,11 +83,12 @@ export class ChangePasswordComponent implements OnInit, OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const email = await firstValueFrom(
|
const [userId, email] = await firstValueFrom(
|
||||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.kdfConfig == null) {
|
if (this.kdfConfig == null) {
|
||||||
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
|
this.kdfConfig = await this.kdfConfigService.getKdfConfig(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new master key
|
// Create new master key
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Directive } from "@angular/core";
|
import { Directive } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||||
@@ -10,6 +11,7 @@ import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/ma
|
|||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
|
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
|
||||||
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
|
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { Verification } from "@bitwarden/common/auth/types/verification";
|
import { Verification } from "@bitwarden/common/auth/types/verification";
|
||||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
@@ -96,8 +98,8 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent {
|
|||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
||||||
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
|
this.kdfConfig = await this.kdfConfigService.getKdfConfig(userId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,10 +110,11 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent imp
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setupSubmitActions(): Promise<boolean> {
|
async setupSubmitActions(): Promise<boolean> {
|
||||||
this.email = await firstValueFrom(
|
const [userId, email] = await firstValueFrom(
|
||||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])),
|
||||||
);
|
);
|
||||||
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
|
this.email = email;
|
||||||
|
this.kdfConfig = await this.kdfConfigService.getKdfConfig(userId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ export class PinService implements PinServiceAbstraction {
|
|||||||
const email = await firstValueFrom(
|
const email = await firstValueFrom(
|
||||||
this.accountService.accounts$.pipe(map((accounts) => accounts[userId].email)),
|
this.accountService.accounts$.pipe(map((accounts) => accounts[userId].email)),
|
||||||
);
|
);
|
||||||
const kdfConfig = await this.kdfConfigService.getKdfConfig();
|
const kdfConfig = await this.kdfConfigService.getKdfConfig(userId);
|
||||||
const pinKey = await this.makePinKey(pin, email, kdfConfig);
|
const pinKey = await this.makePinKey(pin, email, kdfConfig);
|
||||||
|
|
||||||
return await this.encryptService.wrapSymmetricKey(userKey, pinKey);
|
return await this.encryptService.wrapSymmetricKey(userKey, pinKey);
|
||||||
@@ -293,7 +293,7 @@ export class PinService implements PinServiceAbstraction {
|
|||||||
const email = await firstValueFrom(
|
const email = await firstValueFrom(
|
||||||
this.accountService.accounts$.pipe(map((accounts) => accounts[userId].email)),
|
this.accountService.accounts$.pipe(map((accounts) => accounts[userId].email)),
|
||||||
);
|
);
|
||||||
const kdfConfig = await this.kdfConfigService.getKdfConfig();
|
const kdfConfig = await this.kdfConfigService.getKdfConfig(userId);
|
||||||
|
|
||||||
const userKey: UserKey = await this.decryptUserKey(
|
const userKey: UserKey = await this.decryptUserKey(
|
||||||
userId,
|
userId,
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
|||||||
masterKey = await this.keyService.makeMasterKey(
|
masterKey = await this.keyService.makeMasterKey(
|
||||||
verification.secret,
|
verification.secret,
|
||||||
email,
|
email,
|
||||||
await this.kdfConfigService.getKdfConfig(),
|
await this.kdfConfigService.getKdfConfig(userId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
request.masterPasswordHash = alreadyHashed
|
request.masterPasswordHash = alreadyHashed
|
||||||
@@ -186,7 +186,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
|||||||
throw new Error("Email is required. Cannot verify user by master password.");
|
throw new Error("Email is required. Cannot verify user by master password.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const kdfConfig = await this.kdfConfigService.getKdfConfig();
|
const kdfConfig = await this.kdfConfigService.getKdfConfig(userId);
|
||||||
if (!kdfConfig) {
|
if (!kdfConfig) {
|
||||||
throw new Error("KDF config is required. Cannot verify user by master password.");
|
throw new Error("KDF config is required. Cannot verify user by master password.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ import { KdfConfig } from "../models/kdf-config";
|
|||||||
|
|
||||||
export abstract class KdfConfigService {
|
export abstract class KdfConfigService {
|
||||||
abstract setKdfConfig(userId: UserId, KdfConfig: KdfConfig): Promise<void>;
|
abstract setKdfConfig(userId: UserId, KdfConfig: KdfConfig): Promise<void>;
|
||||||
abstract getKdfConfig(): Promise<KdfConfig>;
|
abstract getKdfConfig(userId: UserId): Promise<KdfConfig>;
|
||||||
abstract getKdfConfig$(userId: UserId): Observable<KdfConfig | null>;
|
abstract getKdfConfig$(userId: UserId): Observable<KdfConfig | null>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,90 +26,94 @@ describe("KdfConfigService", () => {
|
|||||||
sutKdfConfigService = new DefaultKdfConfigService(fakeStateProvider);
|
sutKdfConfigService = new DefaultKdfConfigService(fakeStateProvider);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("setKdfConfig(): should set the PBKDF2KdfConfig config", async () => {
|
describe("setKdfConfig", () => {
|
||||||
const kdfConfig: KdfConfig = new PBKDF2KdfConfig(500_000);
|
it("sets the PBKDF2KdfConfig config", async () => {
|
||||||
await sutKdfConfigService.setKdfConfig(mockUserId, kdfConfig);
|
const kdfConfig: KdfConfig = new PBKDF2KdfConfig(500_000);
|
||||||
expect(fakeStateProvider.mock.setUserState).toHaveBeenCalledWith(
|
await sutKdfConfigService.setKdfConfig(mockUserId, kdfConfig);
|
||||||
KDF_CONFIG,
|
expect(fakeStateProvider.mock.setUserState).toHaveBeenCalledWith(
|
||||||
kdfConfig,
|
KDF_CONFIG,
|
||||||
mockUserId,
|
kdfConfig,
|
||||||
);
|
mockUserId,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets the Argon2KdfConfig config", async () => {
|
||||||
|
const kdfConfig: KdfConfig = new Argon2KdfConfig(2, 63, 3);
|
||||||
|
await sutKdfConfigService.setKdfConfig(mockUserId, kdfConfig);
|
||||||
|
expect(fakeStateProvider.mock.setUserState).toHaveBeenCalledWith(
|
||||||
|
KDF_CONFIG,
|
||||||
|
kdfConfig,
|
||||||
|
mockUserId,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws error KDF cannot be null", async () => {
|
||||||
|
try {
|
||||||
|
await sutKdfConfigService.setKdfConfig(mockUserId, null as unknown as KdfConfig);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toEqual(new Error("kdfConfig cannot be null"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws error userId cannot be null", async () => {
|
||||||
|
const kdfConfig: KdfConfig = new Argon2KdfConfig(3, 64, 4);
|
||||||
|
try {
|
||||||
|
await sutKdfConfigService.setKdfConfig(null as unknown as UserId, kdfConfig);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e).toEqual(new Error("userId cannot be null"));
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("setKdfConfig(): should set the Argon2KdfConfig config", async () => {
|
describe("getKdfConfig", () => {
|
||||||
const kdfConfig: KdfConfig = new Argon2KdfConfig(2, 63, 3);
|
it("throws error if userId is null", async () => {
|
||||||
await sutKdfConfigService.setKdfConfig(mockUserId, kdfConfig);
|
await expect(sutKdfConfigService.getKdfConfig(null as unknown as UserId)).rejects.toThrow(
|
||||||
expect(fakeStateProvider.mock.setUserState).toHaveBeenCalledWith(
|
"userId cannot be null",
|
||||||
KDF_CONFIG,
|
);
|
||||||
kdfConfig,
|
});
|
||||||
mockUserId,
|
|
||||||
);
|
it("throws if target user doesn't have a KkfConfig", async () => {
|
||||||
|
const errorMessage = "KdfConfig for user " + mockUserId + " is null";
|
||||||
|
await expect(sutKdfConfigService.getKdfConfig(mockUserId)).rejects.toThrow(errorMessage);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns KdfConfig of target user", async () => {
|
||||||
|
const kdfConfig: KdfConfig = new PBKDF2KdfConfig(500_000);
|
||||||
|
await fakeStateProvider.setUserState(KDF_CONFIG, kdfConfig, mockUserId);
|
||||||
|
await expect(sutKdfConfigService.getKdfConfig(mockUserId)).resolves.toEqual(kdfConfig);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("setKdfConfig(): should throw error KDF cannot be null", async () => {
|
describe("getKdfConfig$", () => {
|
||||||
try {
|
it("gets KdfConfig of provided user", async () => {
|
||||||
await sutKdfConfigService.setKdfConfig(mockUserId, null as unknown as KdfConfig);
|
await expect(
|
||||||
} catch (e) {
|
firstValueFrom(sutKdfConfigService.getKdfConfig$(mockUserId)),
|
||||||
expect(e).toEqual(new Error("kdfConfig cannot be null"));
|
).resolves.toBeNull();
|
||||||
}
|
const kdfConfig: KdfConfig = new PBKDF2KdfConfig(500_000);
|
||||||
});
|
await fakeStateProvider.setUserState(KDF_CONFIG, kdfConfig, mockUserId);
|
||||||
|
await expect(firstValueFrom(sutKdfConfigService.getKdfConfig$(mockUserId))).resolves.toEqual(
|
||||||
|
kdfConfig,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("setKdfConfig(): should throw error userId cannot be null", async () => {
|
it("gets KdfConfig of provided user after changed", async () => {
|
||||||
const kdfConfig: KdfConfig = new Argon2KdfConfig(3, 64, 4);
|
await expect(
|
||||||
try {
|
firstValueFrom(sutKdfConfigService.getKdfConfig$(mockUserId)),
|
||||||
await sutKdfConfigService.setKdfConfig(null as unknown as UserId, kdfConfig);
|
).resolves.toBeNull();
|
||||||
} catch (e) {
|
await fakeStateProvider.setUserState(KDF_CONFIG, new PBKDF2KdfConfig(500_000), mockUserId);
|
||||||
expect(e).toEqual(new Error("userId cannot be null"));
|
const kdfConfigChanged: KdfConfig = new PBKDF2KdfConfig(500_001);
|
||||||
}
|
await fakeStateProvider.setUserState(KDF_CONFIG, kdfConfigChanged, mockUserId);
|
||||||
});
|
await expect(firstValueFrom(sutKdfConfigService.getKdfConfig$(mockUserId))).resolves.toEqual(
|
||||||
|
kdfConfigChanged,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("getKdfConfig(): should get KdfConfig of active user", async () => {
|
it("throws error userId cannot be null", async () => {
|
||||||
const kdfConfig: KdfConfig = new PBKDF2KdfConfig(500_000);
|
try {
|
||||||
await fakeStateProvider.setUserState(KDF_CONFIG, kdfConfig, mockUserId);
|
sutKdfConfigService.getKdfConfig$(null as unknown as UserId);
|
||||||
await expect(sutKdfConfigService.getKdfConfig()).resolves.toEqual(kdfConfig);
|
} catch (e) {
|
||||||
});
|
expect(e).toEqual(new Error("userId cannot be null"));
|
||||||
|
}
|
||||||
it("getKdfConfig(): should throw error KdfConfig can only be retrieved when there is active user", async () => {
|
});
|
||||||
fakeAccountService.activeAccountSubject.next(null);
|
|
||||||
try {
|
|
||||||
await sutKdfConfigService.getKdfConfig();
|
|
||||||
} catch (e) {
|
|
||||||
expect(e).toEqual(new Error("KdfConfig can only be retrieved when there is active user"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("getKdfConfig(): should throw error KdfConfig for active user account state is null", async () => {
|
|
||||||
try {
|
|
||||||
await sutKdfConfigService.getKdfConfig();
|
|
||||||
} catch (e) {
|
|
||||||
expect(e).toEqual(new Error("KdfConfig for active user account state is null"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("getKdfConfig$(UserId): should get KdfConfig of provided user", async () => {
|
|
||||||
await expect(firstValueFrom(sutKdfConfigService.getKdfConfig$(mockUserId))).resolves.toBeNull();
|
|
||||||
const kdfConfig: KdfConfig = new PBKDF2KdfConfig(500_000);
|
|
||||||
await fakeStateProvider.setUserState(KDF_CONFIG, kdfConfig, mockUserId);
|
|
||||||
await expect(firstValueFrom(sutKdfConfigService.getKdfConfig$(mockUserId))).resolves.toEqual(
|
|
||||||
kdfConfig,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("getKdfConfig$(UserId): should get KdfConfig of provided user after changed", async () => {
|
|
||||||
await expect(firstValueFrom(sutKdfConfigService.getKdfConfig$(mockUserId))).resolves.toBeNull();
|
|
||||||
await fakeStateProvider.setUserState(KDF_CONFIG, new PBKDF2KdfConfig(500_000), mockUserId);
|
|
||||||
const kdfConfigChanged: KdfConfig = new PBKDF2KdfConfig(500_001);
|
|
||||||
await fakeStateProvider.setUserState(KDF_CONFIG, kdfConfigChanged, mockUserId);
|
|
||||||
await expect(firstValueFrom(sutKdfConfigService.getKdfConfig$(mockUserId))).resolves.toEqual(
|
|
||||||
kdfConfigChanged,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("getKdfConfig$(UserId): should throw error userId cannot be null", async () => {
|
|
||||||
try {
|
|
||||||
sutKdfConfigService.getKdfConfig$(null as unknown as UserId);
|
|
||||||
} catch (e) {
|
|
||||||
expect(e).toEqual(new Error("userId cannot be null"));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,14 +37,14 @@ export class DefaultKdfConfigService implements KdfConfigService {
|
|||||||
await this.stateProvider.setUserState(KDF_CONFIG, kdfConfig, userId);
|
await this.stateProvider.setUserState(KDF_CONFIG, kdfConfig, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getKdfConfig(): Promise<KdfConfig> {
|
async getKdfConfig(userId: UserId): Promise<KdfConfig> {
|
||||||
const userId = await firstValueFrom(this.stateProvider.activeUserId$);
|
|
||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
throw new Error("KdfConfig can only be retrieved when there is active user");
|
throw new Error("userId cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = await firstValueFrom(this.stateProvider.getUser(userId, KDF_CONFIG).state$);
|
const state = await firstValueFrom(this.stateProvider.getUser(userId, KDF_CONFIG).state$);
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
throw new Error("KdfConfig for active user account state is null");
|
throw new Error("KdfConfig for user " + userId + " is null");
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
|||||||
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
|
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
|
||||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
import { KdfConfig, KdfConfigService, KdfType } from "@bitwarden/key-management";
|
import { KdfConfig, KdfConfigService, KdfType } from "@bitwarden/key-management";
|
||||||
@@ -17,8 +18,12 @@ export class BaseVaultExportService {
|
|||||||
private kdfConfigService: KdfConfigService,
|
private kdfConfigService: KdfConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
protected async buildPasswordExport(clearText: string, password: string): Promise<string> {
|
protected async buildPasswordExport(
|
||||||
const kdfConfig: KdfConfig = await this.kdfConfigService.getKdfConfig();
|
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 salt = Utils.fromBufferToB64(await this.cryptoFunctionService.randomBytes(16));
|
||||||
const key = await this.pinService.makePinKey(password, salt, kdfConfig);
|
const key = await this.pinService.makePinKey(password, salt, kdfConfig);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract
|
|||||||
import { CipherWithIdExport, FolderWithIdExport } from "@bitwarden/common/models/export";
|
import { CipherWithIdExport, FolderWithIdExport } from "@bitwarden/common/models/export";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
|
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 { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
@@ -59,19 +60,21 @@ export class IndividualVaultExportService
|
|||||||
* @param format The format of the export
|
* @param format The format of the export
|
||||||
*/
|
*/
|
||||||
async getExport(format: ExportFormat = "csv"): Promise<ExportedVault> {
|
async getExport(format: ExportFormat = "csv"): Promise<ExportedVault> {
|
||||||
|
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
if (format === "encrypted_json") {
|
if (format === "encrypted_json") {
|
||||||
return this.getEncryptedExport();
|
return this.getEncryptedExport(userId);
|
||||||
} else if (format === "zip") {
|
} 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
|
* @param password The password to encrypt the export with
|
||||||
* @returns A password-protected encrypted individual vault export
|
* @returns A password-protected encrypted individual vault export
|
||||||
*/
|
*/
|
||||||
async getPasswordProtectedExport(password: string): Promise<ExportedVaultAsString> {
|
async getPasswordProtectedExport(password: string): Promise<ExportedVaultAsString> {
|
||||||
|
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
const exportVault = await this.getExport("json");
|
const exportVault = await this.getExport("json");
|
||||||
|
|
||||||
if (exportVault.type !== "text/plain") {
|
if (exportVault.type !== "text/plain") {
|
||||||
@@ -80,19 +83,20 @@ export class IndividualVaultExportService
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
type: "text/plain",
|
type: "text/plain",
|
||||||
data: await this.buildPasswordExport(exportVault.data, password),
|
data: await this.buildPasswordExport(userId, exportVault.data, password),
|
||||||
fileName: ExportHelper.getFileName("", "encrypted_json"),
|
fileName: ExportHelper.getFileName("", "encrypted_json"),
|
||||||
} as ExportedVaultAsString;
|
} as ExportedVaultAsString;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a unencrypted export of an individual vault including attachments
|
/** 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
|
* @returns A unencrypted export including attachments
|
||||||
*/
|
*/
|
||||||
async getDecryptedExportZip(): Promise<ExportedVaultAsBlob> {
|
async getDecryptedExportZip(activeUserId: UserId): Promise<ExportedVaultAsBlob> {
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
|
|
||||||
// ciphers
|
// ciphers
|
||||||
const exportedVault = await this.getDecryptedExport("json");
|
const exportedVault = await this.getDecryptedExport(activeUserId, "json");
|
||||||
zip.file("data.json", exportedVault.data);
|
zip.file("data.json", exportedVault.data);
|
||||||
|
|
||||||
const attachmentsFolder = zip.folder("attachments");
|
const attachmentsFolder = zip.folder("attachments");
|
||||||
@@ -100,8 +104,6 @@ export class IndividualVaultExportService
|
|||||||
throw new Error("Error creating attachments folder");
|
throw new Error("Error creating attachments folder");
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
|
||||||
|
|
||||||
// attachments
|
// attachments
|
||||||
for (const cipher of await this.cipherService.getAllDecrypted(activeUserId)) {
|
for (const cipher of await this.cipherService.getAllDecrypted(activeUserId)) {
|
||||||
if (
|
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 decFolders: FolderView[] = [];
|
||||||
let decCiphers: CipherView[] = [];
|
let decCiphers: CipherView[] = [];
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
|
||||||
|
|
||||||
promises.push(
|
promises.push(
|
||||||
firstValueFrom(this.folderService.folderViews$(activeUserId)).then((folders) => {
|
firstValueFrom(this.folderService.folderViews$(activeUserId)).then((folders) => {
|
||||||
@@ -196,11 +200,10 @@ export class IndividualVaultExportService
|
|||||||
} as ExportedVaultAsString;
|
} as ExportedVaultAsString;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getEncryptedExport(): Promise<ExportedVaultAsString> {
|
private async getEncryptedExport(activeUserId: UserId): Promise<ExportedVaultAsString> {
|
||||||
let folders: Folder[] = [];
|
let folders: Folder[] = [];
|
||||||
let ciphers: Cipher[] = [];
|
let ciphers: Cipher[] = [];
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
|
||||||
|
|
||||||
promises.push(
|
promises.push(
|
||||||
firstValueFrom(this.folderService.folders$(activeUserId)).then((f) => {
|
firstValueFrom(this.folderService.folders$(activeUserId)).then((f) => {
|
||||||
@@ -216,9 +219,7 @@ export class IndividualVaultExportService
|
|||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|
||||||
const userKey = await this.keyService.getUserKeyWithLegacySupport(
|
const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId);
|
||||||
await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)),
|
|
||||||
);
|
|
||||||
const encKeyValidation = await this.encryptService.encrypt(Utils.newGuid(), userKey);
|
const encKeyValidation = await this.encryptService.encrypt(Utils.newGuid(), userKey);
|
||||||
|
|
||||||
const jsonDoc: BitwardenEncryptedIndividualJsonExport = {
|
const jsonDoc: BitwardenEncryptedIndividualJsonExport = {
|
||||||
|
|||||||
@@ -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 { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||||
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 { OrganizationId } 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";
|
||||||
import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data";
|
import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data";
|
||||||
@@ -67,6 +67,7 @@ export class OrganizationVaultExportService
|
|||||||
password: string,
|
password: string,
|
||||||
onlyManagedCollections: boolean,
|
onlyManagedCollections: boolean,
|
||||||
): Promise<ExportedVaultAsString> {
|
): Promise<ExportedVaultAsString> {
|
||||||
|
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
const exportVault = await this.getOrganizationExport(
|
const exportVault = await this.getOrganizationExport(
|
||||||
organizationId,
|
organizationId,
|
||||||
"json",
|
"json",
|
||||||
@@ -75,7 +76,7 @@ export class OrganizationVaultExportService
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
type: "text/plain",
|
type: "text/plain",
|
||||||
data: await this.buildPasswordExport(exportVault.data, password),
|
data: await this.buildPasswordExport(userId, exportVault.data, password),
|
||||||
fileName: ExportHelper.getFileName("org", "encrypted_json"),
|
fileName: ExportHelper.getFileName("org", "encrypted_json"),
|
||||||
} as ExportedVaultAsString;
|
} as ExportedVaultAsString;
|
||||||
}
|
}
|
||||||
@@ -102,12 +103,13 @@ export class OrganizationVaultExportService
|
|||||||
if (format === "zip") {
|
if (format === "zip") {
|
||||||
throw new Error("Zip export not supported for organization");
|
throw new Error("Zip export not supported for organization");
|
||||||
}
|
}
|
||||||
|
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
|
|
||||||
if (format === "encrypted_json") {
|
if (format === "encrypted_json") {
|
||||||
return {
|
return {
|
||||||
type: "text/plain",
|
type: "text/plain",
|
||||||
data: onlyManagedCollections
|
data: onlyManagedCollections
|
||||||
? await this.getEncryptedManagedExport(organizationId)
|
? await this.getEncryptedManagedExport(userId, organizationId)
|
||||||
: await this.getOrganizationEncryptedExport(organizationId),
|
: await this.getOrganizationEncryptedExport(organizationId),
|
||||||
fileName: ExportHelper.getFileName("org", "encrypted_json"),
|
fileName: ExportHelper.getFileName("org", "encrypted_json"),
|
||||||
} as ExportedVaultAsString;
|
} as ExportedVaultAsString;
|
||||||
@@ -116,20 +118,20 @@ export class OrganizationVaultExportService
|
|||||||
return {
|
return {
|
||||||
type: "text/plain",
|
type: "text/plain",
|
||||||
data: onlyManagedCollections
|
data: onlyManagedCollections
|
||||||
? await this.getDecryptedManagedExport(organizationId, format)
|
? await this.getDecryptedManagedExport(userId, organizationId, format)
|
||||||
: await this.getOrganizationDecryptedExport(organizationId, format),
|
: await this.getOrganizationDecryptedExport(userId, organizationId, format),
|
||||||
fileName: ExportHelper.getFileName("org", format),
|
fileName: ExportHelper.getFileName("org", format),
|
||||||
} as ExportedVaultAsString;
|
} as ExportedVaultAsString;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getOrganizationDecryptedExport(
|
private async getOrganizationDecryptedExport(
|
||||||
|
activeUserId: UserId,
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
format: "json" | "csv",
|
format: "json" | "csv",
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const decCollections: CollectionView[] = [];
|
const decCollections: CollectionView[] = [];
|
||||||
const decCiphers: CipherView[] = [];
|
const decCiphers: CipherView[] = [];
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
|
||||||
|
|
||||||
promises.push(
|
promises.push(
|
||||||
this.apiService.getOrganizationExport(organizationId).then((exportData) => {
|
this.apiService.getOrganizationExport(organizationId).then((exportData) => {
|
||||||
@@ -210,6 +212,7 @@ export class OrganizationVaultExportService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getDecryptedManagedExport(
|
private async getDecryptedManagedExport(
|
||||||
|
activeUserId: UserId,
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
format: "json" | "csv",
|
format: "json" | "csv",
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
@@ -217,7 +220,6 @@ export class OrganizationVaultExportService
|
|||||||
let allDecCiphers: CipherView[] = [];
|
let allDecCiphers: CipherView[] = [];
|
||||||
let decCollections: CollectionView[] = [];
|
let decCollections: CollectionView[] = [];
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
|
||||||
|
|
||||||
promises.push(
|
promises.push(
|
||||||
this.collectionService.getAllDecrypted().then(async (collections) => {
|
this.collectionService.getAllDecrypted().then(async (collections) => {
|
||||||
@@ -245,12 +247,14 @@ export class OrganizationVaultExportService
|
|||||||
return this.buildJsonExport(decCollections, decCiphers);
|
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 encCiphers: Cipher[] = [];
|
||||||
let allCiphers: Cipher[] = [];
|
let allCiphers: Cipher[] = [];
|
||||||
let encCollections: Collection[] = [];
|
let encCollections: Collection[] = [];
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
|
||||||
|
|
||||||
promises.push(
|
promises.push(
|
||||||
this.collectionService.getAll().then((collections) => {
|
this.collectionService.getAll().then((collections) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user