X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fcommon.js;h=40a9f554f9ee4f687fe63402e3fed0908162408b;hb=aee1a77b22217893a609e581de7e4d3521e2c54e;hp=ff68e9d7ac7b5ea5258ce1aab70cb99f991d037f;hpb=ca35f167826c0732571da5f35e3c25881d138b79;p=squeep-api-dingus diff --git a/lib/common.js b/lib/common.js index ff68e9d..40a9f55 100644 --- a/lib/common.js +++ b/lib/common.js @@ -11,8 +11,15 @@ const uuid = require('uuid'); const Enum = require('./enum'); /** - * Return a function which combines a part of the filename with a scope, for use in logging. - * @param {string} filename + * @callback ScopeFn + * @param {String} scope + * @returns {String} + */ +/** + * Return a function which prefixes a provided scope with the most- + * relevant part of the filename, for use in logging. + * @param {String} filename + * @returns {ScopeFn} */ const fileScope = (filename) => { let fScope = path.basename(filename, '.js'); @@ -24,9 +31,10 @@ const fileScope = (filename) => { /** * Simple ETag from data. - * @param {string} filePath - * @param {object} fileStat - * @param {*} fileData + * @param {String} filePath (currently unused) + * @param {fs.Stats} fileStat + * @param {crypto.BinaryLike} fileData content + * @returns {String} */ const generateETag = (_filePath, fileStat, fileData) => { const hash = crypto.createHash('sha256'); @@ -39,16 +47,19 @@ const generateETag = (_filePath, fileStat, fileData) => { }; /** - * @param {object} obj - * @param {string} prop - * @param {*} def + * Access property with default. + * @param {Object} obj + * @param {String} prop + * @param {*} def default value if prop does not exist for obj + * @return {*} */ const get = (obj, prop, def) => obj && prop && (prop in obj) ? obj[prop] : def; /** * @param {http.ClientRequest} req * @param {http.ServerResponse} res - * @param {object} ctx + * @param {Object} ctx + * @deprecated after v1.2.5 (integrated into logger module) */ const handlerLogData = (req, res, ctx) => ({ req: requestLogData(req), @@ -57,10 +68,12 @@ const handlerLogData = (req, res, ctx) => ({ }); /** - * + * Determine whether a client has already requested a resource, + * based on If-Modified-Since and If-None-Match headers. * @param {http.ClientRequest} req * @param {Number} modifiedTimeMs - * @param {string} eTag + * @param {String} eTag + * @returns {Boolean} */ const isClientCached = (req, modifiedTimeMs, eTag) => { let clientCached = false; @@ -93,6 +106,7 @@ const isClientCached = (req, modifiedTimeMs, eTag) => { * Expects only one-level deep, is not recursive! * @param {Object} origEnum * @param {Object} additionalEnum + * @returns {Object} */ const mergeEnum = (origEnum, additionalEnum) => { for (const e of Object.keys(additionalEnum)) { @@ -118,9 +132,9 @@ const httpStatusCodeClass = (statusCode) => Math.floor(statusCode / 100); const _isObject = (obj) => obj && typeof obj === 'object'; const _isArray = (obj) => Array.isArray(obj); /** - * Return a new object with all objects combined. - * @param {...any} objects - * @returns + * Return a new object with all objects combined, later properties taking precedence. + * @param {...Object} objects + * @returns {Object} */ const mergeDeep = (...objects) => { return objects.reduce((acc, obj) => { @@ -144,7 +158,8 @@ const mergeDeep = (...objects) => { /** * Return a new object with selected props. * @param {Object} obj - * @param {string[]} props + * @param {String[]} props + * @returns {Object} */ const pick = (obj, props) => { const picked = {}; @@ -158,22 +173,58 @@ const pick = (obj, props) => { /** * Return a subset of a request object, suitable for logging. + * Obscures sensitive header values. * @param {http.ClientRequest} req + * @deprecated after v1.2.5 (integrated into logger module) */ const requestLogData = (req) => { - return pick(req, [ + const data = pick(req, [ 'method', 'url', 'httpVersion', 'headers', 'trailers', ]); + scrubHeaderObject(data); + return data; }; +/** + * Remove sensitive header data. + * @param {Object} data + * @param {Object} data.headers + * @deprecated after v1.2.5 (integrated into logger module) + */ +const scrubHeaderObject = (data) => { + if (data && data.headers && 'authorization' in data.headers) { + data.headers = Object.assign({}, data.headers, { + authorization: obscureAuthorizationHeader(data.headers['authorization']), + }); + } +} + + +/** + * Hide sensitive part of an Authorization header. + * @param {String} authHeader + * @returns {String} + * @deprecated after v1.2.5 (integrated into logger module) + */ +const obscureAuthorizationHeader = (authHeader) => { + if (!authHeader) { + return authHeader; + } + const space = authHeader.indexOf(' '); + // This blurs entire string if no space found, because -1. + return authHeader.slice(0, space + 1) + '*'.repeat(authHeader.length - (space + 1)); +} + + /** * Return a subset of a response object, suitable for logging. * @param {http.ServerResponse} res + * @deprecated after v1.2.5 (integrated into logger module) */ const responseLogData = (res) => { const response = pick(res, [ @@ -186,7 +237,7 @@ const responseLogData = (res) => { /** - * Store updates to defaultOptions, but no new properties. + * Store all properties in defaultOptions on target from either options or defaultOptions. * @param {Object} target * @param {Object} defaultOptions * @param {Object} options @@ -196,10 +247,10 @@ const setOptions = (target, defaultOptions, options) => { }; /** - * Return a list of source split at first delimiter. - * @param {string} src - * @param {string} delimiter - * @param {string} fill trailing stand-in if no delimiter in src + * Return a two-item list of src, split at first delimiter encountered. + * @param {String} src + * @param {String} delimiter + * @param {String} fill trailing stand-in if no delimiter in src */ const splitFirst = (src, delimiter, fill) => { const idx = src.indexOf(delimiter); @@ -211,14 +262,21 @@ const splitFirst = (src, delimiter, fill) => { }; /** - * Generate a new request identifier. + * Generate a new request identifier, a time/host-based uuid. * @returns {String} */ const requestId = () => { return uuid.v1(); }; +/** + * Do nothing. + */ const nop = () => { /**/ }; + +/** + * A logger object which does nothing. + */ const nullLogger = { error: nop, warn: nop, @@ -228,8 +286,9 @@ const nullLogger = { }; /** - * Populates any absent logger levels. + * Populates any absent logger level functions on a logger object. * @param {Object} logger + * @returns {Object} */ const ensureLoggerLevels = (logger = {}) => { for (const level in nullLogger) { @@ -243,6 +302,7 @@ const ensureLoggerLevels = (logger = {}) => { /** * Merges folded header lines * @param {String[]} lines + * @returns {String} */ const unfoldHeaderLines = (lines) => { const foldedLineRE = /^(\t| +)(.*)$/; @@ -272,10 +332,12 @@ module.exports = { mergeEnum, nop, nullLogger, + obscureAuthorizationHeader, pick, requestId, requestLogData, responseLogData, + scrubHeaderObject, setOptions, splitFirst, unfoldHeaderLines,