update enc key

This commit is contained in:
Kyle Spearrin 2018-07-17 17:22:51 -04:00
parent d1395e37fd
commit 49f948844f
7 changed files with 180 additions and 2 deletions

2
jslib

@ -1 +1 @@
Subproject commit 3354f0b8180c319b4ae72c062874e9f8f5a1fe61
Subproject commit f35ecf0cd8bd1b91f2544e07bb7b766d227f54ff

View File

@ -92,6 +92,7 @@ import { TwoFactorSetupComponent } from './settings/two-factor-setup.component';
import { TwoFactorU2fComponent } from './settings/two-factor-u2f.component';
import { TwoFactorVerifyComponent } from './settings/two-factor-verify.component';
import { TwoFactorYubiKeyComponent } from './settings/two-factor-yubikey.component';
import { UpdateKeyComponent } from './settings/update-key.component';
import { UpdateLicenseComponent } from './settings/update-license.component';
import { UserBillingComponent } from './settings/user-billing.component';
import { VerifyEmailComponent } from './settings/verify-email.component';
@ -241,6 +242,7 @@ import { SearchPipe } from 'jslib/angular/pipes/search.pipe';
TwoFactorU2fComponent,
TwoFactorVerifyComponent,
TwoFactorYubiKeyComponent,
UpdateKeyComponent,
UpdateLicenseComponent,
UserBillingComponent,
UserLayoutComponent,
@ -280,6 +282,7 @@ import { SearchPipe } from 'jslib/angular/pipes/search.pipe';
TwoFactorRecoveryComponent,
TwoFactorU2fComponent,
TwoFactorYubiKeyComponent,
UpdateKeyComponent,
],
providers: [],
bootstrap: [AppComponent],

View File

@ -0,0 +1,28 @@
<div class="modal fade">
<div class="modal-dialog">
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
<div class="modal-header">
<h2 class="modal-title">{{'updateEncryptionKey' | i18n}}</h2>
<button type="button" class="close" data-dismiss="modal" attr.aria-label="{{'close' | i18n}}">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>{{'updateEncryptionKeyShortDesc' | i18n}} {{'updateEncryptionKeyDesc' | i18n}}
<a href="https://help.bitwarden.com/article/update-encryption-key/" target="_blank" rel="noopener">{{'learnMore' | i18n}}</a>
</p>
<app-callout type="warning">{{'updateEncryptionKeyWarning' | i18n}}</app-callout>
<label for="masterPassword">{{'masterPass' | i18n}}</label>
<input id="masterPassword" type="password" name="MasterPasswordHash" class="form-control" [(ngModel)]="masterPassword" required
appAutofocus appInputVerbatim>
</div>
<div class="modal-footer">
<button appBlurClick type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
<i class="fa fa-spinner fa-spin"></i>
<span>{{'updateEncryptionKey' | i18n}}</span>
</button>
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">{{'close' | i18n}}</button>
</div>
</form>
</div>
</div>

View File

@ -0,0 +1,98 @@
import { Component } from '@angular/core';
import {
Toast,
ToasterService,
} from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { ApiService } from 'jslib/abstractions/api.service';
import { CipherService } from 'jslib/abstractions/cipher.service';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { FolderService } from 'jslib/abstractions/folder.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { SyncService } from 'jslib/abstractions/sync.service';
import { CipherString } from 'jslib/models/domain/cipherString';
import { CipherWithIdRequest } from 'jslib/models/request/cipherWithIdRequest';
import { FolderWithIdRequest } from 'jslib/models/request/folderWithIdRequest';
import { UpdateKeyRequest } from 'jslib/models/request/updateKeyRequest';
@Component({
selector: 'app-update-key',
templateUrl: 'update-key.component.html',
})
export class UpdateKeyComponent {
masterPassword: string;
formPromise: Promise<any>;
constructor(private apiService: ApiService, private i18nService: I18nService,
private analytics: Angulartics2, private toasterService: ToasterService,
private cryptoService: CryptoService, private messagingService: MessagingService,
private syncService: SyncService, private folderService: FolderService,
private cipherService: CipherService) { }
async submit() {
const hasEncKey = await this.cryptoService.hasEncKey();
if (hasEncKey) {
return;
}
if (this.masterPassword == null || this.masterPassword === '') {
this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'),
this.i18nService.t('masterPassRequired'));
return;
}
try {
this.formPromise = this.makeRequest().then((request) => {
return this.apiService.postAccountKey(request);
});
await this.formPromise;
this.analytics.eventTrack.next({ action: 'Key Updated' });
const toast: Toast = {
type: 'success',
title: this.i18nService.t('keyUpdated'),
body: this.i18nService.t('logBackInOthersToo'),
timeout: 15000,
};
this.toasterService.popAsync(toast);
this.messagingService.send('logout');
} catch { }
}
private async makeRequest(): Promise<UpdateKeyRequest> {
const key = await this.cryptoService.getKey();
const encKey = await this.cryptoService.makeEncKey(key);
const privateKey = await this.cryptoService.getPrivateKey();
let encPrivateKey: CipherString = null;
if (privateKey != null) {
encPrivateKey = await this.cryptoService.encrypt(privateKey, encKey[0]);
}
const request = new UpdateKeyRequest();
request.privateKey = encPrivateKey != null ? encPrivateKey.encryptedString : null;
request.key = encKey[1].encryptedString;
request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, null);
await this.syncService.fullSync(true);
const folders = await this.folderService.getAllDecrypted();
for (let i = 0; i < folders.length; i++) {
const folder = await this.folderService.encrypt(folders[i], encKey[0]);
request.folders.push(new FolderWithIdRequest(folder));
}
const ciphers = await this.cipherService.getAllDecrypted();
for (let i = 0; i < ciphers.length; i++) {
if (ciphers[i].organizationId != null) {
continue;
}
const cipher = await this.cipherService.encrypt(ciphers[i], encKey[0]);
request.ciphers.push(new CipherWithIdRequest(cipher));
}
return request;
}
}

