UNPKG

oidc-lib

Version:

A library for creating OIDC Service Providers

1,290 lines (1,182 loc) 112 kB
"use strict" Object.defineProperty(exports, "__esModule", { value: true }); const HUBINSTANCEMONIKER = 'hub-instance-info'; const HUBFLOCKMONIKER = 'hub-flock-info'; const HUBFLOCKRECORD = 'hub-flock-record'; const DIDNOTPRESENT = 'did-not-present'; const SUPPRESSENCRYPTIONMONIKER = 'suppress-encryption'; var METADATACOLLECTIONNAME; const _CONSTRUCTOR = 'hub_provider'; module.exports = { _constructor = _CONSTRUCTOR, hub_provider: hub_provider, loadHubInfo: loadHubInfo, HUBINSTANCEMONIKER: HUBINSTANCEMONIKER, HUBFLOCKMONIKER: HUBFLOCKMONIKER, DIDNOTPRESENT: DIDNOTPRESENT, flockMembershipFactory: flockMembershipFactory, enableFlock: enableFlock, joinExistingFlock: joinExistingFlock, extendExistingFlock: extendExistingFlock, syncExistingFlock: syncExistingFlock, hubExport: hubExport, simpleResolverFactory: simpleResolverFactory, backgroundSyncControllerFactory: backgroundSyncControllerFactory, deviceManagerFactory: deviceManagerFactory } var keyManagement = require('../../claimer_sts/key_management'); var indexed_db_module = require('./indexed_db'); var moduleName = 'hub_db'; const HUBKEY = 'HUBKEY'; var resolverCache = {}; var hubConfig; var simpleResolver; function hub_provider(){ var pk; const HttpStatusCodes = { NOTFOUND: 404 }; var indexed_db; Object.defineProperty(this, "initialize", { value: function(pkInput, contentModuleName, provider){ return new Promise((resolve, reject) => { var hubDbInfo; var hubDbContents = {}; var schemas = []; var hubInfo; pk = pkInput; hubConfig = pk.util.config.hub; METADATACOLLECTIONNAME = indexed_db_module.METADATACOLLECTIONNAME; loadHubInfo(HUBINSTANCEMONIKER) .then(instanceInfo => { if (instanceInfo){ hubInfo = instanceInfo; if (hubInfo.instanceDid !== DIDNOTPRESENT && syncInForeground()){ return pk.util.simpleResolver.resolve(hubConfig.did) } else{ // by default assume connection return true; } } }, err => { reject(err); }) .then(res => { if (res){ pk.util.offline = false; } }, err => { pk.util.offline = true; }) .then(() => { indexed_db = new indexed_db_module.indexed_db_provider(); return indexed_db.initialize(pkInput, contentModuleName, indexed_db) }) .then(dbInfo => { if (dbInfo){ hubDbInfo = { contentModuleName: dbInfo.contentModuleName, collections: dbInfo.collections, databaseDefinition: dbInfo.databaseDefinition, databaseName: dbInfo.databaseName, databaseUrl: dbInfo.databaseUrl, schemaVersion: dbInfo.schemaVersion, provider: this, dbInfo: dbInfo }; hubDbInfo.authenticationBundle = new authenticationBundleFactory(hubInfo); resolve(hubDbInfo); } }, function(err){ pk.util.log_detail('ERROR getting authentication token', err); }) }); } }) Object.defineProperty(this, "fullSync", { value: function(hubDbInfo){ return new Promise((resolve, reject) => { if (pk.util.offline || !hubDbInfo.authenticationBundle || hubDbInfo.authenticationBundle.instanceDid === undefined){ return resolve(true); } var collectionNames = []; var hubCollectionPromises = []; var hubDbContents = {}; var localDbContents = {}; if (syncInForeground() === false){ resolve(false); return; } var i=0; for (var collectionName in hubDbInfo.collections){ collectionNames[i++] = collectionName; hubCollectionPromises.push(hubDbInfo.provider.queryHubCollection(hubDbInfo, collectionName, {}, { decrypt: false })); } Promise.all(hubCollectionPromises) .then(collectionInfos => { for (var i=0; i < collectionInfos.length; i++){ var collectionName = collectionNames[i]; var collectionElements = collectionInfos[i]; var hubCollection = {}; for (var j=0; j < collectionElements.length; j++){ var hubElement = collectionElements[j]; hubCollection[hubElement.didMeta.object_id] = hubElement; } hubDbContents[collectionName] = hubCollection; } var promises = []; //query the underlying indexeddb so it is not decrypted for (var collectionName in hubDbInfo.collections){ promises.push(hubDbInfo.dbInfo.provider.queryCollection(hubDbInfo.dbInfo, collectionName, {})); } return Promise.all(promises); }, err => { reject(err); }) .then(dbCollections => { for (var i=0; i < dbCollections.length; i++){ var dbCollection = dbCollections[i]; var sync_id; var unsynced_count = 0; var localCollection = {}; for (var j=0; j < dbCollection.length; j++){ var localElement = dbCollection[j]; if (localElement.didMeta.object_id === undefined){ sync_id = '_' + (unsynced_count++).toString(); } else{ sync_id = localElement.didMeta.object_id; } localCollection[sync_id] = localElement; } localDbContents[collectionNames[i]] = localCollection; } hubDbInfo.suppressHubUpdate = true; var localUpdatePromises = [] for (var collection in hubDbContents){ var hubCollection = hubDbContents[collection]; for (var element in hubCollection){ var hubElement = hubCollection[element]; var localElement = localDbContents[collection][element]; if (localElement === undefined || Date.parse(localElement.didMeta.updated_at) < Date.parse(hubElement.didMeta.updated_at)){ localUpdatePromises.push(hubDbInfo.dbInfo.provider.createOrUpdateDocument(hubDbInfo.dbInfo, collection, hubElement, null)); } } } return Promise.all(localUpdatePromises); }, err => { reject(err); }) .then(localUpdateResults => { hubDbInfo.suppressHubUpdate = false; var hubUpdatePromises = [] for (var collectionName in localDbContents){ var localCollection = localDbContents[collectionName]; for (var elementId in localCollection){ var element = localCollection[elementId]; if (element.didMeta.object_id === undefined){ hubUpdatePromises.push(updateHub(hubDbInfo, collectionName, element, { encrypt: false })); } } } return Promise.all(hubUpdatePromises); }, err => { hubDbInfo.suppressHubUpdate = false; reject(err); }) .then(hubUpdateResults => { var date = new Date(); var now = date.getTime(); pk.util.sync_info.last_sync_time = now; resolve(true); }, err => { reject(err); delete hubDbInfo.suppressHubUpdate; }) }); } }) Object.defineProperty(this, "createOrUpdateDocument", { value: function (hubDbInfo, collectionName, objectToWrite) { return new Promise((resolve, reject) => { updateHub(hubDbInfo, collectionName, objectToWrite) .then(function(writeResult){ resolve(objectToWrite); }, function(err){ reject(err); }) }); } }); Object.defineProperty(this, "getDocument", { value: function (hubDbInfo, collectionName, local_id) { return new Promise((resolve, reject) => { indexed_db.getDocument(hubDbInfo.dbInfo, collectionName, local_id) .then( record => { return decryptDbRecord(hubDbInfo, collectionName, record); }, err => { reject(err); }) .then( plaintext => { resolve(plaintext); }, err => { reject(err); }) }); } }); Object.defineProperty(this, "queryCollection", { value: function (hubDbInfo, collectionName, queryDictionaryOrString, indexName) { return new Promise((resolve, reject) => { var encryptedRecords; indexed_db.queryCollection(hubDbInfo.dbInfo, collectionName, queryDictionaryOrString, indexName) .then(function(queryResult){ encryptedRecords = queryResult; var plaintextPromises = []; for (var i=0; i < queryResult.length; i++){ var record = queryResult[i]; plaintextPromises.push(decryptDbRecord(hubDbInfo, collectionName, record)); } return Promise.all(plaintextPromises); }, function(err){ reject(err); }) .then(plaintextRecords => { if (plaintextRecords){ resolve(plaintextRecords); } }, err => { reject(err); }) }); } }); Object.defineProperty(this, "deleteDocument", { value: function(hubDbInfo, collectionName, local_id){ return indexed_db.deleteDocument(hubDbInfo.dbInfo, collectionName, local_id); } }); // options: decrypt - when false, do not decrypt payload // revisions - when true, return revisions Object.defineProperty(this, "queryHubCollection", { value: function (hubDbInfo, collectionName, queryDictionary, options) { var collectionInfo = hubDbInfo.collections[collectionName]; var decodedCommitArray = []; return new Promise((resolve, reject) => { var message = { "@context": "https://schema.identity.foundation/0.1", "@type": "ObjectQueryRequest", "iss": hubDbInfo.authenticationBundle.instanceDid, "aud": hubConfig.did, "sub": hubDbInfo.authenticationBundle.flockDid, "query": { "interface": collectionInfo.hubInterface, "context": collectionInfo.hubContext, "type": collectionInfo.hubType // "object_id": ["3a9de008f526d239..", "a8f3e7..."] } } hubRequest(message, hubDbInfo.authenticationBundle, hubConfig.did) .then(function(resultString){ if (resultString){ var response = JSON.parse(resultString); switch (response['@type']){ case 'ErrorResponse': var msg = 'Error communicating with hub (' + response.error_code + ')'; if (response.developer_message){ msg += ': ' + response.developer_message; } reject(msg); break; case 'ObjectQueryResponse': return(response.objects); break; } } }, function(err){ reject(err); }) .then(objectMetaArray => { if (objectMetaArray){ var objectIdArray = []; if (objectMetaArray.length < 1){ // nothing to return resolve([]); return; } for (var i=0; i < objectMetaArray.length; i++){ var obj = objectMetaArray[i]; objectIdArray.push(obj.id); } var message = { "@context": "https://schema.identity.foundation/0.1", "@type": "CommitQueryRequest", "iss": hubDbInfo.authenticationBundle.instanceDid, "aud": hubConfig.did, "sub": hubDbInfo.authenticationBundle.flockDid, "query": { "object_id": objectIdArray } } return hubRequest(message, hubDbInfo.authenticationBundle, hubConfig.did); } }, err => { reject(err); }) .then(function(resultString){ if (resultString){ var response = JSON.parse(resultString); switch (response['@type']){ case 'ErrorResponse': var msg = 'Error communicating with hub (' + response.error_code + ')'; if (response.developer_message){ msg += ': ' + response.developer_message; } reject(msg); break; case 'CommitQueryResponse': return(response.commits); break; } } }, function(err){ reject(err); }) .then(rawCommitArray => { if (rawCommitArray){ var plaintextPromises = []; for (var i=0; i < rawCommitArray.length; i++){ var commit = rawCommitArray[i]; commit.protected = JSON.parse(pk.base64url.decode(commit.protected)); commit.payload = JSON.parse(pk.base64url.decode(commit.payload)); decodedCommitArray.push(commit); if (options && options.decrypt === false){ plaintextPromises.push(commit.payload); } else{ plaintextPromises.push(decryptDbRecord(hubDbInfo, collectionName, commit.payload)); } } return Promise.all(plaintextPromises); } }, err => { reject(err); }) .then(plaintextPayloads => { if (plaintextPayloads){ var hubCollection = {}; for (var i=0; i < plaintextPayloads.length; i++){ var plaintext = plaintextPayloads[i]; var commit = decodedCommitArray[i]; commit.payload = plaintext; var object_id = commit.header.object_id; if (object_id === undefined){ object_id = commit.protected.object_id; } var hubObject = hubCollection[object_id]; if (hubObject === undefined){ hubObject = { object_id: object_id, integral: {}, revisions: [] }; } hubObject.revisions.push(commit); hubCollection[object_id] = hubObject; } return integrateHubCollection(hubCollection); } }, err => { reject(err); }) .then(integratedCollection => { if (integratedCollection){ var hubCollection = []; for (var key in integratedCollection){ var record = integratedCollection[key].integral; var matchFound = true; if (queryDictionary){ for (var prop in queryDictionary){ if (record[prop] !== queryDictionary[prop]){ matchFound = false; break; } } } if (matchFound){ // when revisions required substitute // the integratedCollection for a record if (options && options.revisions === true){ var ic = integratedCollection[key]; ic.didMeta = { object_id: ic.object_id }; hubCollection.push(ic); } else{ if (record.didMeta === undefined){ record.didMeta = {}; } record.didMeta.object_id = key; var revisions = integratedCollection[key].revisions; record.didMeta.revision = revisions[revisions.length - 1].header.rev; hubCollection.push(record); } } } resolve(hubCollection); } }, err => { reject(err); }) }); } }); function integrateHubCollection(hubCollection){ for(var key in hubCollection){ var hubObject = hubCollection[key]; if (hubObject.revisions.length > 0){ hubObject.revisions.sort(compareRevisions); var assembledObject = hubObject.revisions[hubObject.revisions.length - 1]; var record = assembledObject.payload; record.didMeta.object_id = hubObject.object_id; record.didMeta.updated_at = assembledObject.protected.committed_at; hubObject.integral = record; } } return hubCollection; } function compareRevisions(a, b){ return Date.parse(a.protected.committed_at) - Date.parse(b.protected.committed_at); } } ////////////////////////////////////////////////////////////////////////////////////// var fetch_func; if (typeof window === 'undefined'){ const node_fetch_1 = __importDefault(require("node-fetch")); fetch_func = node_fetch_1; } else { fetch_func = fetch_proxy; } function fetch_proxy(request, options){ return fetch (request, options); } function loadHubInfo(moniker, options, initializedPk){ var createIfAbsent = false; var generateDid = false; var delta = false; if (options){ if (options.createIfAbsent){ createIfAbsent = true; } if (options.generateDid){ generateDid = true; } } return new Promise((resolve, reject) => { var privateKeys = []; var updatedInfo; var hubInfo = new hubInfoFactory(moniker); hubInfo.load() .then(() => { if (hubInfo.privateKeys !== undefined){ return Promise.resolve(true); } else if (createIfAbsent === false){ return resolve(hubInfo); } else{ delta = true; var currentKeyRoot = 'key-'; var currentKeyCount = 0; var currentKeyId; var genParams; var keystore = pk.jose.JWK.createKeyStore(); var date = new Date(); currentKeyId = currentKeyRoot + (currentKeyCount++).toString(); genParams = { alg: 'RS256', key_ops: ['sign', 'verify'], kid: currentKeyId }; return keystore.generate("RSA", 2048, genParams); } }, err => { reject(err); }) .then( key => { if (key !== undefined){ if (key === true){ privateKeys = hubInfo.privateKeys; } else{ privateKeys.push(key.toJSON(true)); } if (hubInfo.instanceDid && hubInfo.instanceDid !== DIDNOTPRESENT){ return hubInfo.instanceDid; } else{ if (generateDid === false){ return DIDNOTPRESENT; } else { if (initializedPk === undefined){ initializedPk = pk; } delta = true; return genDid(initializedPk, privateKeys); } } } }, function(err){ reject(err); }) .then(did => { if (did){ var noDid; if (did === true){ did = noDid; } if (!delta){ resolve(hubInfo); return; } else{ updatedInfo = new hubInfoFactory(moniker, privateKeys, did, hubInfo.flockDid); return updatedInfo.load(); } } }, err => { reject(err); }) .then(doUpdated => { if (doUpdated){ return updatedInfo.save(); } }, err => { reject(err); }) .then(updateSaved => { if (updateSaved){ resolve(updatedInfo); } }, err => { reject(err); }) }); } function simpleResolverFactory(discoveryEndpoint){ //TODO: the version should be in the config var _endpointPath = discoveryEndpoint + '/1.0/identifiers/'; var _resolveCache = {}; Object.defineProperty(this, "resolve", { value: function(did) { return new Promise((resolve, reject) => { if (!did){ reject('resolver invoked with undefined or empty did'); } var cacheHit = resolverCache[did]; if (cacheHit) { return resolve(cacheHit); } fetch_func(_endpointPath + did) .then(function(response){ return response.json(); }, function(err){ reject(err); }) .then(function(result){ if (result){ var didDocument = result.document; if (!didDocument.publicKey) { reject ('Could not find public keys for ' + recipient); return; } var jwk = didDocument.publicKey[0].publicKeyJwk; result.verify = cloneJwk(jwk, { key_ops: ['verify'], alg: 'RS256' }); result.encrypt = cloneJwk(jwk, { key_ops: ['encrypt'], alg: 'RSA-OAEP' }); resolverCache[did] = result; return resolve(result); } }, function(err){ reject(err); }) }) } }); } function shakespeareHandler(){ if ('caches' in window) { /* * Check if the service worker has already cached this city's weather * data. If the service worker has the data, then display the cached * data while the app fetches the latest data. */ caches.match(url).then(function(response) { if (response) { response.json().then(function updateFromCache(json) { var results = json.query.results; results.key = key; results.label = label; results.created = json.query.created; app.updateForecastCard(results); }); } }); } } function cloneJwk(jwk, delta){ var clone = {}; for(var prop in jwk){ if (delta[prop] === undefined){ clone[prop] = jwk[prop]; } } for (var prop in delta){ clone[prop] = delta[prop]; } return clone; } function toPublic(jwk){ if (jwk.kty !== 'RSA'){ throw 'toPublic only supports RSA'; } var publicKey = { alg: jwk.alg, e: jwk.e, key_ops: jwk.key_ops, kid: jwk.kid, kty: jwk.kty, n: jwk.n } return publicKey; } function backgroundSyncControllerFactory(){ const SYNC_TIMER_INTERLUDE = 2; // interval between sync of collections in seconds const RESYNC_INTERLUDE = 4 * 3600; // resync interlude in seconds const UPDATE_HUB_INTERLUDE = 60; const _resyncThreshold = (RESYNC_INTERLUDE) / SYNC_TIMER_INTERLUDE; // 30 => 5 minutes const _beginningDelay = (6 / SYNC_TIMER_INTERLUDE) + 1; const _updateHubDelay = (UPDATE_HUB_INTERLUDE/SYNC_TIMER_INTERLUDE) + 1; const MODE_SYNCHRONIZING = 1; const MODE_WAITING = 0; var _syncQueue = []; var _appRegistrations = {}; var _syncIndex = 0; var _interval = 0; var _timer; var _mode = MODE_WAITING; var _resyncCounter = 0; var _updateHubThreshold = _resyncThreshold; var _checkForDeltas = false; var _inTick = false; Object.defineProperty(this, "syncQueue", { get: function() { return _syncQueue; }, enumerable: true }); Object.defineProperty(this, "registerAppCallback", { value: function(collectionName, callback) { _appRegistrations[collectionName] = callback; } }); Object.defineProperty(this, "begin", { value: function() { _syncIndex = 0; _interval = SYNC_TIMER_INTERLUDE * 1000; _timer = setInterval(tick, _interval); _resyncCounter = _resyncThreshold - _beginningDelay; } }); function tick(){ var syncStart; var syncEnd; var syncDuration; var taskId; var task; var syncConfigPromise; if (pk.util.offline || pk.util.hubInstance.instanceDid === DIDNOTPRESENT){ return; } if (_inTick){ return; } else{ _inTick = true; } if (_mode === MODE_WAITING){ if (_resyncCounter < _resyncThreshold){ if (_updateHubThreshold === 0 || _resyncCounter++ < _updateHubThreshold){ _inTick = false; return; } else{ _updateHubThreshold = 0; _syncQueue = []; _syncIndex = 0; for (var key in pk.dbs){ var dbInfo = pk.dbs[key]; for (var collectionName in dbInfo.collections){ var collectionInfo = dbInfo.collections[collectionName]; if (collectionInfo.highWater > 0){ _syncQueue.push({db: key, collection: collectionName, status: null, syncDuration: 0}); collectionInfo.highWater = 0; } } } if (_syncQueue.length > 0){ syncConfigPromise = Promise.resolve('SyncLocalChanges'); _mode = MODE_SYNCHRONIZING; } else{ var regInfo = { flockDid: pk.util.hubInstance.flockDid, instanceDid: pk.util.hubInstance.instanceDid }; syncConfigPromise = syncStateReceiver(regInfo); } } } else{ _syncQueue = []; _syncIndex = 0; for (var key in pk.dbs){ var dbInfo = pk.dbs[key]; for (var collectionName in dbInfo.collections){ _syncQueue.push({db: key, collection: collectionName, status: null, syncDuration: 0}); } } _resyncCounter = 0; syncConfigPromise = Promise.resolve('SyncAllCollections') _mode = MODE_SYNCHRONIZING; } } else{ syncConfigPromise = Promise.resolve('SyncInProgress'); } syncConfigPromise.then(syncControl => { if (syncControl){ if (typeof syncControl === 'object'){ if (syncControl.delta){ for (var key in pk.dbs){ var dbInfo = pk.dbs[key]; for (var collectionName in dbInfo.collections){ if (syncControl.delta[collectionName] !== undefined){ _syncQueue.push({db: key, collection: collectionName, status: null, syncDuration: 0}); } } } if (_syncQueue.length > 0){ syncControl = 'SyncRemoteDeltas'; _mode = MODE_SYNCHRONIZING; } else { _updateHubThreshold = _resyncCounter + _updateHubDelay; return; } } } return syncControl; } }, err => { reject(err); }) .then(processSync => { if (processSync){ task = _syncQueue[_syncIndex]; taskId = _syncIndex; syncStart = new Date().getTime(); var registered = _appRegistrations[_syncQueue[taskId].collection]; return syncCollection(task, registered); } }, err => { reject(err); }) .then(result => { if (typeof result === 'number'){ syncEnd = new Date().getTime(); syncDuration = syncEnd - syncStart; _syncQueue[taskId] = {db: task.db, collection: task.collection, status: result, syncDuration: syncDuration}; _syncIndex++; if (_syncIndex >= _syncQueue.length){ _mode = MODE_WAITING; var changeInfo = { flockDid: pk.util.hubInstance.flockDid, instanceDid: pk.util.hubInstance.instanceDid, changeInfo: { highWater: {} } }; for (var key in _syncQueue){ var info = _syncQueue[key]; changeInfo.changeInfo.highWater[info.collection] = info.status; } // sync cycle is complete pk.util.sync_info.last_sync_time = syncEnd; return syncStateSender(changeInfo); } } }, err => { syncEnd = new Date().getTime(); syncDuration = syncEnd - syncStart; _syncQueue[_syncIndex] = {db: task.db, collection: task.collection, status: err, syncDuration: syncDuration}; _mode = MODE_WAITING; }) .then(sendResult => { if (sendResult){ _updateHubThreshold = _resyncCounter + _updateHubDelay; } _inTick = false; }, err => { pk.util.log_detail("Tick Error so increasing hub delay: ", err); _updateHubThreshold = _resyncCounter + (_updateHubDelay *10); _inTick = false; }) } function syncStateSender(stateObj){ return new Promise((resolve, reject) => { var requestString = JSON.stringify(stateObj); fetch_func(hubConfig.endpoints.changeNotifications, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': requestString.length.toString() }, body: requestString }) .then(result => { if (result){ if (result.status !== 200) { var msg = 'syncStateSender returns ' + result.status.toString() + ': ' + result.statusText; reject(msg); } else { resolve(result.json()); } } }, err => { reject(err); }) }); } function syncStateReceiver(stateObj){ return new Promise((resolve, reject) => { var uri = hubConfig.endpoints.changeNotifications + pk.util.createParameterString(stateObj); fetch_func(uri) .then(result => { if (result){ if (result.status !== 200) { var msg = 'syncStateReceiver returns ' + result.status.toString() + ': ' + result.statusText; pk.util.log_debug(msg); } else { return result.json(); } } }, err => { reject(err); pk.util.log_detail('Error retrieving changeNotification', err); }) .then(syncResponse => { if (syncResponse){ resolve(syncResponse); } }, err => { reject(err); pk.util.log_detail('Error retrieving syncResponse', err); }) }); } } function syncCollection(task, registered){ var hubDbInfo = pk.dbs[task.db]; var changePromises = []; var highWater = 0; return new Promise((resolve, reject) => { if (pk.util.offline || !hubDbInfo.authenticationBundle || hubDbInfo.authenticationBundle.instanceDid === undefined){ return resolve(true); } var collectionName = task.collection; var keyPath = hubDbInfo.collections[collectionName].keyPath; var hubCollection = {}; var localCollection = {}; hubDbInfo.provider.queryHubCollection(hubDbInfo, collectionName, {}, { decrypt: false }) .then(collectionElements => { for (var j=0; j < collectionElements.length; j++){ var hubElement = collectionElements[j]; hubCollection[hubElement.didMeta.object_id] = hubElement; var upd = Date.parse(hubElement.didMeta.updated_at); if (upd > highWater){ highWater = upd; } } return hubDbInfo.dbInfo.provider.queryCollection(hubDbInfo.dbInfo, collectionName, {}); }, err => { reject(err); }) .then(dbCollection => { var sync_id; var unsynced_count = 0; for (var j=0; j < dbCollection.length; j++){ var localElement = dbCollection[j]; var upd = Date.parse(localElement.didMeta.updated_at); if (upd > highWater){ highWater = upd; } if (localElement.didMeta.object_id === undefined){ sync_id = '_' + (unsynced_count++).toString(); } else{ sync_id = localElement.didMeta.object_id; } localCollection[sync_id] = localElement; } var localUpdatePromises = []; hubDbInfo.suppressHubUpdate = true; for (var element in hubCollection){ var hubElement = hubCollection[element]; var localElement = localCollection[element]; if (localElement === undefined || Date.parse(localElement.didMeta.updated_at) < Date.parse(hubElement.didMeta.updated_at)){ if (registered){ changePromises.push(decryptDbRecord(hubDbInfo, collectionName, hubElement)); } localUpdatePromises.push(hubDbInfo.dbInfo.provider.createOrUpdateDocument(hubDbInfo.dbInfo, collectionName, hubElement)); } } return Promise.all(localUpdatePromises); }, err => { reject(err); }) .then(localUpdateResults => { hubDbInfo.suppressHubUpdate = false; var hubUpdatePromises = [] for (var elementId in localCollection){ var element = localCollection[elementId]; if (element.didMeta.object_id === undefined){ if (registered){ changePromises.push(decryptDbRecord(hubDbInfo, collectionName, element)); } hubUpdatePromises.push(updateHub(hubDbInfo, collectionName, element, { encrypt: false })); } } return Promise.all(hubUpdatePromises); }, err => { hubDbInfo.suppressHubUpdate = false; reject(err); }) .then(hubUpdateResults => { if (hubUpdateResults){ return Promise.all(changePromises) } }, err => { reject(err); }) .then(changes => { if (changes){ if (registered && changes.length > 0){ registered(changes); } resolve(highWater); } }, err => { reject(err); }) }); } function hubInfoFactory(personaId, privateKeys, instanceDid, flockDid){ var _personaId = personaId; var _instanceDid = instanceDid; var _flockDid = flockDid; var _privateKeys = privateKeys; var _kidRoot, _signKey, _verifyKey, _encryptKey, _decryptKey; Object.defineProperty(this, "load", { value: function() { return new Promise((resolve, reject) => { const use_new_key = '**USENEWKEY**'; var storagePromise; if (_privateKeys){ storagePromise = Promise.resolve(use_new_key); } else{ storagePromise = pk.util.local_store.get(personaId, 'utf-8'); } storagePromise .then(instanceState => { if (instanceState === undefined){ // no previous storage resolve(); return; } else if (instanceState === use_new_key){ return true; } else { var hubInstanceStorage = JSON.parse(instanceState); _instanceDid = hubInstanceStorage.instanceDid; _flockDid = hubInstanceStorage.flockDid; _privateKeys = hubInstanceStorage.privateKeys; return true; } }, err => { reject(err); }) .then(setUpKeys => { if (setUpKeys){ _kidRoot = _instanceDid + "#"; // set up _privateSigningKey var jwk = _privateKeys[0]; var instanceKid = _kidRoot + jwk.kid; _signKey = cloneJwk(jwk, { alg: 'RS256', key_ops: ["sign"], kid: instanceKid }) _verifyKey = cloneJwk(toPublic(jwk), { alg: 'RS256', key_ops: ["verify"], kid: instanceKid }); _decryptKey = cloneJwk(jwk, { alg: 'RSA-OAEP', key_ops: ["decrypt"], kid: instanceKid }) _encryptKey = cloneJwk(toPublic(jwk), { alg: 'RSA-OAEP', key_ops: ["encrypt"], kid: instanceKid }); resolve(true); } }, err => { reject(err); }) }) } }); Object.defineProperty(this, "personaId", { get: function() { return _personaId; }, enumerable: true }); Object.defineProperty(this, "instanceDid", { get: function() { return _instanceDid; }, set: function(value) { _instanceDid = value; }, enumerable: true }); Object.defineProperty(this, "flockDid", { get: function() { return _flockDid; }, set: function(value) { _flockDid = value; }, enumerable: true }); Object.defineProperty(this, "privateKeys", { get: function() { return _privateKeys; }, enumerable: false }); Object.defineProperty(this, "signKey", { get: function() { return _signKey; }, enumerable: false }); Object.defineProperty(this, "verifyKey", { get: function() { return _verifyKey; }, enumerable: false }); Object.defineProperty(this, "decryptKey", { get: function() { return _decryptKey; }, enumerable: false }); Object.defineProperty(this, "encryptKey", { get: function() { return _encryptKey; }, enumerable: false }); Object.defineProperty(this, "save", { value: function(changedPersonaId) { return new Promise((resolve, reject) => { if (changedPersonaId){ _personaId = changedPersonaId; } var saveBundle = { instanceDid: _instanceDid, flockDid: _flockDid, personaId: _personaId, privateKeys: _privateKeys } pk.util.local_store.set(_personaId, JSON.stringify(saveBundle)) .then(result => { if (result){ resolve(true); } }, err => { reject(err); }) }) } }); } ///////////////////////////////////////////////////////// function hubRequest(hubMessage, authenticationBundle, hubDid){ return new Promise((resolve, reject) => { var hubMessageString = JSON.stringify(hubMessage); var hubRequestor = new hubRequestorFactory(); hubRequestor.execute(hubMessageString, authenticationBundle, hubDid) .then(function(response){ resolve(response); }, function(err){ reject(err); }) }); } function hubRequestorFactory() { var _decryptKey; Object.defineProperty(this, "execute", { value: function(message, authenticationBundle, partnerDid) { return new Promise((resolve, reject) => { _decryptKey = authenticationBundle.decryptKey; authenticationBundle.getAccessToken(partnerDid) .then(accessToken => { if (accessToken){ return getAuthenticatedRequest(message, authenticationBundle.signKey, partnerDid, accessToken); } }, err => { reject(err); }) .then(authenticatedRequest => { return completeRequest(authenticatedRequest, partnerDid); }, err => { reject(err); }) .then(verifiedPayload => { resolve(verifiedPayload); }, err => { reject(err); }) }) } }); Object.defineProperty(this, "getAccessToken", { value: function(instanceInfo, partnerDid) { return new Promise((resolve, reject) => { _decryptKey= instanceInfo.decryptKey; getAuthenticatedRequest('', instanceInfo.signKey, partnerDid, null) .then(authenticatedRequest => { return completeRequest(authenticatedRequest, partnerDid); }, err => { reject(err); }) .then(verifiedPayload => { resolve(verifiedPayload); }, err => { reject(err); }) }); } }); function completeRequest(requestString, partnerDid){ return new Promise((resolve, reject) => { fetch_func(hubConfig.endpoints.hub, { method: 'POST', headers: { 'Content-Type': 'application/jose',