mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Merge pull request #2461 from node-red/set-flow-with-creds
Allow credentials to be provided as part of /flows api
This commit is contained in:
commit
1830478ec3
@ -57,6 +57,8 @@ var api = module.exports = {
|
|||||||
* Sets the current flow configuration
|
* Sets the current flow configuration
|
||||||
* @param {Object} opts
|
* @param {Object} opts
|
||||||
* @param {User} opts.user - the user calling the api
|
* @param {User} opts.user - the user calling the api
|
||||||
|
* @param {Object} opts.flows - the flow configuration: `{flows: [..], credentials: {}}`
|
||||||
|
* @param {Object} opts.deploymentType - the type of deployment - "full", "nodes", "flows", "reload"
|
||||||
* @param {Object} opts.req - the request to log (optional)
|
* @param {Object} opts.req - the request to log (optional)
|
||||||
* @return {Promise<Flows>} - the active flow configuration
|
* @return {Promise<Flows>} - the active flow configuration
|
||||||
* @memberof @node-red/runtime_flows
|
* @memberof @node-red/runtime_flows
|
||||||
@ -83,7 +85,7 @@ var api = module.exports = {
|
|||||||
return reject(err);
|
return reject(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apiPromise = runtime.nodes.setFlows(flows.flows,deploymentType);
|
apiPromise = runtime.nodes.setFlows(flows.flows,flows.credentials,deploymentType);
|
||||||
}
|
}
|
||||||
apiPromise.then(function(flowId) {
|
apiPromise.then(function(flowId) {
|
||||||
return resolve({rev:flowId});
|
return resolve({rev:flowId});
|
||||||
|
@ -106,15 +106,20 @@ function load(forceStart) {
|
|||||||
// This is a force reload from the API - disable safeMode
|
// This is a force reload from the API - disable safeMode
|
||||||
delete settings.safeMode;
|
delete settings.safeMode;
|
||||||
}
|
}
|
||||||
return setFlows(null,"load",false,forceStart);
|
return setFlows(null,null,"load",false,forceStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* _config - new node array configuration
|
* _config - new node array configuration
|
||||||
|
* _credentials - new credentials configuration (optional)
|
||||||
* type - full/nodes/flows/load (default full)
|
* type - full/nodes/flows/load (default full)
|
||||||
* muteLog - don't emit the standard log messages (used for individual flow api)
|
* muteLog - don't emit the standard log messages (used for individual flow api)
|
||||||
*/
|
*/
|
||||||
function setFlows(_config,type,muteLog,forceStart) {
|
function setFlows(_config,_credentials,type,muteLog,forceStart) {
|
||||||
|
if (typeof _credentials === "string") {
|
||||||
|
type = _credentials;
|
||||||
|
_credentials = null;
|
||||||
|
}
|
||||||
type = type||"full";
|
type = type||"full";
|
||||||
if (settings.safeMode) {
|
if (settings.safeMode) {
|
||||||
if (type !== "load") {
|
if (type !== "load") {
|
||||||
@ -155,16 +160,27 @@ function setFlows(_config,type,muteLog,forceStart) {
|
|||||||
delete newFlowConfig.allNodes[id].credentials;
|
delete newFlowConfig.allNodes[id].credentials;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var credsDirty;
|
||||||
|
|
||||||
// Allow the credential store to remove anything no longer needed
|
if (_credentials) {
|
||||||
credentials.clean(config);
|
// A full set of credentials have been provided. Use those instead
|
||||||
|
configSavePromise = credentials.load(_credentials);
|
||||||
|
credsDirty = true;
|
||||||
|
} else {
|
||||||
|
// Allow the credential store to remove anything no longer needed
|
||||||
|
credentials.clean(config);
|
||||||
|
|
||||||
// Remember whether credentials need saving or not
|
// Remember whether credentials need saving or not
|
||||||
var credsDirty = credentials.dirty();
|
var credsDirty = credentials.dirty();
|
||||||
|
|
||||||
|
configSavePromise = Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
// Get the latest credentials and ask storage to save them (if needed)
|
// Get the latest credentials and ask storage to save them (if needed)
|
||||||
// as well as the new flow configuration.
|
// as well as the new flow configuration.
|
||||||
configSavePromise = credentials.export().then(function(creds) {
|
configSavePromise = configSavePromise.then(function() {
|
||||||
|
return credentials.export()
|
||||||
|
}).then(function(creds) {
|
||||||
var saveConfig = {
|
var saveConfig = {
|
||||||
flows: config,
|
flows: config,
|
||||||
credentialsDirty:credsDirty,
|
credentialsDirty:credsDirty,
|
||||||
@ -515,7 +531,7 @@ function addFlow(flow) {
|
|||||||
var newConfig = clone(activeConfig.flows);
|
var newConfig = clone(activeConfig.flows);
|
||||||
newConfig = newConfig.concat(nodes);
|
newConfig = newConfig.concat(nodes);
|
||||||
|
|
||||||
return setFlows(newConfig,'flows',true).then(function() {
|
return setFlows(newConfig,null,'flows',true).then(function() {
|
||||||
log.info(log._("nodes.flows.added-flow",{label:(flow.label?flow.label+" ":"")+"["+flow.id+"]"}));
|
log.info(log._("nodes.flows.added-flow",{label:(flow.label?flow.label+" ":"")+"["+flow.id+"]"}));
|
||||||
return flow.id;
|
return flow.id;
|
||||||
});
|
});
|
||||||
@ -646,7 +662,7 @@ function updateFlow(id,newFlow) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newConfig = newConfig.concat(nodes);
|
newConfig = newConfig.concat(nodes);
|
||||||
return setFlows(newConfig,'flows',true).then(function() {
|
return setFlows(newConfig,null,'flows',true).then(function() {
|
||||||
log.info(log._("nodes.flows.updated-flow",{label:(label?label+" ":"")+"["+id+"]"}));
|
log.info(log._("nodes.flows.updated-flow",{label:(label?label+" ":"")+"["+id+"]"}));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -668,7 +684,7 @@ function removeFlow(id) {
|
|||||||
return node.z !== id && node.id !== id;
|
return node.z !== id && node.id !== id;
|
||||||
});
|
});
|
||||||
|
|
||||||
return setFlows(newConfig,'flows',true).then(function() {
|
return setFlows(newConfig,null,'flows',true).then(function() {
|
||||||
log.info(log._("nodes.flows.removed-flow",{label:(flow.label?flow.label+" ":"")+"["+flow.id+"]"}));
|
log.info(log._("nodes.flows.removed-flow",{label:(flow.label?flow.label+" ":"")+"["+flow.id+"]"}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ describe("runtime-api/flows", function() {
|
|||||||
var loadFlows;
|
var loadFlows;
|
||||||
var reloadError = false;
|
var reloadError = false;
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
setFlows = sinon.spy(function(flows,type) {
|
setFlows = sinon.spy(function(flows,credentials,type) {
|
||||||
if (flows[0] === "error") {
|
if (flows[0] === "error") {
|
||||||
var err = new Error("error");
|
var err = new Error("error");
|
||||||
err.code = "error";
|
err.code = "error";
|
||||||
@ -91,7 +91,19 @@ describe("runtime-api/flows", function() {
|
|||||||
result.should.eql({rev:"newRev"});
|
result.should.eql({rev:"newRev"});
|
||||||
setFlows.called.should.be.true();
|
setFlows.called.should.be.true();
|
||||||
setFlows.lastCall.args[0].should.eql([4,5,6]);
|
setFlows.lastCall.args[0].should.eql([4,5,6]);
|
||||||
setFlows.lastCall.args[1].should.eql("full");
|
setFlows.lastCall.args[2].should.eql("full");
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
it("includes credentials when part of the request", function(done) {
|
||||||
|
flows.setFlows({
|
||||||
|
flows: {flows:[4,5,6], credentials: {$:"creds"}},
|
||||||
|
}).then(function(result) {
|
||||||
|
result.should.eql({rev:"newRev"});
|
||||||
|
setFlows.called.should.be.true();
|
||||||
|
setFlows.lastCall.args[0].should.eql([4,5,6]);
|
||||||
|
setFlows.lastCall.args[1].should.eql({$:"creds"});
|
||||||
|
setFlows.lastCall.args[2].should.eql("full");
|
||||||
done();
|
done();
|
||||||
}).catch(done);
|
}).catch(done);
|
||||||
});
|
});
|
||||||
@ -103,7 +115,7 @@ describe("runtime-api/flows", function() {
|
|||||||
result.should.eql({rev:"newRev"});
|
result.should.eql({rev:"newRev"});
|
||||||
setFlows.called.should.be.true();
|
setFlows.called.should.be.true();
|
||||||
setFlows.lastCall.args[0].should.eql([4,5,6]);
|
setFlows.lastCall.args[0].should.eql([4,5,6]);
|
||||||
setFlows.lastCall.args[1].should.eql("nodes");
|
setFlows.lastCall.args[2].should.eql("nodes");
|
||||||
done();
|
done();
|
||||||
}).catch(done);
|
}).catch(done);
|
||||||
});
|
});
|
||||||
@ -125,7 +137,7 @@ describe("runtime-api/flows", function() {
|
|||||||
result.should.eql({rev:"newRev"});
|
result.should.eql({rev:"newRev"});
|
||||||
setFlows.called.should.be.true();
|
setFlows.called.should.be.true();
|
||||||
setFlows.lastCall.args[0].should.eql([4,5,6]);
|
setFlows.lastCall.args[0].should.eql([4,5,6]);
|
||||||
setFlows.lastCall.args[1].should.eql("nodes");
|
setFlows.lastCall.args[2].should.eql("nodes");
|
||||||
done();
|
done();
|
||||||
}).catch(done);
|
}).catch(done);
|
||||||
});
|
});
|
||||||
|
@ -67,7 +67,10 @@ describe('flows/index', function() {
|
|||||||
});
|
});
|
||||||
return when.resolve();
|
return when.resolve();
|
||||||
});
|
});
|
||||||
credentialsLoad = sinon.stub(credentials,"load",function() {
|
credentialsLoad = sinon.stub(credentials,"load",function(creds) {
|
||||||
|
if (creds && creds.hasOwnProperty("$") && creds['$'] === "fail") {
|
||||||
|
return when.reject("creds error");
|
||||||
|
}
|
||||||
return when.resolve();
|
return when.resolve();
|
||||||
});
|
});
|
||||||
flowCreate = sinon.stub(Flow,"create",function(parent, global, flow) {
|
flowCreate = sinon.stub(Flow,"create",function(parent, global, flow) {
|
||||||
@ -177,6 +180,23 @@ describe('flows/index', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets the full flow including credentials', function(done) {
|
||||||
|
var originalConfig = [
|
||||||
|
{id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]},
|
||||||
|
{id:"t1",type:"tab"}
|
||||||
|
];
|
||||||
|
var credentials = {"t1-1":{"a":1}};
|
||||||
|
|
||||||
|
flows.init({log:mockLog, settings:{},storage:storage});
|
||||||
|
flows.setFlows(originalConfig,credentials).then(function() {
|
||||||
|
credentialsClean.called.should.be.false();
|
||||||
|
credentialsLoad.called.should.be.true();
|
||||||
|
credentialsLoad.lastCall.args[0].should.eql(credentials);
|
||||||
|
flows.getFlows().flows.should.eql(originalConfig);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('updates existing flows with partial deployment - nodes', function(done) {
|
it('updates existing flows with partial deployment - nodes', function(done) {
|
||||||
var originalConfig = [
|
var originalConfig = [
|
||||||
{id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]},
|
{id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]},
|
||||||
@ -235,6 +255,20 @@ describe('flows/index', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns error if it cannot decrypt credentials', function(done) {
|
||||||
|
var originalConfig = [
|
||||||
|
{id:"t1-1",x:10,y:10,z:"t1",type:"test",wires:[]},
|
||||||
|
{id:"t1",type:"tab"}
|
||||||
|
];
|
||||||
|
var credentials = {"$":"fail"};
|
||||||
|
|
||||||
|
flows.init({log:mockLog, settings:{},storage:storage});
|
||||||
|
flows.setFlows(originalConfig,credentials).then(function() {
|
||||||
|
done("Unexpected success when credentials couldn't be decrypted")
|
||||||
|
}).catch(function(err) {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#load', function() {
|
describe('#load', function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user