1 | 'use strict';
|
2 |
|
3 | var util = require('util');
|
4 | var Promise = require('bluebird');
|
5 | var errors = require('@leisurelink/http-equiv-errors');
|
6 | var _ = require('lodash');
|
7 | var assert = require('assert-plus');
|
8 |
|
9 | function takePrincipalId(id) {
|
10 | var ids = id.split('/');
|
11 | return ids[0];
|
12 | }
|
13 |
|
14 | function takeKeyId(id) {
|
15 | var ids = id.split('/');
|
16 | return (ids.length > 1) ? ids[1] : '';
|
17 | }
|
18 |
|
19 | var requiredHeaders = {
|
20 | POST: ['host', 'date', 'request-line', 'content-length'],
|
21 | PUT: ['host', 'date', 'request-line', 'content-length'],
|
22 | PATCH: ['host', 'date', 'request-line', 'content-length'],
|
23 | GET: ['host', 'date', 'request-line'],
|
24 | DELETE: ['host', 'date', 'request-line']
|
25 | };
|
26 |
|
27 | function hasRequiredHeaders(method, headers) {
|
28 | method = method.toUpperCase();
|
29 | return !_.isEmpty(requiredHeaders[method]) &&
|
30 | _.isEmpty(_.difference(requiredHeaders[method], headers));
|
31 | }
|
32 |
|
33 | module.exports = function SignatureParser(httpSignature, authentic, logger, allowLaxParsing) {
|
34 | assert.object(httpSignature, 'httpSignature');
|
35 | assert.object(authentic, 'authentic');
|
36 | assert.object(logger, 'logger');
|
37 |
|
38 | var getEndpointKey = function(lang, keyId) {
|
39 | return new Promise(function(resolve, reject){
|
40 | authentic.getEndpointKey(lang, takePrincipalId(keyId), takeKeyId(keyId), function(err, response, body) {
|
41 | if (err){
|
42 | return reject(err);
|
43 | }
|
44 | return resolve({
|
45 | response: response,
|
46 | body: body
|
47 | });
|
48 | });
|
49 | });
|
50 | };
|
51 |
|
52 | var laxParseFromHeaders = function(req) {
|
53 | return Promise.try(function(){
|
54 | return {
|
55 | scheme: 'LaxSignature',
|
56 | params: {
|
57 | keyId: req.headers['x-lax-key-id'],
|
58 | algorithm: 'fake',
|
59 | headers: req.headers['x-lax-headers'].split(' '),
|
60 | jwt: req.headers['x-lax-jwt'],
|
61 | signature: 'fake'
|
62 | },
|
63 | signingString: 'fake'
|
64 | };
|
65 | });
|
66 | };
|
67 |
|
68 | var _parse = function(req, lax) {
|
69 | return Promise.try(function(){
|
70 | return httpSignature.parseRequest(req);
|
71 | }).catch(function(err) {
|
72 | logger.warn(util.format('No remote authority; unsupported authorization scheme: %s', req.headers.authorization), err);
|
73 | return null;
|
74 | }).then(function(result){
|
75 | if (!result && lax){
|
76 | return laxParseFromHeaders(req);
|
77 | }
|
78 | return result;
|
79 | });
|
80 | };
|
81 |
|
82 | var verify = function(method, parsed, key, lax) {
|
83 | return Promise.try(function(){
|
84 | if (parsed.scheme === 'LaxSignature' && lax) {
|
85 | return false;
|
86 | }
|
87 | if (!key){
|
88 | throw new errors.UnauthorizedError('authentic:signing-key-not-found');
|
89 | }
|
90 | return httpSignature.verifySignature(parsed, key);
|
91 | }).then(function(verified){
|
92 | if (verified) {
|
93 | if (hasRequiredHeaders(method, parsed.params.headers))
|
94 | return parsed;
|
95 | else
|
96 | return null;
|
97 | }
|
98 | if (lax) {
|
99 | logger.warn('Endpoint signature not verified. Call may be inauthentic. authentic:signature-inauthentic');
|
100 | return parsed;
|
101 | } else {
|
102 | throw new errors.UnauthorizedError('authentic:signature-inauthentic');
|
103 | }
|
104 | });
|
105 | };
|
106 |
|
107 | var parse = function(req) {
|
108 | var _parsed, _lax;
|
109 | return Promise.resolve(allowLaxParsing)
|
110 | .then(function(unwrappedLax){
|
111 | _lax = unwrappedLax;
|
112 | return _parse(req, unwrappedLax);
|
113 | })
|
114 | .then(function(parsed){
|
115 | _parsed = parsed;
|
116 | if (!_parsed || !_parsed.params || !_parsed.params.keyId) {
|
117 | return _parsed;
|
118 | }
|
119 | return getEndpointKey('en-US', parsed.params.keyId).then(function(key){
|
120 | return verify(req.method, _parsed, key.body.result.key, _lax);
|
121 | }).catch(function(err){
|
122 | if (err.name != 'NotFoundError'){
|
123 | throw err;
|
124 | }
|
125 | });
|
126 | });
|
127 |
|
128 | };
|
129 |
|
130 | return { parse: parse };
|
131 | };
|