From f3fdbabb317a0e9c3a0d35dc4c65363bdf697bfa Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Tue, 14 Apr 2015 12:00:04 +0200 Subject: [PATCH 1/9] Baseline implementation of getSignedPolicy --- lib/storage/file.js | 115 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/lib/storage/file.js b/lib/storage/file.js index c9389e10f5f..049e7715688 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -795,6 +795,121 @@ File.prototype.getSignedUrl = function(options, callback) { }); }; +/** + * Get a signed policy document to allow user to upload data + * with a POST. + * + * *[Reference](http://goo.gl/JWJEkG).* + * + * @throws {Error} if an expiration timestamp from the past is given or + * option parameter does not respect + * + * @param {object} options - Configuration object. + * @param {object} options.expiration - Timestamp (seconds since epoch) when this + * policy will expire. + * @param {object[][]=} options.equals - Array of request parameters and + * their expected value (e.g. [["$", ""]]). Values are + * translated into equality constraints in the conditions + * field of the policy document (e.g. ["eq", "$", ""]) + * @param {object[][]=} options.startsWith - Array of request parameters and + * their expected prefixes (e.g. [["$", ""]]). Values are + * translated into starts-with constraints in the conditions field + * of the policy document (e.g. ["starts-with", "$", ""]) + * @param {string=} options.acl - ACL for the object from possibly predefined + * ACLs + * @param {string=} options.successRedirect - The URL to which the user + * client is redirected if the upload is successfull + * @param {string=} options.successStatus - The status of the Google Storage + * response if the upload is successfull + * + * @example + * file.getSignedUrl({ + * equals: [["$Content-Type", "image/jpeg"]], + * expiration: Math.round(Date.now() / 1000) + (60 * 60 * 24 * 14) // 2 weeks. + * }, function(err, policy) {}); + */ +File.prototype.getSignedPolicy = function(options, callback) { + if (options.expiration < Math.floor(Date.now() / 1000)) { + throw new Error('An expiration date cannot be in the past.'); + } + + console.log("New signed policy called"); + + var expirationString = new Date(options.expiration).toISOString(); + var conditions = []; + + // Populate equals conditions + for (var equalConstraint in options.equals) { + if (!(equalConstraint instanceof Array) || equalsConstraint.length != 2) { + throw new Error('Equals constraint must be an array of two elements'); + } else { + conditions.push(['eq', equalConstraint[0], equalConstraint[1]]); + } + } + + // Populate starts with conditions + for (var startConstraint in options.startsWith) { + if (!(startConstraint instanceof Array) || startConstraint.length != 2) { + throw new Error('StartsWith constraint must be an array of two elements'); + } else { + conditions.push(['startsWith', startConstraint[0], startConstraint[1]]); + } + } + + // Object name + conditions.push(['eq', '$key', this.name]); + + // The bucket that will hold the object + conditions.push({'bucket': this.bucket.name}); + + // The ACL for the file, if any provided + if (options.acl) { + conditions.push({'acl': options.acl}); + } + + // The redirect URL in case of successfull upload + if (options.successRedirect) { + console.log("Success redirect " + options.successRedirect); + conditions.push({'success_action_redirect': options.successRedirect}); + } + + // The response status in case of successful upload + if (options.successStatus) { + console.log("Success redirect " + options.successStatus); + conditions.push({'success_action_status': options.successStatus}); + } + + // Policy object + var policy = { + expiration: expirationString, + conditions: conditions + }; + + var makeAuthorizedRequest_ = this.bucket.storage.makeAuthorizedRequest_; + + makeAuthorizedRequest_.getCredentials(function(err, credentials) { + if (err) { + callback(err); + return; + } + + var sign = crypto.createSign('RSA-SHA256'); + var policyString = JSON.stringify(policy); + var policyBase64 = new Buffer(policyString).toString('base64'); + + sign.update(policyBase64); + + var signature = sign.sign(credentials.private_key, 'base64'); + + callback(null, { + string: policyString, + base64: policyBase64, + signature: signature + }); + }); +}; + + /** * Set the file's metadata. * From 0afc1a07fe2ac16f9d7dd9b0b0d280ffbc7538c3 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Tue, 14 Apr 2015 22:33:45 +0200 Subject: [PATCH 2/9] Add support getSignedPolicy for contentLengthRange, minor refactoring and fixes --- lib/storage/file.js | 53 ++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/lib/storage/file.js b/lib/storage/file.js index 049e7715688..bded5aaca11 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -805,8 +805,8 @@ File.prototype.getSignedUrl = function(options, callback) { * option parameter does not respect * * @param {object} options - Configuration object. - * @param {object} options.expiration - Timestamp (seconds since epoch) when this - * policy will expire. + * @param {object} options.expiration - Timestamp (seconds since epoch) + * when this policy will expire. * @param {object[][]=} options.equals - Array of request parameters and * their expected value (e.g. [["$", ""]]). Values are * translated into equality constraints in the conditions @@ -820,7 +820,11 @@ File.prototype.getSignedUrl = function(options, callback) { * @param {string=} options.successRedirect - The URL to which the user * client is redirected if the upload is successfull * @param {string=} options.successStatus - The status of the Google Storage - * response if the upload is successfull + * response if the upload is successfull (must be string) + * @param {object=} options.contentLengthRange - Object providing + * minimum (options.contentLengthRange.min) and maximum + * (options.contentLengthRange.max) value for the request's + * content length * * @example * file.getSignedUrl({ @@ -833,26 +837,32 @@ File.prototype.getSignedPolicy = function(options, callback) { throw new Error('An expiration date cannot be in the past.'); } - console.log("New signed policy called"); - var expirationString = new Date(options.expiration).toISOString(); var conditions = []; // Populate equals conditions - for (var equalConstraint in options.equals) { - if (!(equalConstraint instanceof Array) || equalsConstraint.length != 2) { - throw new Error('Equals constraint must be an array of two elements'); - } else { - conditions.push(['eq', equalConstraint[0], equalConstraint[1]]); + if (options.equals && options.equals instanceof Array) { + var equalsLength = options.equals.length; + for (var i = 0; i < equalsLength; i++) { + var equalCondition = options.equals[i]; + if ((!(equalCondition instanceof Array)) || equalCondition.length !== 2) { + throw new Error('Equals condition must be an array of 2 elements.'); + } else { + conditions.push(['eq', equalCondition[0], equalCondition[1]]); + } } } // Populate starts with conditions - for (var startConstraint in options.startsWith) { - if (!(startConstraint instanceof Array) || startConstraint.length != 2) { - throw new Error('StartsWith constraint must be an array of two elements'); - } else { - conditions.push(['startsWith', startConstraint[0], startConstraint[1]]); + if (options.startsWith && options.startsWith instanceof Array) { + var startsWithLength = options.startsWith.length; + for (var j = 0; j < startsWithLength; j++) { + var startCondition = options.startsWith[j]; + if (!(startCondition instanceof Array) || startCondition.length !== 2) { + throw new Error('StartsWith condition must be an array of 2 elements.'); + } else { + conditions.push(['starts-with', startCondition[0], startCondition[1]]); + } } } @@ -869,16 +879,25 @@ File.prototype.getSignedPolicy = function(options, callback) { // The redirect URL in case of successfull upload if (options.successRedirect) { - console.log("Success redirect " + options.successRedirect); conditions.push({'success_action_redirect': options.successRedirect}); } // The response status in case of successful upload if (options.successStatus) { - console.log("Success redirect " + options.successStatus); conditions.push({'success_action_status': options.successStatus}); } + // Range for the content length + if (options.contentLengthRange) { + var min = options.contentLengthRange.min; + var max = options.contentLengthRange.max; + if (typeof min === 'undefined' || + typeof max === 'undefined') { + throw new Error('ContentLengthRange must have min and max fields.'); + } + conditions.push(['content-length-range', min, max]); + } + // Policy object var policy = { expiration: expirationString, From f98c200ace5b6ddcaf531067ffe125b293c46e75 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Wed, 15 Apr 2015 22:32:51 +0200 Subject: [PATCH 3/9] Fixed and extended getSignedPolicy example and docs --- lib/storage/file.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/storage/file.js b/lib/storage/file.js index bded5aaca11..b95f7593619 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -827,10 +827,15 @@ File.prototype.getSignedUrl = function(options, callback) { * content length * * @example - * file.getSignedUrl({ + * file.getSignedPolicy({ * equals: [["$Content-Type", "image/jpeg"]], + * contentLengthRange: {min: 0, max: 1024}, * expiration: Math.round(Date.now() / 1000) + (60 * 60 * 24 * 14) // 2 weeks. - * }, function(err, policy) {}); + * }, function(err, policy) { + * // policy.string: the policy document, plain text + * // policy.base64: the policy document, base64 + * // policy.signature: the policy signature, base64 + * }); */ File.prototype.getSignedPolicy = function(options, callback) { if (options.expiration < Math.floor(Date.now() / 1000)) { @@ -840,7 +845,7 @@ File.prototype.getSignedPolicy = function(options, callback) { var expirationString = new Date(options.expiration).toISOString(); var conditions = []; - // Populate equals conditions + // Populate eq conditions if (options.equals && options.equals instanceof Array) { var equalsLength = options.equals.length; for (var i = 0; i < equalsLength; i++) { @@ -853,7 +858,7 @@ File.prototype.getSignedPolicy = function(options, callback) { } } - // Populate starts with conditions + // Populate starts-with conditions if (options.startsWith && options.startsWith instanceof Array) { var startsWithLength = options.startsWith.length; for (var j = 0; j < startsWithLength; j++) { @@ -866,7 +871,7 @@ File.prototype.getSignedPolicy = function(options, callback) { } } - // Object name + // Eq condition for object name conditions.push(['eq', '$key', this.name]); // The bucket that will hold the object From 2dda343740f4f93f4db13359351195ba00437b38 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Wed, 15 Apr 2015 22:33:46 +0200 Subject: [PATCH 4/9] Add getSignedPolicy tests --- test/storage/file.js | 168 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/test/storage/file.js b/test/storage/file.js index 857478ab290..a24cae56d1b 100644 --- a/test/storage/file.js +++ b/test/storage/file.js @@ -1041,6 +1041,174 @@ describe('File', function() { }); }); + describe('getSignedPolicy', function() { + var credentials = require('../testdata/privateKeyFile.json'); + + beforeEach(function() { + var storage = bucket.storage; + storage.makeAuthorizedRequest_.getCredentials = function(callback) { + callback(null, credentials); + }; + }); + + it('should create a signed policy', function(done) { + file.getSignedPolicy({ + expiration: Math.round(Date.now() / 1000) + 5 + }, function(err, signedPolicy) { + assert.ifError(err); + assert.equal(typeof signedPolicy.string, 'string'); + assert.equal(typeof signedPolicy.base64, 'string'); + assert.equal(typeof signedPolicy.signature, 'string'); + done(); + }); + }); + + it('should add key equality condition', function(done) { + file.getSignedPolicy({ + expiration: Math.round(Date.now() / 1000) + 5 + }, function(err, signedPolicy) { + var conditionString = '[\"eq\",\"$key\",\"'+file.name+'\"]'; + assert.ifError(err); + assert(signedPolicy.string.indexOf(conditionString) > -1); + done(); + }); + }); + + it('should add ACL condtion', function(done) { + file.getSignedPolicy({ + expiration: Math.round(Date.now() / 1000) + 5, + acl: '' + }, function(err, signedPolicy) { + var conditionString = '{\"acl\":\"\"}'; + assert.ifError(err); + assert(signedPolicy.string.indexOf(conditionString) > -1); + done(); + }); + }); + + describe('expiration', function() { + it('should ISO encode expiration', function(done) { + var expiration = Math.round(Date.now() / 1000) + 5; + var expireDate = new Date(expiration); + file.getSignedPolicy({ + expiration: expiration + }, function(err, signedPolicy) { + assert.ifError(err); + assert(signedPolicy.string.indexOf(expireDate.toISOString()) > -1); + done(); + }); + }); + + it('should throw if a date from the past is given', function() { + var expirationTimestamp = Math.floor(Date.now() / 1000) - 1; + assert.throws(function() { + file.getSignedPolicy({ + expiration: expirationTimestamp + }, function() {}); + }, /cannot be in the past/); + }); + }); + + describe('equality condition', function() { + it('should add equality condition', function(done) { + var expiration = Math.round(Date.now() / 1000) + 5; + file.getSignedPolicy({ + expiration: expiration, + equals: [['$', '']] + }, function(err, signedPolicy) { + var conditionString = '[\"eq\",\"$\",\"\"]'; + assert.ifError(err); + assert(signedPolicy.string.indexOf(conditionString) > -1); + done(); + }); + }); + it('should throw if equal condition is not an array', function() { + var expiration = Math.round(Date.now() / 1000) + 5; + assert.throws(function() { + file.getSignedPolicy({ + expiration: expiration, + equals: [{}] + }, function() {}); + }, /Equals condition must be an array/); + }); + it('should throw if equal condition length is not 2', function() { + var expiration = Math.round(Date.now() / 1000) + 5; + assert.throws(function() { + file.getSignedPolicy({ + expiration: expiration, + equals: [['1', '2', '3']] + }, function() {}); + }, /Equals condition must be an array of 2 elements/); + }); + }); + + describe('prefix conditions', function() { + it('should add prefix condition', function(done) { + var expiration = Math.round(Date.now() / 1000) + 5; + file.getSignedPolicy({ + expiration: expiration, + startsWith: [['$', '']] + }, function(err, signedPolicy) { + var conditionString = '[\"starts-with\",\"$\",\"\"]'; + assert.ifError(err); + assert(signedPolicy.string.indexOf(conditionString) > -1); + done(); + }); + }); + it('should throw if prexif condition is not an array', function() { + var expiration = Math.round(Date.now() / 1000) + 5; + assert.throws(function() { + file.getSignedPolicy({ + expiration: expiration, + startsWith: [{}] + }, function() {}); + }, /StartsWith condition must be an array/); + }); + it('should throw if prefix condition length is not 2', function() { + var expiration = Math.round(Date.now() / 1000) + 5; + assert.throws(function() { + file.getSignedPolicy({ + expiration: expiration, + startsWith: [['1', '2', '3']] + }, function() {}); + }, /StartsWith condition must be an array of 2 elements/); + }); + }); + + describe('content length', function() { + it('should add content length condition', function(done) { + var expiration = Math.round(Date.now() / 1000) + 5; + file.getSignedPolicy({ + expiration: expiration, + contentLengthRange: {min: 0, max: 1} + }, function(err, signedPolicy) { + var conditionString = '[\"content-length-range\",0,1]'; + assert.ifError(err); + assert(signedPolicy.string.indexOf(conditionString) > -1); + done(); + }); + }); + it('should throw if content length has no min', function() { + var expiration = Math.round(Date.now() / 1000) + 5; + assert.throws(function() { + file.getSignedPolicy({ + expiration: expiration, + contentLengthRange: [{max: 1}] + }, function() {}); + }, /ContentLengthRange must have min and max fields/); + }); + it('should throw if content length has no max', function() { + var expiration = Math.round(Date.now() / 1000) + 5; + assert.throws(function() { + file.getSignedPolicy({ + expiration: expiration, + contentLengthRange: [{min: 0}] + }, function() {}); + }, /ContentLengthRange must have min and max fields/); + }); + }); + }); + describe('setMetadata', function() { var metadata = { fake: 'metadata' }; From 79f10027fb9c9a59737d8040a149339a55a4f23a Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Wed, 15 Apr 2015 23:30:20 +0200 Subject: [PATCH 5/9] Fixed missing text in getSignedPolicy docs --- lib/storage/file.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/storage/file.js b/lib/storage/file.js index 6759995198d..ad38bd1d327 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -802,7 +802,7 @@ File.prototype.getSignedUrl = function(options, callback) { * *[Reference](http://goo.gl/JWJEkG).* * * @throws {Error} if an expiration timestamp from the past is given or - * option parameter does not respect + * option parameter does not respect the expected format * * @param {object} options - Configuration object. * @param {object} options.expiration - Timestamp (seconds since epoch) From 6e300601c69ede96702679c89209a58cb413681d Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Wed, 15 Apr 2015 23:47:33 +0200 Subject: [PATCH 6/9] Added spaces for consistency between getSignedPolicy tests --- test/storage/file.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/storage/file.js b/test/storage/file.js index a24cae56d1b..70dc1f33813 100644 --- a/test/storage/file.js +++ b/test/storage/file.js @@ -1122,6 +1122,7 @@ describe('File', function() { done(); }); }); + it('should throw if equal condition is not an array', function() { var expiration = Math.round(Date.now() / 1000) + 5; assert.throws(function() { @@ -1131,6 +1132,7 @@ describe('File', function() { }, function() {}); }, /Equals condition must be an array/); }); + it('should throw if equal condition length is not 2', function() { var expiration = Math.round(Date.now() / 1000) + 5; assert.throws(function() { @@ -1155,6 +1157,7 @@ describe('File', function() { done(); }); }); + it('should throw if prexif condition is not an array', function() { var expiration = Math.round(Date.now() / 1000) + 5; assert.throws(function() { @@ -1164,6 +1167,7 @@ describe('File', function() { }, function() {}); }, /StartsWith condition must be an array/); }); + it('should throw if prefix condition length is not 2', function() { var expiration = Math.round(Date.now() / 1000) + 5; assert.throws(function() { @@ -1188,6 +1192,7 @@ describe('File', function() { done(); }); }); + it('should throw if content length has no min', function() { var expiration = Math.round(Date.now() / 1000) + 5; assert.throws(function() { @@ -1197,6 +1202,7 @@ describe('File', function() { }, function() {}); }, /ContentLengthRange must have min and max fields/); }); + it('should throw if content length has no max', function() { var expiration = Math.round(Date.now() / 1000) + 5; assert.throws(function() { From 2f3de3ea45a47a3ff39236524dd1ff3e1e3f4f68 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Mon, 20 Apr 2015 19:27:35 +0200 Subject: [PATCH 7/9] getSignedPolicy code refactoring and comments removal. Allowed single equality and prefix conditions as 1D array. Docs updated accordingly. Added tests. --- lib/storage/file.js | 101 +++++++++++++++++++++++++------------------ test/storage/file.js | 36 +++++++++++++-- 2 files changed, 91 insertions(+), 46 deletions(-) diff --git a/lib/storage/file.js b/lib/storage/file.js index ad38bd1d327..cf8c0a9308b 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -810,11 +810,15 @@ File.prototype.getSignedUrl = function(options, callback) { * @param {object[][]=} options.equals - Array of request parameters and * their expected value (e.g. [["$", ""]]). Values are * translated into equality constraints in the conditions - * field of the policy document (e.g. ["eq", "$", ""]) + * field of the policy document (e.g. ["eq", "$", ""]). + * If only one equality condition is to be specified, options.equals + * can be a one-dimensional array (e.g. ["$", ""]) * @param {object[][]=} options.startsWith - Array of request parameters and * their expected prefixes (e.g. [["$", ""]]). Values are * translated into starts-with constraints in the conditions field * of the policy document (e.g. ["starts-with", "$", ""]) + * If only one prefix condition is to be specified, options.startsWith + * can be a one-dimensional array (e.g. ["$", ""]) * @param {string=} options.acl - ACL for the object from possibly predefined * ACLs * @param {string=} options.successRedirect - The URL to which the user @@ -828,7 +832,7 @@ File.prototype.getSignedUrl = function(options, callback) { * * @example * file.getSignedPolicy({ - * equals: [["$Content-Type", "image/jpeg"]], + * equals: ["$Content-Type", "image/jpeg"], * contentLengthRange: {min: 0, max: 1024}, * expiration: Math.round(Date.now() / 1000) + (60 * 60 * 24 * 14) // 2 weeks. * }, function(err, policy) { @@ -843,67 +847,80 @@ File.prototype.getSignedPolicy = function(options, callback) { } var expirationString = new Date(options.expiration).toISOString(); - var conditions = []; - - // Populate eq conditions - if (options.equals && options.equals instanceof Array) { - var equalsLength = options.equals.length; - for (var i = 0; i < equalsLength; i++) { - var equalCondition = options.equals[i]; - if ((!(equalCondition instanceof Array)) || equalCondition.length !== 2) { - throw new Error('Equals condition must be an array of 2 elements.'); - } else { - conditions.push(['eq', equalCondition[0], equalCondition[1]]); - } + var conditions = [ + ['eq', '$key', this.name], + { + bucket: this.bucket.name + }, + ]; + + if (util.is(options.equals, 'array')) { + if (options.equals.length === 2 && + util.is(options.equals[0], 'string') && + util.is(options.equals[1], 'string')) { + conditions.push( + ['eq', options.equals[0], options.equals[1]] + ); + } else { + options.equals.forEach(function(condition) { + if (!util.is(condition, 'array') || condition.length !== 2) { + throw new Error( + 'Equals condition must be an array of 2 elements.' + ); + } + conditions.push(['eq', condition[0], condition[1]]); + }); } } - // Populate starts-with conditions - if (options.startsWith && options.startsWith instanceof Array) { - var startsWithLength = options.startsWith.length; - for (var j = 0; j < startsWithLength; j++) { - var startCondition = options.startsWith[j]; - if (!(startCondition instanceof Array) || startCondition.length !== 2) { - throw new Error('StartsWith condition must be an array of 2 elements.'); - } else { - conditions.push(['starts-with', startCondition[0], startCondition[1]]); - } + if (util.is(options.startsWith, 'array')) { + if (options.startsWith.length === 2 && + util.is(options.startsWith[0], 'string') && + util.is(options.startsWith[1], 'string')) { + conditions.push( + ['starts-with', options.startsWith[0], options.startsWith[1]] + ); + } else { + options.startsWith.forEach(function(condition) { + if (!util.is(condition, 'array') || condition.length !== 2) { + throw new Error( + 'StartsWith condition must be an array of 2 elements.' + ); + } + conditions.push(['starts-with', condition[0], condition[1]]); + }); } } - - // Eq condition for object name - conditions.push(['eq', '$key', this.name]); - - // The bucket that will hold the object - conditions.push({'bucket': this.bucket.name}); - - // The ACL for the file, if any provided + if (options.acl) { - conditions.push({'acl': options.acl}); + conditions.push({ + acl: options.acl + }); } - // The redirect URL in case of successfull upload if (options.successRedirect) { - conditions.push({'success_action_redirect': options.successRedirect}); + conditions.push({ + success_action_redirect: options.successRedirect + }); } - // The response status in case of successful upload if (options.successStatus) { - conditions.push({'success_action_status': options.successStatus}); + conditions.push({ + success_action_status: options.successStatus + }); } - // Range for the content length if (options.contentLengthRange) { var min = options.contentLengthRange.min; var max = options.contentLengthRange.max; - if (typeof min === 'undefined' || - typeof max === 'undefined') { - throw new Error('ContentLengthRange must have min and max fields.'); + if (!util.is(min, 'number') || !util.is(max, 'number')) { + throw new Error( + 'ContentLengthRange must have numeric min and max fields.' + ); } conditions.push(['content-length-range', min, max]); } - // Policy object var policy = { expiration: expirationString, conditions: conditions diff --git a/test/storage/file.js b/test/storage/file.js index 70dc1f33813..25ee040a057 100644 --- a/test/storage/file.js +++ b/test/storage/file.js @@ -1110,7 +1110,7 @@ describe('File', function() { }); describe('equality condition', function() { - it('should add equality condition', function(done) { + it('should add equality conditions (array of arrays)', function(done) { var expiration = Math.round(Date.now() / 1000) + 5; file.getSignedPolicy({ expiration: expiration, @@ -1123,6 +1123,19 @@ describe('File', function() { }); }); + it('should add equality condition (array)', function(done) { + var expiration = Math.round(Date.now() / 1000) + 5; + file.getSignedPolicy({ + expiration: expiration, + equals: ['$', ''] + }, function(err, signedPolicy) { + var conditionString = '[\"eq\",\"$\",\"\"]'; + assert.ifError(err); + assert(signedPolicy.string.indexOf(conditionString) > -1); + done(); + }); + }); + it('should throw if equal condition is not an array', function() { var expiration = Math.round(Date.now() / 1000) + 5; assert.throws(function() { @@ -1145,12 +1158,27 @@ describe('File', function() { }); describe('prefix conditions', function() { - it('should add prefix condition', function(done) { + it('should add prefix conditions (array of arrays)', function(done) { var expiration = Math.round(Date.now() / 1000) + 5; file.getSignedPolicy({ expiration: expiration, startsWith: [['$', '']] }, function(err, signedPolicy) { + console.log(signedPolicy); + var conditionString = '[\"starts-with\",\"$\",\"\"]'; + assert.ifError(err); + assert(signedPolicy.string.indexOf(conditionString) > -1); + done(); + }); + }); + + it('should add prefix condition (array)', function(done) { + var expiration = Math.round(Date.now() / 1000) + 5; + file.getSignedPolicy({ + expiration: expiration, + startsWith: ['$', ''] + }, function(err, signedPolicy) { + console.log(signedPolicy); var conditionString = '[\"starts-with\",\"$\",\"\"]'; assert.ifError(err); assert(signedPolicy.string.indexOf(conditionString) > -1); @@ -1200,7 +1228,7 @@ describe('File', function() { expiration: expiration, contentLengthRange: [{max: 1}] }, function() {}); - }, /ContentLengthRange must have min and max fields/); + }, /ContentLengthRange must have numeric min and max fields/); }); it('should throw if content length has no max', function() { @@ -1210,7 +1238,7 @@ describe('File', function() { expiration: expiration, contentLengthRange: [{min: 0}] }, function() {}); - }, /ContentLengthRange must have min and max fields/); + }, /ContentLengthRange must have numeric min and max fields/); }); }); }); From a7fa2f6ca6bd69f522cd77be3bfa53bf1b2e61ee Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Tue, 21 Apr 2015 01:57:53 +0200 Subject: [PATCH 8/9] getSignedPolicy refactoring of single equality and prefix conditions as 1D array --- lib/storage/file.js | 46 ++++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/lib/storage/file.js b/lib/storage/file.js index cf8c0a9308b..a9d54237104 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -855,41 +855,27 @@ File.prototype.getSignedPolicy = function(options, callback) { ]; if (util.is(options.equals, 'array')) { - if (options.equals.length === 2 && - util.is(options.equals[0], 'string') && - util.is(options.equals[1], 'string')) { - conditions.push( - ['eq', options.equals[0], options.equals[1]] - ); - } else { - options.equals.forEach(function(condition) { - if (!util.is(condition, 'array') || condition.length !== 2) { - throw new Error( - 'Equals condition must be an array of 2 elements.' - ); - } - conditions.push(['eq', condition[0], condition[1]]); - }); + if (!util.is(options.equals[0], 'array')) { + options.equals = [options.equals]; } + options.equals.forEach(function(condition) { + if (!util.is(condition, 'array') || condition.length !== 2) { + throw new Error('Equals condition must be an array of 2 elements.'); + } + conditions.push(['eq', condition[0], condition[1]]); + }); } if (util.is(options.startsWith, 'array')) { - if (options.startsWith.length === 2 && - util.is(options.startsWith[0], 'string') && - util.is(options.startsWith[1], 'string')) { - conditions.push( - ['starts-with', options.startsWith[0], options.startsWith[1]] - ); - } else { - options.startsWith.forEach(function(condition) { - if (!util.is(condition, 'array') || condition.length !== 2) { - throw new Error( - 'StartsWith condition must be an array of 2 elements.' - ); - } - conditions.push(['starts-with', condition[0], condition[1]]); - }); + if (!util.is(options.startsWith[0], 'array')) { + options.startsWith = [options.startsWith]; } + options.startsWith.forEach(function(condition) { + if (!util.is(condition, 'array') || condition.length !== 2) { + throw new Error('StartsWith condition must be an array of 2 elements.'); + } + conditions.push(['starts-with', condition[0], condition[1]]); + }); } if (options.acl) { From c7eefbc460b28418e68d3ac1ebafea4dd3d1e375 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Mon, 27 Apr 2015 10:53:44 +0200 Subject: [PATCH 9/9] Removed console.log form getSignedPolicy tests --- test/storage/file.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/storage/file.js b/test/storage/file.js index 25ee040a057..d9c129846b5 100644 --- a/test/storage/file.js +++ b/test/storage/file.js @@ -1164,7 +1164,6 @@ describe('File', function() { expiration: expiration, startsWith: [['$', '']] }, function(err, signedPolicy) { - console.log(signedPolicy); var conditionString = '[\"starts-with\",\"$\",\"\"]'; assert.ifError(err); assert(signedPolicy.string.indexOf(conditionString) > -1); @@ -1178,7 +1177,6 @@ describe('File', function() { expiration: expiration, startsWith: ['$', ''] }, function(err, signedPolicy) { - console.log(signedPolicy); var conditionString = '[\"starts-with\",\"$\",\"\"]'; assert.ifError(err); assert(signedPolicy.string.indexOf(conditionString) > -1);