UNPKG

openhim-core

Version:

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

220 lines (169 loc) 7.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getTrustedClientCerts = getTrustedClientCerts; exports.getServerOptions = getServerOptions; exports.koaMiddleware = koaMiddleware; var _winston = _interopRequireDefault(require("winston")); var _pem = _interopRequireDefault(require("pem")); var _latest = require("ssl-root-cas/latest"); var _clients = require("../model/clients"); var _keystore = require("../model/keystore"); var utils = _interopRequireWildcard(require("../utils")); var _config = require("../config"); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } _config.config.tlsClientLookup = _config.config.get('tlsClientLookup'); /* * Fetches the trusted certificates, callsback with an array of certs. */ function getTrustedClientCerts(done) { return _keystore.KeystoreModel.findOne((err, keystore) => { if (err) { done(err, null); } const certs = _latest.rootCas; if (keystore.ca != null) { for (const cert of Array.from(keystore.ca)) { certs.push(cert.data); } } return done(null, certs); }); } /* * Gets server options object for use with a HTTPS node server * * mutualTLS is a boolean, when true mutual TLS authentication is enabled */ function getServerOptions(mutualTLS, done) { return _keystore.KeystoreModel.findOne((err, keystore) => { let options; if (err) { _winston.default.error(`Could not fetch keystore: ${err}`); return done(err); } if (keystore != null) { options = { key: keystore.key, cert: keystore.cert.data }; // if key has password add it to the options if (keystore.passphrase) { options.passphrase = keystore.passphrase; } } else { return done(new Error('Keystore does not exist')); } if (mutualTLS) { return exports.getTrustedClientCerts((err, certs) => { if (err) { _winston.default.error(`Could not fetch trusted certificates: ${err}`); return done(err, null); } options.ca = certs; options.requestCert = true; options.rejectUnauthorized = false; // we test authority ourselves return done(null, options); }); } else { return done(null, options); } }); } /* * A promise returning function that lookups up a client via the given cert fingerprint, * if not found and config.tlsClientLookup.type is 'in-chain' then the function will * recursively walk up the certificate chain and look for clients with certificates * higher in the chain. */ function clientLookup(fingerprint, subjectCN, issuerCN) { return new Promise((resolve, reject) => { _winston.default.debug(`Looking up client linked to cert with fingerprint ${fingerprint} with subject ${subjectCN} and issuer ${issuerCN}`); _clients.ClientModel.findOne({ certFingerprint: fingerprint }, (err, result) => { if (err) { return reject(err); } if (result != null) { // found a match return resolve(result); } if (subjectCN === issuerCN) { // top certificate reached return resolve(null); } if (_config.config.tlsClientLookup.type === 'in-chain') { // walk further up and cert chain and check return utils.getKeystore((err, keystore) => { if (err) { return reject(err); } let missedMatches = 0; // find the isser cert if (keystore.ca == null || keystore.ca.length < 1) { _winston.default.info(`Issuer cn=${issuerCN} for cn=${subjectCN} not found in keystore.`); return resolve(null); } else { return Array.from(keystore.ca).map(cert => (cert => _pem.default.readCertificateInfo(cert.data, (err, info) => { if (err) { return reject(err); } if (info.commonName === issuerCN) { const promise = clientLookup(cert.fingerprint, info.commonName, info.issuer.commonName); promise.then(resolve); } else { missedMatches++; } if (missedMatches === keystore.ca.length) { _winston.default.info(`Issuer cn=${issuerCN} for cn=${subjectCN} not found in keystore.`); return resolve(null); } }))(cert)); } }); } else { if (_config.config.tlsClientLookup.type !== 'strict') { _winston.default.warn('tlsClientLookup.type config option does not contain a known value, defaulting to \'strict\'. Available options are \'strict\' and \'in-chain\'.'); } return resolve(null); } }); }); } if (process.env.NODE_ENV === 'test') { exports.clientLookup = clientLookup; } /* * Koa middleware for mutual TLS authentication */ async function koaMiddleware(ctx, next) { if (ctx.authenticated != null) { await next(); } else if (ctx.req.client.authorized === true) { const cert = ctx.req.connection.getPeerCertificate(true); _winston.default.info(`${cert.subject.CN} is authenticated via TLS.`); // lookup client by cert fingerprint and set them as the authenticated user try { ctx.authenticated = await clientLookup(cert.fingerprint, cert.subject.CN, cert.issuer.CN); } catch (err) { _winston.default.error(`Failed to lookup client: ${err}`); } if (ctx.authenticated != null) { if (ctx.authenticated.clientID != null) { ctx.header['X-OpenHIM-ClientID'] = ctx.authenticated.clientID; } ctx.authenticationType = 'tls'; await next(); } else { ctx.authenticated = null; _winston.default.info(`Certificate Authentication Failed: the certificate's fingerprint ${cert.fingerprint} did not match any client's certFingerprint attribute, trying next auth mechanism if any...`); await next(); } } else { ctx.authenticated = null; _winston.default.info(`Could NOT authenticate via TLS: ${ctx.req.client.authorizationError}, trying next auth mechanism if any...`); await next(); } } //# sourceMappingURL=tlsAuthentication.js.map