diff --git a/lib/microServiceRouter.js b/lib/microServiceRouter.js index fe46100..3f3b547 100644 --- a/lib/microServiceRouter.js +++ b/lib/microServiceRouter.js @@ -3,7 +3,7 @@ ---------------------------------------------------------------------------- | qewd-microservice-router: Express Integration Module for QEWD | | | - | Copyright (c) 2016-17 M/Gateway Developments Ltd, | + | Copyright (c) 2016-18 M/Gateway Developments Ltd, | | Redhill, Surrey UK. | | All rights reserved. | | | @@ -24,7 +24,7 @@ | limitations under the License. | ---------------------------------------------------------------------------- - 23 November 2017 + 31 January 2018 MicroService Routing Module @@ -134,8 +134,10 @@ function handleMicroService(message, route, destination, handleResponse) { // the nominal one must have the expiry updated var token = this.jwt.handlers.getRestJWT(message); + debug('jwt: %s', token); + debug('route = %s', JSON.stringify(route)); - if (token === '') { + if (token === '' || token === 'undefined') { debug('using updated registration JWT'); token = microService.client.token; @@ -145,7 +147,13 @@ function handleMicroService(message, route, destination, handleResponse) { // prevents arbitrary application change attempts sendToMicroService.call(this, token, message, microService, route, handleResponse); } - else { + else if (route.bypassJWTCheck) { + debug('JWT check being bypassed'); + + // incoming JWT is to be ignored by primary; forward it to the MicroService + // which will be responsible for checking the JWT + sendToMicroService.call(this, token, message, microService, route, handleResponse); + } else { debug('validating message JWT'); // JWT is first validated in worker (to reduce master process CPU load) diff --git a/package.json b/package.json index adc8b6a..0ac57ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "qewd-microservice-router", - "version": "1.0.0", + "version": "1.1.0", "description": "MicroService Routing Module for QEWD", "main": "index.js", "engines": { @@ -41,11 +41,11 @@ }, "devDependencies": { "coveralls": "^3.0.0", - "jasmine": "^2.8.0", + "jasmine": "^2.9.0", "jasmine-spec-reporter": "^4.1.1", "jasmine-spy-matchers": "^1.2.0", "jshint": "^2.9.5", - "nyc": "^11.2.1", + "nyc": "^11.4.1", "pre-commit": "^1.2.2" } } diff --git a/spec/shared/destinations/multiple.js b/spec/shared/destinations/multiple.js index ad0ade7..11bb84d 100644 --- a/spec/shared/destinations/multiple.js +++ b/spec/shared/destinations/multiple.js @@ -116,6 +116,224 @@ module.exports = function (boot) { }); }, + whenTokenHasValueUndefined: function () { + beforeEach(function (done) { + boot(function (_q, _messageObj, _handleResponse) { + q = _q; + messageObj = _messageObj; + handleResponse = _handleResponse; + + done(); + }); + }); + + it('should call handleMessage 2 times with correct arguments', function () { + q.microServiceRouter(messageObj, handleResponse); + + expect(q.handleMessage).toHaveBeenCalledTimes(2); + expect(q.handleMessage.calls.argsFor(0)[0]).toEqual({ + type: 'ewd-jwt-updateExpiry', + params: { + jwt: 'token4', + application: 'sms-micro-service' + } + }, jasmine.any(Function)); + expect(q.handleMessage.calls.argsFor(1)[0]).toEqual({ + type: 'ewd-jwt-updateExpiry', + params: { + jwt: 'token5', + application: 'log-micro-service' + } + }, jasmine.any(Function)); + }); + + it('should call microservice client #send 2 times with correct arguments', function () { + q.microServiceRouter(messageObj, handleResponse); + + destinations.forEach(function (destination) { + /*jshint camelcase: false */ + var microService = q.u_services.byDestination[destination]; + /*jshint camelcase: true */ + + expect(microService.client.send).toHaveBeenCalledWith({ + application: microService.application, + type: 'restRequest', + path: '/api/users', + pathTemplate: '/path/template', + method: 'POST', + headers: { + 'x-foo': 'bar' + }, + params: { + type: 'foo' + }, + query: { + bar: 'baz' + }, + body: { + login: 'johndoe', + password: 'secret' + }, + ip: '127.0.0.1', + ips: ['client'], + token: 'updated-jwt-token', + args: {}, + jwt: true + }, jasmine.any(Function)); + }); + }); + + it('should call handleResponse with composite result', function () { + /*jshint camelcase: false */ + var expected = { + type: 'hello', + message: { + results: { + sms_service: { + application: 'sms-micro-service' + }, + log_service: { + application: 'log-micro-service', + } + }, + token: 'updated-jwt-token' + } + }; + /*jshint camelcase: true */ + + destinations.forEach(function (destination) { + /*jshint camelcase: false */ + var microService = q.u_services.byDestination[destination]; + /*jshint camelcase: true */ + + microService.client.send.and.callFake(function (message, cb) { + var responseObj = { + message: { + application: message.application, + token: message.token + }, + type: message.application + '-type', + finished: true + }; + cb(responseObj); + }); + }); + + q.microServiceRouter(messageObj, handleResponse); + + expect(handleResponse).toHaveBeenCalledWith(expected); + }); + }, + + whenRouteByPassJwtCheckEnabled: function () { + beforeEach(function (done) { + boot(function (_q, _messageObj, _handleResponse) { + q = _q; + messageObj = _messageObj; + handleResponse = _handleResponse; + + done(); + }); + }); + + it('should call handleMessage 2 times with correct arguments', function () { + q.microServiceRouter(messageObj, handleResponse); + + expect(q.handleMessage).toHaveBeenCalledTimes(2); + expect(q.handleMessage.calls.argsFor(0)[0]).toEqual({ + type: 'ewd-jwt-updateExpiry', + params: { + jwt: 'jwt-token', + application: 'sms-micro-service' + } + }, jasmine.any(Function)); + expect(q.handleMessage.calls.argsFor(1)[0]).toEqual({ + type: 'ewd-jwt-updateExpiry', + params: { + jwt: 'jwt-token', + application: 'log-micro-service' + } + }, jasmine.any(Function)); + }); + + it('should call microservice client #send 2 times with correct arguments', function () { + q.microServiceRouter(messageObj, handleResponse); + + destinations.forEach(function (destination) { + /*jshint camelcase: false */ + var microService = q.u_services.byDestination[destination]; + /*jshint camelcase: true */ + + expect(microService.client.send).toHaveBeenCalledWith({ + application: microService.application, + type: 'restRequest', + path: '/api/users', + pathTemplate: '/path/template', + method: 'POST', + headers: { + 'x-foo': 'bar' + }, + params: { + type: 'foo' + }, + query: { + bar: 'baz' + }, + body: { + login: 'johndoe', + password: 'secret' + }, + ip: '127.0.0.1', + ips: ['client'], + token: 'updated-jwt-token', + args: {}, + jwt: true + }, jasmine.any(Function)); + }); + }); + + it('should call handleResponse with composite result', function () { + /*jshint camelcase: false */ + var expected = { + type: 'hello', + message: { + results: { + sms_service: { + application: 'sms-micro-service' + }, + log_service: { + application: 'log-micro-service', + } + }, + token: 'updated-jwt-token' + } + }; + /*jshint camelcase: true */ + + destinations.forEach(function (destination) { + /*jshint camelcase: false */ + var microService = q.u_services.byDestination[destination]; + /*jshint camelcase: true */ + + microService.client.send.and.callFake(function (message, cb) { + var responseObj = { + message: { + application: message.application, + token: message.token + }, + type: message.application + '-type', + finished: true + }; + cb(responseObj); + }); + }); + + q.microServiceRouter(messageObj, handleResponse); + + expect(handleResponse).toHaveBeenCalledWith(expected); + }); + }, + whenTokenReturned: function () { beforeEach(function (done) { boot(function (_q, _messageObj, _handleResponse) { diff --git a/spec/shared/destinations/single.js b/spec/shared/destinations/single.js index f3b2755..989538a 100644 --- a/spec/shared/destinations/single.js +++ b/spec/shared/destinations/single.js @@ -85,6 +85,162 @@ module.exports = function (boot) { }); }, + whenTokenHasValueUndefined: function () { + beforeEach(function (done) { + boot(function (_q, _messageObj, _handleResponse) { + q = _q; + messageObj = _messageObj; + handleResponse = _handleResponse; + + done(); + }); + }); + + it('should call handleMessage with correct arguments', function () { + q.microServiceRouter(messageObj, handleResponse); + + expect(q.handleMessage).toHaveBeenCalledTimes(1); + expect(q.handleMessage).toHaveBeenCalledWith({ + type: 'ewd-jwt-updateExpiry', + params: { + jwt: 'token1', + application: 'login-micro-service' + } + }, jasmine.any(Function)); + }); + + it('should call microservice client #send with correct arguments', function () { + /*jshint camelcase: false */ + var microService = q.u_services.byDestination[destination]; + /*jshint camelcase: true */ + + q.microServiceRouter(messageObj, handleResponse); + + expect(microService.client.send).toHaveBeenCalledTimes(1); + expect(microService.client.send).toHaveBeenCalledWith({ + application: 'login-micro-service', + type: 'restRequest', + path: '/api/users', + pathTemplate: '/path/template', + method: 'POST', + headers: { + 'x-foo': 'bar' + }, + params: { + type: 'foo' + }, + query: { + bar: 'baz' + }, + body: { + login: 'johndoe', + password: 'secret' + }, + ip: '127.0.0.1', + ips: ['client'], + token: 'updated-jwt-token', + args: {}, + jwt: true + }, jasmine.any(Function)); + }); + + it('should call handleResponse', function () { + var responseObj = { + foo: 'bar' + }; + + /*jshint camelcase: false */ + var microService = q.u_services.byDestination[destination]; + /*jshint camelcase: true */ + + microService.client.send.and.callFake(function (message, cb) { + cb(responseObj); + }); + + q.microServiceRouter(messageObj, handleResponse); + + expect(handleResponse).toHaveBeenCalledWith(responseObj); + }); + }, + + whenRouteByPassJwtCheckEnabled: function () { + beforeEach(function (done) { + boot(function (_q, _messageObj, _handleResponse) { + q = _q; + messageObj = _messageObj; + handleResponse = _handleResponse; + + done(); + }); + }); + + it('should call handleMessage with correct arguments', function () { + q.microServiceRouter(messageObj, handleResponse); + + expect(q.handleMessage).toHaveBeenCalledTimes(1); + expect(q.handleMessage).toHaveBeenCalledWith({ + type: 'ewd-jwt-updateExpiry', + params: { + jwt: 'jwt-token', + application: 'login-micro-service' + } + }, jasmine.any(Function)); + }); + + it('should call microservice client #send with correct arguments', function () { + /*jshint camelcase: false */ + var microService = q.u_services.byDestination[destination]; + /*jshint camelcase: true */ + + q.microServiceRouter(messageObj, handleResponse); + + expect(microService.client.send).toHaveBeenCalledTimes(1); + expect(microService.client.send).toHaveBeenCalledWith({ + application: 'login-micro-service', + type: 'restRequest', + path: '/api/users', + pathTemplate: '/path/template', + method: 'POST', + headers: { + 'x-foo': 'bar' + }, + params: { + type: 'foo' + }, + query: { + bar: 'baz' + }, + body: { + login: 'johndoe', + password: 'secret' + }, + ip: '127.0.0.1', + ips: ['client'], + token: 'updated-jwt-token', + args: {}, + jwt: true + }, jasmine.any(Function)); + }); + + it('should call handleResponse', function () { + var responseObj = { + foo: 'bar' + }; + + /*jshint camelcase: false */ + var microService = q.u_services.byDestination[destination]; + /*jshint camelcase: true */ + + microService.client.send.and.callFake(function (message, cb) { + cb(responseObj); + }); + + q.microServiceRouter(messageObj, handleResponse); + + expect(handleResponse).toHaveBeenCalledWith(responseObj); + }); + }, + whenTokenReturned: function () { beforeEach(function (done) { boot(function (_q, _messageObj, _handleResponse) { diff --git a/spec/shared/handleMicroServiceSpec.js b/spec/shared/handleMicroServiceSpec.js index 3992c2c..14a69ca 100644 --- a/spec/shared/handleMicroServiceSpec.js +++ b/spec/shared/handleMicroServiceSpec.js @@ -42,6 +42,25 @@ module.exports = function (boot, route, type) { destinationsSpec.whenNoTokenReturned(); }); + describe('And getRestJWT returns undefined string value', function () { + beforeEach(function () { + q.jwt.handlers.getRestJWT.and.returnValue('undefined'); + }); + + destinationsSpec.whenTokenHasValueUndefined(); + }); + + describe('And JWT check being bypassed', function () { + beforeEach(function () { + q.router.hasRoute.and.returnValue(Object.assign({ + bypassJWTCheck: true + }, route)); + q.jwt.handlers.getRestJWT.and.returnValue('jwt-token'); + }); + + destinationsSpec.whenRouteByPassJwtCheckEnabled(); + }); + describe('And getRestJWT returns non empty token', function () { beforeEach(function () { q.jwt.handlers.getRestJWT.and.returnValue('jwt-token');