This commit is contained in:
gbubemismith 2022-06-16 23:14:44 -06:00
parent 2c56b3ef2a
commit 4809bc6478
7 changed files with 324 additions and 429 deletions

View File

@ -29,338 +29,13 @@
</header>
<!--Testing cpmponent-->
<!-- <div class="col-12">
<div class="row justify-content-md-center mt-5">
<div class="card d-block">
<div class="card-body">
<app-register-form></app-register-form>
</div>
</div>
<div class="row justify-content-md-center mt-5">
<div class="col-4">
<div class="card d-block">
<div class="card-body">
<app-register-form></app-register-form>
</div>
</div>
</div>
</div>
</div> -->
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" class="container" ngNativeValidate>
<div class="row">
<div class="col-7" *ngIf="layout">
<div class="mt-5">
<!-- Default Body -->
<div
*ngIf="
layout === 'teams' ||
layout === 'enterprise' ||
layout === 'enterprise1' ||
layout === 'default'
"
>
<h1>The Bitwarden Password Manager</h1>
<h2>
Trusted by millions of individuals, teams, and organizations worldwide for secure
password storage and sharing.
</h2>
<p>Store logins, secure notes, and more</p>
<p>Collaborate and share securely</p>
<p>Access anywhere on any device</p>
<p>Create your account to get started</p>
</div>
<!-- Teams & Enterprise Body -->
<div *ngIf="layout === 'teams1' || layout === 'teams2' || layout === 'enterprise2'">
<h1>
Start Your <span *ngIf="layout === 'teams1' || layout === 'teams1'">Teams<br /></span
><span *ngIf="layout === 'enterprise2'">Enterprise</span> Free Trial Now
</h1>
<h2>
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
password storage and sharing.
</h2>
<p>Collaborate and share securely</p>
<p>Deploy and manage quickly and easily</p>
<p>Access anywhere on any device</p>
<p>Create your account to get started</p>
</div>
<!-- CNET Campaign Teams & Enterprise Body -->
<div *ngIf="layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnent'">
<h1>
Start Your <span *ngIf="layout === 'cnetcmpgnteams'">Teams<br /></span
><span *ngIf="layout === 'cnetcmpgnent'">Enterprise</span> Free Trial Now
</h1>
<h2>
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
password storage and sharing.
</h2>
<p>Collaborate and share securely</p>
<p>Deploy and manage quickly and easily</p>
<p>Access anywhere on any device</p>
<p>Create your account to get started</p>
</div>
<!-- CNET Campaign Premium Body -->
<div *ngIf="layout === 'cnetcmpgnind'">
<h1>Start Your Premium Account Now</h1>
<h2>
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
password storage and sharing.
</h2>
<p>Store logins, secure notes, and more</p>
<p>Secure your account with advanced two-step login</p>
<p>Access anywhere on any device</p>
<p>Create your account to get started</p>
</div>
</div>
</div>
<div [ngClass]="{ 'col-5': layout, 'col-12': !layout }">
<div class="row justify-content-md-center mt-5">
<div [ngClass]="{ 'col-5': !layout, 'col-12': layout }">
<h1 class="lead text-center mb-4" *ngIf="!layout">{{ "createAccount" | i18n }}</h1>
<div class="card d-block">
<div class="card-body">
<app-callout
title="{{ 'createOrganizationStep1' | i18n }}"
type="info"
icon="bwi bwi-thumb-tack"
*ngIf="showCreateOrgMessage"
>
{{ "createOrganizationCreatePersonalAccount" | i18n }}
</app-callout>
<div class="form-group">
<label for="email">{{ "emailAddress" | i18n }}</label>
<input
id="email"
class="form-control"
type="text"
name="Email"
[(ngModel)]="email"
required
[appAutofocus]="email === ''"
inputmode="email"
appInputVerbatim="false"
/>
<small class="form-text text-muted">{{ "emailAddressDesc" | i18n }}</small>
</div>
<div class="form-group">
<label for="name">{{ "yourName" | i18n }}</label>
<input
id="name"
class="form-control"
type="text"
name="Name"
[(ngModel)]="name"
[appAutofocus]="email !== ''"
/>
<small class="form-text text-muted">{{ "yourNameDesc" | i18n }}</small>
</div>
<div class="form-group">
<app-callout
type="info"
[enforcedPolicyOptions]="enforcedPolicyOptions"
*ngIf="enforcedPolicyOptions"
>
</app-callout>
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
<div class="d-flex">
<div class="w-100">
<input
id="masterPassword"
type="{{ showPassword ? 'text' : 'password' }}"
name="MasterPassword"
class="text-monospace form-control mb-1"
[(ngModel)]="masterPassword"
(input)="updatePasswordStrength()"
required
appInputVerbatim
/>
<app-password-strength [score]="masterPasswordScore" [showText]="true">
</app-password-strength>
</div>
<div>
<button
type="button"
class="ml-1 btn btn-link"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
(click)="togglePassword(false)"
>
<i
class="bwi bwi-lg"
aria-hidden="true"
[ngClass]="{
'bwi-eye': !showPassword,
'bwi-eye-slash': showPassword
}"
></i>
</button>
<div class="progress-bar invisible"></div>
</div>
</div>
<small class="form-text text-muted">{{ "masterPassDesc" | i18n }}</small>
</div>
<div class="form-group">
<label for="masterPasswordRetype">{{ "reTypeMasterPass" | i18n }}</label>
<div class="d-flex">
<input
id="masterPasswordRetype"
type="{{ showPassword ? 'text' : 'password' }}"
name="MasterPasswordRetype"
class="text-monospace form-control"
[(ngModel)]="confirmMasterPassword"
required
appInputVerbatim
/>
<button
type="button"
class="ml-1 btn btn-link"
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
(click)="togglePassword(true)"
>
<i
class="bwi bwi-lg"
aria-hidden="true"
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
></i>
</button>
</div>
</div>
<div class="form-group">
<label for="hint">{{ "masterPassHint" | i18n }}</label>
<input
id="hint"
class="form-control"
type="text"
name="Hint"
[(ngModel)]="hint"
/>
<small class="form-text text-muted">{{ "masterPassHintDesc" | i18n }}</small>
</div>
<div [hidden]="!showCaptcha()">
<iframe id="hcaptcha_iframe" height="80"></iframe>
</div>
<div class="form-group" *ngIf="showTerms">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="acceptPolicies"
[(ngModel)]="acceptPolicies"
name="AcceptPolicies"
/>
<label class="form-check-label small text-muted" for="acceptPolicies">
{{ "acceptPolicies" | i18n }}<br />
<a href="https://bitwarden.com/terms/" target="_blank" rel="noopener">{{
"termsOfService" | i18n
}}</a
>,
<a href="https://bitwarden.com/privacy/" target="_blank" rel="noopener">{{
"privacyPolicy" | i18n
}}</a>
</label>
</div>
</div>
<hr />
<div class="d-flex mb-2">
<button
type="submit"
class="btn btn-primary btn-block btn-submit"
[disabled]="form.loading"
>
<span>{{ "submit" | i18n }}</span>
<i
class="bwi bwi-spinner bwi-spin"
title="{{ 'loading' | i18n }}"
aria-hidden="true"
></i>
</button>
<a routerLink="/login" class="btn btn-outline-secondary btn-block ml-2 mt-0">
{{ "cancel" | i18n }}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-7 d-flex align-items-center">
<div
*ngIf="
layout === 'cnetcmpgnent' || layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnind'
"
>
<figure>
<figcaption>
<cite>
<img
src="../../images/register-layout/cnet-logo.svg"
class="w-25 d-block mx-auto"
alt="cnet logo"
/>
</cite>
</figcaption>
<blockquote class="mx-auto text-center px-4">
"No more excuses; start using Bitwarden today. The identity you save could be your
own. The money definitely will be."
</blockquote>
</figure>
</div>
<div
*ngIf="
layout === 'teams' ||
layout === 'teams1' ||
layout === 'teams2' ||
layout === 'enterprise' ||
layout === 'enterprise1' ||
layout === 'enterprise2' ||
layout === 'default'
"
>
<figure>
<figcaption>
<cite>
<img
src="../../images/register-layout/forbes-logo.svg"
class="w-25 d-block mx-auto"
alt="Forbes Logo"
/>
</cite>
</figcaption>
<blockquote class="mx-auto text-center px-4">
“Bitwarden boasts the backing of some of the world's best security experts and an
attractive, easy-to-use interface”
</blockquote>
</figure>
</div>
</div>
<div
*ngIf="
layout === 'cnetcmpgnent' || layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnind'
"
class="col-5 d-flex align-items-center justify-content-center"
>
<img
src="../../images/register-layout/usnews-360-badge.svg"
class="w-50 d-block"
alt="US News 360 Reviews Best Password Manager"
/>
</div>
<div
*ngIf="
layout === 'teams' ||
layout === 'teams1' ||
layout === 'teams2' ||
layout === 'enterprise' ||
layout === 'enterprise1' ||
layout === 'enterprise2' ||
layout === 'default'
"
class="col-5 d-flex align-items-center justify-content-center"
>
<img
src="../../images/register-layout/usnews-360-badge.svg"
class="w-50 d-block"
alt="US News 360 Reviews Best Password Manager"
/>
</div>
</div>
</form>
</div>

