openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
134 lines (104 loc) • 4.95 kB
JavaScript
;
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