From bd13e3b4b55457de33d3eb3f2000d73dcab8fb68 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Mon, 9 Jun 2025 09:30:48 -0600 Subject: [PATCH] feat(change-password-component): Change Password Update [18720] - Committing intermediate changes. --- .../src/auth/components/set-password.component.ts | 2 ++ .../angular/anon-layout/anon-layout.component.ts | 10 +++++----- .../change-password/change-password.component.html | 7 +------ .../change-password/change-password.component.ts | 14 ++++++++++++-- .../default-change-password.service.spec.ts | 6 +++--- .../default-change-password.service.ts | 2 +- .../input-password/input-password.component.ts | 2 +- libs/auth/src/angular/login/login.component.ts | 8 +++----- .../set-password-jit/set-password-jit.component.ts | 1 + 9 files changed, 29 insertions(+), 23 deletions(-) diff --git a/libs/angular/src/auth/components/set-password.component.ts b/libs/angular/src/auth/components/set-password.component.ts index 53f6abaa33c..aadc99bdd9b 100644 --- a/libs/angular/src/auth/components/set-password.component.ts +++ b/libs/angular/src/auth/components/set-password.component.ts @@ -130,6 +130,8 @@ export class SetPasswordComponent extends BaseChangePasswordComponent implements this.resetPasswordAutoEnroll = orgAutoEnrollStatusResponse.resetPasswordEnabled; }), switchMap((orgAutoEnrollStatusResponse: OrganizationAutoEnrollStatusResponse) => + // Does this actually need to confirm for all organizations. + // Must get org id from response to get master password policy options this.policyApiService.getMasterPasswordPolicyOptsForOrgUser( orgAutoEnrollStatusResponse.id, diff --git a/libs/auth/src/angular/anon-layout/anon-layout.component.ts b/libs/auth/src/angular/anon-layout/anon-layout.component.ts index e99413f5dfe..060327d0e52 100644 --- a/libs/auth/src/angular/anon-layout/anon-layout.component.ts +++ b/libs/auth/src/angular/anon-layout/anon-layout.component.ts @@ -31,9 +31,9 @@ export class AnonLayoutComponent implements OnInit, OnChanges { return ["tw-h-full"]; } - @Input() title?: string; - @Input() subtitle?: string; - @Input() icon?: Icon; + @Input() title: string; + @Input() subtitle: string; + @Input() icon: Icon; @Input() showReadonlyHostname?: boolean; @Input() hideLogo: boolean = false; @Input() hideFooter: boolean = false; @@ -56,8 +56,8 @@ export class AnonLayoutComponent implements OnInit, OnChanges { protected logo = BitwardenLogo; protected year: string; protected clientType: ClientType; - protected hostname?: string; - protected version?: string; + protected hostname: string; + protected version: string; protected hideYearAndVersion = false; diff --git a/libs/auth/src/angular/change-password/change-password.component.html b/libs/auth/src/angular/change-password/change-password.component.html index 7a0da51a012..35fe56372d1 100644 --- a/libs/auth/src/angular/change-password/change-password.component.html +++ b/libs/auth/src/angular/change-password/change-password.component.html @@ -15,12 +15,7 @@ [inlineButtons]="true" [primaryButtonText]="{ key: 'changeMasterPassword' }" (onPasswordFormSubmit)="handlePasswordFormSubmit($event)" - [secondaryButtonText]=" - this.forceSetPasswordReason === ForceSetPasswordReason.AdminForcePasswordReset || - this.forceSetPasswordReason === ForceSetPasswordReason.WeakMasterPassword - ? { key: 'logOut' } - : undefined - " + [secondaryButtonText]="shouldShowLogoutText()" (onSecondaryButtonClick)="logOut()" [showChangePasswordWarning]=" this.forceSetPasswordReason !== ForceSetPasswordReason.AdminForcePasswordReset diff --git a/libs/auth/src/angular/change-password/change-password.component.ts b/libs/auth/src/angular/change-password/change-password.component.ts index 37f2cee69d5..90f17fa48c7 100644 --- a/libs/auth/src/angular/change-password/change-password.component.ts +++ b/libs/auth/src/angular/change-password/change-password.component.ts @@ -52,6 +52,8 @@ export class ChangePasswordComponent implements OnInit { formPromise?: Promise; forceSetPasswordReason: ForceSetPasswordReason = ForceSetPasswordReason.None; + protected readonly ForceSetPasswordReason = ForceSetPasswordReason; + constructor( private accountService: AccountService, private changePasswordService: ChangePasswordService, @@ -77,7 +79,7 @@ export class ChangePasswordComponent implements OnInit { this.email = this.activeAccount.email; if (!this.userId) { - throw new Error("activeUserId not found"); + throw new Error("userId not found"); } this.masterPasswordPolicyOptions = await firstValueFrom( @@ -174,5 +176,13 @@ export class ChangePasswordComponent implements OnInit { } } - protected readonly ForceSetPasswordReason = ForceSetPasswordReason; + /** + * Shows the logout button in the case of admin force reset password or weak password upon login. + */ + protected shouldShowLogoutText(): { key: string } | undefined { + return this.forceSetPasswordReason === ForceSetPasswordReason.AdminForcePasswordReset || + this.forceSetPasswordReason === ForceSetPasswordReason.WeakMasterPassword + ? { key: "logOut" } + : undefined; + } } 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 008e6fcd66b..18a38d754b3 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 @@ -97,7 +97,7 @@ describe("DefaultChangePasswordService", () => { it("should throw if a userId was not found", async () => { // Arrange - const userId: undefined = undefined; + const userId: null = null; // Act const testFn = sut.changePassword(passwordInputResult, userId); @@ -109,7 +109,7 @@ describe("DefaultChangePasswordService", () => { it("should throw if a currentMasterKey was not found", async () => { // Arrange const incorrectPasswordInputResult = { ...passwordInputResult }; - incorrectPasswordInputResult.currentMasterKey = undefined; + incorrectPasswordInputResult.currentMasterKey = null; // Act const testFn = sut.changePassword(incorrectPasswordInputResult, userId); @@ -123,7 +123,7 @@ describe("DefaultChangePasswordService", () => { it("should throw if a currentServerMasterKeyHash was not found", async () => { // Arrange const incorrectPasswordInputResult = { ...passwordInputResult }; - incorrectPasswordInputResult.currentServerMasterKeyHash = undefined; + incorrectPasswordInputResult.currentServerMasterKeyHash = null; // Act const testFn = sut.changePassword(incorrectPasswordInputResult, userId); diff --git a/libs/auth/src/angular/change-password/default-change-password.service.ts b/libs/auth/src/angular/change-password/default-change-password.service.ts index c233047824a..ac2e1e3c1c1 100644 --- a/libs/auth/src/angular/change-password/default-change-password.service.ts +++ b/libs/auth/src/angular/change-password/default-change-password.service.ts @@ -58,7 +58,7 @@ export class DefaultChangePasswordService implements ChangePasswordService { ); const request = new PasswordRequest(); - request.masterPasswordHash = passwordInputResult.currentServerMasterKeyHash ?? ""; + request.masterPasswordHash = passwordInputResult.currentServerMasterKeyHash; request.newMasterPasswordHash = passwordInputResult.newServerMasterKeyHash; request.masterPasswordHint = passwordInputResult.newPasswordHint; request.key = newMasterKeyEncryptedUserKey[1].encryptedString as string; diff --git a/libs/auth/src/angular/input-password/input-password.component.ts b/libs/auth/src/angular/input-password/input-password.component.ts index 15a5c00dbc7..eca88581858 100644 --- a/libs/auth/src/angular/input-password/input-password.component.ts +++ b/libs/auth/src/angular/input-password/input-password.component.ts @@ -163,7 +163,7 @@ export class InputPasswordComponent implements OnInit { protected get minPasswordLengthMsg() { if ( - this.masterPasswordPolicyOptions != null && + this.masterPasswordPolicyOptions != undefined && this.masterPasswordPolicyOptions.minLength > 0 ) { return this.i18nService.t("characterMinimum", this.masterPasswordPolicyOptions.minLength); diff --git a/libs/auth/src/angular/login/login.component.ts b/libs/auth/src/angular/login/login.component.ts index 4c5afa85bc3..fcbb397ddbb 100644 --- a/libs/auth/src/angular/login/login.component.ts +++ b/libs/auth/src/angular/login/login.component.ts @@ -244,6 +244,9 @@ export class LoginComponent implements OnInit, OnDestroy { : null; const orgMasterPasswordPolicyOptions = orgPoliciesFromInvite?.enforcedPasswordPolicyOptions; + + // Grab the org policies from invite so that it can be evaluated after we have logged in and + // received a user id. this.passwordPoliciesFromOrgInvite = orgPoliciesFromInvite?.policies; credentials = new PasswordLoginCredentials( @@ -342,11 +345,6 @@ export class LoginComponent implements OnInit, OnDestroy { // Check if we have policies to set from an org invite scenario. if (this.passwordPoliciesFromOrgInvite) { await this.setPoliciesIntoState(authResult.userId, this.passwordPoliciesFromOrgInvite); - - // Short circuit here so that we prevent the accept organization invite from prematurely - // accepting the org invite by getting routed away to vault. - // await this.router.navigate(["change-password"]); - // return; } } else { // TODO: PM-18269 - evaluate if we can combine this with the diff --git a/libs/auth/src/angular/set-password-jit/set-password-jit.component.ts b/libs/auth/src/angular/set-password-jit/set-password-jit.component.ts index a28ffdbb343..47bfb41f876 100644 --- a/libs/auth/src/angular/set-password-jit/set-password-jit.component.ts +++ b/libs/auth/src/angular/set-password-jit/set-password-jit.component.ts @@ -82,6 +82,7 @@ export class SetPasswordJitComponent implements OnInit { ); this.orgId = autoEnrollStatus.id; this.resetPasswordAutoEnroll = autoEnrollStatus.resetPasswordEnabled; + // Does this actually need to confirm for all organizations. this.masterPasswordPolicyOptions = await this.policyApiService.getMasterPasswordPolicyOptsForOrgUser(autoEnrollStatus.id); } catch {