From be93fef13c51c8e077dc105a2a3424dd74c19901 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Mon, 17 Oct 2022 22:21:38 -0700 Subject: [PATCH] added wizard steps to validate imported encryption keys and validate generated encryption keys. --- .../encryption-manager.component.html | 171 +++++++++++------- .../encryption-manager.component.ts | 101 ++++++++++- .../src/lib/database/pouchdb_repository.ts | 5 +- 3 files changed, 207 insertions(+), 70 deletions(-) diff --git a/frontend/src/app/pages/encryption-manager/encryption-manager.component.html b/frontend/src/app/pages/encryption-manager/encryption-manager.component.html index 8340c7a3..62435403 100644 --- a/frontend/src/app/pages/encryption-manager/encryption-manager.component.html +++ b/frontend/src/app/pages/encryption-manager/encryption-manager.component.html @@ -2,7 +2,7 @@
-

Security Manager

+

Security Manager

Before you use Fasten you'll need to import or generate a new encryption key.
@@ -18,55 +18,12 @@

-
-

Generate an encryption key

-
-

- Fasten has generated an encryption key for you. You can use this encryption key to decode your medical records on this browser, and other devices. -
- - This is the only time the encryption key will be available to view, copy or download. We recommend downloading this key and storing the file in a secure location. -
- You can reset your encryption key at any time, however any previously encrypted data will no longer be accessible. -

- -
- - -
-
- -
- - - + + +
+ +
+ + +

Import existing encryption key

+
+

+ Fasten was unable to find your encryption key on this device, and has detected encrypted data in your database. +
+ You will need to provide your encryption key to access your health records. +

+ +
+
+
+ + +
+ {{importCustomFileError}} +
+
+
+
+ +
+
+ +

Validate encryption key

+
+

+ Thank you for providing your encryption key. Fasten will attempt to decrypt your secured records with this key. +

