mirror of
https://github.com/bitwarden/browser
synced 2026-02-07 04:03:29 +00:00
refactor(change-password-component): Change Password Update [18720] - Fixed up policy service to be made more clear.
This commit is contained in:
@@ -104,9 +104,9 @@ export class WebLoginComponentService
|
||||
if (
|
||||
await this.configService.getFeatureFlag(FeatureFlag.PM16117_ChangeExistingPasswordRefactor)
|
||||
) {
|
||||
// Properly error if we don't have an org invite with
|
||||
// Properly error if we don't have an org invite
|
||||
enforcedPasswordPolicyOptions = await firstValueFrom(
|
||||
this.policyService.masterPasswordPolicyOptions$(orgInvite.userId, policies),
|
||||
this.policyService.masterPasswordPolicyOptionsPriorToSync$(policies),
|
||||
);
|
||||
} else {
|
||||
enforcedPasswordPolicyOptions = await firstValueFrom(
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
import { Params } from "@angular/router";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
export class OrganizationInvite {
|
||||
email: string;
|
||||
initOrganization: boolean;
|
||||
@@ -14,7 +12,6 @@ export class OrganizationInvite {
|
||||
organizationName: string;
|
||||
organizationUserId: string;
|
||||
token: string;
|
||||
userId: UserId;
|
||||
|
||||
static fromJSON(json: Jsonify<OrganizationInvite>): OrganizationInvite | null {
|
||||
if (json == null) {
|
||||
@@ -38,7 +35,6 @@ export class OrganizationInvite {
|
||||
organizationName: params.organizationName,
|
||||
organizationUserId: params.organizationUserId,
|
||||
token: params.token,
|
||||
userId: params.userId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,8 @@ export class LoginComponent implements OnInit, OnDestroy {
|
||||
isKnownDevice = false;
|
||||
loginUiState: LoginUiState = LoginUiState.EMAIL_ENTRY;
|
||||
|
||||
passwordPoliciesFromOrgInvite?: Policy[];
|
||||
|
||||
formGroup = this.formBuilder.group(
|
||||
{
|
||||
email: ["", [Validators.required, Validators.email]],
|
||||
@@ -238,8 +240,15 @@ export class LoginComponent implements OnInit, OnDestroy {
|
||||
this.loginComponentService.getOrgPoliciesFromOrgInvite
|
||||
) {
|
||||
const orgPoliciesFromInvite = await this.loginComponentService.getOrgPoliciesFromOrgInvite();
|
||||
const orgPolicies = orgPoliciesFromInvite?.enforcedPasswordPolicyOptions ?? undefined;
|
||||
credentials = new PasswordLoginCredentials(email, masterPassword, undefined, orgPolicies);
|
||||
const orgMasterPasswordPolicyOptions =
|
||||
orgPoliciesFromInvite?.enforcedPasswordPolicyOptions ?? undefined;
|
||||
this.passwordPoliciesFromOrgInvite = orgPoliciesFromInvite?.policies;
|
||||
credentials = new PasswordLoginCredentials(
|
||||
email,
|
||||
masterPassword,
|
||||
undefined,
|
||||
orgMasterPasswordPolicyOptions,
|
||||
);
|
||||
} else {
|
||||
credentials = new PasswordLoginCredentials(email, masterPassword);
|
||||
}
|
||||
@@ -329,8 +338,13 @@ export class LoginComponent implements OnInit, OnDestroy {
|
||||
// The AuthGuard will handle routing to update-temp-password based on state
|
||||
|
||||
if (
|
||||
!(await this.configService.getFeatureFlag(FeatureFlag.PM16117_ChangeExistingPasswordRefactor))
|
||||
await this.configService.getFeatureFlag(FeatureFlag.PM16117_ChangeExistingPasswordRefactor)
|
||||
) {
|
||||
// Check if we had a
|
||||
if (this.passwordPoliciesFromOrgInvite) {
|
||||
await this.setPoliciesIntoState(authResult.userId, this.passwordPoliciesFromOrgInvite);
|
||||
}
|
||||
} else {
|
||||
// TODO: PM-18269 - evaluate if we can combine this with the
|
||||
// password evaluation done in the password login strategy.
|
||||
// If there's an existing org invite, use it to get the org's password policies
|
||||
|
||||
@@ -98,6 +98,8 @@ export class PasswordLoginStrategy extends LoginStrategy {
|
||||
await this.buildDeviceRequest(),
|
||||
);
|
||||
|
||||
// TODO: add master password policy conditions to the cache so that it is available after 2fa for password evaluation
|
||||
|
||||
this.cache.next(data);
|
||||
|
||||
const [authResult, identityResponse] = await this.startLogIn();
|
||||
|
||||
@@ -44,12 +44,37 @@ 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,
|
||||
policies?: Policy[],
|
||||
) => 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.
|
||||
*/
|
||||
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; 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.
|
||||
*/
|
||||
abstract masterPasswordPolicyOptionsPriorToSync$: (
|
||||
policies: Policy[],
|
||||
) => Observable<MasterPasswordPolicyOptions | undefined>;
|
||||
|
||||
/**
|
||||
* Evaluates whether a proposed Master Password complies with all Master Password policies that apply to the user.
|
||||
*/
|
||||
|
||||
@@ -87,63 +87,19 @@ export class DefaultPolicyService implements PolicyService {
|
||||
policies?: Policy[],
|
||||
): Observable<MasterPasswordPolicyOptions | undefined> {
|
||||
const policies$ = policies ? of(policies) : this.policies$(userId);
|
||||
return policies$.pipe(
|
||||
map((obsPolicies) => {
|
||||
let enforcedOptions: MasterPasswordPolicyOptions | undefined = undefined;
|
||||
const filteredPolicies =
|
||||
obsPolicies.filter((p) => p.type === PolicyType.MasterPassword) ?? [];
|
||||
return policies$.pipe(map((obsPolicies) => this.policyMapping(obsPolicies)));
|
||||
}
|
||||
|
||||
if (filteredPolicies.length === 0) {
|
||||
return;
|
||||
}
|
||||
masterPasswordPolicyOptionsByUserId$(
|
||||
userId: UserId,
|
||||
): Observable<MasterPasswordPolicyOptions | undefined> {
|
||||
return this.policies$(userId).pipe(map((obsPolicies) => this.policyMapping(obsPolicies)));
|
||||
}
|
||||
|
||||
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;
|
||||
}),
|
||||
);
|
||||
masterPasswordPolicyOptionsPriorToSync$(
|
||||
policies: Policy[],
|
||||
): Observable<MasterPasswordPolicyOptions | undefined> {
|
||||
return of(policies).pipe(map((obsPolicies) => this.policyMapping(obsPolicies)));
|
||||
}
|
||||
|
||||
evaluateMasterPassword(
|
||||
@@ -241,4 +197,59 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user