diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 4b5410e..cce8083 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -1,2 +1,5 @@ +- Remove: dependency on deprecated `domain` node module, improving performance +- Remove: `disableDomainMiddleware` config option +- Remove: `DISABLE_DOMAIN_MIDDLEWARE` environment variable - Remove: operations no longer supported in CB API (aligned with Orion 3.10.1) - Upgrade NodeJS version from 14-slim to 16-slim in Dockerfile diff --git a/README.md b/README.md index a087903..cd78fbc 100644 --- a/README.md +++ b/README.md @@ -496,7 +496,6 @@ Right Attempt | ResponseStatus=200 | Token=860864fb6d1a4c8a8cb7d59d16daaa52 | Or * `config.bypassRoleId`: ID of the role that will be considered to have administrative rights over the proxy (so being transparently proxied without validation). Valid values are Role UUIDs. E.g.: `db50362d5f264c8292bebdb5c5783741`. * `config.dieOnRedirectError`: this flags changes the behavior of the PEP Proxy when an error is received when redirecting a request. If the flag is true, the PEP Proxy process is shut down immediately; if it is false, the behavior is the usual: generate a 501 Code error. * `config.bodyLimit`: Controls the maximum request body size allowed, in bytes. Default is 1 Mb -* `config.disableDomainMiddleware`:Disable domain middleware used for logging. Disabled will reduce overhead, but loses info (transaction, correlator, service, subservice, etc) in logs. Default is false. ### Authentication configuration * `config.authentication.checkHeaders`: when the proxy is working with the access control disabled (just user authentication), indicates whether the `fiware-service` and `fiware-servicepath` headers should be checked for existance and validity (checking: the headers exist, thy are not empty and the user is really part of the service and subservice mentioned in the header). This option is ignored when authorization is enabled, and considered to be `true` (as the headers constitute a mandatory part of the authorization process). Default value is `true`. @@ -554,7 +553,6 @@ Some of the configuration values for the attributes above mentioned can be overr | COMPONENT_NAME | config.componentName | | COMPONENT_PLUGIN | config.middlewares and config.componentName if no COMPONENT_NAME provided | | BODY_LIMIT | config.bodyLimit | -| DISABLE_DOMAIN_MIDDLEWARE | config.disableDomainMiddleware | ### Component configuration A special environment variable, called `COMPONENT_PLUGIN` can be set with one of this values: `orion`, `perseo`, `keypass` and `rest`. This variable can be used to select what component plugin to load in order to determine the action of the incoming requests. This variable also rewrites `config.componentName` configuration paramenter. diff --git a/bin/pepProxy b/bin/pepProxy index 7cf38fa..b74da75 100755 --- a/bin/pepProxy +++ b/bin/pepProxy @@ -148,9 +148,6 @@ function loadConfiguration() { if (process.env.BODY_LIMIT) { config.bodyLimit = process.env.BODY_LIMIT; } - if (process.env.DISABLE_DOMAIN_MIDDLEWARE) { - config.disableDomainMiddleware = process.env.DISABLE_DOMAIN_MIDDLEWARE == 'true'; - } } loadConfiguration(); diff --git a/config.js b/config.js index 9cb7f4a..8c03e48 100644 --- a/config.js +++ b/config.js @@ -181,10 +181,4 @@ config.maxQueuedClients = 1000; */ config.bodyLimit = 1048576; -/** - * Disable domain middleware used for logging. Disabled will reduce overhead, but loses info (transaction, correlator, service, - * subservice, etc) in logs. Default is false. - */ -config.disableDomainMiddleware = false; - module.exports = config; diff --git a/docker/Dockerfile b/docker/Dockerfile index aef22d9..0411bd5 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -81,9 +81,12 @@ RUN apt-get update && \ WORKDIR /opt/fiware-pep-steelskin # hadolint ignore=DL3008,DL3009,DL3015 RUN \ - # Ensure that Git is installed prior to running npm install + # Ensure that Git is installed prior to running npm install. + # curl is added here (again) because the RUN ... above is usually + # removed for local testing, and and curl is required for + # docker health-checks. apt-get update && \ - apt-get install -y git && \ + apt-get install -y git curl && \ echo "INFO: npm install --production..." && \ npm install --production && \ # Remove Git and clean apt cache diff --git a/lib/fiware-pep-steelskin.js b/lib/fiware-pep-steelskin.js index 165ca1e..6be28ed 100644 --- a/lib/fiware-pep-steelskin.js +++ b/lib/fiware-pep-steelskin.js @@ -35,7 +35,7 @@ var http = require('http'), authorization, async = require('async'), logger = require('logops'), - domainMiddleware = require('./middleware/domain').requestDomain(), + loggingMiddleware = require('./middleware/logging').requestLogger('PEP' + (config.componentName || 'Proxy')), cacheUtils = require('./services/cacheUtils'), winston = require('winston'), bodyParser = require('body-parser'), @@ -197,6 +197,7 @@ function createDynamicMiddlewareExecutor(proxyObj) { * @param {Function} next Call to the next error handler in the chain. */ function traceRequest(req, res, next) { + const logger = req.logger; logger.debug('Request for path [%s] from [%s]', req.path, req.get('host')); logger.debug('Headers:\n%j\n', req.headers); @@ -230,9 +231,7 @@ function initializeProxy(proxyObj, callback) { proxyObj.proxy.use(xmlRawBody); proxyObj.proxy.use(rawBody); proxyObj.proxy.use(bodyParser.urlencoded({limit: config.bodyLimit ? config.bodyLimit : '1Mb', extended: true})); - if (!config.disableDomainMiddleware) { - proxyObj.proxy.use(domainMiddleware); - } + proxyObj.proxy.use(loggingMiddleware); if (config.logLevel && config.logLevel.toUpperCase() === 'DEBUG') { proxyObj.proxy.use(traceRequest); @@ -316,9 +315,7 @@ function initializeAdmin(proxyObj, callback) { proxyObj.administration.use(bodyParser.json({limit: config.bodyLimit ? config.bodyLimit : '1Mb'})); proxyObj.administration.use(bodyParser.urlencoded({limit: config.bodyLimit ? config.bodyLimit : '1Mb', extended: true})); - if (!config.disableDomainMiddleware) { - proxyObj.administration.use(domainMiddleware); - } + proxyObj.administration.use(loggingMiddleware); proxyObj.administration.use(handleError); adminMiddleware.loadContextRoutes(proxyObj.administration); @@ -373,21 +370,6 @@ function startProxy(callback) { logger.format = logger.formatters.pipe; - logger.getContext = function domainContext() { - var domainObj = require('domain').active || {}; - - return { - corr: domainObj.corr, - trans: domainObj.trans, - op: domainObj.op, - from: domainObj.from, - srv: domainObj.srv, - subsrv: domainObj.subsrv, - msg: domainObj.msg, - comp: 'PEP' + (config.componentName || 'Proxy') - }; - }; - logger.info('Creating proxy'); setAccessLogger(); diff --git a/lib/middleware/domain.js b/lib/middleware/domain.js deleted file mode 100644 index 8aac263..0000000 --- a/lib/middleware/domain.js +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U - * - * This file is part of fiware-pep-steelskin - * - * fiware-pep-steelskin is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * fiware-pep-steelskin is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with fiware-pep-steelskin. - * If not, seehttp://www.gnu.org/licenses/. - * - * For those usages not covered by the GNU Affero General Public License - * please contact with::[iot_support@tid.es] - */ - -'use strict'; - -var domain = require('domain'), - uuid = require('uuid'), - constants = require('../constants'); - -var logger = require('logops'); - -/** - * Express middleWare that creates a domain per request - * It also generates a unique request id that can be used to track requests in logs. - * - * @return {Function} Express middleWare. - */ -function requestDomain() { - - return function requestDomain(req, res, next) { - var reqDomain = domain.create(); - var cleanDomain, domainErrorHandler, requestHandler; - reqDomain.add(req); - reqDomain.add(res); - reqDomain.path = req.path; - reqDomain.op = req.url; - reqDomain.start = Date.now(); - - if (req.headers && req.headers[constants.ORGANIZATION_HEADER]) { - reqDomain.srv = req.headers[constants.ORGANIZATION_HEADER]; - } - - if (req.headers && req.headers[constants.PATH_HEADER]) { - reqDomain.subsrv = req.headers[constants.PATH_HEADER]; - } - - // x-forwarded-for/forwarded overwrites x-real-ip - if (req.headers[constants.X_REAL_IP_HEADER]) { - reqDomain.from = req.headers[constants.X_REAL_IP_HEADER]; - } - if (req.headers[constants.X_FORWARDED_FOR_HEADER]) { - reqDomain.from = req.headers[constants.X_FORWARDED_FOR_HEADER]; - } - if (req.headers[constants.FORWARDED_HEADER]) { - reqDomain.from = req.headers[constants.FORWARDED_HEADER]; - } - - domainErrorHandler = function(err) { - logger.error(err); - cleanDomain(); - }; - - cleanDomain = function() { - var responseTime = Date.now() - reqDomain.start; - logger.debug('response-time: ' + responseTime + ' statusCode: ' + res.statusCode); - reqDomain.removeListener('error', domainErrorHandler); - reqDomain.remove(req); - reqDomain.remove(res); - delete reqDomain.trans; - delete reqDomain.corr; - delete reqDomain.op; - delete reqDomain.from; - delete reqDomain.srv; - delete reqDomain.subsrv; - delete reqDomain.path; - reqDomain.exit(); - }; - - requestHandler = function() { - reqDomain.trans = req.requestId = uuid.v4(); - var corr = req.get(constants.CORRELATOR_HEADER); - if (corr) { - reqDomain.corr = corr; - } else { - reqDomain.corr = reqDomain.trans; - req.corr = reqDomain.corr; // for propagate in FWD request - } - logger.debug('using correlator: %s', reqDomain.corr); - res.set(constants.CORRELATOR_HEADER, reqDomain.corr); // for response - - next(); - }; - - res.once('finish', cleanDomain); - reqDomain.on('error', domainErrorHandler); - reqDomain.enter(); - reqDomain.run(requestHandler); - - }; - -} - -exports.requestDomain = requestDomain; diff --git a/lib/middleware/logging.js b/lib/middleware/logging.js new file mode 100644 index 0000000..75c35df --- /dev/null +++ b/lib/middleware/logging.js @@ -0,0 +1,84 @@ +/* + * Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U + * + * This file is part of fiware-pep-steelskin + * + * fiware-pep-steelskin is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * fiware-pep-steelskin is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with fiware-pep-steelskin. + * If not, seehttp://www.gnu.org/licenses/. + * + * For those usages not covered by the GNU Affero General Public License + * please contact with::[iot_support@tid.es] + */ + +'use strict'; + +var uuid = require('uuid'), + constants = require('../constants'); + +var logger = require('logops'); + +/** + * Express middleWare that creates a domain per request + * It also generates a unique request id that can be used to track requests in logs. + * + * @return {Function} Express middleWare. + */ + +function requestLogger(componentName) { + return function(req, res, next) { + let contextSrv; + if (req.headers && req.headers[constants.ORGANIZATION_HEADER]) { + contextSrv = req.headers[constants.ORGANIZATION_HEADER]; + } + let contextSubsrv; + if (req.headers && req.headers[constants.PATH_HEADER]) { + contextSubsrv = req.headers[constants.PATH_HEADER]; + } + let contextFrom; + // x-forwarded-for/forwarded overwrites x-real-ip + if (req.headers[constants.X_REAL_IP_HEADER]) { + contextFrom = req.headers[constants.X_REAL_IP_HEADER]; + } + if (req.headers[constants.X_FORWARDED_FOR_HEADER]) { + contextFrom = req.headers[constants.X_FORWARDED_FOR_HEADER]; + } + if (req.headers[constants.FORWARDED_HEADER]) { + contextFrom = req.headers[constants.FORWARDED_HEADER]; + } + let contextTrans = req.requestId = uuid.v4(); + let contextCorr = req.get(constants.CORRELATOR_HEADER); + if (!contextCorr) { + contextCorr = contextTrans; + } + req.corr = contextCorr; // for propagate in FWD request + res.set(constants.CORRELATOR_HEADER, contextCorr); // for response + const contextStart = Date.now(); + req.logger = logger.child({ + corr: contextCorr, + trans: contextTrans, + op: req.url, + from: contextFrom, + srv: contextSrv, + subsrv: contextSubsrv, + comp: componentName + }); + res.once('finish', function() { + const responseTime = Date.now() - contextStart; + req.logger.debug('response-time: ' + responseTime + ' statusCode: ' + res.statusCode); + }); + next(); + }; +} + +exports.requestLogger = requestLogger; diff --git a/lib/middleware/proxy.js b/lib/middleware/proxy.js index 24c76d9..86878da 100644 --- a/lib/middleware/proxy.js +++ b/lib/middleware/proxy.js @@ -26,7 +26,6 @@ var config = require('../../config'), errors = require('../errors'), request = require('request'), - logger = require('logops'), constants = require('../constants'), validationHeaders = [ 'fiware-service', @@ -47,6 +46,7 @@ var config = require('../../config'), * @param {Function} next Call to the next middleware in the chain. */ function extractOrganization(req, res, next) { + const logger = req.logger; if (req.headers[constants.ORGANIZATION_HEADER]) { req.organization = req.headers[constants.ORGANIZATION_HEADER]; req.service = req.headers[constants.ORGANIZATION_HEADER]; @@ -66,6 +66,7 @@ function extractOrganization(req, res, next) { * @param {Function} next Call to the next middleware in the chain. */ function extractUserId(req, res, next) { + const logger = req.logger; if (req.headers[constants.AUTHORIZATION_HEADER]) { req.userId = req.headers[constants.AUTHORIZATION_HEADER]; next(); @@ -84,6 +85,7 @@ function extractUserId(req, res, next) { * @param {Function} next Invokes the next middleware in the chain. */ function generateFRN(req, res, next) { + const logger = req.logger; var frn = config.resourceNamePrefix + config.componentName + ':'; if (req.organization) { @@ -124,6 +126,7 @@ function generateFRN(req, res, next) { * @param {Function} next Invokes the next middleware in the chain. */ function sendRequest(req, res, next) { + const logger = req.logger; var options = { uri: 'http://' + config.resource.original.host + ':' + config.resource.original.port + req.path, qs: req.query, diff --git a/lib/plugins/keypassPlugin.js b/lib/plugins/keypassPlugin.js index 4a17a97..3842246 100644 --- a/lib/plugins/keypassPlugin.js +++ b/lib/plugins/keypassPlugin.js @@ -23,8 +23,7 @@ 'use strict'; -var logger = require('logops'), - errors = require('../errors'); +var errors = require('../errors'); /** * Keypass operation identification table. Each row of the table contains an operation with three fields: @@ -47,6 +46,7 @@ var keypassOperations = [ * @param {Object} res Outgoing response. */ function extractAction(req, res, callback) { + const logger = req.logger; logger.debug('Extracting action from URL [%s] and method [%s]', req.url, req.method); if (req.path.match(/\/pdp\/v3.*/) && req.method === 'POST') { diff --git a/lib/plugins/orionPlugin.js b/lib/plugins/orionPlugin.js index 95add11..1ec0afd 100644 --- a/lib/plugins/orionPlugin.js +++ b/lib/plugins/orionPlugin.js @@ -24,7 +24,6 @@ 'use strict'; var sax = require('sax'), - logger = require('logops'), errors = require('../errors'), identificationTable = require('./orionUrls'); @@ -32,10 +31,11 @@ var sax = require('sax'), /** * Translates the updateAction value to the appropriate action name for the Access Control. * + * @param {Object} logger Instance of logops.Logger * @param {String} originalAction String with the action name. * @return {String} The string representation of the action name for the Access Control. */ -function translateAction(originalAction) { +function translateAction(logger, originalAction) { var action; switch (originalAction.toLowerCase()) { @@ -75,9 +75,10 @@ function translateAction(originalAction) { /** * Extract the action from an XML body. * + * @param {Object} logger Instance of logops.Logger * @param {String} body Raw string payload. */ -function inspectBodyXML(body, callback) { +function inspectBodyXML(logger, body, callback) { var parser = sax.parser(true), readingAction = false, errorRaised = false, @@ -115,7 +116,7 @@ function inspectBodyXML(body, callback) { parser.onend = function() { if (action) { - var translatedAction = translateAction(action.trim()); + var translatedAction = translateAction(logger, action.trim()); if (translatedAction) { callback(null, translatedAction); @@ -143,12 +144,13 @@ function inspectBodyXML(body, callback) { /** * Extract the action from a JSON body. * + * @param {Object} logger Instance of logops.Logger * @param {Object} body Javascript Object with the parsed payload. * @param {Object} field Field that will be used to extract the type. */ -function inspectBodyJSON(body, field, callback) { +function inspectBodyJSON(logger, body, field, callback) { if (body && body[field]) { - var translatedAction = translateAction(body[field]); + var translatedAction = translateAction(logger, body[field]); if (translatedAction) { callback(null, translatedAction); @@ -169,6 +171,7 @@ function inspectBodyJSON(body, field, callback) { * @param {Object} res Outgoing response. */ function inspectBodyV1(req, res, callback) { + const logger = req.logger; var actionHandler = function actionHandler(error, action) { req.action = action; callback(error, req, res); @@ -176,10 +179,10 @@ function inspectBodyV1(req, res, callback) { if (req.is('*/json')) { logger.debug('Inspecting JSON body to discover action: \n%j\n\n', req.body); - inspectBodyJSON(req.body, 'updateAction', actionHandler); + inspectBodyJSON(logger, req.body, 'updateAction', actionHandler); } else if (req.is('*/xml')) { logger.debug('Inspecting XML body to discover action: \n%s\n\n', req.rawBody); - inspectBodyXML(req.rawBody, actionHandler); + inspectBodyXML(logger, req.rawBody, actionHandler); } else { // TODO: raise error if the type is not recognized. logger.error('[ORION-PLUGIN-004] Unknown content type: %s', req.headers['content-type']); @@ -189,6 +192,7 @@ function inspectBodyV1(req, res, callback) { } function inspectBodyV2(req, res, callback) { + const logger = req.logger; var actionHandler = function actionHandler(error, action) { req.action = action; callback(error, req, res); @@ -196,7 +200,7 @@ function inspectBodyV2(req, res, callback) { if (req.is('*/json')) { logger.debug('Inspecting JSON body to discover action: \n%j\n\n', req.body); - inspectBodyJSON(req.body, 'actionType', actionHandler); + inspectBodyJSON(logger, req.body, 'actionType', actionHandler); } else { // TODO: raise error if the type is not recognized. @@ -213,7 +217,7 @@ function inspectBodyV2(req, res, callback) { * @param {Object} res Outgoing response. */ function inspectUrl(req, res, callback) { - + const logger = req.logger; var match = false; logger.debug('Extracting action from the URL "%s"', req.url); diff --git a/lib/plugins/urlTablePlugin.js b/lib/plugins/urlTablePlugin.js index d500cf2..fd58468 100644 --- a/lib/plugins/urlTablePlugin.js +++ b/lib/plugins/urlTablePlugin.js @@ -23,8 +23,7 @@ 'use strict'; -var logger = require('logops'), - errors = require('../errors'); +var errors = require('../errors'); /** * Generates a middleware that extracts the action from the request's URL and method using the table passed as a @@ -41,6 +40,7 @@ var logger = require('logops'), */ function extractAction(operations) { return function(req, res, callback) { + const logger = req.logger; logger.debug('Extracting action from URL [%s] and method [%s]', req.url, req.method); for (var i = 0; i < operations.length; i++) { diff --git a/lib/services/cacheUtils.js b/lib/services/cacheUtils.js index be8dbde..752c8bd 100644 --- a/lib/services/cacheUtils.js +++ b/lib/services/cacheUtils.js @@ -24,7 +24,6 @@ 'use strict'; var config = require('../../config'), - domainModule = require('domain'), logger = require('logops'), NodeCache = require('node-cache'), EventEmitter = require('events').EventEmitter, @@ -95,16 +94,13 @@ function cleanCache() { } } -function createDomainEnabledCacheHandler(domain, processValueFn, cache, cacheType, cacheKey, callback) { +function createLoggingEnabledCacheHandler(logger, processValueFn, cache, cacheType, cacheKey, callback) { return function(error, value) { if (error) { - logger.debug('Error found creating cache domain handler'); + logger.debug('Error found creating cache logging handler'); callback(error); } else { var currentValue = cache.data[cacheType].get(cacheKey) || value; - if (!config.disableDomainMiddleware) { - domain.enter(); - } logger.debug('Value found for cache type [%s] key [%s]: %j', cacheType, cacheKey, value); logger.debug('Processing with value: %s', JSON.stringify(cache.data[cacheType].get(cacheKey))); @@ -119,12 +115,13 @@ function createDomainEnabledCacheHandler(domain, processValueFn, cache, cacheTyp * to the cache while the value is being updated, it is put on hold in an event channel, and awaken when the result * of the value retrieval has arrived. * + * @param {Object} logger Instance of logops.Logger * @param {String} cacheType Name of the cache (user, roles or subserviceId). * @param {String} cacheKey Key of the item to retrieve. * @param {Function} retrieveRequestFn Function to call to refresh a particular value. * @param {Function} processValueFn Function to call when the value has been retrieved. */ -function cacheAndHold(cacheType, cacheKey, retrieveRequestFn, processValueFn, callback) { +function cacheAndHold(logger, cacheType, cacheKey, retrieveRequestFn, processValueFn, callback) { var cachedValue = cache.data[cacheType].get(cacheKey); function getCacheEventId() { @@ -139,8 +136,8 @@ function cacheAndHold(cacheType, cacheKey, retrieveRequestFn, processValueFn, ca logger.debug('Cache type [%s] updating for key [%s]. Waiting.', cacheType, cacheKey); cache.channel.on(getCacheEventId(), - createDomainEnabledCacheHandler( - domainModule.active, + createLoggingEnabledCacheHandler( + logger, processValueFn, cache, cacheType, @@ -151,8 +148,8 @@ function cacheAndHold(cacheType, cacheKey, retrieveRequestFn, processValueFn, ca logger.debug('Value [%s] not found in cache. Retrieving new value.', cacheKey); cache.updating[cacheType][cacheKey] = true; cache.channel.removeAllListeners(cacheType); - cache.channel.on(getCacheEventId(), createDomainEnabledCacheHandler( - domainModule.active, + cache.channel.on(getCacheEventId(), createLoggingEnabledCacheHandler( + logger, processValueFn, cache, cacheType, diff --git a/lib/services/idmAuth.js b/lib/services/idmAuth.js index 25e776a..d3bcb7f 100644 --- a/lib/services/idmAuth.js +++ b/lib/services/idmAuth.js @@ -26,7 +26,6 @@ var request = require('request'), config = require('../../config'), errors = require('../errors'), - logger = require('logops'), async = require('async'), apply = async.apply, constants = require('../constants'); @@ -55,6 +54,7 @@ function getRolesFromResponse(rawBody, organization) { } function extractRoles(req, res, next) { + const logger = req.logger; var options = { url: config.authentication.options.protocol + '://' + config.authentication.options.host + ':' + config.authentication.options.port + constants.GET_ROLES_PATH, diff --git a/lib/services/keystoneAuth.js b/lib/services/keystoneAuth.js index 45935c9..a695d67 100644 --- a/lib/services/keystoneAuth.js +++ b/lib/services/keystoneAuth.js @@ -28,10 +28,8 @@ var request = require('request'), errors = require('../errors'), async = require('async'), apply = async.apply, - logger = require('logops'), constants = require('../constants'), EventEmitter = require('events').EventEmitter, - domainModule = require('domain'), waitingRequests = new EventEmitter(), cacheUtils = require('./cacheUtils'), authenticating = false, @@ -64,13 +62,14 @@ function getKeystoneError(error) { * domain scope, and those with project scope otherwise. As a side effect, this function caches the results, so the * next few calls to Keystone can be skipped. * + * @param {Object} logger Instance of logops.Logger * @param {String} rawBody String representation of the JSON response. * @param {String} service UUID of the service. * @param {String} subservice Name of the subservice. * @param {String} cacheKey Key of the cache where the value will be stored. * @return {Array} A list of strings containing the role IDs. */ -function getRolesFromResponse(rawBody, service, subservice, cacheKey) { +function getRolesFromResponse(logger, rawBody, service, subservice, cacheKey) { /* jshint camelcase: false */ var body = JSON.parse(rawBody), @@ -111,6 +110,7 @@ function getRolesFromResponse(rawBody, service, subservice, cacheKey) { * @param {Object} req Request the proxy is processing. */ function retrieveRoles(req, callback) { + const logger = req.logger; var userId = req.userId, subserviceId = req.subserviceId, cacheKey = userId + ':' + subserviceId; @@ -152,7 +152,7 @@ function retrieveRoles(req, callback) { innerCb(new errors.PepProxyAuthenticationRejected(401)); } else if (response.statusCode === 200) { logger.debug('Roles response from Keystone: \n%j\n\n', body); - innerCb(null, getRolesFromResponse(response.body, req.serviceId, req.subserviceId, cacheKey)); + innerCb(null, getRolesFromResponse(logger, response.body, req.serviceId, req.subserviceId, cacheKey)); } else { currentToken = null; // Force ask new PEP token next retry attempt logger.error('Role extraction from Keystone rejected with code %s', response.statusCode); @@ -162,10 +162,11 @@ function retrieveRoles(req, callback) { }); } - cacheUtils.cacheAndHold('roles', cacheKey, retrieveRequest, processValue, callback); + cacheUtils.cacheAndHold(logger, 'roles', cacheKey, retrieveRequest, processValue, callback); } function validateUserHeaders(req, callback) { + const logger = req.logger; if (req.domainName && req.headers['fiware-service'] === req.domainName) { callback(); } else { @@ -182,6 +183,7 @@ function validateUserHeaders(req, callback) { * @param {Object} req Request the proxy is processing. */ function retrieveUser(req, callback) { + const logger = req.logger; var userToken = req.headers[constants.AUTHORIZATION_HEADER]; function processValue(cachedValue, innerCb) { @@ -284,7 +286,7 @@ function retrieveUser(req, callback) { }); } - cacheUtils.cacheAndHold('user', userToken, retrieveRequest, processValue, callback); + cacheUtils.cacheAndHold(logger, 'user', userToken, retrieveRequest, processValue, callback); } /** @@ -295,6 +297,7 @@ function retrieveUser(req, callback) { */ function retrieveSubserviceId(req, callback) { /* jshint camelcase: false */ + const logger = req.logger; var domainId = req.serviceId, subserviceName = req.headers[constants.PATH_HEADER], cacheKey = domainId + subserviceName; @@ -386,7 +389,7 @@ function retrieveSubserviceId(req, callback) { req.subserviceId = '/'; callback(null, '/'); } else { - cacheUtils.cacheAndHold('subservice', cacheKey, retrieveRequest, processValue, callback); + cacheUtils.cacheAndHold(logger, 'subservice', cacheKey, retrieveRequest, processValue, callback); } } @@ -427,23 +430,15 @@ function extractRoles(req, res, next) { }); } -function domainContinuator(domain, callback) { - return function() { - if (!config.disableDomainMiddleware) { - domain.enter(); - } - callback(); - }; -} - /** * Express middleware for authentication of the PEP Proxy against Keystone. The username and password of the PEP * proxy are taken from the config.js file; the domain (service) and project name (subservice) are taken from the * fiware-service and fiware-servicepath headers (extracted in previous middlewares). */ function authenticate(req, res, next) { + const logger = req.logger; if (authenticating) { - waitingRequests.on('token', domainContinuator(domainModule.active, next)); + waitingRequests.on('token', next); } else if (currentToken === null) { var options = { url: config.authentication.options.protocol + '://' + config.authentication.options.host + ':' + @@ -525,6 +520,7 @@ function authenticate(req, res, next) { * file). */ function authenticationProcess(req, res, next) { + const logger = req.logger; var retries = config.authentication.retries || 3, attempts = 0; diff --git a/lib/services/validation.js b/lib/services/validation.js index e6d1fba..14aac23 100644 --- a/lib/services/validation.js +++ b/lib/services/validation.js @@ -42,11 +42,12 @@ var request = require('request'), * Creates the XACML XML payload with the received parameters. To do so, it makes use of the mustache templates loaded * in the loadTemplates() method. * + * @param {Object} logger Instance of logops.Logger * @param {String} roles List of the roles of the user. * @param {String} organization Name of the organization with the frn format. * @param {String} action Name of the action the request is trying to execute. g */ -function createAccessRequest(roles, organization, action, callback) { +function createAccessRequest(logger, roles, organization, action, callback) { var parameters = { organization: organization, action: action, @@ -118,9 +119,10 @@ function parseResponse(body, callback) { /** * Sends the validation request to the Access Control with the XML payload received. * + * @param {Object} logger Instance of logops.Logger * @param {String} accessPayload XACML payload in string format. */ -function sendAccessRequest(headers, accessPayload, callback) { +function sendAccessRequest(logger, headers, accessPayload, callback) { var options = { uri: config.access.protocol + '://' + config.access.host + ':' + config.access.port + config.access.path, method: 'POST', @@ -159,7 +161,7 @@ function sendAccessRequest(headers, accessPayload, callback) { }); } -function validationRequest(roles, frn, action, headers, callback) { +function validationRequest(logger, roles, frn, action, headers, callback) { var cacheKey = frn + '#' + action + '#' + roles.join('-'); function processValue(cachedValue, innerCallback) { @@ -168,8 +170,8 @@ function validationRequest(roles, frn, action, headers, callback) { function retrieveRequest(innerCallback) { async.waterfall([ - apply(createAccessRequest, roles, frn, action), - apply(sendAccessRequest, headers) + apply(createAccessRequest, logger, roles, frn, action), + apply(sendAccessRequest, logger, headers) ], function(error, decision) { cacheUtils.get().updating.validation[cacheKey] = false; @@ -183,7 +185,7 @@ function validationRequest(roles, frn, action, headers, callback) { }); } - cacheUtils.cacheAndHold('validation', cacheKey, retrieveRequest, processValue, callback); + cacheUtils.cacheAndHold(logger, 'validation', cacheKey, retrieveRequest, processValue, callback); } /** @@ -195,6 +197,7 @@ function validationRequest(roles, frn, action, headers, callback) { * @param {Function} next Call to the next middleware in the chain. */ function validationProcess(req, res, next) { + const logger = req.logger; function handleValidation(error, value) { if (error) { next(error); @@ -216,7 +219,7 @@ function validationProcess(req, res, next) { if (req.corr) { req.headers[constants.CORRELATOR_HEADER] = req.corr; } - validationRequest(req.roles, req.frn, req.action, req.headers, handleValidation); + validationRequest(logger, req.roles, req.frn, req.action, req.headers, handleValidation); } } diff --git a/test/unit/simultaneous_requests-test.js b/test/unit/simultaneous_requests-test.js index 02fe858..9bfdb55 100644 --- a/test/unit/simultaneous_requests-test.js +++ b/test/unit/simultaneous_requests-test.js @@ -23,8 +23,7 @@ 'use strict'; -var domain = require('domain'), - serverMocks = require('../tools/serverMocks'), +var serverMocks = require('../tools/serverMocks'), proxyLib = require('../../lib/fiware-pep-steelskin'), orionPlugin = require('../../lib/plugins/orionPlugin'), cacheUtils = require('../../lib/services/cacheUtils'), @@ -64,7 +63,7 @@ describe('Simultaneous requests', function() { proxyLib.start(function(error, proxyObj) { var testExtraction = function(req, res, callback) { - correlatorIds.push(domain.active.corr); + correlatorIds.push(req.corr); callback(null, req, res); }; @@ -119,7 +118,7 @@ describe('Simultaneous requests', function() { sendRequestBackup = proxyPlugin.sendRequest; proxyPlugin.sendRequest = function(req, res, next) { - correlatorIdsPost.push(domain.active.corr); + correlatorIdsPost.push(req.corr); sendRequestBackup(req, res, next); }; @@ -191,7 +190,7 @@ describe('Simultaneous requests', function() { sendRequestBackup = proxyPlugin.sendRequest; proxyPlugin.sendRequest = function(req, res, next) { - correlatorIdsPost.push(domain.active.corr); + correlatorIdsPost.push(req.corr); sendRequestBackup(req, res, next); };