From c90fd1e6d87ca063db78906797c9ad62f617fb87 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 4 Nov 2015 11:13:02 +0000 Subject: [PATCH] Move credential http API handling to api component --- red/api/credentials.js | 46 +++++++++++ red/api/index.js | 4 +- red/nodes/credentials.js | 43 +--------- red/nodes/index.js | 7 +- red/server.js | 2 +- test/red/api/credentials_spec.js | 108 +++++++++++++++++++++++++ test/red/nodes/credentials_spec.js | 124 ++++++----------------------- test/red/nodes/index_spec.js | 2 +- 8 files changed, 190 insertions(+), 146 deletions(-) create mode 100644 red/api/credentials.js create mode 100644 test/red/api/credentials_spec.js diff --git a/red/api/credentials.js b/red/api/credentials.js new file mode 100644 index 000000000..1bfece689 --- /dev/null +++ b/red/api/credentials.js @@ -0,0 +1,46 @@ +/** + * Copyright 2015 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var api = require("../nodes"); + +module.exports = { + get: function (req, res) { + // 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 = req.params.type; + var nodeID = req.params.id; + var credentials = api.getCredentials(nodeID); + if (!credentials) { + res.json({}); + return; + } + var definition = api.getCredentialDefinition(nodeType); + + var sendCredentials = {}; + for (var cred in definition) { + if (definition.hasOwnProperty(cred)) { + if (definition[cred].type == "password") { + var key = 'has_' + cred; + sendCredentials[key] = credentials[cred] != null && credentials[cred] !== ''; + continue; + } + sendCredentials[cred] = credentials[cred] || ''; + } + } + res.json(sendCredentials); + } +} diff --git a/red/api/index.js b/red/api/index.js index 5e7d5372f..a01fca399 100644 --- a/red/api/index.js +++ b/red/api/index.js @@ -27,6 +27,7 @@ var library = require("./library"); var info = require("./info"); var theme = require("./theme"); var locales = require("./locales"); +var credentials = require("./credentials"); var log = require("../log"); @@ -48,7 +49,6 @@ var errorHandler = function(err,req,res,next) { function init(adminApp,storage) { auth.init(settings,storage); - // Editor if (!settings.disableEditor) { ui.init(settings); @@ -94,6 +94,8 @@ function init(adminApp,storage) { adminApp.get("/nodes/:mod/:set",needsPermission("nodes.read"),nodes.getSet); adminApp.put("/nodes/:mod/:set",needsPermission("nodes.write"),nodes.putSet); + adminApp.get('/credentials/:type/:id', needsPermission("credentials.read"),credentials.get); + adminApp.get(/locales\/(.+)\/?$/,locales.get); // Library diff --git a/red/nodes/credentials.js b/red/nodes/credentials.js index 603bbf4ae..57a0da054 100644 --- a/red/nodes/credentials.js +++ b/red/nodes/credentials.js @@ -23,50 +23,10 @@ var needsPermission = require("../api/auth").needsPermission; var credentialCache = {}; var storage = null; var credentialsDef = {}; -var redApp = null; - -/** - * Adds an HTTP endpoint to allow look up of credentials for a given node id. - */ -function registerEndpoint(type) { - redApp.get('/credentials/' + type + '/:id', needsPermission(type+".read"), 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 = credentialCache[nodeID]; - if (credentials === undefined) { - res.json({}); - return; - } - var definition = credentialsDef[nodeType]; - - var sendCredentials = {}; - for (var cred in definition) { - if (definition.hasOwnProperty(cred)) { - if (definition[cred].type == "password") { - var key = 'has_' + cred; - sendCredentials[key] = credentials[cred] != null && credentials[cred] !== ''; - continue; - } - sendCredentials[cred] = credentials[cred] || ''; - } - } - res.json(sendCredentials); - - }); -} - module.exports = { - init: function (_storage,_app) { + init: function (_storage) { storage = _storage; - redApp = _app; }, /** @@ -144,7 +104,6 @@ module.exports = { register: function (type, definition) { var dashedType = type.replace(/\s+/g, '-'); credentialsDef[dashedType] = definition; - registerEndpoint(dashedType); }, /** diff --git a/red/nodes/index.js b/red/nodes/index.js index 21112be39..be90dbb8f 100644 --- a/red/nodes/index.js +++ b/red/nodes/index.js @@ -53,8 +53,8 @@ function createNode(node,def) { } } -function init(_settings,storage,app) { - credentials.init(storage,app); +function init(_settings,storage) { + credentials.init(storage); flows.init(_settings,storage); registry.init(_settings); } @@ -148,5 +148,6 @@ module.exports = { // Credentials addCredentials: credentials.add, getCredentials: credentials.get, - deleteCredentials: credentials.delete + deleteCredentials: credentials.delete, + getCredentialDefinition: credentials.getDefinition }; diff --git a/red/server.js b/red/server.js index e4d7ee277..092974a24 100644 --- a/red/server.js +++ b/red/server.js @@ -64,7 +64,7 @@ function start() { } log.info(log._("runtime.version",{component:"Node.js ",version:process.version})); log.info(log._("server.loading")); - redNodes.init(settings,storage,app); + redNodes.init(settings,storage); return redNodes.load().then(function() { var i; diff --git a/test/red/api/credentials_spec.js b/test/red/api/credentials_spec.js new file mode 100644 index 000000000..70e62c796 --- /dev/null +++ b/test/red/api/credentials_spec.js @@ -0,0 +1,108 @@ +/** + * Copyright 2015 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var should = require("should"); +var request = require('supertest'); +var express = require('express'); +var sinon = require('sinon'); +var when = require('when'); + + +var nodeApi = require("../../../red/nodes"); +var credentials = require("../../../red/api/credentials"); + +describe('credentials api', function() { + var app; + + before(function() { + app = express(); + app.get('/credentials/:type/:id',credentials.get); + + sinon.stub(nodeApi,"getCredentials",function(id) { + if (id === "n1") { + return {user1:"abc",password1:"123"}; + } else { + return null; + } + }); + sinon.stub(nodeApi,"getCredentialDefinition",function(type) { + if (type === "known-type") { + return {user1:{type:"text"},password1:{type:"password"}}; + } else { + return null; + } + }); + }); + after(function() { + nodeApi.getCredentials.restore(); + nodeApi.getCredentialDefinition.restore(); + }) + it('returns empty credentials if unknown type',function(done) { + request(app) + .get("/credentials/unknown-type/n1") + .expect(200) + .expect("Content-Type",/json/) + .end(function(err,res) { + if (err) { + done(err); + } else { + try { + res.body.should.eql({}); + done(); + } catch(e) { + done(e); + } + } + }) + }); + it('returns empty credentials if none are stored',function(done) { + request(app) + .get("/credentials/known-type/n2") + .expect("Content-Type",/json/) + .end(function(err,res) { + if (err) { + done(err); + } else { + try { + res.body.should.eql({}); + done(); + } catch(e) { + done(e); + } + } + }) + }); + it('returns stored credentials',function(done) { + request(app) + .get("/credentials/known-type/n1") + .expect("Content-Type",/json/) + .end(function(err,res) { + if (err) { + done(err); + } else { + try { + res.body.should.have.a.property("user1","abc"); + res.body.should.not.have.a.property("password1"); + res.body.should.have.a.property("has_password1",true); + done(); + } catch(e) { + done(e); + } + } + }) + }); + +}); diff --git a/test/red/nodes/credentials_spec.js b/test/red/nodes/credentials_spec.js index e71863d86..d61d2b5cb 100644 --- a/test/red/nodes/credentials_spec.js +++ b/test/red/nodes/credentials_spec.js @@ -1,5 +1,5 @@ /** - * Copyright 2014 IBM Corp. + * Copyright 2014, 2015 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,13 +29,13 @@ var auth = require("../../../red/api/auth"); describe('Credentials', function() { - + afterEach(function() { index.clearRegistry(); }); - + it('loads from storage',function(done) { - + var storage = { getCredentials: function() { return when.promise(function(resolve,reject) { @@ -43,18 +43,18 @@ describe('Credentials', function() { }); } }; - + credentials.init(storage); - + credentials.load().then(function() { - + credentials.get("a").should.have.property('b',1); credentials.get("a").should.have.property('c',2); - + done(); }); }); - + it('saves to storage', function(done) { var storage = { saveCredentials: function(creds) { @@ -66,8 +66,8 @@ describe('Credentials', function() { res.should.equal("saveCalled"); done(); }); - }); - + }); + it('saves to storage when new cred added', function(done) { var storage = { getCredentials: function() { @@ -90,7 +90,7 @@ describe('Credentials', function() { done(); }); }); - + it('deletes from storage', function(done) { var storage = { getCredentials: function() { @@ -112,9 +112,9 @@ describe('Credentials', function() { storage.saveCredentials.restore(); done(); }); - + }); - + it('clean up from storage', function(done) { var storage = { getCredentials: function() { @@ -137,7 +137,7 @@ describe('Credentials', function() { done(); }); }); - + it('handle error loading from storage', function(done) { var storage = { getCredentials: function() { @@ -153,7 +153,7 @@ describe('Credentials', function() { sinon.stub(log, 'warn', function(msg) { logmsg = msg; }); - + credentials.init(storage); credentials.load().then(function() { log.warn.calledOnce.should.be.true; @@ -164,7 +164,7 @@ describe('Credentials', function() { done(err); }); }); - + it('credential type is not registered when extract', function(done) { var testFlows = [{"type":"test","id":"tab1","label":"Sheet 1"}]; var storage = { @@ -196,7 +196,7 @@ describe('Credentials', function() { }; function TestNode(n) { index.createNode(this, n); - + this.id = 'tab1'; this.type = 'test'; this.name = 'barney'; @@ -214,9 +214,9 @@ describe('Credentials', function() { available: function() { return false;} } index.init(settings, storage); - index.registerType('test', TestNode); + index.registerType('test', TestNode); index.loadFlows().then(function() { - var testnode = new TestNode({id:'tab1',type:'test',name:'barney'}); + var testnode = new TestNode({id:'tab1',type:'test',name:'barney'}); credentials.extract(testnode); log.warn.calledOnce.should.be.true; log.warn.restore(); @@ -226,7 +226,7 @@ describe('Credentials', function() { done(err); }); }); - + it('extract and store credential updates in the provided node', function(done) { credentials.init({saveCredentials:function(){}},express()); credentials.register("test",{ @@ -236,7 +236,7 @@ describe('Credentials', function() { password2:{type:"password"}, user3:{type:"text"}, password3:{type:"password"} - + }); credentials.add("node",{user1:"abc",password1:"123",user2:"def",password2:"456",user3:"ghi",password3:"789"}); var node = {id:"node",type:"test",credentials:{ @@ -248,9 +248,9 @@ describe('Credentials', function() { password3:"newPassword" }}; credentials.extract(node); - + node.should.not.have.a.property("credentials"); - + var newCreds = credentials.get("node"); newCreds.should.have.a.property("user1","abc"); newCreds.should.have.a.property("password1","123"); @@ -258,79 +258,7 @@ describe('Credentials', function() { newCreds.should.not.have.a.property("password2"); newCreds.should.have.a.property("user3","newUser"); newCreds.should.have.a.property("password3","newPassword"); - + done(); }); - - describe('registerEndpoint',function() { - it('returns empty credentials if none are stored',function(done) { - auth.init({}); - var app = express(); - credentials.init({saveCredentials:function(){}},app); - credentials.register("test",{ - user1:{type:"text"}, - password1:{type:"password"}, - user2:{type:"text"}, - password2:{type:"password"}, - user3:{type:"text"}, - password3:{type:"password"} - - }); - request(app) - .get("/credentials/test/123") - .expect("Content-Type",/json/) - .end(function(err,res) { - if (err) { - done(err); - } else { - try { - res.body.should.eql({}); - done(); - } catch(e) { - done(e); - } - } - }) - }); - it('returns stored credentials',function(done) { - auth.init({}); - var app = express(); - credentials.init({saveCredentials:function(){}},app); - credentials.register("test",{ - user1:{type:"text"}, - password1:{type:"password"}, - user2:{type:"text"}, - password2:{type:"password"}, - user3:{type:"text"}, - password3:{type:"password"} - - }); - credentials.add("node",{user1:"abc",password1:"123",user2:"def",password2:"456",user3:"ghi",password3:"789"}); - request(app) - .get("/credentials/test/node") - .expect("Content-Type",/json/) - .end(function(err,res) { - if (err) { - done(err); - } else { - try { - res.body.should.have.a.property("user1","abc"); - res.body.should.have.a.property("user2","def"); - res.body.should.have.a.property("user3","ghi"); - res.body.should.have.a.property("has_password1",true); - res.body.should.have.a.property("has_password2",true); - res.body.should.have.a.property("has_password3",true); - res.body.should.not.have.a.property("password1"); - res.body.should.not.have.a.property("password2"); - res.body.should.not.have.a.property("password3"); - done(); - } catch(e) { - done(e); - } - } - }) - }); - - }); -}) - +}) diff --git a/test/red/nodes/index_spec.js b/test/red/nodes/index_spec.js index fd8f2ce74..418972692 100644 --- a/test/red/nodes/index_spec.js +++ b/test/red/nodes/index_spec.js @@ -134,7 +134,7 @@ describe("red/nodes/index", function() { } }); var testnode = new TestNode({id:'tab1',type:'test',name:'barney', '_alias':'tab1'}); - credentials.getDefinition("test").should.have.property('foo'); + index.getCredentialDefinition("test").should.have.property('foo'); done(); });