UNPKG

express-gateway

Version:

A microservices API gateway built on top of ExpressJS

222 lines (181 loc) 7.47 kB
const db = require('../../db'); const config = require('../../config'); const logger = require('../../../lib/logger').gateway; const scopeNamespace = 'scope'; const scopeCredentialsNamespace = 'scope-credentials'; const scopeDbKey = config.systemConfig.db.redis.namespace.concat('-', scopeNamespace); const dao = {}; dao.insertScopes = function (_scopes) { const scopes = {}; if (Array.isArray(_scopes)) { _scopes.forEach(el => { scopes[el] = 'true'; }); } else scopes[_scopes] = 'true'; return db.hmset(scopeDbKey, scopes); }; dao.associateCredentialWithScopes = function (id, type, scopes) { const credentialKey = buildIdKey(type, id); if (!scopes) { return Promise.resolve(null); } scopes = Array.isArray(scopes) ? scopes : [scopes]; const associationPromises = scopes.map(scope => db.hset(buildScopeKey(scope), credentialKey, 'true')); return Promise.all(associationPromises); }; dao.dissociateCredentialFromScopes = function (id, type, scopes) { const credentialKey = buildIdKey(type, id); scopes = Array.isArray(scopes) ? scopes : [scopes]; const dissociationPromises = scopes.map(scope => db.hdel(buildScopeKey(scope), credentialKey)); return Promise.all(dissociationPromises); }; dao.removeScopes = function (scopes) { let removeScopesTransaction; const getScopeCredentialPromises = []; scopes = Array.isArray(scopes) ? scopes : [scopes]; removeScopesTransaction = db .multi() .hdel(scopeDbKey, scopes); // Get the list of ids with scopes to be removed, and remove scope-ids association scopes.forEach(scope => { getScopeCredentialPromises.push(db.hgetall(buildScopeKey(scope))); removeScopesTransaction = removeScopesTransaction.del(buildScopeKey(scope)); }); return Promise.all(getScopeCredentialPromises) .then(idObjs => { const getCredentialPromises = []; const credentialIdToScopes = {}; scopes.forEach((scope, index) => { const ids = idObjs[index]; for (const id in ids) { if (credentialIdToScopes[id]) { credentialIdToScopes[id].push(scope); } else credentialIdToScopes[id] = [scope]; } }); // Get dissociation promises for the id-scopes combination and promises to update credentials to remove scope for (const credentialId in credentialIdToScopes) { getCredentialPromises.push(db.hgetall(credentialId)); } return Promise.all(getCredentialPromises) .then(credentialObjs => { let credentialScopes, newScopes; const credentialIds = Object.keys(credentialIdToScopes); credentialObjs.forEach((credentialObj, index) => { const credentialId = credentialIds[index]; if (credentialObj && credentialObj.scopes) { credentialScopes = JSON.parse(credentialObj.scopes); newScopes = credentialScopes.filter(scope => scopes.indexOf(scope) === -1); removeScopesTransaction = removeScopesTransaction.hmset(credentialId, { scopes: JSON.stringify(newScopes) }); } }); return removeScopesTransaction.exec() .then(res => res[0]); // .del may yield 0 if a scope wasn't assigned to any credential }); }); }; dao.existsScope = function (scope) { return db.hget(scopeDbKey, scope) .then(res => !!res); }; dao.getAllScopes = function () { return db.hgetall(scopeDbKey) .then(res => { const scopes = Object.keys(res || {}); return scopes.length ? scopes : null; }); }; dao.insertCredential = function (id, type, credentialObj) { if (!credentialObj) { return Promise.resolve(null); } const key = buildIdKey(type, id); if (type === 'key-auth' || type === 'jwt') { if (credentialObj.keyId) credentialObj.id = credentialObj.keyId; return Promise.all([ // build relation consumerId -> [key1, key2] db.sadd(buildIdKey(type, credentialObj.consumerId), id), // store key-auth keyid -> credentialObj db.hmset(key, credentialObj) ]); } return db.hmset(key, credentialObj); }; dao.getCredential = function (id, type) { return db.hgetall(buildIdKey(type, id)).then(credential => { if (!credential || Object.keys(credential).length === 0) return null; credential.isActive = credential.isActive === 'true'; // Redis has no bool type, manual conversion credential.type = type; credential.id = id; return credential; }).catch(logger.warn); }; dao.activateCredential = function (id, type) { return db.hmset(buildIdKey(type, id), { isActive: 'true', updatedAt: String(new Date()) }); }; dao.deactivateCredential = function (id, type) { return db.hmset(buildIdKey(type, id), { isActive: 'false', updatedAt: String(new Date()) }); }; dao.removeCredential = function (id, type) { return db.del(buildIdKey(type, id)); }; /* * Remove all credentials * * @id {String} */ dao.removeAllCredentials = function (id) { const dbTransaction = db.multi(); const credentialTypes = Object.keys(config.models.credentials.properties); const awaitAllPromises = credentialTypes.map(type => { const authKey = buildIdKey(type, id); if (type === 'key-auth' || type === 'jwt') { const promises = []; // id in this call is actually consumerId, so we need to get all referenced keyIds // Get a list of all keys the user owns and all the scopes so we can remove keys from them Promise.all([db.smembers(authKey), dao.getAllScopes()]) .then(([ids, scopes]) => { // Delete each key and remove key from scopes if they exist ids.forEach(keyId => { const idKey = buildIdKey(type, keyId); // Delete key promises.push(dbTransaction.del(idKey)); // Delete key from all scopes if (scopes) { scopes.forEach(scope => { promises.push(dbTransaction.hdel(buildScopeKey(scope), idKey)); }); } }); }); // Now delete the main key for the user that lists all creds promises.push(dbTransaction.del(authKey)); return Promise.all(promises); } else { return dbTransaction.del(authKey); } }); return Promise.all(awaitAllPromises).then(() => dbTransaction.exec()); }; dao.getAllCredentials = function (consumerId) { const credentialTypes = Object.keys(config.models.credentials.properties); const awaitAllPromises = credentialTypes.map(type => { if (type === 'key-auth' || type === 'jwt') { // TODO: replace with separate implementation per type instead of ifs return db.smembers(buildIdKey(type, consumerId)).then(keyIds => { // 1-* relation, finding all key-auth credentials (consumerid => [KeyId1, KeyId2, ..., KeyIdN]) return Promise.all(keyIds.map(keyId => this.getCredential(keyId, type))); }); } return this.getCredential(consumerId, type); }); return Promise.all(awaitAllPromises) .then(results => Array.prototype.concat.apply([], results).filter(c => c)); }; dao.updateCredential = function (id, type, credentialObj) { return this.insertCredential(id, type, credentialObj); }; module.exports = dao; function buildScopeKey (scope) { return config.systemConfig.db.redis.namespace.concat('-', scopeCredentialsNamespace).concat(':', scope); } function buildIdKey (type, id) { return config.systemConfig.db.redis.namespace.concat('-', type).concat(':', id); }