mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 13:53:34 +00:00
[SG-414] Refactor password strength component (#3186)
* moved password strength to libs * refactored password strength component * made changes on desktop and browser to reuse component * resolved suggestions from PR review * shared module restructure * shared module restructure
This commit is contained in:
@@ -25,11 +25,8 @@
|
|||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPassword">
|
<label for="masterPassword">
|
||||||
{{ "masterPass" | i18n }}
|
{{ "masterPass" | i18n }}
|
||||||
<strong
|
<strong class="sub-label text-{{ color }}" *ngIf="text">
|
||||||
class="sub-label text-{{ masterPasswordScoreColor }}"
|
{{ text }}
|
||||||
*ngIf="masterPasswordScoreText"
|
|
||||||
>
|
|
||||||
{{ masterPasswordScoreText }}
|
|
||||||
</strong>
|
</strong>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -38,7 +35,6 @@
|
|||||||
class="monospaced"
|
class="monospaced"
|
||||||
formControlName="masterPassword"
|
formControlName="masterPassword"
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
(input)="updatePasswordStrength()"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
@@ -58,17 +54,14 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress">
|
<app-password-strength
|
||||||
<div
|
[password]="formGroup.get('masterPassword')?.value"
|
||||||
class="progress-bar bg-{{ masterPasswordScoreColor }}"
|
[email]="formGroup.get('email')?.value"
|
||||||
role="progressbar"
|
[name]="formGroup.get('name')?.value"
|
||||||
aria-valuenow="0"
|
(passwordStrengthResult)="getStrengthResult($event)"
|
||||||
aria-valuemin="0"
|
(passwordScoreColor)="getPasswordScoreText($event)"
|
||||||
aria-valuemax="100"
|
>
|
||||||
[ngStyle]="{ width: masterPasswordScoreWidth + '%' }"
|
</app-password-strength>
|
||||||
attr.aria-valuenow="{{ masterPasswordScoreWidth }}"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ import { StateService } from "@bitwarden/common/abstractions/state.service";
|
|||||||
templateUrl: "register.component.html",
|
templateUrl: "register.component.html",
|
||||||
})
|
})
|
||||||
export class RegisterComponent extends BaseRegisterComponent {
|
export class RegisterComponent extends BaseRegisterComponent {
|
||||||
|
color: string;
|
||||||
|
text: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
formValidationErrorService: FormValidationErrorsService,
|
formValidationErrorService: FormValidationErrorsService,
|
||||||
formBuilder: UntypedFormBuilder,
|
formBuilder: UntypedFormBuilder,
|
||||||
|
|||||||
@@ -42,10 +42,10 @@
|
|||||||
<label for="masterPassword"
|
<label for="masterPassword"
|
||||||
>{{ "masterPass" | i18n }}
|
>{{ "masterPass" | i18n }}
|
||||||
<strong
|
<strong
|
||||||
class="sub-label text-{{ masterPasswordScoreColor }}"
|
class="sub-label text-{{ passwordStrengthComponent?.masterPasswordScoreColor }}"
|
||||||
*ngIf="masterPasswordScoreText"
|
*ngIf="passwordStrengthComponent?.masterPasswordScoreText"
|
||||||
>
|
>
|
||||||
{{ masterPasswordScoreText }}
|
{{ passwordStrengthComponent?.masterPasswordScoreText }}
|
||||||
</strong>
|
</strong>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -55,7 +55,6 @@
|
|||||||
class="monospaced"
|
class="monospaced"
|
||||||
[(ngModel)]="masterPassword"
|
[(ngModel)]="masterPassword"
|
||||||
required
|
required
|
||||||
(input)="updatePasswordStrength()"
|
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -76,17 +75,14 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress">
|
|
||||||
<div
|
<app-password-strength
|
||||||
class="progress-bar bg-{{ masterPasswordScoreColor }}"
|
[password]="masterPassword"
|
||||||
role="progressbar"
|
[email]="email"
|
||||||
aria-valuenow="0"
|
(passwordStrengthResult)="getStrengthResult($event)"
|
||||||
aria-valuemin="0"
|
(passwordScoreColor)="getPasswordScoreText($event)"
|
||||||
aria-valuemax="100"
|
>
|
||||||
[ngStyle]="{ width: masterPasswordScoreWidth + '%' }"
|
</app-password-strength>
|
||||||
attr.aria-valuenow="{{ masterPasswordScoreWidth }}"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
|
|||||||
@@ -44,34 +44,4 @@ export class SetPasswordComponent extends BaseSetPasswordComponent {
|
|||||||
stateService
|
stateService
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get masterPasswordScoreWidth() {
|
|
||||||
return this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
get masterPasswordScoreColor() {
|
|
||||||
switch (this.masterPasswordScore) {
|
|
||||||
case 4:
|
|
||||||
return "success";
|
|
||||||
case 3:
|
|
||||||
return "primary";
|
|
||||||
case 2:
|
|
||||||
return "warning";
|
|
||||||
default:
|
|
||||||
return "danger";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get masterPasswordScoreText() {
|
|
||||||
switch (this.masterPasswordScore) {
|
|
||||||
case 4:
|
|
||||||
return this.i18nService.t("strong");
|
|
||||||
case 3:
|
|
||||||
return this.i18nService.t("good");
|
|
||||||
case 2:
|
|
||||||
return this.i18nService.t("weak");
|
|
||||||
default:
|
|
||||||
return this.masterPasswordScore != null ? this.i18nService.t("weak") : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,11 +30,8 @@
|
|||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPassword">
|
<label for="masterPassword">
|
||||||
{{ "masterPass" | i18n }}
|
{{ "masterPass" | i18n }}
|
||||||
<strong
|
<strong class="sub-label text-{{ color }}" *ngIf="text">
|
||||||
class="sub-label text-{{ masterPasswordScoreStyle.Color }}"
|
{{ text }}
|
||||||
*ngIf="masterPasswordScoreStyle.Text"
|
|
||||||
>
|
|
||||||
{{ masterPasswordScoreStyle.Text }}
|
|
||||||
</strong>
|
</strong>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -45,7 +42,6 @@
|
|||||||
[(ngModel)]="masterPassword"
|
[(ngModel)]="masterPassword"
|
||||||
required
|
required
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
(input)="updatePasswordStrength()"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
@@ -65,17 +61,13 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress">
|
<app-password-strength
|
||||||
<div
|
[password]="masterPassword"
|
||||||
class="progress-bar bg-{{ masterPasswordScoreStyle.Color }}"
|
[email]="email"
|
||||||
role="progressbar"
|
(passwordStrengthResult)="getStrengthResult($event)"
|
||||||
aria-valuenow="0"
|
(passwordScoreColor)="getPasswordScoreText($event)"
|
||||||
aria-valuemin="0"
|
>
|
||||||
aria-valuemax="100"
|
</app-password-strength>
|
||||||
[ngStyle]="{ width: masterPasswordScoreStyle.Width + '%' }"
|
|
||||||
attr.aria-valuenow="{{ masterPasswordScoreStyle.Width }}"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,47 +12,11 @@ import { PolicyService } from "@bitwarden/common/abstractions/policy.service";
|
|||||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||||
import { SyncService } from "@bitwarden/common/abstractions/sync.service";
|
import { SyncService } from "@bitwarden/common/abstractions/sync.service";
|
||||||
|
|
||||||
interface MasterPasswordScore {
|
|
||||||
Color: string;
|
|
||||||
Text: string;
|
|
||||||
Width: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-update-temp-password",
|
selector: "app-update-temp-password",
|
||||||
templateUrl: "update-temp-password.component.html",
|
templateUrl: "update-temp-password.component.html",
|
||||||
})
|
})
|
||||||
export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent {
|
export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent {
|
||||||
get masterPasswordScoreStyle(): MasterPasswordScore {
|
|
||||||
const scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
|
||||||
switch (this.masterPasswordScore) {
|
|
||||||
case 4:
|
|
||||||
return {
|
|
||||||
Color: "bg-success",
|
|
||||||
Text: "strong",
|
|
||||||
Width: scoreWidth,
|
|
||||||
};
|
|
||||||
case 3:
|
|
||||||
return {
|
|
||||||
Color: "bg-primary",
|
|
||||||
Text: "good",
|
|
||||||
Width: scoreWidth,
|
|
||||||
};
|
|
||||||
case 2:
|
|
||||||
return {
|
|
||||||
Color: "bg-warning",
|
|
||||||
Text: "weak",
|
|
||||||
Width: scoreWidth,
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return {
|
|
||||||
Color: "bg-danger",
|
|
||||||
Text: "weak",
|
|
||||||
Width: scoreWidth,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
|||||||
@@ -18,11 +18,8 @@
|
|||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPassword">
|
<label for="masterPassword">
|
||||||
{{ "masterPass" | i18n }}
|
{{ "masterPass" | i18n }}
|
||||||
<strong
|
<strong class="sub-label text-{{ color }}" *ngIf="text">
|
||||||
class="sub-label text-{{ masterPasswordScoreColor }}"
|
{{ text }}
|
||||||
*ngIf="masterPasswordScoreText"
|
|
||||||
>
|
|
||||||
{{ masterPasswordScoreText }}
|
|
||||||
</strong>
|
</strong>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -30,7 +27,6 @@
|
|||||||
type="{{ showPassword ? 'text' : 'password' }}"
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
class="monospaced"
|
class="monospaced"
|
||||||
formControlName="masterPassword"
|
formControlName="masterPassword"
|
||||||
(input)="updatePasswordStrength()"
|
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,17 +47,14 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress">
|
<app-password-strength
|
||||||
<div
|
[password]="formGroup.get('masterPassword')?.value"
|
||||||
class="progress-bar bg-{{ masterPasswordScoreColor }}"
|
[email]="formGroup.get('email')?.value"
|
||||||
role="progressbar"
|
[name]="formGroup.get('name')?.value"
|
||||||
aria-valuenow="0"
|
(passwordStrengthResult)="getStrengthResult($event)"
|
||||||
aria-valuemin="0"
|
(passwordScoreColor)="getPasswordScoreText($event)"
|
||||||
aria-valuemax="100"
|
>
|
||||||
[ngStyle]="{ width: masterPasswordScoreWidth + '%' }"
|
</app-password-strength>
|
||||||
attr.aria-valuenow="{{ masterPasswordScoreWidth }}"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
|
|||||||
@@ -37,11 +37,8 @@
|
|||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPassword"
|
<label for="masterPassword"
|
||||||
>{{ "masterPass" | i18n }}
|
>{{ "masterPass" | i18n }}
|
||||||
<strong
|
<strong class="sub-label text-{{ color }}" *ngIf="text">
|
||||||
class="sub-label text-{{ masterPasswordScoreColor }}"
|
{{ text }}
|
||||||
*ngIf="masterPasswordScoreText"
|
|
||||||
>
|
|
||||||
{{ masterPasswordScoreText }}
|
|
||||||
</strong>
|
</strong>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -51,7 +48,6 @@
|
|||||||
class="monospaced"
|
class="monospaced"
|
||||||
[(ngModel)]="masterPassword"
|
[(ngModel)]="masterPassword"
|
||||||
required
|
required
|
||||||
(input)="updatePasswordStrength()"
|
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -72,17 +68,13 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress">
|
<app-password-strength
|
||||||
<div
|
[password]="masterPassword"
|
||||||
class="progress-bar bg-{{ masterPasswordScoreColor }}"
|
[email]="email"
|
||||||
role="progressbar"
|
(passwordStrengthResult)="getStrengthResult($event)"
|
||||||
aria-valuenow="0"
|
(passwordScoreColor)="getPasswordScoreText($event)"
|
||||||
aria-valuemin="0"
|
>
|
||||||
aria-valuemax="100"
|
</app-password-strength>
|
||||||
[ngStyle]="{ width: masterPasswordScoreWidth + '%' }"
|
|
||||||
attr.aria-valuenow="{{ masterPasswordScoreWidth }}"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
|
|||||||
@@ -50,36 +50,6 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get masterPasswordScoreWidth() {
|
|
||||||
return this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
get masterPasswordScoreColor() {
|
|
||||||
switch (this.masterPasswordScore) {
|
|
||||||
case 4:
|
|
||||||
return "success";
|
|
||||||
case 3:
|
|
||||||
return "primary";
|
|
||||||
case 2:
|
|
||||||
return "warning";
|
|
||||||
default:
|
|
||||||
return "danger";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get masterPasswordScoreText() {
|
|
||||||
switch (this.masterPasswordScore) {
|
|
||||||
case 4:
|
|
||||||
return this.i18nService.t("strong");
|
|
||||||
case 3:
|
|
||||||
return this.i18nService.t("good");
|
|
||||||
case 2:
|
|
||||||
return this.i18nService.t("weak");
|
|
||||||
default:
|
|
||||||
return this.masterPasswordScore != null ? this.i18nService.t("weak") : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
await super.ngOnInit();
|
await super.ngOnInit();
|
||||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
|
||||||
|
|||||||
@@ -16,11 +16,8 @@
|
|||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
<label for="masterPassword">
|
<label for="masterPassword">
|
||||||
{{ "masterPass" | i18n }}
|
{{ "masterPass" | i18n }}
|
||||||
<strong
|
<strong class="sub-label text-{{ color }}" *ngIf="text">
|
||||||
class="sub-label text-{{ masterPasswordScoreStyle.Color }}"
|
{{ text }}
|
||||||
*ngIf="masterPasswordScoreStyle.Text"
|
|
||||||
>
|
|
||||||
{{ masterPasswordScoreStyle.Text }}
|
|
||||||
</strong>
|
</strong>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -31,7 +28,6 @@
|
|||||||
[(ngModel)]="masterPassword"
|
[(ngModel)]="masterPassword"
|
||||||
required
|
required
|
||||||
[appAutofocus]="masterPassword === ''"
|
[appAutofocus]="masterPassword === ''"
|
||||||
(input)="updatePasswordStrength()"
|
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -52,17 +48,13 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress">
|
<app-password-strength
|
||||||
<div
|
[password]="masterPassword"
|
||||||
class="progress-bar bg-{{ masterPasswordScoreStyle.Color }}"
|
[email]="email"
|
||||||
role="progressbar"
|
(passwordStrengthResult)="getStrengthResult($event)"
|
||||||
aria-valuenow="0"
|
(passwordScoreColor)="getPasswordScoreText($event)"
|
||||||
aria-valuemin="0"
|
>
|
||||||
aria-valuemax="100"
|
</app-password-strength>
|
||||||
[ngStyle]="{ width: masterPasswordScoreStyle.Width + '%' }"
|
|
||||||
attr.aria-valuenow="{{ masterPasswordScoreStyle.Width }}"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,46 +12,11 @@ import { PolicyService } from "@bitwarden/common/abstractions/policy.service";
|
|||||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||||
import { SyncService } from "@bitwarden/common/abstractions/sync.service";
|
import { SyncService } from "@bitwarden/common/abstractions/sync.service";
|
||||||
|
|
||||||
interface MasterPasswordScore {
|
|
||||||
Color: string;
|
|
||||||
Text: string;
|
|
||||||
Width: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-update-temp-password",
|
selector: "app-update-temp-password",
|
||||||
templateUrl: "update-temp-password.component.html",
|
templateUrl: "update-temp-password.component.html",
|
||||||
})
|
})
|
||||||
export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent {
|
export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent {
|
||||||
get masterPasswordScoreStyle(): MasterPasswordScore {
|
|
||||||
const scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
|
||||||
switch (this.masterPasswordScore) {
|
|
||||||
case 4:
|
|
||||||
return {
|
|
||||||
Color: "bg-success",
|
|
||||||
Text: "strong",
|
|
||||||
Width: scoreWidth,
|
|
||||||
};
|
|
||||||
case 3:
|
|
||||||
return {
|
|
||||||
Color: "bg-primary",
|
|
||||||
Text: "good",
|
|
||||||
Width: scoreWidth,
|
|
||||||
};
|
|
||||||
case 2:
|
|
||||||
return {
|
|
||||||
Color: "bg-warning",
|
|
||||||
Text: "weak",
|
|
||||||
Width: scoreWidth,
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return {
|
|
||||||
Color: "bg-danger",
|
|
||||||
Text: "weak",
|
|
||||||
Width: scoreWidth,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
constructor(
|
constructor(
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
|
|||||||
@@ -32,11 +32,15 @@
|
|||||||
name="MasterPasswordHash"
|
name="MasterPasswordHash"
|
||||||
class="text-monospace form-control mb-1"
|
class="text-monospace form-control mb-1"
|
||||||
[(ngModel)]="masterPassword"
|
[(ngModel)]="masterPassword"
|
||||||
(input)="updatePasswordStrength()"
|
|
||||||
required
|
required
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
/>
|
/>
|
||||||
<app-password-strength [score]="masterPasswordScore" [showText]="true">
|
<app-password-strength
|
||||||
|
[password]="masterPassword"
|
||||||
|
[email]="email"
|
||||||
|
[showText]="true"
|
||||||
|
(passwordStrengthResult)="getStrengthResult($event)"
|
||||||
|
>
|
||||||
</app-password-strength>
|
</app-password-strength>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -44,13 +44,13 @@
|
|||||||
name="NewMasterPasswordHash"
|
name="NewMasterPasswordHash"
|
||||||
class="form-control mb-1"
|
class="form-control mb-1"
|
||||||
[(ngModel)]="masterPassword"
|
[(ngModel)]="masterPassword"
|
||||||
(input)="updatePasswordStrength()"
|
|
||||||
required
|
required
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
/>
|
/>
|
||||||
<app-password-strength
|
<app-password-strength
|
||||||
[score]="masterPasswordScore"
|
[password]="masterPassword"
|
||||||
|
[email]="email"
|
||||||
[showText]="true"
|
[showText]="true"
|
||||||
></app-password-strength>
|
></app-password-strength>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,11 +21,14 @@
|
|||||||
name="MasterPasswordHash"
|
name="MasterPasswordHash"
|
||||||
class="text-monospace form-control mb-1"
|
class="text-monospace form-control mb-1"
|
||||||
[(ngModel)]="masterPassword"
|
[(ngModel)]="masterPassword"
|
||||||
(input)="updatePasswordStrength()"
|
|
||||||
required
|
required
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
/>
|
/>
|
||||||
<app-password-strength [score]="masterPasswordScore" [showText]="true">
|
<app-password-strength
|
||||||
|
[password]="masterPassword"
|
||||||
|
[email]="email"
|
||||||
|
[showText]="true"
|
||||||
|
>
|
||||||
</app-password-strength>
|
</app-password-strength>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
import { Component, Input, OnChanges } from "@angular/core";
|
|
||||||
|
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: "app-password-strength",
|
|
||||||
templateUrl: "password-strength.component.html",
|
|
||||||
})
|
|
||||||
export class PasswordStrengthComponent implements OnChanges {
|
|
||||||
@Input() score?: number;
|
|
||||||
@Input() showText = false;
|
|
||||||
|
|
||||||
scoreWidth = 0;
|
|
||||||
color = "bg-danger";
|
|
||||||
text: string;
|
|
||||||
|
|
||||||
constructor(private i18nService: I18nService) {}
|
|
||||||
|
|
||||||
ngOnChanges(): void {
|
|
||||||
this.scoreWidth = this.score == null ? 0 : (this.score + 1) * 20;
|
|
||||||
switch (this.score) {
|
|
||||||
case 4:
|
|
||||||
this.color = "bg-success";
|
|
||||||
this.text = this.i18nService.t("strong");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
this.color = "bg-primary";
|
|
||||||
this.text = this.i18nService.t("good");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
this.color = "bg-warning";
|
|
||||||
this.text = this.i18nService.t("weak");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.color = "bg-danger";
|
|
||||||
this.text = this.score != null ? this.i18nService.t("weak") : null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -34,7 +34,6 @@
|
|||||||
<input
|
<input
|
||||||
id="register-form_input_master-password"
|
id="register-form_input_master-password"
|
||||||
bitInput
|
bitInput
|
||||||
(input)="updatePasswordStrength()"
|
|
||||||
type="{{ showPassword ? 'text' : 'password' }}"
|
type="{{ showPassword ? 'text' : 'password' }}"
|
||||||
formControlName="masterPassword"
|
formControlName="masterPassword"
|
||||||
/>
|
/>
|
||||||
@@ -50,7 +49,13 @@
|
|||||||
{{ "masterPassImportant" | i18n }}
|
{{ "masterPassImportant" | i18n }}
|
||||||
</bit-hint>
|
</bit-hint>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
<app-password-strength [score]="masterPasswordScore" [showText]="true">
|
<app-password-strength
|
||||||
|
[password]="formGroup.get('masterPassword')?.value"
|
||||||
|
[email]="formGroup.get('email')?.value"
|
||||||
|
[name]="formGroup.get('name')?.value"
|
||||||
|
[showText]="true"
|
||||||
|
(passwordStrengthResult)="getStrengthResult($event)"
|
||||||
|
>
|
||||||
</app-password-strength>
|
</app-password-strength>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export class RegisterFormComponent extends BaseRegisterComponent {
|
|||||||
if (
|
if (
|
||||||
this.enforcedPolicyOptions != null &&
|
this.enforcedPolicyOptions != null &&
|
||||||
!this.policyService.evaluateMasterPassword(
|
!this.policyService.evaluateMasterPassword(
|
||||||
this.masterPasswordScore,
|
this.passwordStrengthResult.score,
|
||||||
this.formGroup.get("masterPassword")?.value,
|
this.formGroup.get("masterPassword")?.value,
|
||||||
this.enforcedPolicyOptions
|
this.enforcedPolicyOptions
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ import {
|
|||||||
MenuModule,
|
MenuModule,
|
||||||
} from "@bitwarden/components";
|
} from "@bitwarden/components";
|
||||||
|
|
||||||
import { PasswordStrengthComponent } from "../components/password-strength.component";
|
|
||||||
import { PaymentComponent } from "../settings/payment.component";
|
import { PaymentComponent } from "../settings/payment.component";
|
||||||
import { TaxInfoComponent } from "../settings/tax-info.component";
|
import { TaxInfoComponent } from "../settings/tax-info.component";
|
||||||
|
|
||||||
@@ -122,7 +121,7 @@ registerLocaleData(localeZhCn, "zh-CN");
|
|||||||
registerLocaleData(localeZhTw, "zh-TW");
|
registerLocaleData(localeZhTw, "zh-TW");
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [PasswordStrengthComponent, PaymentComponent, TaxInfoComponent],
|
declarations: [PaymentComponent, TaxInfoComponent],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
@@ -158,7 +157,6 @@ registerLocaleData(localeZhTw, "zh-TW");
|
|||||||
MenuModule,
|
MenuModule,
|
||||||
FormFieldModule,
|
FormFieldModule,
|
||||||
SubmitButtonModule,
|
SubmitButtonModule,
|
||||||
PasswordStrengthComponent,
|
|
||||||
PaymentComponent,
|
PaymentComponent,
|
||||||
TaxInfoComponent,
|
TaxInfoComponent,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -53,7 +53,6 @@
|
|||||||
required
|
required
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
(input)="updatePasswordStrength()"
|
|
||||||
/>
|
/>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button
|
<button
|
||||||
@@ -78,7 +77,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<app-password-strength [score]="masterPasswordScore" [showText]="true">
|
<app-password-strength [password]="newPassword" [email]="email" [showText]="true">
|
||||||
</app-password-strength>
|
</app-password-strength>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from "@angular/core";
|
||||||
|
|
||||||
|
import { PasswordStrengthComponent } from "@bitwarden/angular/shared/components/password-strength/password-strength.component";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
@@ -22,13 +23,13 @@ export class ResetPasswordComponent implements OnInit {
|
|||||||
@Input() id: string;
|
@Input() id: string;
|
||||||
@Input() organizationId: string;
|
@Input() organizationId: string;
|
||||||
@Output() onPasswordReset = new EventEmitter();
|
@Output() onPasswordReset = new EventEmitter();
|
||||||
|
@ViewChild(PasswordStrengthComponent) passwordStrengthComponent: PasswordStrengthComponent;
|
||||||
|
|
||||||
enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
||||||
newPassword: string = null;
|
newPassword: string = null;
|
||||||
showPassword = false;
|
showPassword = false;
|
||||||
masterPasswordScore: number;
|
masterPasswordScore: number;
|
||||||
formPromise: Promise<any>;
|
formPromise: Promise<any>;
|
||||||
private newPasswordStrengthTimeout: any;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
@@ -52,7 +53,7 @@ export class ResetPasswordComponent implements OnInit {
|
|||||||
async generatePassword() {
|
async generatePassword() {
|
||||||
const options = (await this.passwordGenerationService.getOptions())[0];
|
const options = (await this.passwordGenerationService.getOptions())[0];
|
||||||
this.newPassword = await this.passwordGenerationService.generatePassword(options);
|
this.newPassword = await this.passwordGenerationService.generatePassword(options);
|
||||||
this.updatePasswordStrength();
|
this.passwordStrengthComponent.updatePasswordStrength(this.newPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
togglePassword() {
|
togglePassword() {
|
||||||
@@ -183,35 +184,4 @@ export class ResetPasswordComponent implements OnInit {
|
|||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePasswordStrength() {
|
|
||||||
if (this.newPasswordStrengthTimeout != null) {
|
|
||||||
clearTimeout(this.newPasswordStrengthTimeout);
|
|
||||||
}
|
|
||||||
this.newPasswordStrengthTimeout = setTimeout(() => {
|
|
||||||
const strengthResult = this.passwordGenerationService.passwordStrength(
|
|
||||||
this.newPassword,
|
|
||||||
this.getPasswordStrengthUserInput()
|
|
||||||
);
|
|
||||||
this.masterPasswordScore = strengthResult == null ? null : strengthResult.score;
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getPasswordStrengthUserInput() {
|
|
||||||
let userInput: string[] = [];
|
|
||||||
const atPosition = this.email.indexOf("@");
|
|
||||||
if (atPosition > -1) {
|
|
||||||
userInput = userInput.concat(
|
|
||||||
this.email
|
|
||||||
.substr(0, atPosition)
|
|
||||||
.trim()
|
|
||||||
.toLowerCase()
|
|
||||||
.split(/[^A-Za-z0-9]/)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (this.name != null && this.name !== "") {
|
|
||||||
userInput = userInput.concat(this.name.trim().toLowerCase().split(" "));
|
|
||||||
}
|
|
||||||
return userInput;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,15 +37,17 @@
|
|||||||
name="NewMasterPasswordHash"
|
name="NewMasterPasswordHash"
|
||||||
class="form-control mb-1"
|
class="form-control mb-1"
|
||||||
[(ngModel)]="masterPassword"
|
[(ngModel)]="masterPassword"
|
||||||
(input)="updatePasswordStrength()"
|
|
||||||
required
|
required
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
/>
|
/>
|
||||||
<app-password-strength
|
<app-password-strength
|
||||||
[score]="masterPasswordScore"
|
[password]="masterPassword"
|
||||||
|
[email]="email"
|
||||||
[showText]="true"
|
[showText]="true"
|
||||||
></app-password-strength>
|
(passwordStrengthResult)="getStrengthResult($event)"
|
||||||
|
>
|
||||||
|
</app-password-strength>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
|
|||||||
@@ -39,12 +39,16 @@
|
|||||||
name="NewMasterPasswordHash"
|
name="NewMasterPasswordHash"
|
||||||
class="form-control mb-1"
|
class="form-control mb-1"
|
||||||
[(ngModel)]="masterPassword"
|
[(ngModel)]="masterPassword"
|
||||||
(input)="updatePasswordStrength()"
|
|
||||||
required
|
required
|
||||||
appInputVerbatim
|
appInputVerbatim
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
/>
|
/>
|
||||||
<app-password-strength [score]="masterPasswordScore" [showText]="true">
|
<app-password-strength
|
||||||
|
[password]="masterPassword"
|
||||||
|
[email]="email"
|
||||||
|
[showText]="true"
|
||||||
|
(passwordStrengthResult)="getStrengthResult($event)"
|
||||||
|
>
|
||||||
</app-password-strength>
|
</app-password-strength>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,20 +12,22 @@ import { EncString } from "@bitwarden/common/models/domain/encString";
|
|||||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/masterPasswordPolicyOptions";
|
import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/masterPasswordPolicyOptions";
|
||||||
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey";
|
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey";
|
||||||
|
|
||||||
|
import { PasswordColorText } from "../shared/components/password-strength/password-strength.component";
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
export class ChangePasswordComponent implements OnInit {
|
export class ChangePasswordComponent implements OnInit {
|
||||||
masterPassword: string;
|
masterPassword: string;
|
||||||
masterPasswordRetype: string;
|
masterPasswordRetype: string;
|
||||||
formPromise: Promise<any>;
|
formPromise: Promise<any>;
|
||||||
masterPasswordScore: number;
|
|
||||||
enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
||||||
|
passwordStrengthResult: any;
|
||||||
|
color: string;
|
||||||
|
text: string;
|
||||||
|
|
||||||
protected email: string;
|
protected email: string;
|
||||||
protected kdf: KdfType;
|
protected kdf: KdfType;
|
||||||
protected kdfIterations: number;
|
protected kdfIterations: number;
|
||||||
|
|
||||||
private masterPasswordStrengthTimeout: any;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected i18nService: I18nService,
|
protected i18nService: I18nService,
|
||||||
protected cryptoService: CryptoService,
|
protected cryptoService: CryptoService,
|
||||||
@@ -116,10 +118,7 @@ export class ChangePasswordComponent implements OnInit {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const strengthResult = this.passwordGenerationService.passwordStrength(
|
const strengthResult = this.passwordStrengthResult;
|
||||||
this.masterPassword,
|
|
||||||
this.getPasswordStrengthUserInput()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.enforcedPolicyOptions != null &&
|
this.enforcedPolicyOptions != null &&
|
||||||
@@ -153,19 +152,6 @@ export class ChangePasswordComponent implements OnInit {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePasswordStrength() {
|
|
||||||
if (this.masterPasswordStrengthTimeout != null) {
|
|
||||||
clearTimeout(this.masterPasswordStrengthTimeout);
|
|
||||||
}
|
|
||||||
this.masterPasswordStrengthTimeout = setTimeout(() => {
|
|
||||||
const strengthResult = this.passwordGenerationService.passwordStrength(
|
|
||||||
this.masterPassword,
|
|
||||||
this.getPasswordStrengthUserInput()
|
|
||||||
);
|
|
||||||
this.masterPasswordScore = strengthResult == null ? null : strengthResult.score;
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
async logOut() {
|
async logOut() {
|
||||||
const confirmed = await this.platformUtilsService.showDialog(
|
const confirmed = await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t("logOutConfirmation"),
|
this.i18nService.t("logOutConfirmation"),
|
||||||
@@ -178,18 +164,12 @@ export class ChangePasswordComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPasswordStrengthUserInput() {
|
getStrengthResult(result: any) {
|
||||||
let userInput: string[] = [];
|
this.passwordStrengthResult = result;
|
||||||
const atPosition = this.email.indexOf("@");
|
}
|
||||||
if (atPosition > -1) {
|
|
||||||
userInput = userInput.concat(
|
getPasswordScoreText(event: PasswordColorText) {
|
||||||
this.email
|
this.color = event.color;
|
||||||
.substr(0, atPosition)
|
this.text = event.text;
|
||||||
.trim()
|
|
||||||
.toLowerCase()
|
|
||||||
.split(/[^A-Za-z0-9]/)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return userInput;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import { KeysRequest } from "@bitwarden/common/models/request/keysRequest";
|
|||||||
import { ReferenceEventRequest } from "@bitwarden/common/models/request/referenceEventRequest";
|
import { ReferenceEventRequest } from "@bitwarden/common/models/request/referenceEventRequest";
|
||||||
import { RegisterRequest } from "@bitwarden/common/models/request/registerRequest";
|
import { RegisterRequest } from "@bitwarden/common/models/request/registerRequest";
|
||||||
|
|
||||||
|
import { PasswordColorText } from "../shared/components/password-strength/password-strength.component";
|
||||||
|
|
||||||
import { CaptchaProtectedComponent } from "./captchaProtected.component";
|
import { CaptchaProtectedComponent } from "./captchaProtected.component";
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
@@ -31,10 +33,12 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
|||||||
|
|
||||||
showPassword = false;
|
showPassword = false;
|
||||||
formPromise: Promise<any>;
|
formPromise: Promise<any>;
|
||||||
masterPasswordScore: number;
|
|
||||||
referenceData: ReferenceEventRequest;
|
referenceData: ReferenceEventRequest;
|
||||||
showTerms = true;
|
showTerms = true;
|
||||||
showErrorSummary = false;
|
showErrorSummary = false;
|
||||||
|
passwordStrengthResult: any;
|
||||||
|
color: string;
|
||||||
|
text: string;
|
||||||
|
|
||||||
formGroup = this.formBuilder.group(
|
formGroup = this.formBuilder.group(
|
||||||
{
|
{
|
||||||
@@ -63,7 +67,6 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
|||||||
);
|
);
|
||||||
|
|
||||||
protected successRoute = "login";
|
protected successRoute = "login";
|
||||||
private masterPasswordStrengthTimeout: any;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected formValidationErrorService: FormValidationErrorsService,
|
protected formValidationErrorService: FormValidationErrorsService,
|
||||||
@@ -87,36 +90,6 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
|||||||
this.setupCaptcha();
|
this.setupCaptcha();
|
||||||
}
|
}
|
||||||
|
|
||||||
get masterPasswordScoreWidth() {
|
|
||||||
return this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
get masterPasswordScoreColor() {
|
|
||||||
switch (this.masterPasswordScore) {
|
|
||||||
case 4:
|
|
||||||
return "success";
|
|
||||||
case 3:
|
|
||||||
return "primary";
|
|
||||||
case 2:
|
|
||||||
return "warning";
|
|
||||||
default:
|
|
||||||
return "danger";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get masterPasswordScoreText() {
|
|
||||||
switch (this.masterPasswordScore) {
|
|
||||||
case 4:
|
|
||||||
return this.i18nService.t("strong");
|
|
||||||
case 3:
|
|
||||||
return this.i18nService.t("good");
|
|
||||||
case 2:
|
|
||||||
return this.i18nService.t("weak");
|
|
||||||
default:
|
|
||||||
return this.masterPasswordScore != null ? this.i18nService.t("weak") : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async submit(showToast = true) {
|
async submit(showToast = true) {
|
||||||
let email = this.formGroup.get("email")?.value;
|
let email = this.formGroup.get("email")?.value;
|
||||||
let name = this.formGroup.get("name")?.value;
|
let name = this.formGroup.get("name")?.value;
|
||||||
@@ -147,11 +120,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const strengthResult = this.passwordGenerationService.passwordStrength(
|
if (this.passwordStrengthResult != null && this.passwordStrengthResult.score < 3) {
|
||||||
masterPassword,
|
|
||||||
this.getPasswordStrengthUserInput()
|
|
||||||
);
|
|
||||||
if (strengthResult != null && strengthResult.score < 3) {
|
|
||||||
const result = await this.platformUtilsService.showDialog(
|
const result = await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t("weakMasterPasswordDesc"),
|
this.i18nService.t("weakMasterPasswordDesc"),
|
||||||
this.i18nService.t("weakMasterPassword"),
|
this.i18nService.t("weakMasterPassword"),
|
||||||
@@ -235,39 +204,13 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
|
|||||||
this.showPassword = !this.showPassword;
|
this.showPassword = !this.showPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePasswordStrength() {
|
getStrengthResult(result: any) {
|
||||||
const masterPassword = this.formGroup.get("masterPassword")?.value;
|
this.passwordStrengthResult = result;
|
||||||
|
|
||||||
if (this.masterPasswordStrengthTimeout != null) {
|
|
||||||
clearTimeout(this.masterPasswordStrengthTimeout);
|
|
||||||
}
|
|
||||||
this.masterPasswordStrengthTimeout = setTimeout(() => {
|
|
||||||
const strengthResult = this.passwordGenerationService.passwordStrength(
|
|
||||||
masterPassword,
|
|
||||||
this.getPasswordStrengthUserInput()
|
|
||||||
);
|
|
||||||
this.masterPasswordScore = strengthResult == null ? null : strengthResult.score;
|
|
||||||
}, 300);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPasswordStrengthUserInput() {
|
getPasswordScoreText(event: PasswordColorText) {
|
||||||
let userInput: string[] = [];
|
this.color = event.color;
|
||||||
const email = this.formGroup.get("email")?.value;
|
this.text = event.text;
|
||||||
const name = this.formGroup.get("name").value;
|
|
||||||
const atPosition = email.indexOf("@");
|
|
||||||
if (atPosition > -1) {
|
|
||||||
userInput = userInput.concat(
|
|
||||||
email
|
|
||||||
.substr(0, atPosition)
|
|
||||||
.trim()
|
|
||||||
.toLowerCase()
|
|
||||||
.split(/[^A-Za-z0-9]/)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (name != null && name !== "") {
|
|
||||||
userInput = userInput.concat(name.trim().toLowerCase().split(" "));
|
|
||||||
}
|
|
||||||
return userInput;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getErrorToastMessage() {
|
private getErrorToastMessage() {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { I18nPipe } from "./pipes/i18n.pipe";
|
|||||||
import { SearchCiphersPipe } from "./pipes/search-ciphers.pipe";
|
import { SearchCiphersPipe } from "./pipes/search-ciphers.pipe";
|
||||||
import { SearchPipe } from "./pipes/search.pipe";
|
import { SearchPipe } from "./pipes/search.pipe";
|
||||||
import { UserNamePipe } from "./pipes/user-name.pipe";
|
import { UserNamePipe } from "./pipes/user-name.pipe";
|
||||||
|
import { PasswordStrengthComponent } from "./shared/components/password-strength/password-strength.component";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -68,6 +69,7 @@ import { UserNamePipe } from "./pipes/user-name.pipe";
|
|||||||
StopPropDirective,
|
StopPropDirective,
|
||||||
TrueFalseValueDirective,
|
TrueFalseValueDirective,
|
||||||
UserNamePipe,
|
UserNamePipe,
|
||||||
|
PasswordStrengthComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
A11yInvalidDirective,
|
A11yInvalidDirective,
|
||||||
@@ -97,6 +99,7 @@ import { UserNamePipe } from "./pipes/user-name.pipe";
|
|||||||
StopPropDirective,
|
StopPropDirective,
|
||||||
TrueFalseValueDirective,
|
TrueFalseValueDirective,
|
||||||
UserNamePipe,
|
UserNamePipe,
|
||||||
|
PasswordStrengthComponent,
|
||||||
],
|
],
|
||||||
providers: [CreditCardNumberPipe, DatePipe, I18nPipe, SearchPipe, UserNamePipe],
|
providers: [CreditCardNumberPipe, DatePipe, I18nPipe, SearchPipe, UserNamePipe],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from "@angular/core";
|
||||||
|
|
||||||
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
|
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
||||||
|
|
||||||
|
export interface PasswordColorText {
|
||||||
|
color: string;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-password-strength",
|
||||||
|
templateUrl: "password-strength.component.html",
|
||||||
|
})
|
||||||
|
export class PasswordStrengthComponent implements OnChanges {
|
||||||
|
@Input() showText = false;
|
||||||
|
@Input() email: string;
|
||||||
|
@Input() password: string;
|
||||||
|
@Input() name: string;
|
||||||
|
|
||||||
|
@Output() passwordStrengthResult = new EventEmitter<any>();
|
||||||
|
@Output() passwordScoreColor = new EventEmitter<PasswordColorText>();
|
||||||
|
|
||||||
|
masterPasswordScore: number;
|
||||||
|
scoreWidth = 0;
|
||||||
|
color = "bg-danger";
|
||||||
|
text: string;
|
||||||
|
|
||||||
|
private masterPasswordStrengthTimeout: any;
|
||||||
|
|
||||||
|
//used by desktop and browser to display strength text color
|
||||||
|
get masterPasswordScoreColor() {
|
||||||
|
switch (this.masterPasswordScore) {
|
||||||
|
case 4:
|
||||||
|
return "success";
|
||||||
|
case 3:
|
||||||
|
return "primary";
|
||||||
|
case 2:
|
||||||
|
return "warning";
|
||||||
|
default:
|
||||||
|
return "danger";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//used by desktop and browser to display strength text
|
||||||
|
get masterPasswordScoreText() {
|
||||||
|
switch (this.masterPasswordScore) {
|
||||||
|
case 4:
|
||||||
|
return this.i18nService.t("strong");
|
||||||
|
case 3:
|
||||||
|
return this.i18nService.t("good");
|
||||||
|
case 2:
|
||||||
|
return this.i18nService.t("weak");
|
||||||
|
default:
|
||||||
|
return this.masterPasswordScore != null ? this.i18nService.t("weak") : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private passwordGenerationService: PasswordGenerationService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
this.masterPasswordStrengthTimeout = setTimeout(() => {
|
||||||
|
this.updatePasswordStrength(changes.password?.currentValue);
|
||||||
|
|
||||||
|
this.scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
|
||||||
|
|
||||||
|
switch (this.masterPasswordScore) {
|
||||||
|
case 4:
|
||||||
|
this.color = "bg-success";
|
||||||
|
this.text = this.i18nService.t("strong");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
this.color = "bg-primary";
|
||||||
|
this.text = this.i18nService.t("good");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
this.color = "bg-warning";
|
||||||
|
this.text = this.i18nService.t("weak");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.color = "bg-danger";
|
||||||
|
this.text = this.masterPasswordScore != null ? this.i18nService.t("weak") : null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setPasswordScoreText(this.color, this.text);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePasswordStrength(password: string) {
|
||||||
|
const masterPassword = password;
|
||||||
|
|
||||||
|
if (this.masterPasswordStrengthTimeout != null) {
|
||||||
|
clearTimeout(this.masterPasswordStrengthTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
const strengthResult = this.passwordGenerationService.passwordStrength(
|
||||||
|
masterPassword,
|
||||||
|
this.getPasswordStrengthUserInput()
|
||||||
|
);
|
||||||
|
this.passwordStrengthResult.emit(strengthResult);
|
||||||
|
|
||||||
|
this.masterPasswordScore = strengthResult == null ? null : strengthResult.score;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPasswordStrengthUserInput() {
|
||||||
|
let userInput: string[] = [];
|
||||||
|
const email = this.email;
|
||||||
|
const name = this.name;
|
||||||
|
const atPosition = email.indexOf("@");
|
||||||
|
if (atPosition > -1) {
|
||||||
|
userInput = userInput.concat(
|
||||||
|
email
|
||||||
|
.substr(0, atPosition)
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.split(/[^A-Za-z0-9]/)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (name != null && name !== "") {
|
||||||
|
userInput = userInput.concat(name.trim().toLowerCase().split(" "));
|
||||||
|
}
|
||||||
|
return userInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPasswordScoreText(color: string, text: string) {
|
||||||
|
color = color.slice(3);
|
||||||
|
this.passwordScoreColor.emit({ color: color, text: text });
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user