UNPKG

openhim-core

Version:

The OpenHIM core application that provides logging and routing of http requests

134 lines (104 loc) 4.95 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.authenticate = authenticate; var _crypto = require('crypto'); var _crypto2 = _interopRequireDefault(_crypto); var _winston = require('winston'); var _winston2 = _interopRequireDefault(_winston); var _atnaAudit = require('atna-audit'); var _atnaAudit2 = _interopRequireDefault(_atnaAudit); var _os = require('os'); var _os2 = _interopRequireDefault(_os); var _users = require('../model/users'); var _config = require('../config'); var _auditing = require('../auditing'); var auditing = _interopRequireWildcard(_auditing); var _utils = require('../utils'); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } _config.config.api = _config.config.get('api'); _config.config.auditing = _config.config.get('auditing'); const himSourceID = _config.config.auditing.auditEvents.auditSourceID; // will NOT audit any successful logins on the following paths (specified as regex patterns) // only 'noisy' endpoints should be included, such as heartbeats or endpoints that get polled // // /transactions is treated as a special case - see below const auditingExemptPaths = [/\/tasks/, /\/events.*/, /\/metrics.*/, /\/mediators\/.*\/heartbeat/, /\/audits/, /\/logs/]; const isUndefOrEmpty = string => string == null || string === ''; async function authenticate(ctx, next) { const { header } = ctx.request; const email = header['auth-username']; const authTS = header['auth-ts']; const authSalt = header['auth-salt']; const authToken = header['auth-token']; function auditAuthFailure() { let audit = _atnaAudit2.default.construct.userLoginAudit(_atnaAudit2.default.constants.OUTCOME_SERIOUS_FAILURE, himSourceID, _os2.default.hostname(), email); audit = _atnaAudit2.default.construct.wrapInSyslog(audit); return auditing.sendAuditEvent(audit, () => _winston2.default.debug('Processed internal audit')); } // if any of the required headers aren't present if (isUndefOrEmpty(email) || isUndefOrEmpty(authTS) || isUndefOrEmpty(authSalt) || isUndefOrEmpty(authToken)) { _winston2.default.info(`API request made by ${email} from ${ctx.request.host} is missing required API authentication headers, denying access`); ctx.status = 401; auditAuthFailure(); return; } // check if request is recent const requestDate = new Date(Date.parse(authTS)); const authWindowSeconds = _config.config.api.authWindowSeconds != null ? _config.config.api.authWindowSeconds : 10; const to = new Date(); to.setSeconds(to.getSeconds() + authWindowSeconds); const from = new Date(); from.setSeconds(from.getSeconds() - authWindowSeconds); if (requestDate < from || requestDate > to) { // request expired _winston2.default.info(`API request made by ${email} from ${ctx.request.host} has expired, denying access`); ctx.status = 401; auditAuthFailure(); return; } const user = await _users.UserModelAPI.findOne({ email: (0, _utils.caseInsensitiveRegex)(email) }); ctx.authenticated = user; if (!user) { // not authenticated - user not found _winston2.default.info(`No user exists for ${email}, denying access to API, request originated from ${ctx.request.host}`); ctx.status = 401; auditAuthFailure(); return; } const hash = _crypto2.default.createHash('sha512'); hash.update(user.passwordHash); hash.update(authSalt); hash.update(authTS); if (authToken === hash.digest('hex')) { // authenticated if (ctx.path === '/transactions') { if (!ctx.query.filterRepresentation || ctx.query.filterRepresentation !== 'full') { // exempt from auditing success await next(); return; } } else { for (const pathTest of Array.from(auditingExemptPaths)) { if (pathTest.test(ctx.path)) { // exempt from auditing success await next(); return; } } } // send audit let audit = _atnaAudit2.default.construct.userLoginAudit(_atnaAudit2.default.constants.OUTCOME_SUCCESS, himSourceID, _os2.default.hostname(), email, user.groups.join(','), user.groups.join(',')); audit = _atnaAudit2.default.construct.wrapInSyslog(audit); auditing.sendAuditEvent(audit, () => _winston2.default.debug('Processed internal audit')); await next(); } else { // not authenticated - token mismatch _winston2.default.info(`API token did not match expected value, denying access to API, the request was made by ${email} from ${ctx.request.host}`); ctx.status = 401; return auditAuthFailure(); } } //# sourceMappingURL=authentication.js.map