View File

@ -61,6 +61,17 @@
</a>
</div>
</div>
<div class="card border-warning mb-4" *ngIf="showUpdateKey">
<div class="card-header bg-warning text-white">
<i class="fa fa-warning fa-fw"></i> {{'updateKeyTitle' | i18n}}
</div>
<div class="card-body">
<p>{{'updateEncryptionKeyShortDesc' | i18n}}</p>
<button class="btn btn-block btn-outline-secondary" type="button" (click)="updateKey()">
{{'updateEncryptionKey' | i18n}}
</button>
</div>
</div>
<div class="card">
<div class="card-header">
{{'organizations' | i18n}}
@ -80,3 +91,4 @@
<ng-template #bulkDeleteTemplate></ng-template>
<ng-template #bulkMoveTemplate></ng-template>
<ng-template #bulkShareTemplate></ng-template>
<ng-template #updateKeyTemplate></ng-template>

View File

@ -18,6 +18,7 @@ import { CipherView } from 'jslib/models/view/cipherView';
import { ModalComponent } from '../modal.component';
import { OrganizationsComponent } from '../settings/organizations.component';
import { UpdateKeyComponent } from '../settings/update-key.component';
import { AddEditComponent } from './add-edit.component';
import { AttachmentsComponent } from './attachments.component';
import { BulkDeleteComponent } from './bulk-delete.component';
@ -50,6 +51,7 @@ export class VaultComponent implements OnInit {
@ViewChild('bulkDeleteTemplate', { read: ViewContainerRef }) bulkDeleteModalRef: ViewContainerRef;
@ViewChild('bulkMoveTemplate', { read: ViewContainerRef }) bulkMoveModalRef: ViewContainerRef;
@ViewChild('bulkShareTemplate', { read: ViewContainerRef }) bulkShareModalRef: ViewContainerRef;
@ViewChild('updateKeyTemplate', { read: ViewContainerRef }) updateKeyModalRef: ViewContainerRef;
favorites: boolean = false;
type: CipherType = null;
@ -70,7 +72,7 @@ export class VaultComponent implements OnInit {
this.showVerifyEmail = !(await this.tokenService.getEmailVerified());
this.showBrowserOutdated = window.navigator.userAgent.indexOf('MSIE') !== -1;
const hasEncKey = await this.cryptoService.hasEncKey();
this.showUpdateKey = !this.showVerifyEmail && hasEncKey;
this.showUpdateKey = !hasEncKey;
this.route.queryParams.subscribe(async (params) => {
await this.syncService.fullSync(false);
@ -363,6 +365,20 @@ export class VaultComponent implements OnInit {
this.ciphersComponent.selectAll(select);
}
updateKey() {
if (this.modal != null) {
this.modal.close();
}
const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
this.modal = this.updateKeyModalRef.createComponent(factory).instance;
this.modal.show<UpdateKeyComponent>(UpdateKeyComponent, this.updateKeyModalRef);
this.modal.onClosed.subscribe(() => {
this.modal = null;
});
}
private clearFilters() {
this.folderId = null;
this.collectionId = null;

View File

@ -837,6 +837,9 @@
"logBackIn": {
"message": "Please log back in."
},
"logBackInOthersToo": {
"message": "Please log back in. If you are using other Bitwarden applications log out and back in to those as well."
},
"changeMasterPassword": {
"message": "Change Master Password"
},
@ -2273,5 +2276,23 @@
"example": "15"
}
}
},
"keyUpdated": {
"message": "Key Updated"
},
"updateKeyTitle": {
"message": "Update Key"
},
"updateEncryptionKey": {
"message": "Update Encryption Key"
},
"updateEncryptionKeyShortDesc": {
"message": "You are currently using an outdated encryption scheme."
},
"updateEncryptionKeyDesc": {
"message": "We've moved to larger encryption keys that provide better security and access to newer features. Updating your encryption key is quick and easy. Just type your master password below. This update will eventually become mandatory."
},
"updateEncryptionKeyWarning": {
"message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed."
}
}