+
+ Loading... +
+
+
+
diff --git a/frontend/src/app/pages/encryption-manager/encryption-manager.component.ts b/frontend/src/app/pages/encryption-manager/encryption-manager.component.ts index 0dd426d7..a02c61ed 100644 --- a/frontend/src/app/pages/encryption-manager/encryption-manager.component.ts +++ b/frontend/src/app/pages/encryption-manager/encryption-manager.component.ts @@ -4,6 +4,8 @@ import {PouchdbCryptConfig, PouchdbCrypto} from '../../../lib/database/plugins/c import {FastenDbService} from '../../services/fasten-db.service'; import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser'; import {Router} from '@angular/router'; +import {ToastService} from '../../services/toast.service'; +import {ToastNotification, ToastType} from '../../models/fasten/toast'; export enum CryptoPanelType { Loading, @@ -24,7 +26,15 @@ export class EncryptionManagerComponent implements OnInit { generateCryptoConfigUrl: SafeResourceUrl = "" generateCryptoConfigFilename: string = "" - constructor(private fastenDbService: FastenDbService, private sanitizer: DomSanitizer, private router: Router) { } + generateCustomFileError: string = "" + + importCustomFileError: string = "" + + currentStep: number + lastStep: number + + + constructor(private fastenDbService: FastenDbService, private sanitizer: DomSanitizer, private router: Router, private toastService: ToastService) { } ngOnInit(): void { @@ -39,8 +49,22 @@ export class EncryptionManagerComponent implements OnInit { } + nextHandler() { + this.currentStep += 1 + // if (!this.stepsService.isLastStep()) { + // this.stepsService.moveToNextStep(); + // } else { + // this.onSubmit(); + // } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Generate Wizard Methods + ///////////////////////////////////////////////////////////////////////////////////////////////// async showGenerateCryptoConfig(): Promise { this.cryptoPanel = CryptoPanelType.Generate + this.currentStep = 1 + this.lastStep = 2 if(!this.currentCryptoConfig){ this.currentCryptoConfig = await PouchdbCrypto.CryptConfig(uuidv4(), this.fastenDbService.current_user) await PouchdbCrypto.StoreCryptConfig(this.currentCryptoConfig) //store in indexdb @@ -50,17 +74,56 @@ export class EncryptionManagerComponent implements OnInit { let currentCryptoConfigBlob = new Blob([JSON.stringify(this.currentCryptoConfig)], { type: 'application/json' }); this.generateCryptoConfigUrl = this.sanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(currentCryptoConfigBlob)); this.generateCryptoConfigFilename = `fasten-${this.fastenDbService.current_user}.key.json` - } return this.currentCryptoConfig } - async showImportCryptoConfig(): Promise { - this.cryptoPanel = CryptoPanelType.Import + generateOpenFileHandler(fileList: FileList) { + this.generateCustomFileError = "" + let file = fileList[0]; + this.readFileContent(file) + .then((content) => { + let parsedCryptoConfig = JSON.parse(content) as PouchdbCryptConfig + + //check if the parsed encryption key matches the currently set encryption key + + if(parsedCryptoConfig.key == this.currentCryptoConfig.key && + parsedCryptoConfig.username == this.currentCryptoConfig.username && + parsedCryptoConfig.config == this.currentCryptoConfig.config){ + return true + } else { + //throw an error & notify user + this.generateCustomFileError = "Crypto configuration file does not match" + throw new Error(this.generateCustomFileError) + } + }) + .then(() => { + + const toastNotification = new ToastNotification() + toastNotification.type = ToastType.Success + toastNotification.message = "Successfully validated & stored encryption key." + toastNotification.autohide = true + this.toastService.show(toastNotification) + + //redirect user to dashboard + return this.router.navigate(['/dashboard']); + }) + .catch(console.error) } - openFileHandler(fileList: FileList) { + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import Wizard Methods + ///////////////////////////////////////////////////////////////////////////////////////////////// + + async showImportCryptoConfig(): Promise { + this.cryptoPanel = CryptoPanelType.Import + this.currentStep = 1 + this.lastStep = 2 + } + + importOpenFileHandler(fileList: FileList) { + this.importCustomFileError = "" let file = fileList[0]; this.readFileContent(file) .then((content) => { @@ -70,13 +133,37 @@ export class EncryptionManagerComponent implements OnInit { return PouchdbCrypto.StoreCryptConfig(cryptoConfig) } else { //throw an error & notify user - console.error("Invalid crypto configuration file") + this.importCustomFileError = "Invalid crypto configuration file" + throw new Error(this.importCustomFileError) } }) .then(() => { - //redirect user to dashboard + //go to step 2 + this.currentStep = 2 + //attempt to initialize pouchdb with specified crypto + this.fastenDbService.ResetDB() + return this.fastenDbService.GetSources() + + }) + .then(() => { + const toastNotification = new ToastNotification() + toastNotification.type = ToastType.Success + toastNotification.message = "Successfully validated & imported encryption key." + toastNotification.autohide = true + this.toastService.show(toastNotification) + return this.router.navigate(['/dashboard']); }) + .catch((err) => { + console.error(err) + //an error occurred while importing credential + const toastNotification = new ToastNotification() + toastNotification.type = ToastType.Error + toastNotification.message = "Provided encryption key does not match. Please try a different key" + toastNotification.autohide = false + this.toastService.show(toastNotification) + this.currentStep = 1 + }) } diff --git a/frontend/src/lib/database/pouchdb_repository.ts b/frontend/src/lib/database/pouchdb_repository.ts index de86e47a..8a0705b5 100644 --- a/frontend/src/lib/database/pouchdb_repository.ts +++ b/frontend/src/lib/database/pouchdb_repository.ts @@ -241,7 +241,10 @@ export class PouchdbRepository implements IDatabaseRepository { // All functions below here will return the raw PouchDB responses, and may need to be wrapped in // new ResourceFhir(result.doc) /////////////////////////////////////////////////////////////////////////////////////// - + public ResetDB(){ + this.pouchDb = null + this.encryptionInitComplete = false + } // Get the active PouchDB instance. Throws an error if no PouchDB instance is // available (ie, user has not yet been configured with call to .configureForUser()). public async GetDB(skipEncryption: boolean = false): Promise {