mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Encrypt credentials by default
This commit is contained in:
parent
44c35d2644
commit
d29abc2724
@ -72,6 +72,7 @@
|
|||||||
"nodes": {
|
"nodes": {
|
||||||
"credentials": {
|
"credentials": {
|
||||||
"error":"Error loading credentials: __message__",
|
"error":"Error loading credentials: __message__",
|
||||||
|
"error-saving":"Error saving credentials: __message__",
|
||||||
"not-registered": "Credential type '__type__' is not registered"
|
"not-registered": "Credential type '__type__' is not registered"
|
||||||
},
|
},
|
||||||
"flows": {
|
"flows": {
|
||||||
|
@ -15,32 +15,142 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
var when = require("when");
|
var when = require("when");
|
||||||
|
var crypto = require('crypto');
|
||||||
|
var settings;
|
||||||
|
var log;
|
||||||
|
|
||||||
var log = require("../log");
|
var encryptedCredentials = null;
|
||||||
|
|
||||||
var credentialCache = {};
|
var credentialCache = {};
|
||||||
var credentialsDef = {};
|
var credentialsDef = {};
|
||||||
var dirty = false;
|
var dirty = false;
|
||||||
|
|
||||||
|
var removeDefaultKey = false;
|
||||||
|
var encryptionEnabled = null;
|
||||||
|
var encryptionAlgorithm = "aes-256-ctr";
|
||||||
|
var encryptionKey;
|
||||||
|
|
||||||
|
function decryptCredentials(key,credentials) {
|
||||||
|
var creds = credentials["$"];
|
||||||
|
var initVector = new Buffer(creds.substring(0, 32),'hex');
|
||||||
|
creds = creds.substring(32);
|
||||||
|
var decipher = crypto.createDecipheriv(encryptionAlgorithm, key, initVector);
|
||||||
|
var decrypted = decipher.update(creds, 'base64', 'utf8') + decipher.final('utf8');
|
||||||
|
return JSON.parse(decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
var api = module.exports = {
|
var api = module.exports = {
|
||||||
init: function() {
|
init: function(runtime) {
|
||||||
|
log = runtime.log;
|
||||||
|
settings = runtime.settings;
|
||||||
dirty = false;
|
dirty = false;
|
||||||
credentialCache = {};
|
credentialCache = {};
|
||||||
credentialsDef = {};
|
credentialsDef = {};
|
||||||
|
encryptionEnabled = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the credentials from storage.
|
* Sets the credentials from storage.
|
||||||
*/
|
*/
|
||||||
load: function (credentials) {
|
load: function (credentials) {
|
||||||
credentialCache = credentials;
|
|
||||||
dirty = false;
|
dirty = false;
|
||||||
return when.resolve();
|
/*
|
||||||
// return storage.getCredentials().then(function (creds) {
|
- if encryptionEnabled === null, check the current configuration
|
||||||
// credentialCache = creds;
|
*/
|
||||||
// }).otherwise(function (err) {
|
var credentialsEncrypted = credentials.hasOwnProperty("$") && Object.keys(credentials).length === 1;
|
||||||
// log.warn(log._("nodes.credentials.error",{message: err}));
|
var setupEncryptionPromise = when.resolve();
|
||||||
// });
|
if (encryptionEnabled === null) {
|
||||||
|
var defaultKey;
|
||||||
|
try {
|
||||||
|
defaultKey = settings.get('_credentialSecret');
|
||||||
|
} catch(err) {
|
||||||
|
}
|
||||||
|
if (defaultKey) {
|
||||||
|
defaultKey = crypto.createHash('sha256').update(defaultKey).digest();
|
||||||
|
}
|
||||||
|
var userKey;
|
||||||
|
try {
|
||||||
|
userKey = settings.get('credentialSecret');
|
||||||
|
} catch(err) {
|
||||||
|
userKey = false;
|
||||||
|
}
|
||||||
|
if (userKey === false) {
|
||||||
|
log.debug("red/runtime/nodes/credentials.load : user disabled encryption");
|
||||||
|
// User has disabled encryption
|
||||||
|
encryptionEnabled = false;
|
||||||
|
// Check if we have a generated _credSecret to decrypt with and remove
|
||||||
|
if (defaultKey) {
|
||||||
|
log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
|
||||||
|
if (credentialsEncrypted) {
|
||||||
|
try {
|
||||||
|
credentials = decryptCredentials(defaultKey,credentials)
|
||||||
|
} catch(err) {
|
||||||
|
credentials = {};
|
||||||
|
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dirty = true;
|
||||||
|
removeDefaultKey = true;
|
||||||
|
}
|
||||||
|
} else if (typeof userKey === 'string') {
|
||||||
|
log.debug("red/runtime/nodes/credentials.load : user provided key");
|
||||||
|
// User has provided own encryption key, get the 32-byte hash of it
|
||||||
|
encryptionKey = crypto.createHash('sha256').update(userKey).digest();
|
||||||
|
encryptionEnabled = true;
|
||||||
|
|
||||||
|
if (defaultKey) {
|
||||||
|
log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
|
||||||
|
// User has provided their own key, but we already have a default key
|
||||||
|
// Decrypt using default key
|
||||||
|
if (credentialsEncrypted) {
|
||||||
|
try {
|
||||||
|
credentials = decryptCredentials(defaultKey,credentials)
|
||||||
|
} catch(err) {
|
||||||
|
credentials = {};
|
||||||
|
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dirty = true;
|
||||||
|
removeDefaultKey = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("red/runtime/nodes/credentials.load : no user key present");
|
||||||
|
// User has not provide their own key
|
||||||
|
encryptionKey = defaultKey;
|
||||||
|
encryptionEnabled = true;
|
||||||
|
if (encryptionKey === undefined) {
|
||||||
|
log.debug("red/runtime/nodes/credentials.load : no default key present - generating one");
|
||||||
|
// No user-provided key, no generated key
|
||||||
|
// Generate a new key
|
||||||
|
defaultKey = crypto.randomBytes(32).toString('hex');
|
||||||
|
try {
|
||||||
|
setupEncryptionPromise = settings.set('_credentialSecret',defaultKey);
|
||||||
|
encryptionKey = crypto.createHash('sha256').update(defaultKey).digest();
|
||||||
|
} catch(err) {
|
||||||
|
log.debug("red/runtime/nodes/credentials.load : settings unavailable - disabling encryption");
|
||||||
|
// Settings unavailable
|
||||||
|
encryptionEnabled = false;
|
||||||
|
encryptionKey = null;
|
||||||
|
}
|
||||||
|
dirty = true;
|
||||||
|
} else {
|
||||||
|
log.debug("red/runtime/nodes/credentials.load : using default key");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return setupEncryptionPromise.then(function() {
|
||||||
|
if (credentials.hasOwnProperty("$")) {
|
||||||
|
// These are encrypted credentials
|
||||||
|
try {
|
||||||
|
credentialCache = decryptCredentials(encryptionKey,credentials)
|
||||||
|
} catch(err) {
|
||||||
|
credentialCache = {};
|
||||||
|
dirty = true;
|
||||||
|
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
credentialCache = credentials;
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -172,7 +282,26 @@ var api = module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
export: function() {
|
export: function() {
|
||||||
|
var result = credentialCache;
|
||||||
|
if (dirty && encryptionEnabled) {
|
||||||
|
try {
|
||||||
|
log.debug("red/runtime/nodes/credentials.export : encrypting");
|
||||||
|
var initVector = crypto.randomBytes(16);
|
||||||
|
var cipher = crypto.createCipheriv(encryptionAlgorithm, encryptionKey, initVector);
|
||||||
|
result = {"$":initVector.toString('hex') + cipher.update(JSON.stringify(credentialCache), 'utf8', 'base64') + cipher.final('base64')};
|
||||||
|
} catch(err) {
|
||||||
|
log.warn(log._("nodes.credentials.error-saving",{message:err.toString()}))
|
||||||
|
}
|
||||||
|
}
|
||||||
dirty = false;
|
dirty = false;
|
||||||
return when.resolve(credentialCache);
|
if (removeDefaultKey) {
|
||||||
|
log.debug("red/runtime/nodes/credentials.export : removing unused default key");
|
||||||
|
return settings.delete('_credentialSecret').then(function() {
|
||||||
|
removeDefaultKey = false;
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return when.resolve(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ function createNode(node,def) {
|
|||||||
|
|
||||||
function init(runtime) {
|
function init(runtime) {
|
||||||
settings = runtime.settings;
|
settings = runtime.settings;
|
||||||
credentials.init();
|
credentials.init(runtime);
|
||||||
flows.init(runtime);
|
flows.init(runtime);
|
||||||
registry.init(runtime);
|
registry.init(runtime);
|
||||||
context.init(runtime.settings);
|
context.init(runtime.settings);
|
||||||
|
@ -71,6 +71,19 @@ var persistentSettings = {
|
|||||||
return storage.saveSettings(globalSettings);
|
return storage.saveSettings(globalSettings);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
delete: function(prop) {
|
||||||
|
if (userSettings.hasOwnProperty(prop)) {
|
||||||
|
throw new Error(log._("settings.property-read-only", {prop:prop}));
|
||||||
|
}
|
||||||
|
if (globalSettings === null) {
|
||||||
|
throw new Error(log._("settings.not-available"));
|
||||||
|
}
|
||||||
|
if (globalSettings.hasOwnProperty(prop)) {
|
||||||
|
delete globalSettings[prop];
|
||||||
|
return storage.saveSettings(globalSettings);
|
||||||
|
}
|
||||||
|
return when.resolve();
|
||||||
|
},
|
||||||
|
|
||||||
available: function() {
|
available: function() {
|
||||||
return (globalSettings !== null);
|
return (globalSettings !== null);
|
||||||
|
@ -54,6 +54,14 @@ module.exports = {
|
|||||||
// property to true:
|
// property to true:
|
||||||
//flowFilePretty: true,
|
//flowFilePretty: true,
|
||||||
|
|
||||||
|
// By default, credentials are encrypted in storage using a generated key. To
|
||||||
|
// specify your own secret, set the following property.
|
||||||
|
// If you want to disable encryption of credentials, set this property to false.
|
||||||
|
// Note: once you set this property, do not change it - doing so will prevent
|
||||||
|
// node-red from being able to decrypt your existing credentials and they will be
|
||||||
|
// lost.
|
||||||
|
//credentialSecret: "a-secret-key",
|
||||||
|
|
||||||
// By default, all user data is stored in the Node-RED install directory. To
|
// By default, all user data is stored in the Node-RED install directory. To
|
||||||
// use a different location, the following property can be used
|
// use a different location, the following property can be used
|
||||||
//userDir: '/home/nol/.node-red/',
|
//userDir: '/home/nol/.node-red/',
|
||||||
|
@ -92,7 +92,7 @@ module.exports = {
|
|||||||
return messageId;
|
return messageId;
|
||||||
};
|
};
|
||||||
|
|
||||||
redNodes.init({settings:settings, storage:storage});
|
redNodes.init({settings:settings, storage:storage,log:log});
|
||||||
RED.nodes.registerType("helper", helperNode);
|
RED.nodes.registerType("helper", helperNode);
|
||||||
if (Array.isArray(testNode)) {
|
if (Array.isArray(testNode)) {
|
||||||
for (i = 0; i < testNode.length; i++) {
|
for (i = 0; i < testNode.length; i++) {
|
||||||
|
@ -26,12 +26,21 @@ var log = require("../../../../red/runtime/log");
|
|||||||
|
|
||||||
describe('red/runtime/nodes/credentials', function() {
|
describe('red/runtime/nodes/credentials', function() {
|
||||||
|
|
||||||
|
var encryptionDisabledSettings = {
|
||||||
|
get: function(key) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
index.clearRegistry();
|
index.clearRegistry();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('loads provided credentials',function(done) {
|
it('loads provided credentials',function(done) {
|
||||||
credentials.init();
|
credentials.init({
|
||||||
|
log: log,
|
||||||
|
settings: encryptionDisabledSettings
|
||||||
|
});
|
||||||
|
|
||||||
credentials.load({"a":{"b":1,"c":2}}).then(function() {
|
credentials.load({"a":{"b":1,"c":2}}).then(function() {
|
||||||
|
|
||||||
@ -42,37 +51,46 @@ describe('red/runtime/nodes/credentials', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('adds a new credential',function(done) {
|
it('adds a new credential',function(done) {
|
||||||
credentials.init();
|
credentials.init({
|
||||||
|
log: log,
|
||||||
|
settings: encryptionDisabledSettings
|
||||||
|
});
|
||||||
credentials.load({"a":{"b":1,"c":2}}).then(function() {
|
credentials.load({"a":{"b":1,"c":2}}).then(function() {
|
||||||
credentials.dirty().should.be.false;
|
credentials.dirty().should.be.false();
|
||||||
should.not.exist(credentials.get("b"));
|
should.not.exist(credentials.get("b"));
|
||||||
credentials.add("b",{"foo":"bar"}).then(function() {
|
credentials.add("b",{"foo":"bar"}).then(function() {
|
||||||
credentials.get("b").should.have.property("foo","bar");
|
credentials.get("b").should.have.property("foo","bar");
|
||||||
credentials.dirty().should.be.true;
|
credentials.dirty().should.be.true();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('deletes an existing credential',function(done) {
|
it('deletes an existing credential',function(done) {
|
||||||
credentials.init();
|
credentials.init({
|
||||||
|
log: log,
|
||||||
|
settings: encryptionDisabledSettings
|
||||||
|
});
|
||||||
credentials.load({"a":{"b":1,"c":2}}).then(function() {
|
credentials.load({"a":{"b":1,"c":2}}).then(function() {
|
||||||
credentials.dirty().should.be.false;
|
credentials.dirty().should.be.false();
|
||||||
credentials.delete("a");
|
credentials.delete("a");
|
||||||
should.not.exist(credentials.get("a"));
|
should.not.exist(credentials.get("a"));
|
||||||
credentials.dirty().should.be.true;
|
credentials.dirty().should.be.true();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('exports the credentials, clearing dirty flag', function(done) {
|
it('exports the credentials, clearing dirty flag', function(done) {
|
||||||
credentials.init();
|
credentials.init({
|
||||||
|
log: log,
|
||||||
|
settings: encryptionDisabledSettings
|
||||||
|
});
|
||||||
var creds = {"a":{"b":1,"c":2}};
|
var creds = {"a":{"b":1,"c":2}};
|
||||||
credentials.load(creds).then(function() {
|
credentials.load(creds).then(function() {
|
||||||
credentials.add("b",{"foo":"bar"}).then(function() {
|
credentials.add("b",{"foo":"bar"}).then(function() {
|
||||||
credentials.dirty().should.be.true;
|
credentials.dirty().should.be.true();
|
||||||
credentials.export().then(function(exported) {
|
credentials.export().then(function(exported) {
|
||||||
exported.should.eql(creds);
|
exported.should.eql(creds);
|
||||||
credentials.dirty().should.be.false;
|
credentials.dirty().should.be.false();
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -81,14 +99,17 @@ describe('red/runtime/nodes/credentials', function() {
|
|||||||
|
|
||||||
describe("#clean",function() {
|
describe("#clean",function() {
|
||||||
it("removes credentials of unknown nodes",function(done) {
|
it("removes credentials of unknown nodes",function(done) {
|
||||||
credentials.init();
|
credentials.init({
|
||||||
|
log: log,
|
||||||
|
settings: encryptionDisabledSettings
|
||||||
|
});
|
||||||
var creds = {"a":{"b":1,"c":2},"b":{"d":3}};
|
var creds = {"a":{"b":1,"c":2},"b":{"d":3}};
|
||||||
credentials.load(creds).then(function() {
|
credentials.load(creds).then(function() {
|
||||||
credentials.dirty().should.be.false;
|
credentials.dirty().should.be.false();
|
||||||
should.exist(credentials.get("a"));
|
should.exist(credentials.get("a"));
|
||||||
should.exist(credentials.get("b"));
|
should.exist(credentials.get("b"));
|
||||||
credentials.clean([{id:"b"}]).then(function() {
|
credentials.clean([{id:"b"}]).then(function() {
|
||||||
credentials.dirty().should.be.true;
|
credentials.dirty().should.be.true();
|
||||||
should.not.exist(credentials.get("a"));
|
should.not.exist(credentials.get("a"));
|
||||||
should.exist(credentials.get("b"));
|
should.exist(credentials.get("b"));
|
||||||
done();
|
done();
|
||||||
@ -96,14 +117,17 @@ describe('red/runtime/nodes/credentials', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
it("extracts credentials of known nodes",function(done) {
|
it("extracts credentials of known nodes",function(done) {
|
||||||
credentials.init();
|
credentials.init({
|
||||||
|
log: log,
|
||||||
|
settings: encryptionDisabledSettings
|
||||||
|
});
|
||||||
credentials.register("testNode",{"b":"text","c":"password"})
|
credentials.register("testNode",{"b":"text","c":"password"})
|
||||||
var creds = {"a":{"b":1,"c":2}};
|
var creds = {"a":{"b":1,"c":2}};
|
||||||
var newConfig = [{id:"a",type:"testNode",credentials:{"b":"newBValue","c":"newCValue"}}];
|
var newConfig = [{id:"a",type:"testNode",credentials:{"b":"newBValue","c":"newCValue"}}];
|
||||||
credentials.load(creds).then(function() {
|
credentials.load(creds).then(function() {
|
||||||
credentials.dirty().should.be.false;
|
credentials.dirty().should.be.false();
|
||||||
credentials.clean(newConfig).then(function() {
|
credentials.clean(newConfig).then(function() {
|
||||||
credentials.dirty().should.be.true;
|
credentials.dirty().should.be.true();
|
||||||
credentials.get("a").should.have.property('b',"newBValue");
|
credentials.get("a").should.have.property('b',"newBValue");
|
||||||
credentials.get("a").should.have.property('c',"newCValue");
|
credentials.get("a").should.have.property('c',"newCValue");
|
||||||
should.not.exist(newConfig[0].credentials);
|
should.not.exist(newConfig[0].credentials);
|
||||||
@ -116,7 +140,10 @@ describe('red/runtime/nodes/credentials', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('warns if a node has no credential definition', function(done) {
|
it('warns if a node has no credential definition', function(done) {
|
||||||
credentials.init();
|
credentials.init({
|
||||||
|
log: log,
|
||||||
|
settings: encryptionDisabledSettings
|
||||||
|
});
|
||||||
credentials.load({}).then(function() {
|
credentials.load({}).then(function() {
|
||||||
var node = {id:"node",type:"test",credentials:{
|
var node = {id:"node",type:"test",credentials:{
|
||||||
user1:"newUser",
|
user1:"newUser",
|
||||||
@ -124,7 +151,7 @@ describe('red/runtime/nodes/credentials', function() {
|
|||||||
}};
|
}};
|
||||||
sinon.spy(log,"warn");
|
sinon.spy(log,"warn");
|
||||||
credentials.extract(node);
|
credentials.extract(node);
|
||||||
log.warn.called.should.be.true;
|
log.warn.called.should.be.true();
|
||||||
should.not.exist(node.credentials);
|
should.not.exist(node.credentials);
|
||||||
log.warn.restore();
|
log.warn.restore();
|
||||||
done();
|
done();
|
||||||
@ -132,7 +159,10 @@ describe('red/runtime/nodes/credentials', function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('extract credential updates in the provided node', function(done) {
|
it('extract credential updates in the provided node', function(done) {
|
||||||
credentials.init();
|
credentials.init({
|
||||||
|
log: log,
|
||||||
|
settings: encryptionDisabledSettings
|
||||||
|
});
|
||||||
var defintion = {
|
var defintion = {
|
||||||
user1:{type:"text"},
|
user1:{type:"text"},
|
||||||
password1:{type:"password"},
|
password1:{type:"password"},
|
||||||
@ -155,12 +185,12 @@ describe('red/runtime/nodes/credentials', function() {
|
|||||||
user3:"newUser",
|
user3:"newUser",
|
||||||
password3:"newPassword"
|
password3:"newPassword"
|
||||||
}};
|
}};
|
||||||
credentials.dirty().should.be.false;
|
credentials.dirty().should.be.false();
|
||||||
credentials.extract(node);
|
credentials.extract(node);
|
||||||
|
|
||||||
node.should.not.have.a.property("credentials");
|
node.should.not.have.a.property("credentials");
|
||||||
|
|
||||||
credentials.dirty().should.be.true;
|
credentials.dirty().should.be.true();
|
||||||
var newCreds = credentials.get("node");
|
var newCreds = credentials.get("node");
|
||||||
newCreds.should.have.a.property("user1","abc");
|
newCreds.should.have.a.property("user1","abc");
|
||||||
newCreds.should.have.a.property("password1","123");
|
newCreds.should.have.a.property("password1","123");
|
||||||
@ -173,14 +203,269 @@ describe('red/runtime/nodes/credentials', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('extract ignores node without credentials', function(done) {
|
it('extract ignores node without credentials', function(done) {
|
||||||
credentials.init();
|
credentials.init({
|
||||||
|
log: log,
|
||||||
|
settings: encryptionDisabledSettings
|
||||||
|
});
|
||||||
credentials.load({"node":{user1:"abc",password1:"123"}}).then(function() {
|
credentials.load({"node":{user1:"abc",password1:"123"}}).then(function() {
|
||||||
var node = {id:"node",type:"test"};
|
var node = {id:"node",type:"test"};
|
||||||
|
|
||||||
credentials.dirty().should.be.false;
|
credentials.dirty().should.be.false();
|
||||||
credentials.extract(node);
|
credentials.extract(node);
|
||||||
credentials.dirty().should.be.false;
|
credentials.dirty().should.be.false();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("encryption",function() {
|
||||||
|
var settings = {};
|
||||||
|
var runtime = {
|
||||||
|
log: log,
|
||||||
|
settings: {
|
||||||
|
get: function(key) {
|
||||||
|
return settings[key];
|
||||||
|
},
|
||||||
|
set: function(key,value) {
|
||||||
|
settings[key] = value;
|
||||||
|
return when.resolve();
|
||||||
|
},
|
||||||
|
delete: function(key) {
|
||||||
|
delete settings[key];
|
||||||
|
return when.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it('migrates to encrypted and generates default key', function(done) {
|
||||||
|
settings = {};
|
||||||
|
credentials.init(runtime);
|
||||||
|
credentials.load({"node":{user1:"abc",password1:"123"}}).then(function() {
|
||||||
|
settings.should.have.a.property("_credentialSecret");
|
||||||
|
settings._credentialSecret.should.have.a.length(64);
|
||||||
|
credentials.dirty().should.be.true();
|
||||||
|
credentials.export().then(function(result) {
|
||||||
|
result.should.have.a.property("$");
|
||||||
|
// reset everything - but with _credentialSecret still set
|
||||||
|
credentials.init(runtime);
|
||||||
|
// load the freshly encrypted version
|
||||||
|
credentials.load(result).then(function() {
|
||||||
|
should.exist(credentials.get("node"));
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('uses default key', function(done) {
|
||||||
|
settings = {
|
||||||
|
_credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a"
|
||||||
|
};
|
||||||
|
// {"node":{user1:"abc",password1:"123"}}
|
||||||
|
var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"};
|
||||||
|
credentials.init(runtime);
|
||||||
|
credentials.load(cryptedFlows).then(function() {
|
||||||
|
should.exist(credentials.get("node"));
|
||||||
|
credentials.dirty().should.be.false();
|
||||||
|
credentials.add("node",{user1:"def",password1:"456"});
|
||||||
|
credentials.export().then(function(result) {
|
||||||
|
result.should.have.a.property("$");
|
||||||
|
// reset everything - but with _credentialSecret still set
|
||||||
|
credentials.init(runtime);
|
||||||
|
// load the freshly encrypted version
|
||||||
|
credentials.load(result).then(function() {
|
||||||
|
should.exist(credentials.get("node"));
|
||||||
|
credentials.get("node").should.have.a.property("user1","def");
|
||||||
|
credentials.get("node").should.have.a.property("password1","456");
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('uses user key', function(done) {
|
||||||
|
settings = {
|
||||||
|
credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a"
|
||||||
|
};
|
||||||
|
// {"node":{user1:"abc",password1:"123"}}
|
||||||
|
var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"};
|
||||||
|
credentials.init(runtime);
|
||||||
|
credentials.load(cryptedFlows).then(function() {
|
||||||
|
credentials.dirty().should.be.false();
|
||||||
|
should.exist(credentials.get("node"));
|
||||||
|
credentials.add("node",{user1:"def",password1:"456"});
|
||||||
|
credentials.export().then(function(result) {
|
||||||
|
result.should.have.a.property("$");
|
||||||
|
|
||||||
|
// reset everything - but with _credentialSecret still set
|
||||||
|
credentials.init(runtime);
|
||||||
|
// load the freshly encrypted version
|
||||||
|
credentials.load(result).then(function() {
|
||||||
|
should.exist(credentials.get("node"));
|
||||||
|
credentials.get("node").should.have.a.property("user1","def");
|
||||||
|
credentials.get("node").should.have.a.property("password1","456");
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('uses user key - when settings are otherwise unavailable', function(done) {
|
||||||
|
var runtime = {
|
||||||
|
log: log,
|
||||||
|
settings: {
|
||||||
|
get: function(key) {
|
||||||
|
if (key === 'credentialSecret') {
|
||||||
|
return "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a";
|
||||||
|
}
|
||||||
|
throw new Error();
|
||||||
|
},
|
||||||
|
set: function(key,value) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// {"node":{user1:"abc",password1:"123"}}
|
||||||
|
var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"};
|
||||||
|
credentials.init(runtime);
|
||||||
|
credentials.load(cryptedFlows).then(function() {
|
||||||
|
should.exist(credentials.get("node"));
|
||||||
|
credentials.add("node",{user1:"def",password1:"456"});
|
||||||
|
credentials.export().then(function(result) {
|
||||||
|
result.should.have.a.property("$");
|
||||||
|
|
||||||
|
// reset everything - but with _credentialSecret still set
|
||||||
|
credentials.init(runtime);
|
||||||
|
// load the freshly encrypted version
|
||||||
|
credentials.load(result).then(function() {
|
||||||
|
should.exist(credentials.get("node"));
|
||||||
|
credentials.get("node").should.have.a.property("user1","def");
|
||||||
|
credentials.get("node").should.have.a.property("password1","456");
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('migrates from default key to user key', function(done) {
|
||||||
|
settings = {
|
||||||
|
_credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a",
|
||||||
|
credentialSecret: "aaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbcccccccccccccddddddddddddeeeee"
|
||||||
|
};
|
||||||
|
// {"node":{user1:"abc",password1:"123"}}
|
||||||
|
var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"};
|
||||||
|
credentials.init(runtime);
|
||||||
|
credentials.load(cryptedFlows).then(function() {
|
||||||
|
credentials.dirty().should.be.true();
|
||||||
|
should.exist(credentials.get("node"));
|
||||||
|
credentials.export().then(function(result) {
|
||||||
|
result.should.have.a.property("$");
|
||||||
|
settings.should.not.have.a.property("_credentialSecret");
|
||||||
|
|
||||||
|
// reset everything - but with _credentialSecret still set
|
||||||
|
credentials.init(runtime);
|
||||||
|
// load the freshly encrypted version
|
||||||
|
credentials.load(result).then(function() {
|
||||||
|
should.exist(credentials.get("node"));
|
||||||
|
credentials.get("node").should.have.a.property("user1","abc");
|
||||||
|
credentials.get("node").should.have.a.property("password1","123");
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('migrates from default key to user key - unencrypted original', function(done) {
|
||||||
|
settings = {
|
||||||
|
_credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a",
|
||||||
|
credentialSecret: "aaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbcccccccccccccddddddddddddeeeee"
|
||||||
|
};
|
||||||
|
// {"node":{user1:"abc",password1:"123"}}
|
||||||
|
var unencryptedFlows = {"node":{user1:"abc",password1:"123"}};
|
||||||
|
credentials.init(runtime);
|
||||||
|
credentials.load(unencryptedFlows).then(function() {
|
||||||
|
credentials.dirty().should.be.true();
|
||||||
|
should.exist(credentials.get("node"));
|
||||||
|
credentials.export().then(function(result) {
|
||||||
|
result.should.have.a.property("$");
|
||||||
|
settings.should.not.have.a.property("_credentialSecret");
|
||||||
|
|
||||||
|
// reset everything - but with _credentialSecret still set
|
||||||
|
credentials.init(runtime);
|
||||||
|
// load the freshly encrypted version
|
||||||
|
credentials.load(result).then(function() {
|
||||||
|
should.exist(credentials.get("node"));
|
||||||
|
credentials.get("node").should.have.a.property("user1","abc");
|
||||||
|
credentials.get("node").should.have.a.property("password1","123");
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('migrates from default key to unencrypted', function(done) {
|
||||||
|
settings = {
|
||||||
|
_credentialSecret: "e3a36f47f005bf2aaa51ce3fc6fcaafd79da8d03f2b1a9281f8fb0a285e6255a",
|
||||||
|
credentialSecret: false
|
||||||
|
};
|
||||||
|
// {"node":{user1:"abc",password1:"123"}}
|
||||||
|
var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"};
|
||||||
|
credentials.init(runtime);
|
||||||
|
credentials.load(cryptedFlows).then(function() {
|
||||||
|
credentials.dirty().should.be.true();
|
||||||
|
should.exist(credentials.get("node"));
|
||||||
|
credentials.export().then(function(result) {
|
||||||
|
result.should.not.have.a.property("$");
|
||||||
|
settings.should.not.have.a.property("_credentialSecret");
|
||||||
|
result.should.eql({"node":{user1:"abc",password1:"123"}});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('handles bad default key - resets credentials', function(done) {
|
||||||
|
settings = {
|
||||||
|
_credentialSecret: "badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadb"
|
||||||
|
};
|
||||||
|
// {"node":{user1:"abc",password1:"123"}}
|
||||||
|
var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"};
|
||||||
|
credentials.init(runtime);
|
||||||
|
credentials.load(cryptedFlows).then(function() {
|
||||||
|
credentials.dirty().should.be.true();
|
||||||
|
should.not.exist(credentials.get("node"));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('handles bad user key - resets credentials', function(done) {
|
||||||
|
settings = {
|
||||||
|
credentialSecret: "badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadb"
|
||||||
|
};
|
||||||
|
// {"node":{user1:"abc",password1:"123"}}
|
||||||
|
var cryptedFlows = {"$":"5b89d8209b5158a3c313675561b1a5b5phN1gDBe81Zv98KqS/hVDmc9EKvaKqRIvcyXYvBlFNzzzJtvN7qfw06i"};
|
||||||
|
credentials.init(runtime);
|
||||||
|
credentials.load(cryptedFlows).then(function() {
|
||||||
|
credentials.dirty().should.be.true();
|
||||||
|
should.not.exist(credentials.get("node"));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles unavailable settings - leaves creds unencrypted', function(done) {
|
||||||
|
var runtime = {
|
||||||
|
log: log,
|
||||||
|
settings: {
|
||||||
|
get: function(key) {
|
||||||
|
throw new Error();
|
||||||
|
},
|
||||||
|
set: function(key,value) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// {"node":{user1:"abc",password1:"123"}}
|
||||||
|
credentials.init(runtime);
|
||||||
|
credentials.load({"node":{user1:"abc",password1:"123"}}).then(function() {
|
||||||
|
credentials.dirty().should.be.false();
|
||||||
|
should.exist(credentials.get("node"));
|
||||||
|
credentials.export().then(function(result) {
|
||||||
|
result.should.not.have.a.property("$");
|
||||||
|
result.should.have.a.property("node");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -50,12 +50,14 @@ describe("red/nodes/index", function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var settings = {
|
var settings = {
|
||||||
available: function() { return false }
|
available: function() { return false },
|
||||||
|
get: function() { return false }
|
||||||
};
|
};
|
||||||
|
|
||||||
var runtime = {
|
var runtime = {
|
||||||
settings: settings,
|
settings: settings,
|
||||||
storage: storage
|
storage: storage,
|
||||||
|
log: {debug:function(){},warn:function(){}}
|
||||||
};
|
};
|
||||||
|
|
||||||
function TestNode(n) {
|
function TestNode(n) {
|
||||||
|
Loading…
Reference in New Issue
Block a user