1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Tidy up runtime credentials

This commit is contained in:
Nick O'Leary 2014-07-21 15:56:38 +01:00
parent 7687cf6661
commit fea6280bff
2 changed files with 124 additions and 85 deletions

View File

@ -17,33 +17,31 @@
var util = require("util"); var util = require("util");
var when = require("when"); var when = require("when");
var credentials = {}; var credentialCache = {};
var storage = null; var storage = null;
var credentialsDef = {}; var credentialsDef = {};
var redApp = null; var redApp = null;
var querystring = require('querystring');
var Credentials;
function getCredDef(type) { /**
var dashedType = type.replace(/\s+/g, '-'); * Adds an HTTP endpoint to allow look up of credentials for a given node id.
return credentialsDef[dashedType]; */
} function registerEndpoint(type) {
function isRegistered(type) {
return getCredDef(type) !== undefined;
}
function restGET(type) {
redApp.get('/credentials/' + type + '/:id', function (req, res) { 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 nodeType = type;
var nodeID = req.params.id; var nodeID = req.params.id;
var credentials = Credentials.get(nodeID); var credentials = credentialCache[nodeID];
if (credentials === undefined) { if (credentials === undefined) {
res.json({}); res.json({});
return; return;
} }
var definition = getCredDef(nodeType); var definition = credentialsDef[nodeType];
var sendCredentials = {}; var sendCredentials = {};
for (var cred in definition) { for (var cred in definition) {
@ -61,87 +59,141 @@ function restGET(type) {
}); });
} }
module.exports = { module.exports = {
init: function (_storage) { init: function (_storage) {
storage = _storage; storage = _storage;
// TODO: this should get passed in init function call rather than
// required directly.
redApp = require("../server").app; redApp = require("../server").app;
Credentials = this;
}, },
/**
* Loads the credentials from storage.
*/
load: function () { load: function () {
return storage.getCredentials().then(function (creds) { return storage.getCredentials().then(function (creds) {
credentials = creds; credentialCache = creds;
}).otherwise(function (err) { }).otherwise(function (err) {
util.log("[red] Error loading credentials : " + 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) { add: function (id, creds) {
credentials[id] = creds; credentialCache[id] = creds;
storage.saveCredentials(credentials); 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) { 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: function (id) {
delete credentials[id]; delete credentialCache[id];
storage.saveCredentials(credentials); 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) { clean: function (getNode) {
var deletedCredentials = false; var deletedCredentials = false;
for (var c in credentials) { for (var c in credentialCache) {
if (credentials.hasOwnProperty(c)) { if (credentialCache.hasOwnProperty(c)) {
var n = getNode(c); var n = getNode(c);
if (!n) { if (!n) {
deletedCredentials = true; deletedCredentials = true;
delete credentials[c]; delete credentialCache[c];
} }
} }
} }
if (deletedCredentials) { 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) { register: function (type, definition) {
var dashedType = type.replace(/\s+/g, '-'); var dashedType = type.replace(/\s+/g, '-');
credentialsDef[dashedType] = definition; credentialsDef[dashedType] = definition;
restGET(dashedType); registerEndpoint(dashedType);
}, },
/** /**
* Merge the new credentials with the existings one * Extracts and stores any credential updates in the provided node.
* @param nodeID * The provided node may have a .credentials property that contains
* @param nodeType * new credentials for the node.
* @param newCreds * 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) { extract: function(node) {
var savedCredentials = Credentials.get(nodeID) || {}; var nodeID = node.id;
var nodeType = node.type;
var newCreds = node.credentials;
if (newCreds) {
var savedCredentials = credentialCache[nodeID] || {};
if (!isRegistered(nodeType)) { var dashedType = nodeType.replace(/\s+/g, '-');
util.log('Credential Type ' + nodeType + ' is not registered.'); var definition = credentialsDef[dashedType];
return;
}
var definition = getCredDef(nodeType); if (!definition) {
for (var cred in definition) { util.log('Credential Type ' + nodeType + ' is not registered.');
if (definition.hasOwnProperty(cred)) { return;
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];
} }
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 () { save: function () {
return storage.saveCredentials(credentials); return storage.saveCredentials(credentialCache);
} }
} }

View File

@ -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 parseConfig = function() {
var i; var i;
@ -173,16 +154,22 @@ var flowNodes = module.exports = {
getFlows: function() { getFlows: function() {
return activeConfig; return activeConfig;
}, },
setFlows: function (conf) { setFlows: function (config) {
return parseCredentials(conf).then(function (confCredsRemoved) { // Extract any credential updates
return storage.saveFlows(confCredsRemoved).then(function () { for (var i=0; i<config.length; i++) {
return stopFlows().then(function () { var node = config[i];
activeConfig = confCredsRemoved; if (node.credentials) {
parseConfig(); credentials.extract(node);
}); delete node.credentials;
}) }
}
}) return credentials.save()
.then(function() { return storage.saveFlows(config);})
.then(function() { return stopFlows();})
.then(function () {
activeConfig = config;
parseConfig();
});
}, },
stopFlows: stopFlows stopFlows: stopFlows
}; };