working encryption at rest.
Medicare storage isnt working on first init - but Sync works fine.
This commit is contained in:
parent
1b0bfab2ac
commit
75e858d0f2
|
@ -33,7 +33,9 @@
|
|||
"chart.js": "2.9.4",
|
||||
"crypto-pouch": "^4.0.1",
|
||||
"fhirclient": "^2.5.1",
|
||||
"garbados-crypt": "^3.0.0-beta",
|
||||
"humanize-duration": "^3.27.3",
|
||||
"idb": "^7.1.0",
|
||||
"moment": "^2.29.4",
|
||||
"ng2-charts": "^2.3.0",
|
||||
"ngx-dropzone": "^3.1.0",
|
||||
|
@ -45,6 +47,7 @@
|
|||
"pouchdb-find": "^7.3.0",
|
||||
"pouchdb-upsert": "^2.2.0",
|
||||
"rxjs": "~6.5.4",
|
||||
"transform-pouch": "^2.0.0",
|
||||
"tslib": "^2.0.0",
|
||||
"uuid": "^9.0.0",
|
||||
"zone.js": "~0.11.8"
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
// This is a Typescript module that recreates the functionality defined in https://github.com/calvinmetcalf/crypto-pouch/blob/master/index.js
|
||||
// This file only exists because the PouchDB crypto plugin must work in both the browser and web-worker environment (where `window` is
|
||||
// undefined and causes errors).
|
||||
// Also, crypto-pouch does not support storing encrypted data in the remote database by default, which I'm attempting to do by commenting out the
|
||||
// NO_COUCH error.
|
||||
//
|
||||
// We've attempted to use the Typescript Module Plugin/Augmentation pattern to modify the global `pouchdb` object, however that
|
||||
// failed for a variety of reasons, so instead we're using a PouchdbCrypto class with static methods to re-implement the crypto logic
|
||||
//
|
||||
//
|
||||
// See:
|
||||
// - https://github.com/calvinmetcalf/crypto-pouch/blob/master/index.js
|
||||
// - https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-plugin-d-ts.html
|
||||
// - https://www.typescriptlang.org/docs/handbook/declaration-merging.html
|
||||
// - https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-plugin-d-ts.html
|
||||
// - https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html
|
||||
// - https://stackoverflow.com/questions/35074713/extending-typescript-global-object-in-node-js
|
||||
// - https://github.com/Microsoft/TypeScript/issues/15818
|
||||
|
||||
import * as Crypt from 'garbados-crypt';
|
||||
import { openDB, deleteDB, wrap, unwrap } from 'idb';
|
||||
|
||||
// const Crypt = require()
|
||||
|
||||
const LOCAL_ID = '_local/crypto'
|
||||
const IGNORE = ['_id', '_rev', '_deleted', '_conflicts']
|
||||
|
||||
// const NO_COUCH = 'crypto-pouch does not work with pouchdb\'s http adapter. Use a local adapter instead.'
|
||||
|
||||
export class PouchdbCryptoOptions {
|
||||
password?: string
|
||||
ignore?: string[]
|
||||
}
|
||||
|
||||
export class PouchdbCrypto {
|
||||
public static async localIdb(){
|
||||
const dbPromise = openDB('crypto-store', 1, {
|
||||
upgrade(db) {
|
||||
db.createObjectStore('crypto');
|
||||
},
|
||||
});
|
||||
|
||||
return await dbPromise
|
||||
}
|
||||
|
||||
public static async crypto(db, password, options: PouchdbCryptoOptions = {}) {
|
||||
// if (db.adapter === 'http') {
|
||||
// throw new Error(NO_COUCH)
|
||||
// }
|
||||
if (typeof password === 'object') {
|
||||
// handle `db.crypto({ password, ...options })`
|
||||
options = password
|
||||
password = password.password
|
||||
delete options.password
|
||||
}
|
||||
// setup ignore list
|
||||
db._ignore = IGNORE.concat(options.ignore || [])
|
||||
// setup crypto helper
|
||||
const trySetup = async () => {
|
||||
// try saving credentials to a local doc
|
||||
try {
|
||||
// first we try to get saved creds from the local doc
|
||||
const localDb = await PouchdbCrypto.localIdb()
|
||||
let exportString = await localDb.get('crypto','encryption_data')
|
||||
if(!exportString){
|
||||
// no existing encryption key found
|
||||
|
||||
// do first-time setup
|
||||
db._crypt = new Crypt(password)
|
||||
let exportString = await db._crypt.export()
|
||||
await localDb.put('crypto', exportString, 'encryption_data')
|
||||
} else {
|
||||
db._crypt = await Crypt.import(password, exportString)
|
||||
}
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
await trySetup()
|
||||
// instrument document transforms
|
||||
db.transform({
|
||||
incoming: async (doc) => {
|
||||
// if no crypt, ex: after .removeCrypto(), just return the doc
|
||||
if (!db._crypt) {
|
||||
console.warn("=======>WARNING DOCUMENT NOT ENCRYPTED")
|
||||
|
||||
return doc
|
||||
}
|
||||
console.log("=======>saving... raw doc", doc)
|
||||
|
||||
if (doc._attachments && !db._ignore.includes('_attachments')) {
|
||||
throw new Error('Attachments cannot be encrypted. Use {ignore: "_attachments"} option')
|
||||
}
|
||||
let encrypted: any = {}
|
||||
for (let key of db._ignore) {
|
||||
// attach ignored fields to encrypted doc
|
||||
if (key in doc) encrypted[key] = doc[key]
|
||||
}
|
||||
encrypted.payload = await db._crypt.encrypt(JSON.stringify(doc))
|
||||
console.log("=======>saving encrpted doc", encrypted)
|
||||
return encrypted
|
||||
},
|
||||
outgoing: async (doc) => {
|
||||
// if no crypt, ex: after .removeCrypto(), just return the doc
|
||||
if (!db._crypt) { return doc }
|
||||
console.log("=======>retrieved doc", doc.payload)
|
||||
let decryptedString = await db._crypt.decrypt(doc.payload)
|
||||
console.log("=======>retrieved decrypted string", decryptedString)
|
||||
let decrypted = JSON.parse(decryptedString)
|
||||
for (let key of db._ignore) {
|
||||
// patch decrypted doc with ignored fields
|
||||
if (key in doc) decrypted[key] = doc[key]
|
||||
}
|
||||
console.log("=======>retrieved decrypted", decrypted)
|
||||
return decrypted
|
||||
}
|
||||
})
|
||||
return db
|
||||
}
|
||||
public static removeCrypto(db) {
|
||||
delete db._crypt
|
||||
}
|
||||
}
|
|
@ -1,3 +1,18 @@
|
|||
// This is a Typescript module that recreates the functionality defined in https://github.com/pouchdb/upsert/blob/master/index.js
|
||||
// This file only exists because the PouchDB upsert plugin must work in both the browser and web-worker environment (where `window` is
|
||||
// undefined and causes errors).
|
||||
//
|
||||
// We've attempted to use the Typescript Module Plugin/Augmentation pattern to modify the global `pouchdb` object, however that
|
||||
// failed for a variety of reasons, so instead we're using a PouchdbUpsert class with static methods to re-implement the upsert logic
|
||||
//
|
||||
// See:
|
||||
// - https://github.com/pouchdb/upsert/blob/master/index.js
|
||||
// - https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-plugin-d-ts.html
|
||||
// - https://www.typescriptlang.org/docs/handbook/declaration-merging.html
|
||||
// - https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-plugin-d-ts.html
|
||||
// - https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html
|
||||
// - https://stackoverflow.com/questions/35074713/extending-typescript-global-object-in-node-js
|
||||
// - https://github.com/Microsoft/TypeScript/issues/15818
|
||||
export class PouchdbUpsert {
|
||||
public static upsert(db, docId, diffFun, cb?) {
|
||||
var promise = PouchdbUpsert.upsertInner(db, docId, diffFun);
|
||||
|
|
|
@ -7,12 +7,17 @@ import {Base64} from '../utils/base64';
|
|||
|
||||
// PouchDB & plugins
|
||||
import * as PouchDB from 'pouchdb/dist/pouchdb';
|
||||
import * as PouchCrypto from 'crypto-pouch';
|
||||
|
||||
// import * as PouchCrypto from 'crypto-pouch';
|
||||
// PouchDB.plugin(PouchCrypto);
|
||||
|
||||
import * as PouchTransform from 'transform-pouch';
|
||||
PouchDB.plugin(PouchTransform);
|
||||
|
||||
import {PouchdbUpsert} from './plugins/upsert';
|
||||
import {UpsertSummary} from '../models/fasten/upsert-summary';
|
||||
PouchDB.plugin(PouchCrypto);
|
||||
|
||||
|
||||
import {PouchdbCrypto, PouchdbCryptoOptions} from './plugins/crypto';
|
||||
|
||||
// !!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!
|
||||
// most pouchdb plugins seem to fail when used in a webworker.
|
||||
|
@ -52,6 +57,7 @@ export class PouchdbRepository implements IDatabaseRepository {
|
|||
// replicationHandler: any
|
||||
remotePouchEndpoint: string // "http://localhost:5984"
|
||||
encryptionKey: string
|
||||
encryptionInitComplete: boolean = false
|
||||
pouchDb: PouchDB.Database
|
||||
|
||||
/**
|
||||
|
@ -217,18 +223,21 @@ export class PouchdbRepository implements IDatabaseRepository {
|
|||
if(!this.pouchDb) {
|
||||
throw(new Error( "Database is not available - please configure an instance." ));
|
||||
}
|
||||
// if(this.encryptionKey){
|
||||
// return this.pouchDb.crypto(this.encryptionKey, {ignore:[
|
||||
// 'doc_type',
|
||||
// 'source_id',
|
||||
// 'source_resource_type',
|
||||
// 'source_resource_id',
|
||||
// ]}).then(() => {
|
||||
// return this.pouchDb
|
||||
// })
|
||||
// } else {
|
||||
if(this.encryptionKey && !this.encryptionInitComplete){
|
||||
return PouchdbCrypto.crypto(this.pouchDb, this.encryptionKey, {ignore:[
|
||||
'doc_type',
|
||||
'source_id',
|
||||
'source_resource_type',
|
||||
'source_resource_id',
|
||||
]})
|
||||
.then((encryptedPouchDb) => {
|
||||
this.pouchDb = encryptedPouchDb
|
||||
this.encryptionInitComplete = true
|
||||
return this.pouchDb
|
||||
})
|
||||
} else {
|
||||
return this.pouchDb;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -4579,6 +4579,11 @@ icss-utils@^5.0.0, icss-utils@^5.1.0:
|
|||
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
|
||||
integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
|
||||
|
||||
idb@^7.1.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.0.tgz#2cc886be57738419e57f9aab58f647e5e2160270"
|
||||
integrity sha512-Wsk07aAxDsntgYJY4h0knZJuTxM73eQ4reRAO+Z1liOh8eMCJ/MoDS8fCui1vGT9mnjtl1sOu3I2i/W1swPYZg==
|
||||
|
||||
ieee754@^1.1.13:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
|
|
Loading…
Reference in New Issue