UNPKG

openhim-core

Version:

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

540 lines (539 loc) 20.1 kB
<!doctype html> <html lang="en"> <head> <title>Code coverage report for src/middleware/tlsAuthentication.coffee</title> <meta charset="utf-8" /> <link rel="stylesheet" href="../../prettify.css" /> <link rel="stylesheet" href="../../base.css" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <style type='text/css'> .coverage-summary .sorter { background-image: url(../../sort-arrow-sprite.png); } </style> </head> <body> <div class='wrapper'> <div class='pad1'> <h1> <a href="../../index.html">All files</a> / <a href="index.html">src/middleware</a> tlsAuthentication.coffee </h1> <div class='clearfix'> <div class='fl pad1y space-right2'> <span class="strong">29.58% </span> <span class="quiet">Statements</span> <span class='fraction'>21/71</span> </div> <div class='fl pad1y space-right2'> <span class="strong">0% </span> <span class="quiet">Branches</span> <span class='fraction'>0/8</span> </div> <div class='fl pad1y space-right2'> <span class="strong">0% </span> <span class="quiet">Functions</span> <span class='fraction'>0/12</span> </div> <div class='fl pad1y space-right2'> <span class="strong">30% </span> <span class="quiet">Lines</span> <span class='fraction'>21/70</span> </div> </div> </div> <div class='status-line low'></div> <pre><table class="coverage"> <tr><td class="line-count quiet">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159</td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">1x</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-no">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">fs = require "fs" Q = require "q" Client = require("../model/clients").Client Keystore = require("../model/keystore").Keystore logger = require "winston" utils = require '../utils' pem = require 'pem' rootCAs = require('ssl-root-cas/latest').rootCas &nbsp; config = require '../config/config' config.tlsClientLookup = config.get('tlsClientLookup') statsdServer = config.get 'statsd' application = config.get 'application' SDC = require 'statsd-client' os = require 'os' &nbsp; domain = "#{os.hostname()}.#{application.name}.appMetrics" sdc = new SDC statsdServer &nbsp; ### # Fetches the trusted certificates, callsback with an array of certs. ### exports.getTrustedClientCerts = <span class="fstat-no" title="function not covered" >(</span>done) -&gt; Keystore.findOne <span class="fstat-no" title="function not covered" >(</span>err, keystore) -&gt; <span class="cstat-no" title="statement not covered" ></span> done err, null if err <span class="cstat-no" title="statement not covered" > certs = r</span>ootCAs if keystore.ca? <span class="cstat-no" title="statement not covered" ><span class="cstat-no" title="statement not covered" > for cert in keystore.ca</span></span> <span class="cstat-no" title="statement not covered" > certs.push cert.data</span> &nbsp; <span class="cstat-no" title="statement not covered" > return d</span>one null, certs &nbsp; ### # Gets server options object for use with a HTTPS node server # # mutualTLS is a boolean, when true mutual TLS authentication is enabled ### exports.getServerOptions = <span class="fstat-no" title="function not covered" >(</span>mutualTLS, done) -&gt; Keystore.findOne <span class="fstat-no" title="function not covered" >(</span>err, keystore) -&gt; if err <span class="cstat-no" title="statement not covered" > logger.error "Could not fetch keystore: #{err}"</span> <span class="cstat-no" title="statement not covered" > return done err</span> &nbsp; if keystore? options = key: keystore.key cert: keystore.cert.data &nbsp; #if key has password add it to the options <span class="cstat-no" title="statement not covered" > if keystore.passphrase</span> <span class="cstat-no" title="statement not covered" > options.passphrase = keystore.passphrase</span> &nbsp; else <span class="cstat-no" title="statement not covered" > return done(new Error 'Keystore does not exist')</span> &nbsp; if mutualTLS exports.getTrustedClientCerts <span class="fstat-no" title="function not covered" >(</span>err, certs) -&gt; if err <span class="cstat-no" title="statement not covered" > logger.error "Could not fetch trusted certificates: #{err}"</span> <span class="cstat-no" title="statement not covered" > return done err, null</span> &nbsp; <span class="cstat-no" title="statement not covered" > options.ca = c</span>erts <span class="cstat-no" title="statement not covered" > options.requestCert = t</span>rue <span class="cstat-no" title="statement not covered" > options.rejectUnauthorized = f</span>alse # we test authority ourselves done null, options else done null, options &nbsp; ### # 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. ### clientLookup = <span class="fstat-no" title="function not covered" >(</span>fingerprint, subjectCN, issuerCN) -&gt; <span class="cstat-no" title="statement not covered" > logger.debug "Looking up client linked to cert with fingerprint #{fingerprint} with subject #{subjectCN} and issuer #{issuerCN}"</span> <span class="cstat-no" title="statement not covered" > deferred = Q</span>.defer() &nbsp; <span class="cstat-no" title="statement not covered" > Client.findOne certFingerprint: fingerprint, <span class="fstat-no" title="function not covered" >(</span>err, result) -&gt;</span> <span class="cstat-no" title="statement not covered" ></span> deferred.reject err if err &nbsp; if result? # found a match <span class="cstat-no" title="statement not covered" > return deferred.resolve result</span> &nbsp; if subjectCN is issuerCN # top certificate reached <span class="cstat-no" title="statement not covered" > return deferred.resolve null</span> &nbsp; if config.tlsClientLookup.type is 'in-chain' # walk further up and cert chain and check utils.getKeystore <span class="fstat-no" title="function not covered" >(</span>err, keystore) -&gt; <span class="cstat-no" title="statement not covered" ></span> deferred.reject err if err <span class="cstat-no" title="statement not covered" > missedMatches = 0</span> # find the isser cert if not keystore.ca? || keystore.ca.length &lt; 1 <span class="cstat-no" title="statement not covered" > logger.info "Issuer cn=#{issuerCN} for cn=#{subjectCN} not found in keystore."</span> deferred.resolve null else <span class="cstat-no" title="statement not covered" > for cert in keystore.ca</span> do <span class="fstat-no" title="function not covered" >(</span>cert) -&gt; pem.readCertificateInfo cert.data, <span class="fstat-no" title="function not covered" >(</span>err, info) -&gt; if err <span class="cstat-no" title="statement not covered" > return deferred.reject err</span> &nbsp; if info.commonName is issuerCN <span class="cstat-no" title="statement not covered" > promise = c</span>lientLookup cert.fingerprint, info.commonName, info.issuer.commonName <span class="cstat-no" title="statement not covered" > promise.then <span class="fstat-no" title="function not covered" >(</span>result) -&gt; deferred.resolve result</span> else <span class="cstat-no" title="statement not covered" > missedMatches++</span> &nbsp; if missedMatches is keystore.ca.length <span class="cstat-no" title="statement not covered" > logger.info "Issuer cn=#{issuerCN} for cn=#{subjectCN} not found in keystore."</span> deferred.resolve null else if config.tlsClientLookup.type isnt 'strict' <span class="cstat-no" title="statement not covered" > logger.warn "tlsClientLookup.type config option does not contain a known value, defaulting to 'strict'. Available options are 'strict' and 'in-chain'."</span> deferred.resolve null &nbsp; <span class="cstat-no" title="statement not covered" > return deferred.p</span>romise &nbsp; if process.env.NODE_ENV == "test" exports.clientLookup = clientLookup &nbsp; ### # Koa middleware for mutual TLS authentication ### exports.koaMiddleware = <span class="fstat-no" title="function not covered" >(</span>next) -&gt; <span class="cstat-no" title="statement not covered" ></span> startTime = new Date() if statsdServer.enabled if this.authenticated? yield next else <span class="cstat-no" title="statement not covered" > if this.req.client.authorized is true</span> <span class="cstat-no" title="statement not covered" > cert = t</span>his.req.connection.getPeerCertificate true <span class="cstat-no" title="statement not covered" > logger.info "#{cert.subject.CN} is authenticated via TLS."</span> &nbsp; # lookup client by cert fingerprint and set them as the authenticated user try <span class="cstat-no" title="statement not covered" > this.authenticated = yield clientLookup cert.fingerprint, cert.subject.CN, cert.issuer.CN</span> catch <span class="cstat-no" title="statement not covered" >err</span> <span class="cstat-no" title="statement not covered" > logger.error "Failed to lookup client: #{err}"</span> &nbsp; <span class="cstat-no" title="statement not covered" > if this.authenticated?</span> if this.authenticated.clientID? <span class="cstat-no" title="statement not covered" > this.header['X-OpenHIM-ClientID'] = this.authenticated.clientID</span> <span class="cstat-no" title="statement not covered" ></span> sdc.timing "#{domain}.tlsAuthenticationMiddleware", startTime if statsdServer.enabled <span class="cstat-no" title="statement not covered" > this.authenticationType = '</span>tls' yield next else <span class="cstat-no" title="statement not covered" > this.authenticated = n</span>ull <span class="cstat-no" title="statement not covered" > logger.info "Certificate Authentication Failed: the certificate's fingerprint #{cert.fingerprint} did not match any client's certFingerprint attribute, trying next auth mechanism if any..."</span> <span class="cstat-no" title="statement not covered" ></span> sdc.timing "#{domain}.tlsAuthenticationMiddleware", startTime if statsdServer.enabled yield next else <span class="cstat-no" title="statement not covered" > this.authenticated = n</span>ull <span class="cstat-no" title="statement not covered" > logger.info "Could NOT authenticate via TLS: #{this.req.client.authorizationError}, trying next auth mechanism if any..."</span> <span class="cstat-no" title="statement not covered" ></span> sdc.timing "#{domain}.tlsAuthenticationMiddleware", startTime if statsdServer.enabled yield next &nbsp;</pre></td></tr> </table></pre> <div class='push'></div><!-- for sticky footer --> </div><!-- /wrapper --> <div class='footer quiet pad2 space-top1 center small'> Code coverage generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Mon Oct 10 2016 13:39:22 GMT+0200 (SAST) </div> </div> <script src="../../prettify.js"></script> <script> window.onload = function () { if (typeof prettyPrint === 'function') { prettyPrint(); } }; </script> <script src="../../sorter.js"></script> </body> </html>