From 49f948844f8b957cd9e63f8d49aab71c8c4a95a9 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Tue, 17 Jul 2018 17:22:51 -0400 Subject: [PATCH] update enc key --- jslib | 2 +- src/app/app.module.ts | 3 + src/app/settings/update-key.component.html | 28 +++++++ src/app/settings/update-key.component.ts | 98 ++++++++++++++++++++++ src/app/vault/vault.component.html | 12 +++ src/app/vault/vault.component.ts | 18 +++- src/locales/en/messages.json | 21 +++++ 7 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 src/app/settings/update-key.component.html create mode 100644 src/app/settings/update-key.component.ts diff --git a/jslib b/jslib index 3354f0b8..f35ecf0c 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 3354f0b8180c319b4ae72c062874e9f8f5a1fe61 +Subproject commit f35ecf0cd8bd1b91f2544e07bb7b766d227f54ff diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c5b65c90..411fd5a7 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -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], diff --git a/src/app/settings/update-key.component.html b/src/app/settings/update-key.component.html new file mode 100644 index 00000000..d2322964 --- /dev/null +++ b/src/app/settings/update-key.component.html @@ -0,0 +1,28 @@ + diff --git a/src/app/settings/update-key.component.ts b/src/app/settings/update-key.component.ts new file mode 100644 index 00000000..3710f01a --- /dev/null +++ b/src/app/settings/update-key.component.ts @@ -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; + + 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 { + 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; + } +} diff --git a/src/app/vault/vault.component.html b/src/app/vault/vault.component.html index 092b04f4..161c3426 100644 --- a/src/app/vault/vault.component.html +++ b/src/app/vault/vault.component.html @@ -61,6 +61,17 @@ +
+
+ {{'updateKeyTitle' | i18n}} +
+
+

{{'updateEncryptionKeyShortDesc' | i18n}}

+ +
+
{{'organizations' | i18n}} @@ -80,3 +91,4 @@ + diff --git a/src/app/vault/vault.component.ts b/src/app/vault/vault.component.ts index e5296e56..900ceede 100644 --- a/src/app/vault/vault.component.ts +++ b/src/app/vault/vault.component.ts @@ -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, this.updateKeyModalRef); + + this.modal.onClosed.subscribe(() => { + this.modal = null; + }); + } + private clearFilters() { this.folderId = null; this.collectionId = null; diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 4b5a3002..410e136d 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -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." } }