diff --git a/libs/auth/src/angular/change-password/default-change-password.service.spec.ts b/libs/auth/src/angular/change-password/default-change-password.service.spec.ts index 03d614f6654..8c51df23ba9 100644 --- a/libs/auth/src/angular/change-password/default-change-password.service.spec.ts +++ b/libs/auth/src/angular/change-password/default-change-password.service.spec.ts @@ -1,5 +1,6 @@ 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"; @@ -8,19 +9,28 @@ 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", () => { - const userId = "userId" as UserId; - let keyService: MockProxy; let masterPasswordApiService: MockProxy; let masterPasswordService: MockProxy; let sut: ChangePasswordService; - const inputPasswordResult = { + 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", @@ -57,14 +67,14 @@ describe("DefaultChangePasswordService", () => { describe("changePassword()", () => { it("should call the postPassword() API method with a the correct PasswordRequest credentials", async () => { // Act - await sut.changePassword(inputPasswordResult, userId); + await sut.changePassword(passwordInputResult, userId); // Assert expect(masterPasswordApiService.postPassword).toHaveBeenCalledWith( expect.objectContaining({ - masterPasswordHash: inputPasswordResult.currentServerMasterKeyHash, - masterPasswordHint: inputPasswordResult.newPasswordHint, - newMasterPasswordHash: inputPasswordResult.newServerMasterKeyHash, + masterPasswordHash: passwordInputResult.currentServerMasterKeyHash, + masterPasswordHint: passwordInputResult.newPasswordHint, + newMasterPasswordHash: passwordInputResult.newServerMasterKeyHash, key: newMasterKeyEncryptedUserKey[1].encryptedString, }), ); @@ -72,26 +82,107 @@ describe("DefaultChangePasswordService", () => { it("should call decryptUserKeyWithMasterKey and encryptUserKeyWithMasterKey", async () => { // Act - await sut.changePassword(inputPasswordResult, userId); + await sut.changePassword(passwordInputResult, userId); // Assert expect(masterPasswordService.decryptUserKeyWithMasterKey).toHaveBeenCalledWith( - inputPasswordResult.currentMasterKey, + passwordInputResult.currentMasterKey, userId, ); expect(keyService.encryptUserKeyWithMasterKey).toHaveBeenCalledWith( - inputPasswordResult.newMasterKey, + 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 & Assert - await expect(sut.changePassword(inputPasswordResult, userId)).rejects.toThrow( - "Could not decrypt user key", + // 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", + ); + }); + }); + + describe("rotateUserKeyAndEncryptedDataLegacy()", () => { + it("should throw an error (the method is only implemented in Web)", async () => { + // Act + const testFn = sut.rotateUserKeyAndEncryptedDataLegacy("newPassword", user); + + // Assert + await expect(testFn).rejects.toThrow( + "rotateUserKeyAndEncryptedDataLegacy() is only implemented in Web", ); }); });