From 7386fd8780e37c4354d33c7facbcc501902871bd Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Tue, 27 May 2025 16:24:17 -0400 Subject: [PATCH] feat(change-password-component): Change Password Update [18720] - Tidied up code. --- apps/web/src/app/oss-routing.module.ts | 12 ++------- .../anon-layout-wrapper.component.html | 1 - .../anon-layout-wrapper.component.ts | 7 ----- .../change-password.component.html | 1 - .../change-password.component.ts | 27 ++++++++++++------- .../change-password.service.abstraction.ts | 10 +++++++ .../input-password.component.ts | 10 +------ 7 files changed, 30 insertions(+), 38 deletions(-) diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 7a629458eec..6bcaed12d3c 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -148,7 +148,7 @@ const routes: Routes = [ canActivate: [ canAccessFeature( FeatureFlag.PM16117_ChangeExistingPasswordRefactor, - true, + false, "change-password", false, ), @@ -600,15 +600,7 @@ const routes: Routes = [ }, { path: "change-password", - data: { - hideFooter: true, - } satisfies AnonLayoutWrapperData, - children: [ - { - path: "", - component: ChangePasswordComponent, - }, - ], + component: ChangePasswordComponent, }, ], }, diff --git a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.html b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.html index 1ade9ba06a6..95b1e6cadfe 100644 --- a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.html +++ b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.html @@ -5,7 +5,6 @@ [showReadonlyHostname]="showReadonlyHostname" [maxWidth]="maxWidth" [titleAreaMaxWidth]="titleAreaMaxWidth" - [hideFooter]="hideFooter" > diff --git a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts index 5ae2fdbf347..f331c3ac3d1 100644 --- a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts +++ b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts @@ -39,11 +39,6 @@ export interface AnonLayoutWrapperData { * Optional flag to set the max-width of the title area. Defaults to null if not provided. */ titleAreaMaxWidth?: "md"; - - /** - * Optional flag to hide the whole footer. - */ - hideFooter?: boolean; } @Component({ @@ -60,7 +55,6 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy { protected showReadonlyHostname: boolean; protected maxWidth: "md" | "3xl"; protected titleAreaMaxWidth: "md"; - protected hideFooter: boolean; constructor( private router: Router, @@ -112,7 +106,6 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy { this.showReadonlyHostname = Boolean(firstChildRouteData["showReadonlyHostname"]); this.maxWidth = firstChildRouteData["maxWidth"]; this.titleAreaMaxWidth = firstChildRouteData["titleAreaMaxWidth"]; - this.hideFooter = Boolean(firstChildRouteData["hideFooter"]); } private listenForServiceDataChanges() { 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 eee0c4fe2ea..f6ba86ef7c2 100644 --- a/libs/auth/src/angular/change-password/change-password.component.html +++ b/libs/auth/src/angular/change-password/change-password.component.html @@ -10,7 +10,6 @@ [flow]="inputPasswordFlow" [email]="email" [userId]="activeUserId" - [forceSetPasswordReason]="forceSetPasswordReason" [loading]="submitting" [masterPasswordPolicyOptions]="masterPasswordPolicyOptions" [inlineButtons]="true" 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 c8309098a91..85d38d1c048 100644 --- a/libs/auth/src/angular/change-password/change-password.component.ts +++ b/libs/auth/src/angular/change-password/change-password.component.ts @@ -11,7 +11,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { SyncService } from "@bitwarden/common/platform/sync"; import { UserId } from "@bitwarden/common/types/guid"; -import { DialogService, ToastService, Translation } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; import { AnonLayoutWrapperDataService } from "../anon-layout/anon-layout-wrapper-data.service"; @@ -24,6 +24,14 @@ import { PasswordInputResult } from "../input-password/password-input-result"; import { ChangePasswordService } from "./change-password.service.abstraction"; +/** + * Change Password Component + * + * NOTE: The change password component uses the input-password component which will show the + * current password input form in some flows, although it could be left off. This is intentional + * and by design to maintain a strong security posture as some flows could have the user + * end up at a change password without having one before. + */ @Component({ standalone: true, selector: "auth-change-password", @@ -41,7 +49,6 @@ export class ChangePasswordComponent implements OnInit { submitting = false; formPromise?: Promise; forceSetPasswordReason: ForceSetPasswordReason = ForceSetPasswordReason.None; - warningText?: Translation; constructor( private accountService: AccountService, @@ -59,11 +66,16 @@ export class ChangePasswordComponent implements OnInit { async ngOnInit() { this.activeAccount = await firstValueFrom(this.accountService.activeAccount$); - this.activeUserId = this.activeAccount?.id; - this.email = this.activeAccount?.email; + + if (!this.activeAccount) { + throw new Error("No active active account found while trying to change passwords."); + } + + this.activeUserId = this.activeAccount.id; + this.email = this.activeAccount.email; if (!this.activeUserId) { - throw new Error("userId not found"); + throw new Error("activeUserId not found"); } this.masterPasswordPolicyOptions = await firstValueFrom( @@ -76,10 +88,6 @@ export class ChangePasswordComponent implements OnInit { this.initializing = false; - if (this.masterPasswordPolicyOptions?.enforceOnLogin) { - this.warningText = { key: "masterPasswordInvalidWarning" }; - } - if (this.forceSetPasswordReason === ForceSetPasswordReason.AdminForcePasswordReset) { this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({ pageIcon: LockIcon, @@ -91,7 +99,6 @@ export class ChangePasswordComponent implements OnInit { pageIcon: LockIcon, pageTitle: { key: "updateMasterPassword" }, pageSubtitle: { key: "updateMasterPasswordSubtitle" }, - hideFooter: true, maxWidth: "lg", }); } diff --git a/libs/auth/src/angular/change-password/change-password.service.abstraction.ts b/libs/auth/src/angular/change-password/change-password.service.abstraction.ts index f3a6363edb5..c6150968887 100644 --- a/libs/auth/src/angular/change-password/change-password.service.abstraction.ts +++ b/libs/auth/src/angular/change-password/change-password.service.abstraction.ts @@ -34,6 +34,16 @@ export abstract class ChangePasswordService { */ abstract changePassword(passwordInputResult: PasswordInputResult, userId: UserId): Promise; + /** + * Changes the user's password and re-encrypts the user key with the `newMasterKey`. + * - Specifically, this method uses credentials from the `passwordInputResult` to: + * 1. Decrypt the user key with the `currentMasterKey` + * 2. Re-encrypt that user key with the `newMasterKey`, resulting in a `newMasterKeyEncryptedUserKey` + * 3. Build a `PasswordRequest` object that gets PUTed to `"/accounts/update-temp-password"` so that the + * ForcePasswordReset gets set to false. + * @param passwordInputResult + * @param userId + */ abstract changePasswordForAccountRecovery( passwordInputResult: PasswordInputResult, userId: UserId, 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 7a06a8ea2b8..1b5390e1af7 100644 --- a/libs/auth/src/angular/input-password/input-password.component.ts +++ b/libs/auth/src/angular/input-password/input-password.component.ts @@ -10,7 +10,6 @@ import { import { AuditService } from "@bitwarden/common/abstractions/audit.service"; 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 { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -67,8 +66,6 @@ export enum InputPasswordFlow { ChangePassword, /** * All form elements above, plus: [Checkbox] Rotate account encryption key (as the last element in the UI) - * - * Consider changing this to an input. */ ChangePasswordWithOptionalUserKeyRotation, } @@ -111,8 +108,7 @@ export class InputPasswordComponent implements OnInit { @Input() userId?: UserId; @Input() loading = false; - @Input() masterPasswordPolicyOptions: MasterPasswordPolicyOptions | undefined = undefined; - @Input() forceSetPasswordReason?: ForceSetPasswordReason; + @Input() masterPasswordPolicyOptions?: MasterPasswordPolicyOptions; @Input() inlineButtons = false; @Input() primaryButtonText?: Translation; @@ -548,10 +544,6 @@ export class InputPasswordComponent implements OnInit { this.passwordStrengthScore = score; } - /** - * Checks if the flow is either ChangePassword or ChangePasswordWithOptionalUserKeyRotation - * @private - */ protected isChangePasswordFlow(): boolean { return ( this.flow === InputPasswordFlow.ChangePassword ||