signer.js
7.17 KB
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
var AWS = require('../core'),
url = AWS.util.url,
crypto = AWS.util.crypto.lib,
base64Encode = AWS.util.base64.encode,
inherit = AWS.util.inherit;
var queryEncode = function (string) {
var replacements = {
'+': '-',
'=': '_',
'/': '~'
};
return string.replace(/[\+=\/]/g, function (match) {
return replacements[match];
});
};
var signPolicy = function (policy, privateKey) {
var sign = crypto.createSign('RSA-SHA1');
sign.write(policy);
return queryEncode(sign.sign(privateKey, 'base64'));
};
var signWithCannedPolicy = function (url, expires, keyPairId, privateKey) {
var policy = JSON.stringify({
Statement: [
{
Resource: url,
Condition: { DateLessThan: { 'AWS:EpochTime': expires } }
}
]
});
return {
Expires: expires,
'Key-Pair-Id': keyPairId,
Signature: signPolicy(policy.toString(), privateKey)
};
};
var signWithCustomPolicy = function (policy, keyPairId, privateKey) {
policy = policy.replace(/\s/mg, '');
return {
Policy: queryEncode(base64Encode(policy)),
'Key-Pair-Id': keyPairId,
Signature: signPolicy(policy, privateKey)
};
};
var determineScheme = function (url) {
var parts = url.split('://');
if (parts.length < 2) {
throw new Error('Invalid URL.');
}
return parts[0].replace('*', '');
};
var getRtmpUrl = function (rtmpUrl) {
var parsed = url.parse(rtmpUrl);
return parsed.path.replace(/^\//, '') + (parsed.hash || '');
};
var getResource = function (url) {
switch (determineScheme(url)) {
case 'http':
case 'https':
return url;
case 'rtmp':
return getRtmpUrl(url);
default:
throw new Error('Invalid URI scheme. Scheme must be one of'
+ ' http, https, or rtmp');
}
};
var handleError = function (err, callback) {
if (!callback || typeof callback !== 'function') {
throw err;
}
callback(err);
};
var handleSuccess = function (result, callback) {
if (!callback || typeof callback !== 'function') {
return result;
}
callback(null, result);
};
AWS.CloudFront.Signer = inherit({
/**
* A signer object can be used to generate signed URLs and cookies for granting
* access to content on restricted CloudFront distributions.
*
* @see http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html
*
* @param keyPairId [String] (Required) The ID of the CloudFront key pair
* being used.
* @param privateKey [String] (Required) A private key in RSA format.
*/
constructor: function Signer(keyPairId, privateKey) {
if (keyPairId === void 0 || privateKey === void 0) {
throw new Error('A key pair ID and private key are required');
}
this.keyPairId = keyPairId;
this.privateKey = privateKey;
},
/**
* Create a signed Amazon CloudFront Cookie.
*
* @param options [Object] The options to create a signed cookie.
* @option options url [String] The URL to which the signature will grant
* access. Required unless you pass in a full
* policy.
* @option options expires [Number] A Unix UTC timestamp indicating when the
* signature should expire. Required unless you
* pass in a full policy.
* @option options policy [String] A CloudFront JSON policy. Required unless
* you pass in a url and an expiry time.
*
* @param cb [Function] if a callback is provided, this function will
* pass the hash as the second parameter (after the error parameter) to
* the callback function.
*
* @return [Object] if called synchronously (with no callback), returns the
* signed cookie parameters.
* @return [null] nothing is returned if a callback is provided.
*/
getSignedCookie: function (options, cb) {
var signatureHash = 'policy' in options
? signWithCustomPolicy(options.policy, this.keyPairId, this.privateKey)
: signWithCannedPolicy(options.url, options.expires, this.keyPairId, this.privateKey);
var cookieHash = {};
for (var key in signatureHash) {
if (Object.prototype.hasOwnProperty.call(signatureHash, key)) {
cookieHash['CloudFront-' + key] = signatureHash[key];
}
}
return handleSuccess(cookieHash, cb);
},
/**
* Create a signed Amazon CloudFront URL.
*
* Keep in mind that URLs meant for use in media/flash players may have
* different requirements for URL formats (e.g. some require that the
* extension be removed, some require the file name to be prefixed
* - mp4:<path>, some require you to add "/cfx/st" into your URL).
*
* @param options [Object] The options to create a signed URL.
* @option options url [String] The URL to which the signature will grant
* access. Any query params included with
* the URL should be encoded. Required.
* @option options expires [Number] A Unix UTC timestamp indicating when the
* signature should expire. Required unless you
* pass in a full policy.
* @option options policy [String] A CloudFront JSON policy. Required unless
* you pass in a url and an expiry time.
*
* @param cb [Function] if a callback is provided, this function will
* pass the URL as the second parameter (after the error parameter) to
* the callback function.
*
* @return [String] if called synchronously (with no callback), returns the
* signed URL.
* @return [null] nothing is returned if a callback is provided.
*/
getSignedUrl: function (options, cb) {
try {
var resource = getResource(options.url);
} catch (err) {
return handleError(err, cb);
}
var parsedUrl = url.parse(options.url, true),
signatureHash = Object.prototype.hasOwnProperty.call(options, 'policy')
? signWithCustomPolicy(options.policy, this.keyPairId, this.privateKey)
: signWithCannedPolicy(resource, options.expires, this.keyPairId, this.privateKey);
parsedUrl.search = null;
for (var key in signatureHash) {
if (Object.prototype.hasOwnProperty.call(signatureHash, key)) {
parsedUrl.query[key] = signatureHash[key];
}
}
try {
var signedUrl = determineScheme(options.url) === 'rtmp'
? getRtmpUrl(url.format(parsedUrl))
: url.format(parsedUrl);
} catch (err) {
return handleError(err, cb);
}
return handleSuccess(signedUrl, cb);
}
});
/**
* @api private
*/
module.exports = AWS.CloudFront.Signer;