-
Notifications
You must be signed in to change notification settings - Fork 104
/
Copy pathwebhook_notification_gateway.js
98 lines (81 loc) · 3.18 KB
/
webhook_notification_gateway.js
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
'use strict';
let xml2js = require('xml2js');
let _ = require('underscore');
let Digest = require('./digest').Digest;
let Gateway = require('./gateway').Gateway;
let exceptions = require('./exceptions');
let Util = require('./util').Util;
let WebhookNotification = require('./webhook_notification').WebhookNotification;
let wrapPrototype = require('@braintree/wrap-promise').wrapPrototype;
class WebhookNotificationGateway extends Gateway {
constructor(gateway) {
super();
this.gateway = gateway;
this.config = this.gateway.config;
}
parse(signature, payload) {
if (payload.match(/[^A-Za-z0-9+=\/\n]/)) {
return Promise.reject(exceptions.InvalidSignatureError('payload contains illegal characters')); // eslint-disable-line new-cap
}
let err = this.validateSignature(signature, payload);
if (err) {
return Promise.reject(err);
}
let xmlPayload = new Buffer(payload, 'base64').toString('utf8');
let parser = new xml2js.Parser({
explicitRoot: true
});
return new Promise((resolve, reject) => {
parser.parseString(xmlPayload, (parseError, result) => {
if (parseError) {
reject(parseError);
} else {
let attributes = Util.convertNodeToObject(result);
let handler = this.createResponseHandler('notification', WebhookNotification);
handler(attributes).then((responseHandlerResponse) => {
resolve(responseHandlerResponse.notification);
});
}
});
});
}
validateSignature(signatureString, payload) {
let signaturePairs = signatureString.split('&').filter((pair) => pair.indexOf('|') !== -1).map((pair) => pair.split('|'));
let signature = this.matchingSignature(signaturePairs);
if (!signature) {
return exceptions.InvalidSignatureError('no matching public key'); // eslint-disable-line new-cap
}
let self = this;
let matches = _.some([payload, payload + '\n'], (data) => {
return Digest.secureCompare(signature, Digest.Sha1hexdigest(self.gateway.config.privateKey, data));
});
if (!matches) {
return exceptions.InvalidSignatureError('signature does not match payload - one has been modified'); // eslint-disable-line new-cap
}
return null;
}
verify(challenge, callback) {
if (!challenge.match(/^[a-f0-9]{20,32}$/)) {
if (callback != null) {
callback(exceptions.InvalidChallengeError('challenge contains non-hex characters'), null); // eslint-disable-line new-cap
return;
}
throw exceptions.InvalidChallengeError('challenge contains non-hex characters'); // eslint-disable-line new-cap
}
let digest = Digest.Sha1hexdigest(this.gateway.config.privateKey, challenge);
return `${this.gateway.config.publicKey}|${digest}`; // eslint-disable-line consistent-return
}
matchingSignature(signaturePairs) {
for (let keyPair of signaturePairs) {
let publicKey = keyPair[0];
let signature = keyPair[1];
if (this.gateway.config.publicKey === publicKey) {
return signature;
}
}
return null;
}
}
module.exports = {WebhookNotificationGateway: wrapPrototype(WebhookNotificationGateway, {
ignoreMethods: ['verify']
})};