1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-15 16:05:03 +00:00

refactor(change-password-component): Change Password Update [18720] - Routing changes and policy service changes.

This commit is contained in:
Patrick Pimentel
2025-06-03 09:52:38 -04:00
parent c9120b0bef
commit 765766cc41
4 changed files with 71 additions and 93 deletions

View File

@@ -104,10 +104,7 @@ export class WebLoginComponentService
if (
await this.configService.getFeatureFlag(FeatureFlag.PM16117_ChangeExistingPasswordRefactor)
) {
// Properly error if we don't have an org invite
enforcedPasswordPolicyOptions = await firstValueFrom(
this.policyService.masterPasswordPolicyOptionsPriorToSync$(policies),
);
enforcedPasswordPolicyOptions = this.policyService.combineMasterPasswordPolicies(policies);
} else {
enforcedPasswordPolicyOptions = await firstValueFrom(
this.accountService.activeAccount$.pipe(

View File

@@ -14,8 +14,10 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
export const authGuard: CanActivateFn = async (
@@ -28,6 +30,7 @@ export const authGuard: CanActivateFn = async (
const keyConnectorService = inject(KeyConnectorService);
const accountService = inject(AccountService);
const masterPasswordService = inject(MasterPasswordServiceAbstraction);
const configService = inject(ConfigService);
const authStatus = await authService.getAuthStatus();
@@ -66,10 +69,17 @@ export const authGuard: CanActivateFn = async (
}
if (
forceSetPasswordReason !== ForceSetPasswordReason.None &&
!routerState.url.includes("update-temp-password")
(forceSetPasswordReason !== ForceSetPasswordReason.None &&
!routerState.url.includes("update-temp-password")) ||
!routerState.url.includes("change-password")
) {
return router.createUrlTree(["/update-temp-password"]);
const SetInitialPasswordRefactorFlagOn = await configService.getFeatureFlag(
FeatureFlag.PM16117_ChangeExistingPasswordRefactor,
);
const route = SetInitialPasswordRefactorFlagOn ? "/change-password" : "/update-temp-password";
return router.createUrlTree([route]);
}
return true;

View File

@@ -44,8 +44,6 @@ export abstract class PolicyService {
* @param policies The policies to be evaluated; if null or undefined, it will default to using policies from sync data.
* @returns a set of options which represent the minimum Master Password settings that the user must
* comply with in order to comply with **all** applicable Master Password policies.
*
* @deprecated Deprecating because the parameters can be made more strict and clear.
*/
abstract masterPasswordPolicyOptions$: (
userId: UserId,
@@ -53,27 +51,12 @@ export abstract class PolicyService {
) => Observable<MasterPasswordPolicyOptions | undefined>;
/**
* Combines all Master Password policies that apply to the user.
* Used for after a login / sync has occurred and the policy state has been set in state.
* @param userId The user against whom the policy needs to be enforced.
* @returns a set of options which represent the minimum Master Password settings that the user must
* comply with in order to comply with **all** applicable Master Password policies.
* Combines all Master Password policies that are passed in.
* @param policies
*/
abstract masterPasswordPolicyOptionsByUserId$: (
userId: UserId,
) => Observable<MasterPasswordPolicyOptions | undefined>;
/**
* Combines all Master Password policies that apply to the user that comes from the policies that
* are passed to this function.
* This would be used prior to obtaining a user id, such as before login for an org invite.
* @param policies The policies to be evaluated.
* @returns a set of options which represent the minimum Master Password settings that the user must
* comply with in order to comply with **all** applicable Master Password policies.
*/
abstract masterPasswordPolicyOptionsPriorToSync$: (
abstract combineMasterPasswordPolicies(
policies: Policy[],
) => Observable<MasterPasswordPolicyOptions | undefined>;
): MasterPasswordPolicyOptions | undefined;
/**
* Evaluates whether a proposed Master Password complies with all Master Password policies that apply to the user.

View File

@@ -87,19 +87,62 @@ export class DefaultPolicyService implements PolicyService {
policies?: Policy[],
): Observable<MasterPasswordPolicyOptions | undefined> {
const policies$ = policies ? of(policies) : this.policies$(userId);
return policies$.pipe(map((obsPolicies) => this.policyMapping(obsPolicies)));
return policies$.pipe(map((obsPolicies) => this.combineMasterPasswordPolicies(obsPolicies)));
}
masterPasswordPolicyOptionsByUserId$(
userId: UserId,
): Observable<MasterPasswordPolicyOptions | undefined> {
return this.policies$(userId).pipe(map((obsPolicies) => this.policyMapping(obsPolicies)));
}
combineMasterPasswordPolicies(policies: Policy[]): MasterPasswordPolicyOptions | undefined {
let enforcedOptions: MasterPasswordPolicyOptions | undefined = undefined;
const filteredPolicies = policies.filter((p) => p.type === PolicyType.MasterPassword) ?? [];
masterPasswordPolicyOptionsPriorToSync$(
policies: Policy[],
): Observable<MasterPasswordPolicyOptions | undefined> {
return of(policies).pipe(map((obsPolicies) => this.policyMapping(obsPolicies)));
if (filteredPolicies.length === 0) {
return;
}
filteredPolicies.forEach((currentPolicy) => {
if (!currentPolicy.enabled || !currentPolicy.data) {
return;
}
if (!enforcedOptions) {
enforcedOptions = new MasterPasswordPolicyOptions();
}
if (
currentPolicy.data.minComplexity != null &&
currentPolicy.data.minComplexity > enforcedOptions.minComplexity
) {
enforcedOptions.minComplexity = currentPolicy.data.minComplexity;
}
if (
currentPolicy.data.minLength != null &&
currentPolicy.data.minLength > enforcedOptions.minLength
) {
enforcedOptions.minLength = currentPolicy.data.minLength;
}
if (currentPolicy.data.requireUpper) {
enforcedOptions.requireUpper = true;
}
if (currentPolicy.data.requireLower) {
enforcedOptions.requireLower = true;
}
if (currentPolicy.data.requireNumbers) {
enforcedOptions.requireNumbers = true;
}
if (currentPolicy.data.requireSpecial) {
enforcedOptions.requireSpecial = true;
}
if (currentPolicy.data.enforceOnLogin) {
enforcedOptions.enforceOnLogin = true;
}
});
return enforcedOptions;
}
evaluateMasterPassword(
@@ -197,59 +240,4 @@ export class DefaultPolicyService implements PolicyService {
return organization.canManagePolicies;
}
}
private policyMapping(obsPolicies: Policy[]): MasterPasswordPolicyOptions | undefined {
let enforcedOptions: MasterPasswordPolicyOptions | undefined = undefined;
const filteredPolicies = obsPolicies.filter((p) => p.type === PolicyType.MasterPassword) ?? [];
if (filteredPolicies.length === 0) {
return;
}
filteredPolicies.forEach((currentPolicy) => {
if (!currentPolicy.enabled || !currentPolicy.data) {
return;
}
if (!enforcedOptions) {
enforcedOptions = new MasterPasswordPolicyOptions();
}
if (
currentPolicy.data.minComplexity != null &&
currentPolicy.data.minComplexity > enforcedOptions.minComplexity
) {
enforcedOptions.minComplexity = currentPolicy.data.minComplexity;
}
if (
currentPolicy.data.minLength != null &&
currentPolicy.data.minLength > enforcedOptions.minLength
) {
enforcedOptions.minLength = currentPolicy.data.minLength;
}
if (currentPolicy.data.requireUpper) {
enforcedOptions.requireUpper = true;
}
if (currentPolicy.data.requireLower) {
enforcedOptions.requireLower = true;
}
if (currentPolicy.data.requireNumbers) {
enforcedOptions.requireNumbers = true;
}
if (currentPolicy.data.requireSpecial) {
enforcedOptions.requireSpecial = true;
}
if (currentPolicy.data.enforceOnLogin) {
enforcedOptions.enforceOnLogin = true;
}
});
return enforcedOptions;
}
}