1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-28 02:23:25 +00:00
Files
browser/libs/auth/src/angular/change-password/default-change-password.service.spec.ts
rr-bw afbddeaf86 refactor(set-change-password): [Auth/PM-18458] Create new ChangePasswordComponent (#14226)
This PR creates a new ChangePasswordComponent. The first use-case of the ChangePasswordComponent is to place it inside a new PasswordSettingsComponent, which is accessed by going to Account Settings > Security.

The ChangePasswordComponent will be updated in future PRs to handle more change password scenarios.

Feature Flags: PM16117_ChangeExistingPasswordRefactor
2025-05-16 10:41:46 -07:00

178 lines
6.3 KiB
TypeScript

import { mock, MockProxy } from "jest-mock-extended";
import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { UserId } from "@bitwarden/common/types/guid";
import { MasterKey, UserKey } from "@bitwarden/common/types/key";
import { KeyService, PBKDF2KdfConfig } from "@bitwarden/key-management";
import { PasswordInputResult } from "../input-password/password-input-result";
import { ChangePasswordService } from "./change-password.service.abstraction";
import { DefaultChangePasswordService } from "./default-change-password.service";
describe("DefaultChangePasswordService", () => {
let keyService: MockProxy<KeyService>;
let masterPasswordApiService: MockProxy<MasterPasswordApiService>;
let masterPasswordService: MockProxy<InternalMasterPasswordServiceAbstraction>;
let sut: ChangePasswordService;
const userId = "userId" as UserId;
const user: Account = {
id: userId,
email: "email",
emailVerified: false,
name: "name",
};
const passwordInputResult: PasswordInputResult = {
currentMasterKey: new SymmetricCryptoKey(new Uint8Array(32)) as MasterKey,
currentServerMasterKeyHash: "currentServerMasterKeyHash",
newPassword: "newPassword",
newPasswordHint: "newPasswordHint",
newMasterKey: new SymmetricCryptoKey(new Uint8Array(32)) as MasterKey,
newServerMasterKeyHash: "newServerMasterKeyHash",
newLocalMasterKeyHash: "newLocalMasterKeyHash",
kdfConfig: new PBKDF2KdfConfig(),
};
const decryptedUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey;
const newMasterKeyEncryptedUserKey: [UserKey, EncString] = [
decryptedUserKey,
{ encryptedString: "newMasterKeyEncryptedUserKey" } as EncString,
];
beforeEach(() => {
keyService = mock<KeyService>();
masterPasswordApiService = mock<MasterPasswordApiService>();
masterPasswordService = mock<InternalMasterPasswordServiceAbstraction>();
sut = new DefaultChangePasswordService(
keyService,
masterPasswordApiService,
masterPasswordService,
);
masterPasswordService.decryptUserKeyWithMasterKey.mockResolvedValue(decryptedUserKey);
keyService.encryptUserKeyWithMasterKey.mockResolvedValue(newMasterKeyEncryptedUserKey);
});
describe("changePassword()", () => {
it("should call the postPassword() API method with a the correct PasswordRequest credentials", async () => {
// Act
await sut.changePassword(passwordInputResult, userId);
// Assert
expect(masterPasswordApiService.postPassword).toHaveBeenCalledWith(
expect.objectContaining({
masterPasswordHash: passwordInputResult.currentServerMasterKeyHash,
masterPasswordHint: passwordInputResult.newPasswordHint,
newMasterPasswordHash: passwordInputResult.newServerMasterKeyHash,
key: newMasterKeyEncryptedUserKey[1].encryptedString,
}),
);
});
it("should call decryptUserKeyWithMasterKey and encryptUserKeyWithMasterKey", async () => {
// Act
await sut.changePassword(passwordInputResult, userId);
// Assert
expect(masterPasswordService.decryptUserKeyWithMasterKey).toHaveBeenCalledWith(
passwordInputResult.currentMasterKey,
userId,
);
expect(keyService.encryptUserKeyWithMasterKey).toHaveBeenCalledWith(
passwordInputResult.newMasterKey,
decryptedUserKey,
);
});
it("should throw if a userId was not found", async () => {
// Arrange
const userId: null = null;
// Act
const testFn = sut.changePassword(passwordInputResult, userId);
// Assert
await expect(testFn).rejects.toThrow("userId not found");
});
it("should throw if a currentMasterKey was not found", async () => {
// Arrange
const incorrectPasswordInputResult = { ...passwordInputResult };
incorrectPasswordInputResult.currentMasterKey = null;
// Act
const testFn = sut.changePassword(incorrectPasswordInputResult, userId);
// Assert
await expect(testFn).rejects.toThrow(
"currentMasterKey or currentServerMasterKeyHash not found",
);
});
it("should throw if a currentServerMasterKeyHash was not found", async () => {
// Arrange
const incorrectPasswordInputResult = { ...passwordInputResult };
incorrectPasswordInputResult.currentServerMasterKeyHash = null;
// Act
const testFn = sut.changePassword(incorrectPasswordInputResult, userId);
// Assert
await expect(testFn).rejects.toThrow(
"currentMasterKey or currentServerMasterKeyHash not found",
);
});
it("should throw an error if user key decryption fails", async () => {
// Arrange
masterPasswordService.decryptUserKeyWithMasterKey.mockResolvedValue(null);
// Act
const testFn = sut.changePassword(passwordInputResult, userId);
// Assert
await expect(testFn).rejects.toThrow("Could not decrypt user key");
});
it("should throw an error if postPassword() fails", async () => {
// Arrange
masterPasswordApiService.postPassword.mockRejectedValueOnce(new Error("error"));
// Act
const testFn = sut.changePassword(passwordInputResult, userId);
// Assert
await expect(testFn).rejects.toThrow("Could not change password");
expect(masterPasswordApiService.postPassword).toHaveBeenCalled();
});
});
describe("rotateUserKeyMasterPasswordAndEncryptedData()", () => {
it("should throw an error (the method is only implemented in Web)", async () => {
// Act
const testFn = sut.rotateUserKeyMasterPasswordAndEncryptedData(
"currentPassword",
"newPassword",
user,
"newPasswordHint",
);
// Assert
await expect(testFn).rejects.toThrow(
"rotateUserKeyMasterPasswordAndEncryptedData() is only implemented in Web",
);
});
});
});