UNPKG

3.78 kBJavaScriptView Raw
1'use strict';
2
3var util = require('util');
4var Promise = require('bluebird');
5var errors = require('@leisurelink/http-equiv-errors');
6var _ = require('lodash');
7var assert = require('assert-plus');
8
9function takePrincipalId(id) {
10 var ids = id.split('/');
11 return ids[0];
12}
13
14function takeKeyId(id) {
15 var ids = id.split('/');
16 return (ids.length > 1) ? ids[1] : '';
17}
18
19var 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
27function hasRequiredHeaders(method, headers) {
28 method = method.toUpperCase();
29 return !_.isEmpty(requiredHeaders[method]) &&
30 _.isEmpty(_.difference(requiredHeaders[method], headers));
31}
32
33module.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};