[SG-1016] Move MP confirmation to modal for encryptions settings changes (#4762)

* work: moving the form

* fix: lint do be crazy sometimes

* fix: remove debug stuff

* fix: dialogService

* per Danielle Flinn: option 1, leave sizing wide.

* work: move modules to own container

* work: reorg modules

---------

Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com>
This commit is contained in:
Brandon Maharaj 2023-03-09 23:54:11 -05:00 committed by GitHub
parent e90372cb5e
commit 0c84fcbcef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 170 additions and 70 deletions

View File

@ -0,0 +1,50 @@
<bit-dialog>
<span bitDialogTitle>
{{ "changeKdf" | i18n }}
</span>
<span bitDialogContent>
<bit-callout type="warning">{{ "changeKdfLoggedOutWarning" | i18n }}</bit-callout>
<form
id="form"
[formGroup]="form"
(ngSubmit)="submit()"
[appApiAction]="formPromise"
ngNativeValidate
autocomplete="off"
>
<div class="row">
<div class="col-12">
<bit-form-field class="tw-mb-1"
><bit-label>{{ "masterPass" | i18n }}</bit-label>
<input
bitInput
type="password"
required
formControlName="masterPassword"
appAutofocus
/>
<button
type="button"
bitSuffix
bitIconButton
bitPasswordInputToggle
[(toggled)]="showPassword"
></button
><bit-hint>
{{ "confirmIdentity" | i18n }}
</bit-hint></bit-form-field
>
</div>
</div>
</form>
</span>
<div bitDialogFooter class="tw-flex tw-flex-row tw-gap-2">
<button bitButton buttonType="primary" type="submit" [loading]="loading" form="form">
<span>{{ "changeKdf" | i18n }}</span>
</button>
<button bitButton buttonType="secondary" type="button" data-dismiss="modal">
{{ "cancel" | i18n }}
</button>
</div>
</bit-dialog>

View File

@ -1,4 +1,6 @@
import { Component, OnInit } from "@angular/core";
import { DIALOG_DATA } from "@angular/cdk/dialog";
import { Component, Inject } from "@angular/core";
import { FormGroup, FormControl, Validators } from "@angular/forms";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
@ -8,28 +10,24 @@ import { MessagingService } from "@bitwarden/common/abstractions/messaging.servi
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
import {
DEFAULT_KDF_CONFIG,
DEFAULT_PBKDF2_ITERATIONS,
DEFAULT_ARGON2_ITERATIONS,
DEFAULT_ARGON2_MEMORY,
DEFAULT_ARGON2_PARALLELISM,
KdfType,
} from "@bitwarden/common/enums/kdfType";
import { KdfType } from "@bitwarden/common/enums/kdfType";
import { KdfRequest } from "@bitwarden/common/models/request/kdf.request";
@Component({
selector: "app-change-kdf",
templateUrl: "change-kdf.component.html",
selector: "app-change-kdf-confirmation",
templateUrl: "change-kdf-confirmation.component.html",
})
export class ChangeKdfComponent implements OnInit {
export class ChangeKdfConfirmationComponent {
kdf: KdfType;
kdfConfig: KdfConfig;
form = new FormGroup({
masterPassword: new FormControl(null, Validators.required),
});
showPassword = false;
masterPassword: string;
kdf = KdfType.PBKDF2_SHA256;
kdfConfig: KdfConfig = DEFAULT_KDF_CONFIG;
kdfType = KdfType;
kdfOptions: any[] = [];
formPromise: Promise<any>;
recommendedPbkdf2Iterations = DEFAULT_PBKDF2_ITERATIONS;
loading = false;
constructor(
private apiService: ApiService,
@ -37,21 +35,17 @@ export class ChangeKdfComponent implements OnInit {
private platformUtilsService: PlatformUtilsService,
private cryptoService: CryptoService,
private messagingService: MessagingService,
private stateService: StateService,
private logService: LogService,
private stateService: StateService
@Inject(DIALOG_DATA) params: { kdf: KdfType; kdfConfig: KdfConfig }
) {
this.kdfOptions = [
{ name: "PBKDF2 SHA-256", value: KdfType.PBKDF2_SHA256 },
{ name: "Argon2id", value: KdfType.Argon2id },
];
}
async ngOnInit() {
this.kdf = await this.stateService.getKdfType();
this.kdfConfig = await this.stateService.getKdfConfig();
this.kdf = params.kdf;
this.kdfConfig = params.kdfConfig;
this.masterPassword = null;
}
async submit() {
this.loading = true;
const hasEncKey = await this.cryptoService.hasEncKey();
if (!hasEncKey) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("updateKey"));
@ -69,41 +63,27 @@ export class ChangeKdfComponent implements OnInit {
this.messagingService.send("logout");
} catch (e) {
this.logService.error(e);
}
}
async onChangeKdf(newValue: KdfType) {
if (newValue === KdfType.PBKDF2_SHA256) {
this.kdfConfig = new KdfConfig(DEFAULT_PBKDF2_ITERATIONS);
} else if (newValue === KdfType.Argon2id) {
this.kdfConfig = new KdfConfig(
DEFAULT_ARGON2_ITERATIONS,
DEFAULT_ARGON2_MEMORY,
DEFAULT_ARGON2_PARALLELISM
);
} else {
throw new Error("Unknown KDF type.");
} finally {
this.loading = false;
}
}
private async makeKeyAndSaveAsync() {
const masterPassword = this.form.value.masterPassword;
const request = new KdfRequest();
request.kdf = this.kdf;
request.kdfIterations = this.kdfConfig.iterations;
request.kdfMemory = this.kdfConfig.memory;
request.kdfParallelism = this.kdfConfig.parallelism;
request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, null);
request.masterPasswordHash = await this.cryptoService.hashPassword(masterPassword, null);
const email = await this.stateService.getEmail();
const newKey = await this.cryptoService.makeKey(
this.masterPassword,
masterPassword,
email,
this.kdf,
this.kdfConfig
);
request.newMasterPasswordHash = await this.cryptoService.hashPassword(
this.masterPassword,
newKey
);
request.newMasterPasswordHash = await this.cryptoService.hashPassword(masterPassword, newKey);
const newEncKey = await this.cryptoService.remakeEncKey(newKey);
request.key = newEncKey[1].encryptedString;

View File

@ -2,23 +2,7 @@
<h1>{{ "encKeySettings" | i18n }}</h1>
</div>
<bit-callout type="warning">{{ "changeKdfLoggedOutWarning" | i18n }}</bit-callout>
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate autocomplete="off">
<div class="row">
<div class="col-6">
<div class="form-group">
<label for="kdfMasterPassword">{{ "masterPass" | i18n }}</label>
<input
id="kdfMasterPassword"
type="password"
name="MasterPasswordHash"
class="form-control"
[(ngModel)]="masterPassword"
required
appInputVerbatim
/>
</div>
</div>
</div>
<form #form ngNativeValidate autocomplete="off">
<div class="row">
<div class="col-6">
<div class="form-group mb-0">
@ -122,7 +106,13 @@
</ng-container>
</div>
</div>
<button type="submit" buttonType="primary" bitButton [loading]="form.loading">
<button
(click)="openConfirmationModal()"
type="button"
buttonType="primary"
bitButton
[loading]="form.loading"
>
{{ "changeKdf" | i18n }}
</button>
</form>

View File

@ -0,0 +1,62 @@
import { Component, OnInit } from "@angular/core";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
import {
DEFAULT_KDF_CONFIG,
DEFAULT_PBKDF2_ITERATIONS,
DEFAULT_ARGON2_ITERATIONS,
DEFAULT_ARGON2_MEMORY,
DEFAULT_ARGON2_PARALLELISM,
KdfType,
} from "@bitwarden/common/enums/kdfType";
import { DialogService } from "@bitwarden/components";
import { ChangeKdfConfirmationComponent } from "./change-kdf-confirmation.component";
@Component({
selector: "app-change-kdf",
templateUrl: "change-kdf.component.html",
})
export class ChangeKdfComponent implements OnInit {
kdf = KdfType.PBKDF2_SHA256;
kdfConfig: KdfConfig = DEFAULT_KDF_CONFIG;
kdfType = KdfType;
kdfOptions: any[] = [];
recommendedPbkdf2Iterations = DEFAULT_PBKDF2_ITERATIONS;
constructor(private stateService: StateService, private dialogService: DialogService) {
this.kdfOptions = [
{ name: "PBKDF2 SHA-256", value: KdfType.PBKDF2_SHA256 },
{ name: "Argon2id", value: KdfType.Argon2id },
];
}
async ngOnInit() {
this.kdf = await this.stateService.getKdfType();
this.kdfConfig = await this.stateService.getKdfConfig();
}
async onChangeKdf(newValue: KdfType) {
if (newValue === KdfType.PBKDF2_SHA256) {
this.kdfConfig = new KdfConfig(DEFAULT_PBKDF2_ITERATIONS);
} else if (newValue === KdfType.Argon2id) {
this.kdfConfig = new KdfConfig(
DEFAULT_ARGON2_ITERATIONS,
DEFAULT_ARGON2_MEMORY,
DEFAULT_ARGON2_PARALLELISM
);
} else {
throw new Error("Unknown KDF type.");
}
}
async openConfirmationModal() {
this.dialogService.open(ChangeKdfConfirmationComponent, {
data: {
kdf: this.kdf,
kdfConfig: this.kdfConfig,
},
});
}
}

View File

@ -0,0 +1,14 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { SharedModule } from "../../shared";
import { ChangeKdfConfirmationComponent } from "./change-kdf-confirmation.component";
import { ChangeKdfComponent } from "./change-kdf.component";
@NgModule({
imports: [CommonModule, SharedModule],
declarations: [ChangeKdfComponent, ChangeKdfConfirmationComponent],
exports: [ChangeKdfComponent, ChangeKdfConfirmationComponent],
})
export class ChangeKdfModule {}

View File

@ -74,7 +74,7 @@ import { BillingHistoryComponent } from "../settings/billing-history.component";
import { BillingSyncKeyComponent } from "../settings/billing-sync-key.component";
import { ChangeAvatarComponent } from "../settings/change-avatar.component";
import { ChangeEmailComponent } from "../settings/change-email.component";
import { ChangeKdfComponent } from "../settings/change-kdf.component";
import { ChangeKdfModule } from "../settings/change-kdf/change-kdf.module";
import { ChangePasswordComponent } from "../settings/change-password.component";
import { CreateOrganizationComponent } from "../settings/create-organization.component";
import { DeleteAccountComponent } from "../settings/delete-account.component";
@ -117,7 +117,13 @@ import { SharedModule } from "./shared.module";
// Please do not add to this list of declarations - we should refactor these into modules when doing so makes sense until there are none left.
// If you are building new functionality, please create or extend a feature module instead.
@NgModule({
imports: [SharedModule, OrganizationCreateModule, RegisterFormModule, ProductSwitcherModule],
imports: [
SharedModule,
OrganizationCreateModule,
RegisterFormModule,
ProductSwitcherModule,
ChangeKdfModule,
],
declarations: [
PremiumBadgeComponent,
AcceptEmergencyComponent,
@ -135,7 +141,6 @@ import { SharedModule } from "./shared.module";
AttachmentsComponent,
BillingSyncKeyComponent,
ChangeEmailComponent,
ChangeKdfComponent,
ChangePasswordComponent,
CollectionsComponent,
CreateOrganizationComponent,
@ -245,7 +250,6 @@ import { SharedModule } from "./shared.module";
ApiKeyComponent,
AttachmentsComponent,
ChangeEmailComponent,
ChangeKdfComponent,
ChangePasswordComponent,
CollectionsComponent,
CreateOrganizationComponent,

View File

@ -6491,7 +6491,7 @@
"message": "Change KDF settings"
},
"changeKdfLoggedOutWarning": {
"message": "Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour. We recommend exporting your vault before changing your encryption settings to prevent data loss."
"message": "Proceeding will log you out of all active sessions. You will need to log back in and complete two-step login setup. We recommend exporting your vault before changing your encryption settings to prevent data loss."
},
"secretsManagerBeta": {
"message": "Secrets Manager Beta"