View File

@ -1,4 +1,5 @@
import { Component } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
@ -32,6 +33,7 @@ export class RegisterComponent extends BaseRegisterComponent {
private policies: Policy[];
constructor(
formBuilder: FormBuilder,
authService: AuthService,
router: Router,
i18nService: I18nService,
@ -47,6 +49,7 @@ export class RegisterComponent extends BaseRegisterComponent {
private routerService: RouterService
) {
super(
formBuilder,
authService,
router,
i18nService,

View File

@ -161,7 +161,7 @@ import { FolderAddEditComponent } from "../vault/folder-add-edit.component";
import { ShareComponent } from "../vault/share.component";
import { PipesModule } from "./pipes/pipes.module";
import { RegisterFormModule , RegisterFormModule, RegisterFormModule } from "./register-form/register-form.module";
import { RegisterFormModule } from "./register-form/register-form.module";
import { SharedModule } from "./shared.module";
import { VaultFilterModule } from "./vault-filter/vault-filter.module";
import { OrganizationBadgeModule } from "./vault/modules/organization-badge/organization-badge.module";

View File

@ -3,43 +3,46 @@
(ngSubmit)="submit()"
[appApiAction]="formPromise"
class="tw-container tw-mx-auto"
[formGroup]="formData"
[formGroup]="formGroup"
>
<div class="">
<!--Email-->
<div>
<div class="tw-mb-3">
<bit-form-field>
<bit-label>{{ "emailAddress" | i18n }}</bit-label>
<input bitInput type="email" formControlName="email" />
<bit-hint class="tw-text-sm">{{ "emailAddressDesc" | i18n }}</bit-hint>
</bit-form-field>
<small class="tw-mb-2 tw-text-grey-500">{{ "emailAddressDesc" | i18n }}</small>
</div>
<div class="tw-mb-3">
<bit-form-field>
<bit-label>{{ "name" | i18n }}</bit-label>
<input bitInput type="text" formControlName="name" />
<bit-hint class="tw-text-sm">{{ "yourNameDesc" | i18n }}</bit-hint>
</bit-form-field>
<small class="tw-mb-2 tw-text-grey-500">{{ "yourNameDesc" | i18n }}</small>
</div>
<div class="tw-mb-3">
<!-- <app-callout
type="info"
[enforcedPolicyOptions]="enforcedPolicyOptions"
*ngIf="enforcedPolicyOptions"
>
</app-callout> -->
<app-callout
type="info"
[enforcedPolicyOptions]="enforcedPolicyOptions"
*ngIf="enforcedPolicyOptions"
>
</app-callout>
<bit-form-field>
<bit-label>{{ "masterPass" | i18n }}</bit-label>
<input
bitInput
type="{{ showPassword ? 'text' : 'password' }}"
formControlName="masterPassword"
/>
<app-password-strength [score]="masterPasswordScore" [showText]="true">
</app-password-strength>
<div class="tw-w-full">
<input
#masterPassword
class="mb-2"
bitInput
(input)="updatePasswordStrength()"
type="{{ showPassword ? 'text' : 'password' }}"
formControlName="masterPassword"
/>
<app-password-strength [score]="masterPasswordScore" [showText]="true">
</app-password-strength>
</div>
<button
type="button"
@ -57,15 +60,18 @@
}"
></i>
</button>
<bit-hint class="tw-text-sm">
<span class="tw-font-semibold">Important:</span>
{{ "masterPassImportant" | i18n }}</bit-hint
>
</bit-form-field>
<small class="tw-text-grey-500">{{ "masterPassDesc" | i18n }}</small>
</div>
<div class="tw-mb-3">
<bit-form-field>
<bit-label>{{ "reTypeMasterPass" | i18n }}</bit-label>
<input
#masterPasswordRetype
bitInput
type="{{ showPassword ? 'text' : 'password' }}"
formControlName="confirmMasterPassword"
@ -91,8 +97,8 @@
<bit-form-field>
<bit-label>{{ "masterPassHint" | i18n }}</bit-label>
<input bitInput type="text" formControlName="hint" />
<bit-hint class="tw-tracking-tight tw-text-sm">{{ "masterPassHintDesc" | i18n }}</bit-hint>
</bit-form-field>
<small class="tw-text-grey-500">{{ "masterPassHintDesc" | i18n }}</small>
</div>
<div [hidden]="!showCaptcha()">
@ -100,7 +106,7 @@
</div>
<div class="tw-flex tw-items-start tw-mb-3" *ngIf="showTerms">
<div class="tw-flex tw-items-center tw-h-5">
<div class="tw-flex tw-items-center tw-h-6">
<input
class="tw-w-4 tw-rounded tw-border tw-border-gray-300"
bitInput
@ -108,16 +114,16 @@
formControlName="acceptPolicies"
/>
</div>
<bit-label
>{{ "acceptPolicies" | i18n }}<br />
<bit-label class="ml-2">
{{ "acceptPolicies" | i18n }}<br />
<a href="https://bitwarden.com/terms/" target="_blank" rel="noopener">{{
"termsOfService" | i18n
}}</a
>,
<a href="https://bitwarden.com/privacy/" target="_blank" rel="noopener">{{
"privacyPolicy" | i18n
}}</a></bit-label
>
}}</a>
</bit-label>
</div>
<hr />
@ -128,6 +134,7 @@
type="submit"
bitButton
buttonType="primary"
class="btn-submit"
[disabled]="form.loading"
>
<span>{{ "createAccount" | i18n }}</span>

View File

@ -1,9 +1,9 @@
import { Component, Input } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { Component, ElementRef, Input, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component";
import { CaptchaProtectedComponent } from "@bitwarden/angular/components/captchaProtected.component";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
@ -14,10 +14,13 @@ import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwo
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { PolicyService } from "@bitwarden/common/abstractions/policy.service";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { DEFAULT_KDF_ITERATIONS, DEFAULT_KDF_TYPE } from "@bitwarden/common/enums/kdfType";
import { PolicyData } from "@bitwarden/common/models/data/policyData";
import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/masterPasswordPolicyOptions";
import { Policy } from "@bitwarden/common/models/domain/policy";
import { KeysRequest } from "@bitwarden/common/models/request/keysRequest";
import { ReferenceEventRequest } from "@bitwarden/common/models/request/referenceEventRequest";
import { RegisterRequest } from "@bitwarden/common/models/request/registerRequest";
import { RouterService } from "src/app/services/router.service";
@ -25,57 +28,52 @@ import { RouterService } from "src/app/services/router.service";
selector: "app-register-form",
templateUrl: "./register-form.component.html",
})
export class RegisterFormComponent extends BaseRegisterComponent {
export class RegisterFormComponent extends CaptchaProtectedComponent implements OnInit {
@ViewChild("masterPassword") masterPasswordRef: ElementRef;
@ViewChild("masterPasswordRetype") masterPasswordRetypeRef: ElementRef;
showTerms = true;
showCreateOrgMessage = false;
@Input() layout = "";
masterPasswordScore: number;
showPassword = false;
referenceData: ReferenceEventRequest;
enforcedPolicyOptions: MasterPasswordPolicyOptions;
formGroup: FormGroup;
formPromise: Promise<any>;
showErrorSummary = false;
private policies: Policy[];
formData = this.formBuilder.group({
email: ["", [Validators.required, Validators.email]],
name: ["", [Validators.required]],
masterPassword: ["", [Validators.required]],
confirmMasterPassword: ["", [Validators.required]],
hint: [],
acceptPolicies: [false, [Validators.requiredTrue]],
});
protected successRoute = "login";
private masterPasswordStrengthTimeout: any;
constructor(
private formBuilder: FormBuilder,
authService: AuthService,
router: Router,
private router: Router,
i18nService: I18nService,
cryptoService: CryptoService,
apiService: ApiService,
private cryptoService: CryptoService,
private apiService: ApiService,
private route: ActivatedRoute,
stateService: StateService,
private stateService: StateService,
platformUtilsService: PlatformUtilsService,
passwordGenerationService: PasswordGenerationService,
private passwordGenerationService: PasswordGenerationService,
private policyService: PolicyService,
environmentService: EnvironmentService,
logService: LogService,
private logService: LogService,
private routerService: RouterService
) {
super(
authService,
router,
i18nService,
cryptoService,
apiService,
stateService,
platformUtilsService,
passwordGenerationService,
environmentService,
logService
);
super(environmentService, i18nService, platformUtilsService);
this.showTerms = !platformUtilsService.isSelfHost();
}
async ngOnInit() {
this.createRegisterForm();
this.route.queryParams.pipe(first()).subscribe((qParams) => {
this.referenceData = new ReferenceEventRequest();
if (qParams.email != null && qParams.email.indexOf("@") > -1) {
this.email = qParams.email;
this.formGroup.get("email")?.setValue(qParams.email);
}
if (qParams.premium != null) {
this.routerService.setPreviousUrl("/settings/premium");
@ -87,9 +85,7 @@ export class RegisterFormComponent extends BaseRegisterComponent {
});
this.routerService.setPreviousUrl(route.toString());
}
if (qParams.layout != null) {
this.layout = this.referenceData.layout = qParams.layout;
}
if (qParams.reference != null) {
this.referenceData.id = qParams.reference;
} else {
@ -135,15 +131,68 @@ export class RegisterFormComponent extends BaseRegisterComponent {
);
}
await super.ngOnInit();
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() {
let email = this.formGroup.get("email")?.value;
let name = this.formGroup.get("name")?.value;
const masterPassword = this.formGroup.get("masterPassword")?.value;
const confirmMasterPassword = this.formGroup.get("confirmMasterPassword")?.value;
const hint = this.formGroup.get("hint")?.value;
const acceptPolicies = this.formGroup.get("acceptPolicies")?.value;
// if (!acceptPolicies && this.showTerms) {
// this.platformUtilsService.showToast(
// "error",
// this.i18nService.t("errorOccurred"),
// this.i18nService.t("acceptPoliciesError")
// );
// return;
// }
this.formGroup.markAllAsTouched();
this.showErrorSummary = true;
if (!this.formGroup.valid) {
return;
}
if (
this.enforcedPolicyOptions != null &&
!this.policyService.evaluateMasterPassword(
this.masterPasswordScore,
this.masterPassword,
masterPassword,
this.enforcedPolicyOptions
)
) {
@ -155,6 +204,138 @@ export class RegisterFormComponent extends BaseRegisterComponent {
return;
}
await super.submit();
console.log("Here::", acceptPolicies, this.showTerms);
if (masterPassword !== confirmMasterPassword) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("masterPassDoesntMatch")
);
return;
}
const strengthResult = this.passwordGenerationService.passwordStrength(
masterPassword,
this.getPasswordStrengthUserInput()
);
if (strengthResult != null && strengthResult.score < 3) {
const result = await this.platformUtilsService.showDialog(
this.i18nService.t("weakMasterPasswordDesc"),
this.i18nService.t("weakMasterPassword"),
this.i18nService.t("yes"),
this.i18nService.t("no"),
"warning"
);
if (!result) {
return;
}
}
if (hint === masterPassword) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("hintEqualsPassword")
);
return;
}
name = name === "" ? null : name;
email = email.trim().toLowerCase();
const kdf = DEFAULT_KDF_TYPE;
const kdfIterations = DEFAULT_KDF_ITERATIONS;
const key = await this.cryptoService.makeKey(masterPassword, email, kdf, kdfIterations);
const encKey = await this.cryptoService.makeEncKey(key);
const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key);
const keys = await this.cryptoService.makeKeyPair(encKey[0]);
const request = new RegisterRequest(
email,
name,
hashedPassword,
hint,
encKey[1].encryptedString,
kdf,
kdfIterations,
this.referenceData,
this.captchaToken
);
request.keys = new KeysRequest(keys[0], keys[1].encryptedString);
const orgInvite = await this.stateService.getOrganizationInvitation();
if (orgInvite != null && orgInvite.token != null && orgInvite.organizationUserId != null) {
request.token = orgInvite.token;
request.organizationUserId = orgInvite.organizationUserId;
}
try {
this.formPromise = this.apiService.postRegister(request);
try {
await this.formPromise;
} catch (e) {
if (this.handleCaptchaRequired(e)) {
return;
} else {
throw e;
}
}
this.platformUtilsService.showToast("success", null, this.i18nService.t("newAccountCreated"));
this.router.navigate([this.successRoute], { queryParams: { email: email } });
} catch (e) {
this.logService.error(e);
}
}
togglePassword(confirmField: boolean) {
this.showPassword = !this.showPassword;
confirmField
? this.masterPasswordRetypeRef.nativeElement.focus()
: this.masterPasswordRef.nativeElement.focus();
// document.getElementById(confirmField ? "masterPasswordRetype" : "masterPassword").focus();
}
updatePasswordStrength() {
const masterPassword = this.formGroup.get("masterPassword")?.value;
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() {
let userInput: string[] = [];
const email = this.formGroup.get("email")?.value;
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 createRegisterForm() {
this.formGroup = this.formBuilder.group({
email: ["", [Validators.required, Validators.email]],
name: [""],
masterPassword: ["", [Validators.required]],
confirmMasterPassword: ["", [Validators.required]],
hint: [],
acceptPolicies: [false, [Validators.requiredTrue]],
});
}
}

View File

@ -593,6 +593,9 @@
"masterPassDesc": {
"message": "The master password is the password you use to access your vault. It is very important that you do not forget your master password. There is no way to recover the password in the event that you forget it."
},
"masterPassImportant": {
"message": "Master passwords cannot be recovered if you forget it!"
},
"masterPassHintDesc": {
"message": "A master password hint can help you remember your password if you forget it."
},

View File

@ -1,4 +1,5 @@
import { Directive, OnInit } from "@angular/core";
import { Directive, ElementRef, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { Router } from "@angular/router";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
@ -19,6 +20,8 @@ import { CaptchaProtectedComponent } from "./captchaProtected.component";
@Directive()
export class RegisterComponent extends CaptchaProtectedComponent implements OnInit {
@ViewChild("masterPassword") masterPasswordRef: ElementRef;
@ViewChild("masterPasswordRetype") masterPasswordRetypeRef: ElementRef;
name = "";
email = "";
masterPassword = "";
@ -31,10 +34,20 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
showTerms = true;
acceptPolicies = false;
formGroup = this.formBuilder.group({
email: ["", [Validators.required, Validators.email]],
name: [""],
masterPassword: ["", [Validators.required]],
confirmMasterPassword: ["", [Validators.required]],
hint: [],
acceptPolicies: [false, [Validators.requiredTrue]],
});
protected successRoute = "login";
private masterPasswordStrengthTimeout: any;
constructor(
protected formBuilder: FormBuilder,
protected authService: AuthService,
protected router: Router,
i18nService: I18nService,
@ -85,7 +98,20 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
}
async submit() {
if (!this.acceptPolicies && this.showTerms) {
let email = this.formGroup.get("email")?.value;
let name = this.formGroup.get("name")?.value;
const masterPassword = this.formGroup.get("masterPassword")?.value;
const confirmMasterPassword = this.formGroup.get("confirmMasterPassword")?.value;
const hint = this.formGroup.get("hint")?.value;
const acceptPolicies = this.formGroup.get("acceptPolicies")?.value;
this.formGroup.markAllAsTouched();
if (!this.formGroup.valid) {
return;
}
if (!acceptPolicies && this.showTerms) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
@ -94,7 +120,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
return;
}
if (this.email == null || this.email === "") {
if (email == null || email === "") {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
@ -102,7 +128,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
);
return;
}
if (this.email.indexOf("@") === -1) {
if (email.indexOf("@") === -1) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
@ -110,7 +136,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
);
return;
}
if (this.masterPassword == null || this.masterPassword === "") {
if (masterPassword == null || masterPassword === "") {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
@ -118,7 +144,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
);
return;
}
if (this.masterPassword.length < 8) {
if (masterPassword.length < 8) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
@ -126,7 +152,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
);
return;
}
if (this.masterPassword !== this.confirmMasterPassword) {
if (masterPassword !== confirmMasterPassword) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
@ -136,7 +162,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
}
const strengthResult = this.passwordGenerationService.passwordStrength(
this.masterPassword,
masterPassword,
this.getPasswordStrengthUserInput()
);
if (strengthResult != null && strengthResult.score < 3) {
@ -152,7 +178,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
}
}
if (this.hint === this.masterPassword) {
if (hint === masterPassword) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
@ -161,24 +187,19 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
return;
}
this.name = this.name === "" ? null : this.name;
this.email = this.email.trim().toLowerCase();
name = name === "" ? null : name;
email = email.trim().toLowerCase();
const kdf = DEFAULT_KDF_TYPE;
const kdfIterations = DEFAULT_KDF_ITERATIONS;
const key = await this.cryptoService.makeKey(
this.masterPassword,
this.email,
kdf,
kdfIterations
);
const key = await this.cryptoService.makeKey(masterPassword, email, kdf, kdfIterations);
const encKey = await this.cryptoService.makeEncKey(key);
const hashedPassword = await this.cryptoService.hashPassword(this.masterPassword, key);
const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key);
const keys = await this.cryptoService.makeKeyPair(encKey[0]);
const request = new RegisterRequest(
this.email,
this.name,
email,
name,
hashedPassword,
this.hint,
hint,
encKey[1].encryptedString,
kdf,
kdfIterations,
@ -204,7 +225,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
}
}
this.platformUtilsService.showToast("success", null, this.i18nService.t("newAccountCreated"));
this.router.navigate([this.successRoute], { queryParams: { email: this.email } });
this.router.navigate([this.successRoute], { queryParams: { email: email } });
} catch (e) {
this.logService.error(e);
}
@ -212,18 +233,21 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
togglePassword(confirmField: boolean) {
this.showPassword = !this.showPassword;
console.log("Here::", document.getElementById("masterPasswordRetype"));
console.log("Here2::", document.getElementById("masterPassword"));
document.getElementById(confirmField ? "masterPasswordRetype" : "masterPassword").focus();
confirmField
? this.masterPasswordRetypeRef.nativeElement.focus()
: this.masterPasswordRef.nativeElement.focus();
// document.getElementById(confirmField ? "masterPasswordRetype" : "masterPassword").focus();
}
updatePasswordStrength() {
const masterPassword = this.formGroup.get("masterPassword")?.value;
if (this.masterPasswordStrengthTimeout != null) {
clearTimeout(this.masterPasswordStrengthTimeout);
}
this.masterPasswordStrengthTimeout = setTimeout(() => {
const strengthResult = this.passwordGenerationService.passwordStrength(
this.masterPassword,
masterPassword,
this.getPasswordStrengthUserInput()
);
this.masterPasswordScore = strengthResult == null ? null : strengthResult.score;
@ -232,18 +256,20 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
private getPasswordStrengthUserInput() {
let userInput: string[] = [];
const atPosition = this.email.indexOf("@");
const email = this.formGroup.get("email")?.value;
const name = this.formGroup.get("name").value;
const atPosition = email.indexOf("@");
if (atPosition > -1) {
userInput = userInput.concat(
this.email
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(" "));
if (name != null && name !== "") {
userInput = userInput.concat(name.trim().toLowerCase().split(" "));
}
return userInput;
}