From d7bd6d492fc1e29ef890d598c578ce77db3ea208 Mon Sep 17 00:00:00 2001 From: iolufemi Date: Thu, 17 Aug 2017 17:07:31 +0100 Subject: [PATCH] added user routes and started generating checksum for encrypted data --- TODO.md | 29 +++-- controllers/Users.js | 216 +++++++++++++++++------------------ gulpfile.js | 2 - routes/users.js | 42 +++++++ services/encryption/index.js | 70 ++++++------ services/queue/jobs.js | 8 +- services/queue/workers.js | 12 +- services/response/ok.js | 3 +- test/services/encryption.js | 5 +- test/services/queue.js | 4 +- test/services/response.js | 4 +- 11 files changed, 210 insertions(+), 185 deletions(-) diff --git a/TODO.md b/TODO.md index 604c6f7e..eec7a703 100644 --- a/TODO.md +++ b/TODO.md @@ -1,25 +1,24 @@ ### TODOs | Filename | line # | TODO |:------|:------:|:------ -| gulpfile.js | 146 | Add gulp-banner to add GNU GPL notice on every js file. | controllers/Initialize.js | 18 | Test initialize controller | controllers/Users.js | 13 | Test buildProjection function -| controllers/Users.js | 116 | Test limiting -| controllers/Users.js | 117 | Test that response contains count of total record for the query -| controllers/Users.js | 118 | Test that the last document Id in the return array of documents is in the response -| controllers/Users.js | 119 | Test that sorting works -| controllers/Users.js | 120 | Test that projection works -| controllers/Users.js | 121 | Test that populating works -| controllers/Users.js | 122 | Test that date range works -| controllers/Users.js | 295 | Test users controller -| controllers/Users.js | 296 | Finish users route -| controllers/Users.js | 297 | Test that any deleted data is backed up +| controllers/Users.js | 45 | Test that search works +| controllers/Users.js | 128 | Test limiting +| controllers/Users.js | 129 | Test that response contains count of total record for the query +| controllers/Users.js | 130 | Test that the last document Id in the return array of documents is in the response +| controllers/Users.js | 131 | Test that sorting works +| controllers/Users.js | 132 | Test that projection works +| controllers/Users.js | 133 | Test that populating works +| controllers/Users.js | 134 | Test that date range works +| controllers/Users.js | 284 | Test users controller +| controllers/Users.js | 285 | Test that any deleted data is backed up | routes/index.js | 214 | Implement API Generator | routes/initialize.js | 10 | Test initialize route +| routes/users.js | 43 | Test users route | services/queue/clock.js | 11 | work on a clock functionality so kue can support scheduled jobs | services/queue/jobs.js | 80 | Test saveToTrash job | services/queue/jobs.js | 93 | Test Webhook Event -| services/queue/jobs.js | 137 | Test Secure Webhooks -| services/queue/jobs.js | 144 | Test Unsecure Webhooks -| services/queue/jobs.js | 169 | Test sendHTTPRequest Job -| services/encryption/index.js | 40 | Generate checksum here \ No newline at end of file +| services/queue/jobs.js | 133 | Test Secure Webhooks +| services/queue/jobs.js | 140 | Test Unsecure Webhooks +| services/queue/jobs.js | 165 | Test sendHTTPRequest Job \ No newline at end of file diff --git a/controllers/Users.js b/controllers/Users.js index f5894cc8..8b9084a8 100644 --- a/controllers/Users.js +++ b/controllers/Users.js @@ -32,99 +32,113 @@ UsersController.buildProjection = function(projection){ }; UsersController.find = function(req,res,next){ - var query = req.query; - var projection = query.projection.split(','); - var ourProjection; - query.createdAt = {}; - if(projection){ - ourProjection = this.buildProjection(projection); - delete query.projection; - } - var limit = query.limit; - if(limit){ - delete query.limit; - } - var to = query.to; - if(to){ - delete query.to; - } - var from = query.from; - if(from){ - query.createdAt.$gt = from; - delete query.from; - if(!to){ - to = new Date().toISOString(); - } - query.createdAt.$lt = to; - } - var lastId = query.lastId; - if(lastId){ - query._id = {}; - query._id.$gt = lastId; - delete query.lastId; - } - var sort = query.sort; // -fieldName: means descending while fieldName without the minus mean ascending bith by fieldName. eg, '-fieldName1 fieldName2' - if(sort){ - delete query.sort; - } - var populate = query.populate; // Samples: 'name location' will populate name and location references. only supports this for now | 'name', 'firstname' will populate name referenece and only pick the firstname attribute - var total = Users.count(query); - var question = Users.find(query); - - if(limit){ - var ourLimit = limit * 1; - question = question.limit(limit); - }else{ - limit = 0; - } - if(sort){ - question = question.sort(sort); - } - if(populate){ - question = question.populate(populate); - } - - if(projection){ - q.all([ourProjection,total]) - .spread(function(resp,total){ - return [question.select(resp),total]; - }) - .spread(function(resp,total){ - var ourLastId = resp[resp.length - 1]._id; - var extraData = {}; - extraData.limit = limit * 1; - extraData.total = total; - extraData.lastId = ourLastId; - res.ok(resp, false, extraData); + var query; + if(req.query.search){ + query = req.query.query; + Users.search(query) + .then(function(resp){ + res.ok(resp); }) .catch(function(err){ next(err); }); + // ToDo: Test that search works }else{ - q.all([question,total]) - .spread(function(resp,total){ - var ourLastId = resp[resp.length - 1]._id; - var extraData = {}; - extraData.limit = limit * 1; - extraData.total = total; - extraData.lastId = ourLastId; - res.ok(resp, false, extraData); - }) - .catch(function(err){ - next(err); - }); - // ToDo: Test limiting - // ToDO: Test that response contains count of total record for the query - // ToDo: Test that the last document Id in the return array of documents is in the response - // ToDo: Test that sorting works - // ToDo: Test that projection works - // ToDo: Test that populating works - // ToDo: Test that date range works + query = req.query; + var projection = query.projection.split(','); + var ourProjection; + query.createdAt = {}; + if(projection){ + ourProjection = this.buildProjection(projection); + delete query.projection; + } + var limit = query.limit; + if(limit){ + delete query.limit; + } + var to = query.to; + if(to){ + delete query.to; + } + var from = query.from; + if(from){ + query.createdAt.$gt = from; + delete query.from; + if(!to){ + to = new Date().toISOString(); + } + query.createdAt.$lt = to; + } + var lastId = query.lastId; + if(lastId){ + query._id = {}; + query._id.$gt = lastId; + delete query.lastId; + } + var sort = query.sort; // -fieldName: means descending while fieldName without the minus mean ascending bith by fieldName. eg, '-fieldName1 fieldName2' + if(sort){ + delete query.sort; + } + var populate = query.populate; // Samples: 'name location' will populate name and location references. only supports this for now | 'name', 'firstname' will populate name referenece and only pick the firstname attribute + var total = Users.count(query); + var question = Users.find(query); + + if(limit){ + var ourLimit = limit * 1; + question = question.limit(limit); + }else{ + limit = 0; + } + if(sort){ + question = question.sort(sort); + } + if(populate){ + question = question.populate(populate); + } + + if(projection){ + q.all([ourProjection,total]) + .spread(function(resp,total){ + return [question.select(resp),total]; + }) + .spread(function(resp,total){ + var ourLastId = resp[resp.length - 1]._id; + var extraData = {}; + extraData.limit = limit * 1; + extraData.total = total; + extraData.lastId = ourLastId; + res.ok(resp, false, extraData); + }) + .catch(function(err){ + next(err); + }); + }else{ + q.all([question,total]) + .spread(function(resp,total){ + var ourLastId = resp[resp.length - 1]._id; + var extraData = {}; + extraData.limit = limit * 1; + extraData.total = total; + extraData.lastId = ourLastId; + res.ok(resp, false, extraData); + }) + .catch(function(err){ + next(err); + }); + // ToDo: Test limiting + // ToDO: Test that response contains count of total record for the query + // ToDo: Test that the last document Id in the return array of documents is in the response + // ToDo: Test that sorting works + // ToDo: Test that projection works + // ToDo: Test that populating works + // ToDo: Test that date range works + } + } }; UsersController.findOne = function(req,res,next){ - var id = req.query.id; + var id = req.params.id; Users.findById(id) .then(function(resp){ res.ok(resp); @@ -134,17 +148,6 @@ UsersController.findOne = function(req,res,next){ }); }; -UsersController.search = function(req,res,next){ - var query = req.query.query; - Users.search(query) - .then(function(resp){ - res.ok(resp); - }) - .catch(function(err){ - next(err); - }); -}; - UsersController.create = function(req,res,next){ var data = req.body; Users.create(data) @@ -159,7 +162,7 @@ UsersController.create = function(req,res,next){ UsersController.update = function(req,res,next){ var query = req.query; var data = req.body; - Users.update(query,data) + Users.updateMany(query,data) .then(function(resp){ res.ok(resp); }) @@ -169,7 +172,7 @@ UsersController.update = function(req,res,next){ }; UsersController.updateOne = function(req,res,next){ - var id = req.query.id; + var id = req.params.id; var data = req.body; Users.findByIdAndUpdate(id,data) .then(function(resp){ @@ -180,20 +183,6 @@ UsersController.updateOne = function(req,res,next){ }); }; -UsersController.count = function(req,res,next){ - var query = req.query; - if(!query){ - query = {}; - } - Users.count(query) - .then(function(resp){ - res.ok(resp); - }) - .catch(function(err){ - next(err); - }); -}; - UsersController.delete = function(req,res,next){ var query = req.query; // Find match @@ -237,7 +226,7 @@ UsersController.delete = function(req,res,next){ }; UsersController.deleteOne = function(req,res,next){ - var id = req.query.id; + var id = req.params.id; // Find match Users.findById(id) .then(function(resp){ @@ -267,7 +256,7 @@ UsersController.deleteOne = function(req,res,next){ }; UsersController.restore = function(req,res,next){ - var id = req.query.id; + var id = req.params.id; // Find data by ID from trash Trash.findById(id) .then(function(resp){ @@ -293,5 +282,4 @@ UsersController.restore = function(req,res,next){ module.exports = UsersController; // Todo: Test users controller -// Todo: Finish users route // ToDo: Test that any deleted data is backed up diff --git a/gulpfile.js b/gulpfile.js index a6b82210..0189773c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -142,5 +142,3 @@ gulp.task('release', function (callback) { callback(error); }); }); - -// ToDo: Add gulp-banner to add GNU GPL notice on every js file. diff --git a/routes/users.js b/routes/users.js index 3918c74e..0ef5111c 100644 --- a/routes/users.js +++ b/routes/users.js @@ -1 +1,43 @@ "use strict"; +var express = require('express'); +var router = express.Router(); +var usersController = require('../controllers/Users'); + +var service = 'users'; + +// get users or search users +router.get('/'+service, usersController.find); + +// get user +router.get('/'+service+'/:id', usersController.findOne); + +// To add validation, add a middlewave like the below. Works for just POST calls only +// function(req,res,next){ +// req._required = [ // _required should contain all the fails that are required +// 'name', +// 'name2' +// ]; + +// next(); +// } + +// create user(s) a single user object will create one user while an array of users will create multiple users +router.post('/'+service, usersController.create); + +// update all records that matches the query +router.put('/'+service, usersController.updateOne); + +// update a single record +router.put('/'+service+'/:id', usersController.findOne); + +// delete all records that matches the query +router.delete('/'+service, usersController.delete); + +// Delete a single record +router.delete('/'+service+'/:id', usersController.deleteOne); + +// restore a previously deleted record +router.post('/'+service+'/:id/restore', usersController.restore); + +module.exports = router; +// Todo: Test users route diff --git a/services/encryption/index.js b/services/encryption/index.js index 16e98307..103f6341 100644 --- a/services/encryption/index.js +++ b/services/encryption/index.js @@ -37,7 +37,9 @@ module.exports = { debug('our key: ', key); key = aesjs.utils.hex.toBytes(key); debug('in buffer: ', key); - // ToDo: Generate checksum here + var truth = crypto.createHash('sha512') + .update(text) + .digest('hex'); return q.Promise(function(resolve){ debug('encrypting...'); debug('our key: ', key); @@ -52,7 +54,7 @@ module.exports = { // Convert our bytes back into text var encryptedText = aesjs.utils.hex.fromBytes(encryptedBytes); debug('finished encryption'); - resolve(encryptedText); + resolve({truth: truth, encryptedText: encryptedText}); }); }, @@ -95,44 +97,44 @@ module.exports = { } }); - }, +}, - interpreter: function(req, res, next){ - var encryption = require('./'); - if( req.get('x-tag') ){ - res.set('x-tag', req.get('x-tag')); - res.set('Access-Control-Expose-Headers','x-tag'); +interpreter: function(req, res, next){ + var encryption = require('./'); + if( req.get('x-tag') ){ + res.set('x-tag', req.get('x-tag')); + res.set('Access-Control-Expose-Headers','x-tag'); - var key = req.get('x-tag'); + var key = req.get('x-tag'); - if(req.method === 'POST' && config.secureMode){ - if(req.body.secureData){ - var truthHash = req.body.truth; - encryption.decrypt(req.body.secureData, key, truthHash) - .then(function(resp){ - if(typeof resp === 'object'){ - req.body = resp; - next(); - }else{ - debug('decryptedText: ', resp); - var parsedJSON = JSON.parse(resp); - req.body = parsedJSON; - next(); - } - }) - .catch(function(err){ - next(new Error(err.message)); - }); - }else{ - res.badRequest(false,'Expecting an encrypted data to be sent in the secureData body parameter.'); - } + if(req.method === 'POST' && config.secureMode){ + if(req.body.secureData){ + var truthHash = req.body.truth; + encryption.decrypt(req.body.secureData, key, truthHash) + .then(function(resp){ + if(typeof resp === 'object'){ + req.body = resp; + next(); + }else{ + debug('decryptedText: ', resp); + var parsedJSON = JSON.parse(resp); + req.body = parsedJSON; + next(); + } + }) + .catch(function(err){ + next(new Error(err.message)); + }); }else{ - next(); + res.badRequest(false,'Expecting an encrypted data to be sent in the secureData body parameter.'); } - }else if(req.method !== 'POST'){ - next(); }else{ - res.badRequest(false,'Please initialize and send the x-tag header in every request.'); + next(); } + }else if(req.method !== 'POST'){ + next(); + }else{ + res.badRequest(false,'Please initialize and send the x-tag header in every request.'); } +} }; diff --git a/services/queue/jobs.js b/services/queue/jobs.js index 4260c460..f24e47aa 100644 --- a/services/queue/jobs.js +++ b/services/queue/jobs.js @@ -117,11 +117,6 @@ jobs.sendWebhook = function(data, done){ // Convert the Object to String var stringData = JSON.stringify(data.data); - // Generate Checksum - var checksum = crypto.createHash('sha512') - .update(stringData) - .digest('hex'); - hookData.truth = checksum; // Encrypt Data var key; hookPromise = encryption.generateKey() @@ -131,7 +126,8 @@ jobs.sendWebhook = function(data, done){ return encryption.encrypt(stringData, key); }) .then(function(resp){ - hookData.data = resp; + hookData.data = resp.encryptedData; + hookData.truth = resp.truth; return hookData; }); // ToDo: Test Secure Webhooks diff --git a/services/queue/workers.js b/services/queue/workers.js index 599e402b..c7106c55 100644 --- a/services/queue/workers.js +++ b/services/queue/workers.js @@ -3,27 +3,27 @@ var queue = require('./'); var jobs = require('./jobs'); -queue.process('searchIndex', 2, function(job, done){ +queue.process('searchIndex', function(job, done){ jobs.createSearchTags(job.data, done); }); -queue.process('logRequest', 2, function(job, done){ +queue.process('logRequest', function(job, done){ jobs.createRequestLog(job.data, done); }); -queue.process('logResponse', 2, function(job, done){ +queue.process('logResponse', function(job, done){ jobs.updateRequestLog(job.data, done); }); -queue.process('saveToTrash', 2, function(job, done){ +queue.process('saveToTrash', function(job, done){ jobs.saveToTrash(job.data, done); }); -queue.process('sendWebhook', 2, function(job,done){ +queue.process('sendWebhook', function(job,done){ jobs.sendWebhook(job.data, done); }); -queue.process('sendHTTPRequest', 2, function(job,done){ +queue.process('sendHTTPRequest', function(job,done){ jobs.sendHTTPRequest(job.data, done); }); diff --git a/services/response/ok.js b/services/response/ok.js index e3fa8a86..b827bd56 100644 --- a/services/response/ok.js +++ b/services/response/ok.js @@ -38,7 +38,8 @@ module.exports = function(data, cache, extraData){ debug("got response from encryption method: ",resp); log.info('Sending ok response: ', response.response); response.response.secure = true; - response.response.data = resp; + response.response.data = resp.encryptedText; + response.response.truth = resp.truth; res.status(200).json(response.response); }) .catch(function(err){ diff --git a/test/services/encryption.js b/test/services/encryption.js index 431da4cd..40f91f16 100644 --- a/test/services/encryption.js +++ b/test/services/encryption.js @@ -81,8 +81,9 @@ describe('#Encryption service test', function(){ }) .then(function(resp){ console.log("encrypted data: ", resp); - res.set('encryptedData', resp); - return encryption.decrypt(resp, req.get('x-tag'), demoDataHash); + res.set('encryptedData', resp.encryptedText); + console.log('hash: ', demoDataHash, 'generated hash: ', resp.truth); + return encryption.decrypt(resp.encryptedText, req.get('x-tag'), resp.truth); }) .then(function(resp){ console.log("decrypted data: ", resp); diff --git a/test/services/queue.js b/test/services/queue.js index a1e4ba12..cc839f6b 100644 --- a/test/services/queue.js +++ b/test/services/queue.js @@ -47,11 +47,9 @@ describe('#Queue service', function(){ it('should load processes', function() { var process = require('../../services/queue/workers'); // We have configured queue to create 2 workers per job making a total of 6 workers for 3 jobs that we currently have - process.workers.length.should.equal(12); + process.workers.length.should.equal(6); }); - - // Test Jobs describe('#Testing Jobs', function(){ it('should run createRequestLog successfully', function(done){ diff --git a/test/services/response.js b/test/services/response.js index e1529cab..c1bb5a6d 100644 --- a/test/services/response.js +++ b/test/services/response.js @@ -183,11 +183,11 @@ describe('#Response service test', function(){ return encryption.encrypt(demoData, tag); }) .then(function(res){ - console.log('Our encrypted data: ', res); + console.log('Our encrypted data: ', res.encryptedText); return agent2. post('/secure') .set('x-tag', tag) - .send({truth: demoDataHash,secureData: res}) + .send({truth: res.truth,secureData: res.encryptedText}) .expect(200); }) .then(function(res){