From bfbaa2d6f027f2db5e64c5d78e57df73529774a3 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Tue, 13 Jan 2026 12:56:08 -0800 Subject: [PATCH] [PM-27086] Changing direction: remove new methods --- ...sktop-set-initial-password.service.spec.ts | 56 -- .../desktop-set-initial-password.service.ts | 11 - .../web-set-initial-password.service.spec.ts | 57 -- .../web-set-initial-password.service.ts | 33 - ...initial-password.service.implementation.ts | 169 +---- ...fault-set-initial-password.service.spec.ts | 588 ------------------ .../set-initial-password.component.ts | 50 -- ...et-initial-password.service.abstraction.ts | 25 - 8 files changed, 2 insertions(+), 987 deletions(-) diff --git a/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts index 2976bb654e0..11d50ad1f8b 100644 --- a/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts +++ b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts @@ -2,9 +2,7 @@ import { MockProxy, mock } from "jest-mock-extended"; import { BehaviorSubject, of } from "rxjs"; import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; -import { DefaultSetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/default-set-initial-password.service.implementation"; import { - SetInitialPasswordCredentials, SetInitialPasswordCredentialsOld, SetInitialPasswordService, SetInitialPasswordUserType, @@ -21,7 +19,6 @@ import { AccountCryptographicStateService } from "@bitwarden/common/key-manageme import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; -import { MasterPasswordSalt } from "@bitwarden/common/key-management/master-password/types/master-password.types"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -185,57 +182,4 @@ describe("DesktopSetInitialPasswordService", () => { }); }); }); - - describe("setInitialPassword()", () => { - let credentials: SetInitialPasswordCredentials; - let userType: SetInitialPasswordUserType; - let userId: UserId; - - beforeEach(() => { - credentials = { - newPassword: "newPassword", - newPasswordHint: "newPasswordHint", - kdfConfig: DEFAULT_KDF_CONFIG, - salt: "salt" as MasterPasswordSalt, - orgSsoIdentifier: "orgSsoIdentifier", - orgId: "orgId", - resetPasswordAutoEnroll: false, - }; - userId = "userId" as UserId; - userType = SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER; - }); - - describe("given the initial password was successfully set", () => { - it("should send a 'redrawMenu' message", async () => { - // Arrange - jest - .spyOn(DefaultSetInitialPasswordService.prototype, "setInitialPassword") - .mockResolvedValue(undefined); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(messagingService.send).toHaveBeenCalledTimes(1); - expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); - }); - }); - - describe("given the initial password was NOT successfully set (due to parent method failure)", () => { - it("should NOT send a 'redrawMenu' message", async () => { - // Arrange - const parentError = new Error("Parent setInitialPassword failed"); - jest - .spyOn(DefaultSetInitialPasswordService.prototype, "setInitialPassword") - .mockRejectedValue(parentError); - - // Act - const promise = sut.setInitialPassword(credentials, userType, userId); - - // Assert - await expect(promise).rejects.toThrow(parentError); - expect(messagingService.send).not.toHaveBeenCalled(); - }); - }); - }); }); diff --git a/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.ts b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.ts index c6e06076a10..60422c50b12 100644 --- a/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.ts +++ b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.ts @@ -1,7 +1,6 @@ import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { DefaultSetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/default-set-initial-password.service.implementation"; import { - SetInitialPasswordCredentials, SetInitialPasswordCredentialsOld, SetInitialPasswordService, SetInitialPasswordUserType, @@ -63,14 +62,4 @@ export class DesktopSetInitialPasswordService this.messagingService.send("redrawMenu"); } - - override async setInitialPassword( - credentials: SetInitialPasswordCredentials, - userType: SetInitialPasswordUserType, - userId: UserId, - ) { - await super.setInitialPassword(credentials, userType, userId); - - this.messagingService.send("redrawMenu"); - } } diff --git a/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts index bf0816a79fb..654539ac1a2 100644 --- a/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts +++ b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts @@ -2,9 +2,7 @@ import { MockProxy, mock } from "jest-mock-extended"; import { BehaviorSubject, of } from "rxjs"; import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; -import { DefaultSetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/default-set-initial-password.service.implementation"; import { - SetInitialPasswordCredentials, SetInitialPasswordCredentialsOld, SetInitialPasswordService, SetInitialPasswordUserType, @@ -22,7 +20,6 @@ import { AccountCryptographicStateService } from "@bitwarden/common/key-manageme import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; -import { MasterPasswordSalt } from "@bitwarden/common/key-management/master-password/types/master-password.types"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; @@ -214,58 +211,4 @@ describe("WebSetInitialPasswordService", () => { }); }); }); - - describe("setInitialPassword()", () => { - let credentials: SetInitialPasswordCredentials; - let userType: SetInitialPasswordUserType; - let userId: UserId; - - beforeEach(() => { - credentials = { - newPassword: "newPassword", - newPasswordHint: "newPasswordHint", - kdfConfig: DEFAULT_KDF_CONFIG, - salt: "salt" as MasterPasswordSalt, - orgSsoIdentifier: "orgSsoIdentifier", - orgId: "orgId", - resetPasswordAutoEnroll: false, - }; - userId = "userId" as UserId; - userType = SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER; - }); - - describe("given the initial password was successfully set", () => { - it("should call additional state clearing methods", async () => { - // Arrange - jest - .spyOn(DefaultSetInitialPasswordService.prototype, "setInitialPassword") - .mockResolvedValue(undefined); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(routerService.getAndClearLoginRedirectUrl).toHaveBeenCalledTimes(1); - expect(organizationInviteService.clearOrganizationInvitation).toHaveBeenCalledTimes(1); - }); - }); - - describe("given the initial password was NOT successfully set (due to parent method failure)", () => { - it("should NOT call any further methods", async () => { - // Arrange - const parentError = new Error("Parent setInitialPassword failed"); - jest - .spyOn(DefaultSetInitialPasswordService.prototype, "setInitialPassword") - .mockRejectedValue(parentError); - - // Act - const promise = sut.setInitialPassword(credentials, userType, userId); - - // Assert - await expect(promise).rejects.toThrow(parentError); - expect(routerService.getAndClearLoginRedirectUrl).not.toHaveBeenCalled(); - expect(organizationInviteService.clearOrganizationInvitation).not.toHaveBeenCalled(); - }); - }); - }); }); diff --git a/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.ts b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.ts index 1a97b78f24c..8ae1c0cfaea 100644 --- a/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.ts +++ b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.ts @@ -1,7 +1,6 @@ import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { DefaultSetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/default-set-initial-password.service.implementation"; import { - SetInitialPasswordCredentials, SetInitialPasswordCredentialsOld, SetInitialPasswordService, SetInitialPasswordUserType, @@ -87,36 +86,4 @@ export class WebSetInitialPasswordService await this.routerService.getAndClearLoginRedirectUrl(); await this.organizationInviteService.clearOrganizationInvitation(); } - - override async setInitialPassword( - credentials: SetInitialPasswordCredentials, - userType: SetInitialPasswordUserType, - userId: UserId, - ) { - await super.setInitialPassword(credentials, userType, userId); - - /** - * TODO: Investigate refactoring the following logic in https://bitwarden.atlassian.net/browse/PM-22615 - * --- - * When a user has been invited to an org, they can be accepted into the org in two different ways: - * - * 1) By clicking the email invite link, which triggers the normal AcceptOrganizationComponent flow - * a. This flow sets an org invite in state - * b. However, if the user does not already have an account AND the org has SSO enabled AND the require - * SSO policy enabled, the AcceptOrganizationComponent will send the user to /sso to accelerate - * the user through the SSO JIT provisioning process (see #2 below) - * - * 2) By logging in via SSO, which triggers the JIT provisioning process - * a. This flow does NOT (itself) set an org invite in state - * b. The set initial password process on the server accepts the user into the org after successfully - * setting the password (see server - SetInitialMasterPasswordCommand.cs) - * - * If a user clicks the email link but gets accelerated through the SSO JIT process (see 1b), - * the SSO JIT process will accept the user into the org upon setting their initial password (see 2b), - * at which point we must remember to clear the deep linked URL used for accepting the org invite, as well - * as clear the org invite itself that was originally set in state by the AcceptOrganizationComponent. - */ - await this.routerService.getAndClearLoginRedirectUrl(); - await this.organizationInviteService.clearOrganizationInvitation(); - } } diff --git a/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.implementation.ts b/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.implementation.ts index a1cd77a4b0e..91576e6992f 100644 --- a/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.implementation.ts +++ b/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.implementation.ts @@ -19,27 +19,19 @@ import { AccountCryptographicStateService } from "@bitwarden/common/key-manageme import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; -import { - MasterPasswordSalt, - MasterPasswordAuthenticationData, - MasterPasswordAuthenticationHash, - MasterPasswordUnlockData, -} from "@bitwarden/common/key-management/master-password/types/master-password.types"; +import { MasterPasswordSalt } from "@bitwarden/common/key-management/master-password/types/master-password.types"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -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 { KdfConfigService, KeyService, KdfConfig } from "@bitwarden/key-management"; -import { PureCrypto } from "@bitwarden/sdk-internal"; import { SetInitialPasswordService, SetInitialPasswordCredentialsOld, SetInitialPasswordUserType, SetInitialPasswordTdeOffboardingCredentials, - SetInitialPasswordCredentials, } from "./set-initial-password.service.abstraction"; export class DefaultSetInitialPasswordService implements SetInitialPasswordService { @@ -210,144 +202,6 @@ export class DefaultSetInitialPasswordService implements SetInitialPasswordServi } } - async setInitialPassword( - credentials: SetInitialPasswordCredentials, - userType: SetInitialPasswordUserType, - userId: UserId, - ): Promise { - const { - newPassword, - newPasswordHint, - kdfConfig, - salt, - orgSsoIdentifier, - orgId, - resetPasswordAutoEnroll, - } = credentials; - - for (const [key, value] of Object.entries(credentials)) { - if (value == null) { - throw new Error(`${key} not found. Could not set password.`); - } - } - if (userId == null) { - throw new Error("userId not found. Could not set password."); - } - if (userType == null) { - throw new Error("userType not found. Could not set password."); - } - - let userKey: UserKey = await firstValueFrom(this.keyService.userKey$(userId)); - - if (userKey == null) { - userKey = new SymmetricCryptoKey(PureCrypto.make_user_key_aes256_cbc_hmac()) as UserKey; - } - - const authenticationData: MasterPasswordAuthenticationData = - await this.masterPasswordService.makeMasterPasswordAuthenticationData( - newPassword, - kdfConfig, - salt, - ); - - const unlockData: MasterPasswordUnlockData = - await this.masterPasswordService.makeMasterPasswordUnlockData( - newPassword, - kdfConfig, - salt, - userKey, - ); - - let keyPair: [string, EncString] | null = null; - let keysRequest: KeysRequest | null = null; - - if (userType === SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER) { - /** - * A user being JIT provisioned into a MP encryption org does not yet have a user - * asymmetric key pair, so we create it for them here. - * - * Sidenote: - * In the case of a TDE user whose permissions require that they have a MP - that user - * will already have a user asymmetric key pair by this point, so we skip this if-block - * so that we don't create a new key pair for them. - */ - - // Extra safety check (see description on https://github.com/bitwarden/clients/pull/10180): - // In case we have have a local private key and are not sure whether it has been posted to the server, - // we post the local private key instead of generating a new one - const existingUserPrivateKey = (await firstValueFrom( - this.keyService.userPrivateKey$(userId), - )) as Uint8Array; - - const existingUserPublicKey = await firstValueFrom(this.keyService.userPublicKey$(userId)); - - if (existingUserPrivateKey != null && existingUserPublicKey != null) { - const existingUserPublicKeyB64 = Utils.fromBufferToB64(existingUserPublicKey); - - // Existing key pair - keyPair = [ - existingUserPublicKeyB64, - await this.encryptService.wrapDecapsulationKey(existingUserPrivateKey, userKey), - ]; - } else { - // New key pair - keyPair = await this.keyService.makeKeyPair(userKey); - } - - if (keyPair == null) { - throw new Error("keyPair not found. Could not set password."); - } - if (!keyPair[1].encryptedString) { - throw new Error("encrypted private key not found. Could not set password."); - } - - keysRequest = new KeysRequest(keyPair[0], keyPair[1].encryptedString); - } - - const request = SetPasswordRequest.newConstructor( - authenticationData, - unlockData, - newPasswordHint, - orgSsoIdentifier, - keysRequest, - ); - - await this.masterPasswordApiService.setPassword(request); - - // Clear force set password reason to allow navigation back to vault. - await this.masterPasswordService.setForceSetPasswordReason(ForceSetPasswordReason.None, userId); - - // User now has a password so update account decryption options in state - await this.updateAccountDecryptionProperties(unlockData, userKey, userId); - - /** - * Set the private key only for new JIT provisioned users in MP encryption orgs. - * (Existing TDE users will have their private key set on sync or on login.) - */ - if (keyPair != null && userType === SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER) { - if (!keyPair[1].encryptedString) { - throw new Error("encrypted private key not found. Could not set private key in state."); - } - await this.keyService.setPrivateKey(keyPair[1].encryptedString, userId); - await this.accountCryptographicStateService.setAccountCryptographicState( - { - V1: { - private_key: keyPair[1].encryptedString, - }, - }, - userId, - ); - } - - if (resetPasswordAutoEnroll) { - await this.handleResetPasswordAutoEnroll( - authenticationData.masterPasswordAuthenticationHash, - orgId, - userId, - ); - } - } - /** * @deprecated To be removed in PM-28143 */ @@ -399,25 +253,6 @@ export class DefaultSetInitialPasswordService implements SetInitialPasswordServi await this.keyService.setUserKey(masterKeyEncryptedUserKey[0], userId); } - private async updateAccountDecryptionProperties( - unlockData: MasterPasswordUnlockData, - userKey: UserKey, - userId: UserId, - ) { - const userDecryptionOpts = await firstValueFrom( - this.userDecryptionOptionsService.userDecryptionOptionsById$(userId), - ); - userDecryptionOpts.hasMasterPassword = true; - - await this.userDecryptionOptionsService.setUserDecryptionOptionsById( - userId, - userDecryptionOpts, - ); - await this.masterPasswordService.setMasterPasswordUnlockData(unlockData, userId); - await this.kdfConfigService.setKdfConfig(userId, unlockData.kdf); - await this.keyService.setUserKey(userKey, userId); - } - /** * @deprecated To be removed in PM-28143 * @@ -445,7 +280,7 @@ export class DefaultSetInitialPasswordService implements SetInitialPasswordServi } private async handleResetPasswordAutoEnroll( - masterKeyHash: MasterPasswordAuthenticationHash | string, // In PM-28143, remove `| string`; should only accept MasterPasswordAuthenticationHash. + masterKeyHash: string, orgId: string, userId: UserId, ) { diff --git a/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.spec.ts b/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.spec.ts index a397efb6995..14f547f5c22 100644 --- a/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.spec.ts +++ b/libs/angular/src/auth/password-management/set-initial-password/default-set-initial-password.service.spec.ts @@ -27,13 +27,6 @@ import { EncString, } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; -import { - MasterKeyWrappedUserKey, - MasterPasswordAuthenticationData, - MasterPasswordAuthenticationHash, - MasterPasswordSalt, - MasterPasswordUnlockData, -} from "@bitwarden/common/key-management/master-password/types/master-password.types"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -42,11 +35,9 @@ import { CsprngArray } from "@bitwarden/common/types/csprng"; import { UserId } from "@bitwarden/common/types/guid"; import { MasterKey, UserKey, UserPrivateKey, UserPublicKey } from "@bitwarden/common/types/key"; import { DEFAULT_KDF_CONFIG, KdfConfigService, KeyService } from "@bitwarden/key-management"; -import { PureCrypto } from "@bitwarden/sdk-internal"; import { DefaultSetInitialPasswordService } from "./default-set-initial-password.service.implementation"; import { - SetInitialPasswordCredentials, SetInitialPasswordCredentialsOld, SetInitialPasswordService, SetInitialPasswordTdeOffboardingCredentials, @@ -737,585 +728,6 @@ describe("DefaultSetInitialPasswordService", () => { }); }); - describe("setInitialPassword()", () => { - // Mock function parameters - let credentials: SetInitialPasswordCredentials; - let userType: SetInitialPasswordUserType; - - // Mock other function data - let authenticationData: MasterPasswordAuthenticationData; - let unlockData: MasterPasswordUnlockData; - - let existingUserPublicKey: UserPublicKey; - let existingUserPrivateKey: UserPrivateKey; - let userKeyEncryptedPrivateKey: EncString; - - let keyPair: [string, EncString]; - let keysRequest: KeysRequest; - - let organizationKeys: OrganizationKeysResponse; - let orgPublicKeyEncryptedUserKey: EncString; - - let userDecryptionOptions: UserDecryptionOptions; - let userDecryptionOptionsSubject: BehaviorSubject; - let setPasswordRequest: SetPasswordRequest; - - let enrollmentRequest: OrganizationUserResetPasswordEnrollmentRequest; - - beforeEach(() => { - jest.clearAllMocks(); - - // Mock function arguments - credentials = { - newPassword: "newPassword123!", - newPasswordHint: "newPasswordHint", - kdfConfig: DEFAULT_KDF_CONFIG, - salt: "user@example.com" as MasterPasswordSalt, - orgSsoIdentifier: "orgSsoIdentifier", - orgId: "orgId", - resetPasswordAutoEnroll: false, - }; - userType = SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER; - - // Mock other function data - authenticationData = { - salt: credentials.salt, - kdf: credentials.kdfConfig, - masterPasswordAuthenticationHash: - "masterPasswordAuthenticationHash" as MasterPasswordAuthenticationHash, - }; - - unlockData = { - salt: credentials.salt, - kdf: credentials.kdfConfig, - masterKeyWrappedUserKey: "masterKeyWrappedUserKey" as MasterKeyWrappedUserKey, - - toJSON(): any { - return { - salt: this.salt, - kdf: this.kdf, - masterKeyWrappedUserKey: this.masterKeyWrappedUserKey, - }; - }, - }; - - existingUserPublicKey = Utils.fromB64ToArray("existingUserPublicKey") as UserPublicKey; - existingUserPrivateKey = Utils.fromB64ToArray("existingUserPrivateKey") as UserPrivateKey; - userKeyEncryptedPrivateKey = new EncString("userKeyEncryptedPrivateKey"); - - keyPair = ["publicKey", new EncString("privateKey")]; - keysRequest = new KeysRequest(keyPair[0], keyPair[1].encryptedString); - - organizationKeys = { - privateKey: "orgPrivateKey", - publicKey: "orgPublicKey", - } as OrganizationKeysResponse; - orgPublicKeyEncryptedUserKey = new EncString("orgPublicKeyEncryptedUserKey"); - - userDecryptionOptions = new UserDecryptionOptions({ hasMasterPassword: true }); - userDecryptionOptionsSubject = new BehaviorSubject(userDecryptionOptions); - userDecryptionOptionsService.userDecryptionOptionsById$.mockReturnValue( - userDecryptionOptionsSubject, - ); - - setPasswordRequest = SetPasswordRequest.newConstructor( - authenticationData, - unlockData, - credentials.newPasswordHint, - credentials.orgSsoIdentifier, - keysRequest, - ); - - enrollmentRequest = new OrganizationUserResetPasswordEnrollmentRequest(); - enrollmentRequest.masterPasswordHash = authenticationData.masterPasswordAuthenticationHash; - enrollmentRequest.resetPasswordKey = orgPublicKeyEncryptedUserKey.encryptedString; - }); - - interface MockConfig { - userType: SetInitialPasswordUserType; - userHasUserKey: boolean; - userHasLocalKeyPair: boolean; - resetPasswordAutoEnroll: boolean; - } - - const defaultMockConfig: MockConfig = { - userType: SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER, - userHasUserKey: true, - userHasLocalKeyPair: false, - resetPasswordAutoEnroll: false, - }; - - function setupMocks(config: MockConfig = defaultMockConfig) { - if (config.userHasUserKey) { - keyService.userKey$.mockReturnValue(of(userKey)); - } else { - keyService.userKey$.mockReturnValue(of(null)); - jest.spyOn(PureCrypto, "make_user_key_aes256_cbc_hmac").mockReturnValue(new Uint8Array(64)); - } - - // Mock MasterPasswordAuthenticationData and MasterPasswordUnlockData - masterPasswordService.makeMasterPasswordAuthenticationData.mockResolvedValue( - authenticationData, - ); - masterPasswordService.makeMasterPasswordUnlockData.mockResolvedValue(unlockData); - - // Mock keyPair values - if (config.userType === SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER) { - if (config.userHasLocalKeyPair) { - keyService.userPrivateKey$.mockReturnValue(of(existingUserPrivateKey)); - keyService.userPublicKey$.mockReturnValue(of(existingUserPublicKey)); - encryptService.wrapDecapsulationKey.mockResolvedValue(userKeyEncryptedPrivateKey); - } else { - keyService.userPrivateKey$.mockReturnValue(of(null)); - keyService.userPublicKey$.mockReturnValue(of(null)); - keyService.makeKeyPair.mockResolvedValue(keyPair); - } - } - - // Mock handleResetPasswordAutoEnroll() values - if (config.resetPasswordAutoEnroll) { - organizationApiService.getKeys.mockResolvedValue(organizationKeys); - encryptService.encapsulateKeyUnsigned.mockResolvedValue(orgPublicKeyEncryptedUserKey); - keyService.userKey$.mockReturnValue(of(userKey)); - } - } - - describe("general error handling", () => { - [ - "newPassword", - "newPasswordHint", - "kdfConfig", - "salt", - "orgSsoIdentifier", - "orgId", - "resetPasswordAutoEnroll", - ].forEach((key) => { - it(`should throw if ${key} is not provided on the SetInitialPasswordCredentials object`, async () => { - // Arrange - const invalidCredentials: SetInitialPasswordCredentials = { - ...credentials, - [key]: null, - }; - - // Act - const promise = sut.setInitialPassword(invalidCredentials, userType, userId); - - // Assert - await expect(promise).rejects.toThrow(`${key} not found. Could not set password.`); - }); - }); - - ["userId", "userType"].forEach((param) => { - it(`should throw if ${param} was not passed in`, async () => { - // Arrange & Act - const promise = sut.setInitialPassword( - credentials, - param === "userType" ? null : userType, - param === "userId" ? null : userId, - ); - - // Assert - await expect(promise).rejects.toThrow(`${param} not found. Could not set password.`); - }); - }); - }); - - describe("given SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER", () => { - beforeEach(() => { - userType = SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER; - }); - - it("should create MasterPasswordAuthenticationData", async () => { - // Arrange - setupMocks(); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordService.makeMasterPasswordAuthenticationData).toHaveBeenCalledWith( - credentials.newPassword, - credentials.kdfConfig, - credentials.salt, - ); - }); - - it("should create MasterPasswordUnlockData", async () => { - // Arrange - setupMocks(); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordService.makeMasterPasswordUnlockData).toHaveBeenCalledWith( - credentials.newPassword, - credentials.kdfConfig, - credentials.salt, - userKey, - ); - }); - - describe("given the user has an existing local key pair", () => { - it("should NOT create a brand new key pair for the user", async () => { - // Arrange - setPasswordRequest.keys = { - encryptedPrivateKey: userKeyEncryptedPrivateKey.encryptedString, - publicKey: Utils.fromBufferToB64(existingUserPublicKey), - }; - - setupMocks({ ...defaultMockConfig, userHasLocalKeyPair: true }); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - expect(keyService.userPrivateKey$).toHaveBeenCalledWith(userId); - expect(keyService.userPublicKey$).toHaveBeenCalledWith(userId); - expect(encryptService.wrapDecapsulationKey).toHaveBeenCalledWith( - existingUserPrivateKey, - userKey, - ); - expect(keyService.makeKeyPair).not.toHaveBeenCalled(); - }); - }); - - describe("given the user has a userKey", () => { - it("should successfully set an initial password", async () => { - // Arrange - setupMocks(); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - }); - }); - - describe("given the user does NOT have a userKey", () => { - it("should successfully set an initial password", async () => { - // Arrange - setupMocks({ ...defaultMockConfig, userHasUserKey: false }); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - }); - }); - - it("should throw if a key pair is not found", async () => { - // Arrange - keyPair = null; - - setupMocks(); - - // Act - const promise = sut.setInitialPassword(credentials, userType, userId); - - // Assert - await expect(promise).rejects.toThrow("keyPair not found. Could not set password."); - expect(masterPasswordApiService.setPassword).not.toHaveBeenCalled(); - }); - - it("should throw if an encrypted private key is not found", async () => { - // Arrange - keyPair[1].encryptedString = "" as EncryptedString; - - setupMocks(); - - // Act - const promise = sut.setInitialPassword(credentials, userType, userId); - - // Assert - await expect(promise).rejects.toThrow( - "encrypted private key not found. Could not set password.", - ); - expect(masterPasswordApiService.setPassword).not.toHaveBeenCalled(); - }); - - describe("given the initial password has been successfully set", () => { - it("should clear the ForceSetPasswordReason by setting it to None", async () => { - // Arrange - setupMocks(); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - expect(masterPasswordService.setForceSetPasswordReason).toHaveBeenCalledWith( - ForceSetPasswordReason.None, - userId, - ); - }); - - it("should update account decryption properties", async () => { - // Arrange - setupMocks(); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(userDecryptionOptionsService.setUserDecryptionOptionsById).toHaveBeenCalledWith( - userId, - userDecryptionOptions, - ); - expect(masterPasswordService.setMasterPasswordUnlockData).toHaveBeenCalledWith( - unlockData, - userId, - ); - expect(kdfConfigService.setKdfConfig).toHaveBeenCalledWith(userId, unlockData.kdf); - expect(keyService.setUserKey).toHaveBeenCalledWith(userKey, userId); - }); - - it("should set the private key to state", async () => { - // Arrange - setupMocks(); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - expect(keyService.setPrivateKey).toHaveBeenCalledWith(keyPair[1].encryptedString, userId); - expect( - accountCryptographicStateService.setAccountCryptographicState, - ).toHaveBeenCalledWith( - { - V1: { - private_key: keyPair[1].encryptedString as EncryptedString, - }, - }, - userId, - ); - }); - - describe("given resetPasswordAutoEnroll is true", () => { - it(`should handle reset password (account recovery) auto enroll`, async () => { - // Arrange - credentials.resetPasswordAutoEnroll = true; - - setupMocks({ ...defaultMockConfig, resetPasswordAutoEnroll: true }); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - expect( - organizationUserApiService.putOrganizationUserResetPasswordEnrollment, - ).toHaveBeenCalledWith(credentials.orgId, userId, enrollmentRequest); - }); - - it("should throw if organization keys are not found", async () => { - // Arrange - credentials.resetPasswordAutoEnroll = true; - organizationKeys = null; - - setupMocks({ ...defaultMockConfig, resetPasswordAutoEnroll: true }); - - // Act - const promise = sut.setInitialPassword(credentials, userType, userId); - - // Assert - await expect(promise).rejects.toThrow( - "Organization keys response is null. Could not handle reset password auto enroll.", - ); - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - expect( - organizationUserApiService.putOrganizationUserResetPasswordEnrollment, - ).not.toHaveBeenCalled(); - }); - - ["orgPublicKeyEncryptedUserKey", "orgPublicKeyEncryptedUserKey.encryptedString"].forEach( - (property) => { - it("should throw if orgPublicKeyEncryptedUserKey is not found", async () => { - // Arrange - credentials.resetPasswordAutoEnroll = true; - - if (property === "orgPublicKeyEncryptedUserKey") { - orgPublicKeyEncryptedUserKey = null; - } else { - orgPublicKeyEncryptedUserKey.encryptedString = "" as EncryptedString; - } - - setupMocks({ ...defaultMockConfig, resetPasswordAutoEnroll: true }); - - // Act - const promise = sut.setInitialPassword(credentials, userType, userId); - - // Assert - await expect(promise).rejects.toThrow( - "orgPublicKeyEncryptedUserKey not found. Could not handle reset password auto enroll.", - ); - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith( - setPasswordRequest, - ); - expect( - organizationUserApiService.putOrganizationUserResetPasswordEnrollment, - ).not.toHaveBeenCalled(); - }); - }, - ); - }); - - describe("given resetPasswordAutoEnroll is false", () => { - it(`should NOT handle reset password (account recovery) auto enroll`, async () => { - // Arrange - credentials.resetPasswordAutoEnroll = false; - - setupMocks(); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - expect( - organizationUserApiService.putOrganizationUserResetPasswordEnrollment, - ).not.toHaveBeenCalled(); - }); - }); - }); - }); - - describe("given SetInitialPasswordUserType.TDE_ORG_USER_RESET_PASSWORD_PERMISSION_REQUIRES_MP", () => { - beforeEach(() => { - userType = SetInitialPasswordUserType.TDE_ORG_USER_RESET_PASSWORD_PERMISSION_REQUIRES_MP; - setPasswordRequest.keys = null; - }); - - it("should NOT generate a keyPair", async () => { - // Arrange - setupMocks({ ...defaultMockConfig, userType }); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(keyService.userPrivateKey$).not.toHaveBeenCalled(); - expect(keyService.userPublicKey$).not.toHaveBeenCalled(); - expect(encryptService.wrapDecapsulationKey).not.toHaveBeenCalled(); - expect(keyService.makeKeyPair).not.toHaveBeenCalled(); - }); - - describe("given the user has a userKey", () => { - it("should successfully set an initial password", async () => { - // Arrange - setupMocks({ ...defaultMockConfig, userType }); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - }); - }); - - describe("given the user does NOT have a userKey", () => { - it("should successfully set an initial password", async () => { - // Arrange - setupMocks({ ...defaultMockConfig, userType }); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - }); - }); - - describe("given the initial password has been successfully set", () => { - it("should clear the ForceSetPasswordReason by setting it to None", async () => { - // Arrange - setupMocks({ ...defaultMockConfig, userType }); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - expect(masterPasswordService.setForceSetPasswordReason).toHaveBeenCalledWith( - ForceSetPasswordReason.None, - userId, - ); - }); - - it("should update account decryption properties", async () => { - // Arrange - setupMocks({ ...defaultMockConfig, userType }); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - expect(userDecryptionOptionsService.setUserDecryptionOptionsById).toHaveBeenCalledWith( - userId, - userDecryptionOptions, - ); - expect(masterPasswordService.setMasterPasswordUnlockData).toHaveBeenCalledWith( - unlockData, - userId, - ); - expect(kdfConfigService.setKdfConfig).toHaveBeenCalledWith(userId, credentials.kdfConfig); - expect(keyService.setUserKey).toHaveBeenCalledWith(masterKeyEncryptedUserKey[0], userId); - }); - - it("should NOT set the private key to state", async () => { - // Arrange - setupMocks({ ...defaultMockConfig, userType }); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - expect(keyService.setPrivateKey).not.toHaveBeenCalled(); - }); - - describe("given resetPasswordAutoEnroll is true", () => { - it(`should handle reset password (account recovery) auto enroll`, async () => { - // Arrange - credentials.resetPasswordAutoEnroll = true; - - setupMocks({ ...defaultMockConfig, userType, resetPasswordAutoEnroll: true }); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - expect( - organizationUserApiService.putOrganizationUserResetPasswordEnrollment, - ).toHaveBeenCalledWith(credentials.orgId, userId, enrollmentRequest); - }); - }); - - describe("given resetPasswordAutoEnroll is false", () => { - it(`should NOT handle reset password (account recovery) auto enroll`, async () => { - // Arrange - setupMocks({ ...defaultMockConfig, userType }); - - // Act - await sut.setInitialPassword(credentials, userType, userId); - - // Assert - expect(masterPasswordApiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); - expect( - organizationUserApiService.putOrganizationUserResetPasswordEnrollment, - ).not.toHaveBeenCalled(); - }); - }); - }); - }); - }); - describe("setInitialPasswordTdeOffboarding(...)", () => { // Mock function parameters let credentials: SetInitialPasswordTdeOffboardingCredentials; diff --git a/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.component.ts b/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.component.ts index 82d221b9abc..48934d6c679 100644 --- a/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.component.ts +++ b/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.component.ts @@ -39,7 +39,6 @@ import { import { I18nPipe } from "@bitwarden/ui-common"; import { - SetInitialPasswordCredentials, SetInitialPasswordCredentialsOld, SetInitialPasswordService, SetInitialPasswordTdeOffboardingCredentials, @@ -196,14 +195,7 @@ export class SetInitialPasswordComponent implements OnInit { switch (this.userType) { case SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER: case SetInitialPasswordUserType.TDE_ORG_USER_RESET_PASSWORD_PERMISSION_REQUIRES_MP: - // Remove wrapping "if" check and early return in PM-28143 - if (passwordInputResult.newApisFlagEnabled) { - await this.setInitialPassword(passwordInputResult); - return; - } - await this.setInitialPasswordOld(passwordInputResult); // remove in PM-28143 - break; case SetInitialPasswordUserType.OFFBOARDED_TDE_ORG_USER: await this.setInitialPasswordTdeOffboarding(passwordInputResult); @@ -265,48 +257,6 @@ export class SetInitialPasswordComponent implements OnInit { } } - private async setInitialPassword(passwordInputResult: PasswordInputResult) { - const ctx = "Could not set initial password."; - - assertTruthy(passwordInputResult.newPassword, "newPassword", ctx); - assertTruthy(passwordInputResult.kdfConfig, "kdfConfig", ctx); - assertTruthy(passwordInputResult.salt, "salt", ctx); - assertNonNullish(passwordInputResult.newPasswordHint, "newPasswordHint", ctx); // can have an empty string as a valid value, so check non-nullish - - assertTruthy(this.orgSsoIdentifier, "orgSsoIdentifier", ctx); - assertTruthy(this.orgId, "orgId", ctx); - assertTruthy(this.userType, "userType", ctx); - assertTruthy(this.userId, "userId", ctx); - assertNonNullish(this.resetPasswordAutoEnroll, "resetPasswordAutoEnroll", ctx); // can have `false` as a valid value, so check non-nullish - - try { - const credentials: SetInitialPasswordCredentials = { - newPassword: passwordInputResult.newPassword, - newPasswordHint: passwordInputResult.newPasswordHint, - kdfConfig: passwordInputResult.kdfConfig, - salt: passwordInputResult.salt, - orgSsoIdentifier: this.orgSsoIdentifier, - orgId: this.orgId, - resetPasswordAutoEnroll: this.resetPasswordAutoEnroll, - }; - - await this.setInitialPasswordService.setInitialPassword( - credentials, - this.userType, - this.userId, - ); - - this.showSuccessToastByUserType(); - - this.submitting = false; - await this.router.navigate(["vault"]); - } catch (e) { - this.logService.error("Error setting initial password", e); - this.validationService.showError(e); - this.submitting = false; - } - } - private async setInitialPasswordTdeOffboarding(passwordInputResult: PasswordInputResult) { const ctx = "Could not set initial password."; assertTruthy(passwordInputResult.newMasterKey, "newMasterKey", ctx); diff --git a/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.service.abstraction.ts b/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.service.abstraction.ts index eca82fd7c02..2d41fc8deb4 100644 --- a/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.service.abstraction.ts +++ b/libs/angular/src/auth/password-management/set-initial-password/set-initial-password.service.abstraction.ts @@ -58,16 +58,6 @@ export interface SetInitialPasswordCredentialsOld { salt: MasterPasswordSalt; } -export interface SetInitialPasswordCredentials { - newPassword: string; - newPasswordHint: string; - kdfConfig: KdfConfig; - salt: MasterPasswordSalt; - orgSsoIdentifier: string; - orgId: string; - resetPasswordAutoEnroll: boolean; -} - export interface SetInitialPasswordTdeOffboardingCredentials { newMasterKey: MasterKey; newServerMasterKeyHash: string; @@ -110,19 +100,4 @@ export abstract class SetInitialPasswordService { credentials: SetInitialPasswordTdeOffboardingCredentials, userId: UserId, ) => Promise; - - /** - * Sets an initial password for an existing authed user who is either: - * - {@link SetInitialPasswordUserType.JIT_PROVISIONED_MP_ORG_USER} - * - {@link SetInitialPasswordUserType.TDE_ORG_USER_RESET_PASSWORD_PERMISSION_REQUIRES_MP} - * - * @param credentials An object of the credentials needed to set the initial password - * @throws If any property on the `credentials` object is null or undefined, or if a - * keyPair could not be found/created for a JIT_PROVISIONED_MP_ORG_USER - */ - abstract setInitialPassword: ( - credentials: SetInitialPasswordCredentials, - userType: SetInitialPasswordUserType, - userId: UserId, - ) => Promise; }