· 7 years ago · Oct 27, 2018, 07:00 PM
1/**
2 * JavaScript based IAM Authorization for Neptune
3 * protocol WebSocket
4 *
5 * v3 (support for EC2 IAM Roles)
6 * by lior@amazon.com
7 *
8 * IAM Authentication:
9 * See https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html
10 * Here are the ways you can supply your credentials in order of recommendation:
11 * Loaded from AWS Identity and Access Management (IAM) roles for Amazon EC2 (if running on Amazon EC2)
12 * Loaded from the shared credentials file (~/.aws/credentials) preferrably with a dedicated profile.
13 * Loaded from environment variables
14 *
15 **/
16
17var crypto = require('crypto-js');
18const gremlin = require('gremlin');
19var AWS = require('aws-sdk');
20
21function isUndefined(value) {
22 return typeof value === 'undefined' || value === null;
23};
24
25function makeTwoDigits(n) {
26 if (n > 9) {
27 return n;
28 } else {
29 return '0' + n;
30 }
31}
32
33function getDateTimeString() {
34 var d = new Date();
35
36 //
37 // The additional ''s are used to force JavaScript to interpret the
38 // '+' operator as string concatenation rather than arithmetic.
39 //
40 return d.getUTCFullYear() + '' +
41 makeTwoDigits(d.getUTCMonth() + 1) + '' +
42 makeTwoDigits(d.getUTCDate()) + 'T' + '' +
43 makeTwoDigits(d.getUTCHours()) + '' +
44 makeTwoDigits(d.getUTCMinutes()) + '' +
45 makeTwoDigits(d.getUTCSeconds()) + 'Z';
46}
47
48function getDateString(dateTimeString) {
49 return dateTimeString.substring(0, dateTimeString.indexOf('T'));
50}
51
52function getSignatureKey(key, dateStamp, regionName, serviceName) {
53 var kDate = crypto.HmacSHA256(dateStamp, 'AWS4' + key, {
54 asBytes: true
55 });
56 var kRegion = crypto.HmacSHA256(regionName, kDate, {
57 asBytes: true
58 });
59 var kService = crypto.HmacSHA256(serviceName, kRegion, {
60 asBytes: true
61 });
62 var kSigning = crypto.HmacSHA256('aws4_request', kService, {
63 asBytes: true
64 });
65 return kSigning;
66}
67
68function getSignature(method, scheme, hostname, port, path, accessId, secretKey,
69 region, serviceName, payload, canonicalQueryString, today, datetimeStamp, debug) {
70
71 var canonicalHeaders = 'host:' + hostname.toLowerCase() + ':' + port + '\n' + 'x-amz-date:' + datetimeStamp + '\n';
72 var signedHeaders = 'host;x-amz-date';
73
74 var canonicalRequest = method + '\n' + // method
75 path + '\n' + // path
76 canonicalQueryString + '\n' + // query params
77 canonicalHeaders + // headers
78 '\n' + // required
79 signedHeaders + '\n' + // signed header list
80 crypto.SHA256(payload, {
81 asBytes: true
82 }); // hash of payload (empty string)
83
84 if (debug === true) {
85 console.log('Canonical request: ' + ' \n ' + canonicalRequest.replace(/(\r\n\t|\n|\r\t)/gm," \n ") + '\n');
86 }
87
88 var hashedCanonicalRequest = crypto.SHA256(canonicalRequest, {
89 asBytes: true
90 });
91
92 if (debug === true) {
93 console.log('Hashed canonical request: ' + hashedCanonicalRequest + '\n');
94 }
95
96 var stringToSign = 'AWS4-HMAC-SHA256\n' +
97 datetimeStamp + '\n' +
98 today + '/' + region + '/' + serviceName + '/aws4_request\n' +
99 hashedCanonicalRequest;
100
101 if (debug === true) {
102 console.log('String to sign: ' + ' \n ' + stringToSign.replace(/(\r\n\t|\n|\r\t)/gm," \n ") + '\n');
103 }
104
105 var signingKey = getSignatureKey(secretKey, today, region, serviceName);
106
107 if (debug === true) {
108 console.log('Signing key: ' + signingKey + '\n');
109 }
110
111 var signature = crypto.HmacSHA256(stringToSign, signingKey, {
112 asBytes: true
113 });
114
115 if (debug === true) {
116 console.log('Signature: ' + signature + '\n');
117 }
118
119 return signature;
120}
121
122function getAuthHeader(options, awsAccessId, awsSecretKey, datetimeStamp) {
123 var today = getDateString(datetimeStamp);
124 var signature = getSignature(options.method, options.scheme, options.hostname, options.port, options.path,
125 awsAccessId, awsSecretKey, options.region, options.serviceName, options.payload, options.canonicalQueryString,
126 today, datetimeStamp, options.debug)
127
128 return 'AWS4-HMAC-SHA256 ' +
129 'Credential=' + awsAccessId + '/' + today + '/' + options.region + '/' + options.serviceName + '/aws4_request,' +
130 'SignedHeaders=host;x-amz-date,' +
131 'Signature=' + signature;
132}
133
134AWS.config.getCredentials(Main);
135
136function Main(err) {
137
138 /// Main logic
139
140 if (err) {
141 console.log('Could not load credentials...');
142 throw err;
143 };
144
145 var datetimeStamp = getDateTimeString();
146 var creds = AWS.config.credentials
147
148 //TODO change hostname, region.
149 var options = {
150 method: 'GET',
151 scheme: 'ws://',
152 hostname: 'your.neptune-endpoint.eu-west-1.neptune.amazonaws.com', //either cluster or instance
153 port: 8182,
154 path: '/gremlin',
155 method: 'GET',
156 payload: '', // empty string for HTTP GET
157 canonicalQueryString: '', // empty string for websocket
158 debug: true, // enable verbose output for signature process.
159 region: 'eu-west-1',
160 serviceName: 'neptune-db'
161 };
162
163 //Setup Headers for Authorization
164 if (options.debug === true) {
165 console.log("\x1b[44m%s\x1b[0m", 'Credential source: ' + creds.constructor.name, '\n');
166 };
167
168 var awsheaders = {};
169 awsheaders['Authorization'] = getAuthHeader(options, creds.accessKeyId, creds.secretAccessKey, datetimeStamp);
170 awsheaders['x-amz-date'] = datetimeStamp;
171 if (!isUndefined(AWS.config.credentials.sessionToken)) {
172 awsheaders['X-Amz-Security-Token'] = creds.sessionToken
173 };
174
175 if (options.debug === true) {
176 console.log('Final HTTP headers: ' + JSON.stringify(awsheaders, null, 2) + '\n');
177 };
178
179 //Build the URL
180 var url = options.scheme + options.hostname + ':' + options.port + options.path
181 if (options.debug === true) {
182 console.log('URL to send: ' + url + '\n');
183 };
184
185 //Connect with Gremlin
186 const DriverRemoteConnection = gremlin.driver.DriverRemoteConnection;
187 const Graph = gremlin.structure.Graph;
188 dc = new DriverRemoteConnection(url, {
189 headers: awsheaders
190 });
191
192 // Use Gremlin
193 const graph = new Graph();
194 const g = graph.traversal().withRemote(dc);
195
196 g.V().limit(1).count().next().
197 then(data => {
198 console.log(data);
199 dc.close();
200 }).catch(error => {
201 console.log('ERROR', error);
202 dc.close();
203 });
204}