123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- 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;
|