mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
* feat(change-password-component): Change Password Update [18720] - Very close to complete. * fix(policy-enforcement): [PM-21085] Fix Bug with Policy Enforcement - Removed temp code to force the state I need to verify correctness. * fix(policy-enforcement): [PM-21085] Fix Bug with Policy Enforcement - Recover account working with change password component. * fix(policy-enforcement): [PM-21085] Fix Bug with Policy Enforcement - Made code more dry. * fix(change-password-component): Change Password Update [18720] - Updates to routing and the extension. Extension is still a wip. * fix(change-password-component): Change Password Update [18720] - Extension routing changes. * feat(change-password-component): Change Password Update [18720] - More extension work * feat(change-password-component): Change Password Update [18720] - Pausing work for now while we wait for product to hear back. * feat(change-password-component): Change Password Update [18720] - Removed duplicated anon layouts. * feat(change-password-component): Change Password Update [18720] - Tidied up code. * feat(change-password-component): Change Password Update [18720] - Small fixes to the styling * feat(change-password-component): Change Password Update [18720] - Adding more content for the routing. * feat(change-password-component): Change Password Update [18720] - Removed circular loop for now. * feat(change-password-component): Change Password Update [18720] - Made comments regarding the change password routing complexities with change-password and auth guard. * feat(change-password-component): Change Password Update [18720] - Undid some changes because they will be conflicts later on. * feat(change-password-component): Change Password Update [18720] - Small directive change. * feat(change-password-component): Change Password Update [18720] - Small changes and added some clarification on where I'm blocked * feat(change-password-component): Change Password Update [18720] - Org invite is seemingly working, found one bug to iron out. * refactor(change-password-component): Change Password Update [18720] - Fixed up policy service to be made more clear. * docs(change-password-component): Change Password Update [18720] - Updated documentation. * refactor(change-password-component): Change Password Update [18720] - Routing changes and policy service changes. * fix(change-password-component): Change Password Update [18720] - Wrapping up changes. * feat(change-password-component): Change Password Update [18720] - Should be working fully * feat(change-password-component): Change Password Update [18720] - Found a bug, working on password policy being present on login. * feat(change-password-component): Change Password Update [18720] - Turned on auth guard on other clients for change-password route. * feat(change-password-component): Change Password Update [18720] - Committing intermediate changes. * feat(change-password-component): Change Password Update [18720] - The master password policy endpoint has been added! Should be working. Testing now. * feat(change-password-component): Change Password Update [18720] - Minor fixes. * feat(change-password-component): Change Password Update [18720] - Undid naming change. * feat(change-password-component): Change Password Update [18720] - Removed comment. * feat(change-password-component): Change Password Update [18720] - Removed unneeded code. * fix(change-password-component): Change Password Update [18720] - Took org invite state out of service and made it accessible. * fix(change-password-component): Change Password Update [18720] - Small changes. * fix(change-password-component): Change Password Update [18720] - Split up org invite service into client specific implementations and have them injected into clients properly * feat(change-password-component): Change Password Update [18720] - Stopping work and going to switch to a new branch to pare down some of the solutions that were made to get this over the finish line * feat(change-password-component): Change Password Update [18720] - Started to remove functionality in the login.component and the password login strategy. * feat(change-password-component): Change Password Update [18720] - Removed more unneded changes. * feat(change-password-component): Change Password Update [18720] - Change password clearing state working properly. * fix(change-password-component): Change Password Update [18720] - Added docs and moved web implementation. * comments(change-password-component): Change Password Update [18720] - Added more notes. * test(change-password-component): Change Password Update [18720] - Added in tests for policy service. * comment(change-password-component): Change Password Update [18720] - Updated doc with correct ticket number. * comment(change-password-component): Change Password Update [18720] - Fixed doc. * test(change-password-component): Change Password Update [18720] - Fixed tests. * test(change-password-component): Change Password Update [18720] - Fixed linting errors. Have more tests to fix. * test(change-password-component): Change Password Update [18720] - Added back in ignore for typesafety. * fix(change-password-component): Change Password Update [18720] - Fixed other type issues. * test(change-password-component): Change Password Update [18720] - Fixed tests. * test(change-password-component): Change Password Update [18720] - Fixed more tests. * test(change-password-component): Change Password Update [18720] - Fixed tiny duplicate code. * fix(change-password-component): Change Password Update [18720] - Fixed desktop component. * fix(change-password-component): Change Password Update [18720] - Removed unused code * fix(change-password-component): Change Password Update [18720] - Fixed locales. * fix(change-password-component): Change Password Update [18720] - Removed tracing. * fix(change-password-component): Change Password Update [18720] - Removed duplicative services module entry. * fix(change-password-component): Change Password Update [18720] - Added comment. * fix(change-password-component): Change Password Update [18720] - Fixed unneeded call in two factor to get user id. * fix(change-password-component): Change Password Update [18720] - Fixed a couple of tiny things. * fix(change-password-component): Change Password Update [18720] - Added comment for later fix. * fix(change-password-component): Change Password Update [18720] - Fixed linting error. * PM-18720 - AuthGuard - move call to get isChangePasswordFlagOn down after other conditions for efficiency. * PM-18720 - PasswordLoginStrategy tests - test new feature flagged combine org invite policies logic for weak password evaluation. * PM-18720 - CLI - fix dep issue * PM-18720 - ChangePasswordComp - extract change password warning up out of input password component * PM-18720 - InputPassword - remove unused dependency. * PM-18720 - ChangePasswordComp - add callout dep * PM-18720 - Revert all anon-layout changes * PM-18720 - Anon Layout - finish reverting changes. * PM-18720 - WIP move of change password out of libs/auth * PM-18720 - Clean up remaining imports from moving change password out of libs/auth * PM-18720 - Add change-password barrel file for better import grouping * PM-18720 - Change Password comp - restore maxWidth * PM-18720 - After merge, fix errors * PM-18720 - Desktop - fix api service import * PM-18720 - NDV - fix routing. * PM-18720 - Change Password Comp - add logout service todo * PM-18720 - PasswordSettings - per feedback, component is already feature flagged behind PM16117_ChangeExistingPasswordRefactor so we can just delete the replaced callout (new text is in change-password comp) * PM-18720 - Routing Modules - properly flag new component behind feature flag. * PM-18720 - SSO Login Strategy - fix config service import since it is now in shared deps from main merge. * PM-18720 - Fix SSO login strategy tests * PM-18720 - Default Policy Service - address AC PR feedback --------- Co-authored-by: Jared Snider <jsnider@bitwarden.com> Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com>
233 lines
7.5 KiB
TypeScript
233 lines
7.5 KiB
TypeScript
// FIXME: Update this file to be type safe and remove this and next line
|
|
// @ts-strict-ignore
|
|
import { Directive, OnDestroy, OnInit } from "@angular/core";
|
|
import { Subject, firstValueFrom, map, switchMap, takeUntil } from "rxjs";
|
|
|
|
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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
|
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
|
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
|
import { UserKey, MasterKey } from "@bitwarden/common/types/key";
|
|
import { DialogService, ToastService } from "@bitwarden/components";
|
|
import { KdfConfig, KdfConfigService, KeyService } from "@bitwarden/key-management";
|
|
|
|
import { PasswordColorText } from "../../tools/password-strength/password-strength.component";
|
|
|
|
@Directive()
|
|
export class ChangePasswordComponent implements OnInit, OnDestroy {
|
|
masterPassword: string;
|
|
masterPasswordRetype: string;
|
|
formPromise: Promise<any>;
|
|
enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
|
passwordStrengthResult: any;
|
|
color: string;
|
|
text: string;
|
|
leakedPassword: boolean;
|
|
minimumLength = Utils.minimumPasswordLength;
|
|
|
|
protected email: string;
|
|
protected kdfConfig: KdfConfig;
|
|
|
|
protected destroy$ = new Subject<void>();
|
|
|
|
constructor(
|
|
protected accountService: AccountService,
|
|
protected dialogService: DialogService,
|
|
protected i18nService: I18nService,
|
|
protected kdfConfigService: KdfConfigService,
|
|
protected keyService: KeyService,
|
|
protected masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
|
protected messagingService: MessagingService,
|
|
protected platformUtilsService: PlatformUtilsService,
|
|
protected policyService: PolicyService,
|
|
protected toastService: ToastService,
|
|
) {}
|
|
|
|
async ngOnInit() {
|
|
this.email = await firstValueFrom(
|
|
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
|
);
|
|
this.accountService.activeAccount$
|
|
.pipe(
|
|
getUserId,
|
|
switchMap((userId) => this.policyService.masterPasswordPolicyOptions$(userId)),
|
|
takeUntil(this.destroy$),
|
|
)
|
|
.subscribe(
|
|
(enforcedPasswordPolicyOptions) =>
|
|
(this.enforcedPolicyOptions ??= enforcedPasswordPolicyOptions),
|
|
);
|
|
|
|
if (this.enforcedPolicyOptions?.minLength) {
|
|
this.minimumLength = this.enforcedPolicyOptions.minLength;
|
|
}
|
|
}
|
|
|
|
ngOnDestroy(): void {
|
|
this.destroy$.next();
|
|
this.destroy$.complete();
|
|
}
|
|
|
|
async submit() {
|
|
if (!(await this.strongPassword())) {
|
|
return;
|
|
}
|
|
|
|
if (!(await this.setupSubmitActions())) {
|
|
return;
|
|
}
|
|
|
|
const [userId, email] = await firstValueFrom(
|
|
this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])),
|
|
);
|
|
|
|
if (this.kdfConfig == null) {
|
|
this.kdfConfig = await this.kdfConfigService.getKdfConfig(userId);
|
|
}
|
|
|
|
// Create new master key
|
|
const newMasterKey = await this.keyService.makeMasterKey(
|
|
this.masterPassword,
|
|
email.trim().toLowerCase(),
|
|
this.kdfConfig,
|
|
);
|
|
const newMasterKeyHash = await this.keyService.hashMasterKey(this.masterPassword, newMasterKey);
|
|
|
|
let newProtectedUserKey: [UserKey, EncString] = null;
|
|
const userKey = await this.keyService.getUserKey();
|
|
if (userKey == null) {
|
|
newProtectedUserKey = await this.keyService.makeUserKey(newMasterKey);
|
|
} else {
|
|
newProtectedUserKey = await this.keyService.encryptUserKeyWithMasterKey(newMasterKey);
|
|
}
|
|
|
|
await this.performSubmitActions(newMasterKeyHash, newMasterKey, newProtectedUserKey);
|
|
}
|
|
|
|
async setupSubmitActions(): Promise<boolean> {
|
|
// Override in sub-class
|
|
// Can be used for additional validation and/or other processes the should occur before changing passwords
|
|
return true;
|
|
}
|
|
|
|
async performSubmitActions(
|
|
newMasterKeyHash: string,
|
|
newMasterKey: MasterKey,
|
|
newUserKey: [UserKey, EncString],
|
|
) {
|
|
// Override in sub-class
|
|
}
|
|
|
|
async strongPassword(): Promise<boolean> {
|
|
if (this.masterPassword == null || this.masterPassword === "") {
|
|
this.toastService.showToast({
|
|
variant: "error",
|
|
title: this.i18nService.t("errorOccurred"),
|
|
message: this.i18nService.t("masterPasswordRequired"),
|
|
});
|
|
return false;
|
|
}
|
|
if (this.masterPassword.length < this.minimumLength) {
|
|
this.toastService.showToast({
|
|
variant: "error",
|
|
title: this.i18nService.t("errorOccurred"),
|
|
message: this.i18nService.t("masterPasswordMinimumlength", this.minimumLength),
|
|
});
|
|
return false;
|
|
}
|
|
if (this.masterPassword !== this.masterPasswordRetype) {
|
|
this.toastService.showToast({
|
|
variant: "error",
|
|
title: this.i18nService.t("errorOccurred"),
|
|
message: this.i18nService.t("masterPassDoesntMatch"),
|
|
});
|
|
return false;
|
|
}
|
|
|
|
const strengthResult = this.passwordStrengthResult;
|
|
|
|
if (
|
|
this.enforcedPolicyOptions != null &&
|
|
!this.policyService.evaluateMasterPassword(
|
|
strengthResult.score,
|
|
this.masterPassword,
|
|
this.enforcedPolicyOptions,
|
|
)
|
|
) {
|
|
this.toastService.showToast({
|
|
variant: "error",
|
|
title: this.i18nService.t("errorOccurred"),
|
|
message: this.i18nService.t("masterPasswordPolicyRequirementsNotMet"),
|
|
});
|
|
return false;
|
|
}
|
|
|
|
const weakPassword = strengthResult != null && strengthResult.score < 3;
|
|
|
|
if (weakPassword && this.leakedPassword) {
|
|
const result = await this.dialogService.openSimpleDialog({
|
|
title: { key: "weakAndExposedMasterPassword" },
|
|
content: { key: "weakAndBreachedMasterPasswordDesc" },
|
|
type: "warning",
|
|
});
|
|
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if (weakPassword) {
|
|
const result = await this.dialogService.openSimpleDialog({
|
|
title: { key: "weakMasterPassword" },
|
|
content: { key: "weakMasterPasswordDesc" },
|
|
type: "warning",
|
|
});
|
|
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
}
|
|
if (this.leakedPassword) {
|
|
const result = await this.dialogService.openSimpleDialog({
|
|
title: { key: "exposedMasterPassword" },
|
|
content: { key: "exposedMasterPasswordDesc" },
|
|
type: "warning",
|
|
});
|
|
|
|
if (!result) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
async logOut() {
|
|
const confirmed = await this.dialogService.openSimpleDialog({
|
|
title: { key: "logOut" },
|
|
content: { key: "logOutConfirmation" },
|
|
acceptButtonText: { key: "logOut" },
|
|
type: "warning",
|
|
});
|
|
|
|
if (confirmed) {
|
|
this.messagingService.send("logout");
|
|
}
|
|
}
|
|
|
|
getStrengthResult(result: any) {
|
|
this.passwordStrengthResult = result;
|
|
}
|
|
|
|
getPasswordScoreText(event: PasswordColorText) {
|
|
this.color = event.color;
|
|
this.text = event.text;
|
|
}
|
|
}
|