From fea6280bffbffbbedf67d6372bc5087338ebf3ef Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 21 Jul 2014 15:56:38 +0100 Subject: [PATCH] Tidy up runtime credentials --- red/nodes/credentials.js | 164 ++++++++++++++++++++++++++------------- red/nodes/flows.js | 45 ++++------- 2 files changed, 124 insertions(+), 85 deletions(-) diff --git a/red/nodes/credentials.js b/red/nodes/credentials.js index 219c23916..0b1ad2e35 100644 --- a/red/nodes/credentials.js +++ b/red/nodes/credentials.js @@ -17,33 +17,31 @@ var util = require("util"); var when = require("when"); -var credentials = {}; +var credentialCache = {}; var storage = null; var credentialsDef = {}; var redApp = null; -var querystring = require('querystring'); -var Credentials; -function getCredDef(type) { - var dashedType = type.replace(/\s+/g, '-'); - return credentialsDef[dashedType]; -} - -function isRegistered(type) { - return getCredDef(type) !== undefined; -} - -function restGET(type) { +/** + * Adds an HTTP endpoint to allow look up of credentials for a given node id. + */ +function registerEndpoint(type) { redApp.get('/credentials/' + type + '/:id', function (req, res) { + // TODO: This could be a generic endpoint with the type value + // parameterised. + // + // TODO: It should verify the given node id is of the type specified - + // but that would add a dependency from this module to the + // registry module that knows about node types. var nodeType = type; var nodeID = req.params.id; - var credentials = Credentials.get(nodeID); + var credentials = credentialCache[nodeID]; if (credentials === undefined) { res.json({}); return; } - var definition = getCredDef(nodeType); + var definition = credentialsDef[nodeType]; var sendCredentials = {}; for (var cred in definition) { @@ -61,87 +59,141 @@ function restGET(type) { }); } + module.exports = { init: function (_storage) { storage = _storage; + // TODO: this should get passed in init function call rather than + // required directly. redApp = require("../server").app; - Credentials = this; }, + + /** + * Loads the credentials from storage. + */ load: function () { return storage.getCredentials().then(function (creds) { - credentials = creds; + credentialCache = creds; }).otherwise(function (err) { util.log("[red] Error loading credentials : " + err); }); }, + + /** + * Adds a set of credentials for the given node id. + * @param id the node id for the credentials + * @param creds an object of credential key/value pairs + * @return a promise for the saving of credentials to storage + */ add: function (id, creds) { - credentials[id] = creds; - storage.saveCredentials(credentials); + credentialCache[id] = creds; + return storage.saveCredentials(credentialCache); }, + /** + * Gets the credentials for the given node id. + * @param id the node id for the credentials + * @return the credentials + */ get: function (id) { - return credentials[id]; + return credentialCache[id]; }, + /** + * Deletes the credentials for the given node id. + * @param id the node id for the credentials + * @return a promise for the saving of credentials to storage + */ delete: function (id) { - delete credentials[id]; - storage.saveCredentials(credentials); + delete credentialCache[id]; + storage.saveCredentials(credentialCache); }, + /** + * Deletes any credentials for nodes that no longer exist + * @param getNode a function that can return a node for a given id + * @return a promise for the saving of credentials to storage + */ clean: function (getNode) { var deletedCredentials = false; - for (var c in credentials) { - if (credentials.hasOwnProperty(c)) { + for (var c in credentialCache) { + if (credentialCache.hasOwnProperty(c)) { var n = getNode(c); if (!n) { deletedCredentials = true; - delete credentials[c]; + delete credentialCache[c]; } } } if (deletedCredentials) { - storage.saveCredentials(credentials); + return storage.saveCredentials(credentialCache); + } else { + return when.resolve(); } - }, + + /** + * Registers a node credential definition. + * @param type the node type + * @param definition the credential definition + */ register: function (type, definition) { var dashedType = type.replace(/\s+/g, '-'); credentialsDef[dashedType] = definition; - restGET(dashedType); + registerEndpoint(dashedType); }, + /** - * Merge the new credentials with the existings one - * @param nodeID - * @param nodeType - * @param newCreds + * Extracts and stores any credential updates in the provided node. + * The provided node may have a .credentials property that contains + * new credentials for the node. + * This function loops through the credentials in the definition for + * the node-type and applies any of the updates provided in the node. + * + * This function does not save the credentials to disk as it is expected + * to be called multiple times when a new flow is deployed. + * + * @param node the node to extract credentials from */ - merge: function (nodeID, nodeType, newCreds) { - var savedCredentials = Credentials.get(nodeID) || {}; - - if (!isRegistered(nodeType)) { - util.log('Credential Type ' + nodeType + ' is not registered.'); - return; - } - - var definition = getCredDef(nodeType); - for (var cred in definition) { - if (definition.hasOwnProperty(cred)) { - if (newCreds[cred] === undefined) { - continue; - } - if (definition[cred].type == "password" && newCreds[cred] == '__PWRD__') { - continue; - } - if (0 === newCreds[cred].length || /^\s*$/.test(newCreds[cred])) { - delete savedCredentials[cred]; - continue; - } - savedCredentials[cred] = newCreds[cred]; + extract: function(node) { + var nodeID = node.id; + var nodeType = node.type; + var newCreds = node.credentials; + if (newCreds) { + var savedCredentials = credentialCache[nodeID] || {}; + + var dashedType = nodeType.replace(/\s+/g, '-'); + var definition = credentialsDef[dashedType]; + + if (!definition) { + util.log('Credential Type ' + nodeType + ' is not registered.'); + return; } + + for (var cred in definition) { + if (definition.hasOwnProperty(cred)) { + if (newCreds[cred] === undefined) { + continue; + } + if (definition[cred].type == "password" && newCreds[cred] == '__PWRD__') { + continue; + } + if (0 === newCreds[cred].length || /^\s*$/.test(newCreds[cred])) { + delete savedCredentials[cred]; + continue; + } + savedCredentials[cred] = newCreds[cred]; + } + } + credentialCache[nodeID] = savedCredentials; } - credentials[nodeID] = savedCredentials; }, + + /** + * Saves the credentials to storage + * @return a promise for the saving of credentials to storage + */ save: function () { - return storage.saveCredentials(credentials); + return storage.saveCredentials(credentialCache); } } diff --git a/red/nodes/flows.js b/red/nodes/flows.js index b4de13cb0..8c5f0bd6b 100644 --- a/red/nodes/flows.js +++ b/red/nodes/flows.js @@ -40,25 +40,6 @@ events.on('type-registered',function(type) { } } }); -var parseCredentials = function (config) { - return when.promise(function (resolve, defect) { - for (var i in config) { - if (config.hasOwnProperty(i)) { - var node = config[i]; - if (node.credentials) { - var type = node.type; - credentials.merge(node.id, type, node.credentials); - delete node.credentials; - } - } - } - credentials.save().then(function () { - resolve(config); - }).otherwise(function (err) { - defect(err); - }); - }); -} var parseConfig = function() { var i; @@ -173,16 +154,22 @@ var flowNodes = module.exports = { getFlows: function() { return activeConfig; }, - setFlows: function (conf) { - return parseCredentials(conf).then(function (confCredsRemoved) { - return storage.saveFlows(confCredsRemoved).then(function () { - return stopFlows().then(function () { - activeConfig = confCredsRemoved; - parseConfig(); - }); - }) - - }) + setFlows: function (config) { + // Extract any credential updates + for (var i=0; i