Skip to content
This repository has been archived by the owner on May 5, 2023. It is now read-only.

Commit

Permalink
Merge pull request #564 from andrerod/dev
Browse files Browse the repository at this point in the history
#499: Make generating blob URL with SAS easier
  • Loading branch information
André Rodrigues committed Jan 30, 2013
2 parents db8ff69 + 596e056 commit 2712ccb
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 28 deletions.
2 changes: 1 addition & 1 deletion examples/geophoto/services/pushpinService.js
Expand Up @@ -82,7 +82,7 @@ PushpinService.prototype.createPushpin = function (pushpinData, pushpinImage, ca
entity.PartitionKey = DEFAULT_PARTITION; entity.PartitionKey = DEFAULT_PARTITION;


if (blob) { if (blob) {
entity.imageUrl = self.blobClient.getBlobUrl(blob.container, blob.blob).url(); entity.imageUrl = self.blobClient.getBlobUrl(blob.container, blob.blob);
} }


self.tableClient.insertEntity(TABLE_NAME, entity, callback); self.tableClient.insertEntity(TABLE_NAME, entity, callback);
Expand Down
2 changes: 1 addition & 1 deletion examples/myevents/eventService.js
Expand Up @@ -31,7 +31,7 @@ EventService.prototype = {
res.render('detail', { res.render('detail', {
title: eventItem.name, title: eventItem.name,
eventItem: eventItem, eventItem: eventItem,
imageUrl: self.blobClient.getBlobUrl('photos', eventItem.RowKey).url(), imageUrl: self.blobClient.getBlobUrl('photos', eventItem.RowKey),
}); });
} }
}); });
Expand Down
Expand Up @@ -146,7 +146,7 @@ exports.createOrEditRole = function (roleData, roleImage, callback) {


function insertOrUpdateEntity(error, role) { function insertOrUpdateEntity(error, role) {
if (role) { if (role) {
roleData.imageUrl = blobClient.getBlobUrl(role.container, role.blob).url(); roleData.imageUrl = blobClient.getBlobUrl(role.container, role.blob);
} }


if (!_.isUndefined(roleData.RowKey) && !_.isUndefined(roleData.PartitionKey)) { if (!_.isUndefined(roleData.RowKey) && !_.isUndefined(roleData.PartitionKey)) {
Expand Down
2 changes: 2 additions & 0 deletions lib/azure.js
Expand Up @@ -208,6 +208,8 @@ exports.ServiceBusSettings = require('./services/core/servicebussettings');
exports.ServiceManagementSettings = require('./services/core/servicemanagementsettings'); exports.ServiceManagementSettings = require('./services/core/servicemanagementsettings');
exports.Validate = require('./util/validate'); exports.Validate = require('./util/validate');


exports.date = require('./util/date');

/* /*
* Convenience functions. * Convenience functions.
*/ */
Expand Down
35 changes: 22 additions & 13 deletions lib/services/blob/blobservice.js
Expand Up @@ -2142,7 +2142,7 @@ BlobService.prototype.listBlobBlocks = function (container, blob, blocklisttype,
* @param {string} [blob] The blob name. * @param {string} [blob] The blob name.
* @param {object} sharedAccessPolicy The shared access policy. * @param {object} sharedAccessPolicy The shared access policy.
* @param {string} [sharedAccessPolicy.Id] The signed identifier. * @param {string} [sharedAccessPolicy.Id] The signed identifier.
* @param {SharedAccessPermissions} sharedAccessPolicy.AccessPolicy.Permissions The permission type. * @param {object} [sharedAccessPolicy.AccessPolicy.Permissions] The permission type.
* @param {date|string} [sharedAccessPolicy.AccessPolicy.Start] The time at which the Shared Access Signature becomes valid (The UTC value will be used). * @param {date|string} [sharedAccessPolicy.AccessPolicy.Start] The time at which the Shared Access Signature becomes valid (The UTC value will be used).
* @param {date|string} sharedAccessPolicy.AccessPolicy.Expiry The time at which the Shared Access Signature becomes expired (The UTC value will be used). * @param {date|string} sharedAccessPolicy.AccessPolicy.Expiry The time at which the Shared Access Signature becomes expired (The UTC value will be used).
* @return {object} An object with the shared access signature. * @return {object} An object with the shared access signature.
Expand All @@ -2156,6 +2156,10 @@ BlobService.prototype.generateSharedAccessSignature = function (container, blob,
resourceType = BlobConstants.ResourceTypes.BLOB; resourceType = BlobConstants.ResourceTypes.BLOB;
} }


if (azureutil.objectIsNull(sharedAccessPolicy.AccessPolicy.Permissions)) {
sharedAccessPolicy.AccessPolicy.Permissions = BlobConstants.SharedAccessPermissions.READ;
}

if (!azureutil.objectIsNull(sharedAccessPolicy.AccessPolicy.Start)) { if (!azureutil.objectIsNull(sharedAccessPolicy.AccessPolicy.Start)) {
sharedAccessPolicy.AccessPolicy.Start = ISO8061Date.format(sharedAccessPolicy.AccessPolicy.Start, true); sharedAccessPolicy.AccessPolicy.Start = ISO8061Date.format(sharedAccessPolicy.AccessPolicy.Start, true);
} }
Expand Down Expand Up @@ -2186,24 +2190,29 @@ BlobService.prototype.generateSharedAccessSignature = function (container, blob,
* @this {BlobService} * @this {BlobService}
* @param {string} container The container name. * @param {string} container The container name.
* @param {string} [blob] The blob name. * @param {string} [blob] The blob name.
* @param {object} [sharedAccessPolicy] The shared access policy.
* @param {string} [sharedAccessPolicy.Id] The signed identifier.
* @param {object} [sharedAccessPolicy.AccessPolicy.Permissions] The permission type.
* @param {date|string} [sharedAccessPolicy.AccessPolicy.Start] The time at which the Shared Access Signature becomes valid (The UTC value will be used).
* @param {date|string} sharedAccessPolicy.AccessPolicy.Expiry The time at which the Shared Access Signature becomes expired (The UTC value will be used).
* @return {object} An object with the blob URL. * @return {object} An object with the blob URL.
*/ */
BlobService.prototype.getBlobUrl = function (container, blob) { BlobService.prototype.getBlobUrl = function (container, blob, sharedAccessPolicy) {
// Validate container name. Blob name is optional. // Validate container name. Blob name is optional.
validateContainerName(container); validateContainerName(container);


var resourceName = createResourceName(container, blob); var signedQueryString = {};

if (sharedAccessPolicy) {
var baseUrl = this.protocol + this.host + ':' + this.port; signedQueryString = this.generateSharedAccessSignature(container, blob, sharedAccessPolicy).queryString;
var path = this._getPath('/' + resourceName); }


return { return url.format({
baseUrl: baseUrl, protocol: this._isHttps() ? 'https:' : 'http:',
path: path, hostname: this.host,
url: function () { port: this.port,
return baseUrl + path; pathname: this._getPath('/' + createResourceName(container, blob)),
} query: signedQueryString
}; });
}; };


// Private methods // Private methods
Expand Down
13 changes: 13 additions & 0 deletions lib/services/core/serviceclient.js
Expand Up @@ -29,6 +29,9 @@ var HeaderConstants = Constants.HeaderConstants;
var HttpConstants = Constants.HttpConstants; var HttpConstants = Constants.HttpConstants;
var Logger = require('../../diagnostics/logger'); var Logger = require('../../diagnostics/logger');


var moduleVersion = require('../../../package.json').version;
var operatingSystem = process.platform;

// Expose 'ServiceClient'. // Expose 'ServiceClient'.
exports = module.exports = ServiceClient; exports = module.exports = ServiceClient;


Expand Down Expand Up @@ -206,6 +209,8 @@ ServiceClient.prototype._performRequest = function (webResource, body, options,
finalCallback(requestOptions); finalCallback(requestOptions);
}); });
} else { } else {
self._tagRequest(requestOptions);

self.logger.log(Logger.LogLevels.DEBUG, "REQUEST OPTIONS:\n" + util.inspect(requestOptions)); self.logger.log(Logger.LogLevels.DEBUG, "REQUEST OPTIONS:\n" + util.inspect(requestOptions));


var operation = function (finalRequestOptions, operationCallback, next) { var operation = function (finalRequestOptions, operationCallback, next) {
Expand Down Expand Up @@ -284,6 +289,14 @@ ServiceClient.prototype._performRequest = function (webResource, body, options,
}); });
}; };


ServiceClient.prototype._tagRequest = function (requestOptions) {
if (!this.userAgent) {
this.userAgent = util.format('Windows Azure SDK/%s (Node.js; %s)', moduleVersion, operatingSystem);
}

requestOptions.headers[HeaderConstants.USER_AGENT] = this.userAgent;
};

/** /**
* Process the response. * Process the response.
* *
Expand Down
8 changes: 0 additions & 8 deletions lib/util/constants.js
Expand Up @@ -2111,14 +2111,6 @@ var Constants = {
*/ */
USER_AGENT: 'user-agent', USER_AGENT: 'user-agent',


/**
* Specifies the value to use for UserAgent header.
*
* @const
* @type {string}
*/
USER_AGENT_PREFIX: 'WA-Storage',

/** /**
* The pop receipt header. * The pop receipt header.
* *
Expand Down
62 changes: 62 additions & 0 deletions lib/util/date.js
@@ -0,0 +1,62 @@
/**
* Copyright (c) Microsoft. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Generates a Date object which is in the given days from now.
*
* @param {int} days The days timespan.
* @return {Date}
*/
exports.daysFromNow = function (days) {
var date = new Date()
date.setDate(date.getDate() + days);
return date;
};

/**
* Generates a Date object which is in the given hours from now.
*
* @param {int} hours The hours timespan.
* @return {Date}
*/
exports.hoursFromNow = function (hours) {
var date = new Date()
date.setHours(date.getHours() + hours);
return date;
};

/**
* Generates a Date object which is in the given minutes from now.
*
* @param {int} minutes The minutes timespan.
* @return {Date}
*/
exports.minutesFromNow = function (minutes) {
var date = new Date()
date.setMinutes(date.getMinutes() + minutes);
return date;
};

/**
* Generates a Date object which is in the given seconds from now.
*
* @param {int} seconds The seconds timespan.
* @return {Date}
*/
exports.secondsFromNow = function (seconds) {
var date = new Date()
date.setSeconds(date.getSeconds() + seconds);
return date;
};
50 changes: 46 additions & 4 deletions test/services/blob/blobservice-tests.js
Expand Up @@ -18,6 +18,7 @@ var assert = require('assert');
var fs = require('fs'); var fs = require('fs');
var path = require("path"); var path = require("path");
var util = require('util'); var util = require('util');
var sinon = require('sinon');


// Test includes // Test includes
var testutil = require('../../util/util'); var testutil = require('../../util/util');
Expand Down Expand Up @@ -1250,11 +1251,52 @@ suite('blobservice-tests', function () {


var blobServiceassert = azure.createBlobService('storageAccount', 'storageAccessKey', 'host.com:80'); var blobServiceassert = azure.createBlobService('storageAccount', 'storageAccessKey', 'host.com:80');


var urlParts = blobServiceassert.getBlobUrl(containerName); var blobUrl = blobServiceassert.getBlobUrl(containerName);
assert.equal(urlParts.url(), 'http://host.com:80/' + containerName); assert.equal(blobUrl, 'http://host.com:80/' + containerName);


urlParts = blobServiceassert.getBlobUrl(containerName, blobName); blobUrl = blobServiceassert.getBlobUrl(containerName, blobName);
assert.equal(urlParts.url(), 'http://host.com:80/' + containerName + '/' + blobName); assert.equal(blobUrl, 'http://host.com:80/' + containerName + '/' + blobName);

done();
});

test('GetBlobSharedUrl', function (done) {
var containerName = 'container';
var blobName = 'blob';

var blobServiceassert = azure.createBlobService('storageAccount', 'storageAccessKey', 'host.com:80');

var sharedAccessPolicy = {
AccessPolicy: {
Expiry: new Date('October 12, 2011 11:53:40 am GMT')
}
};

var blobUrl = blobServiceassert.getBlobUrl(containerName, blobName, sharedAccessPolicy);
assert.equal(blobUrl, 'http://host.com:80/' + containerName + '/' + blobName + '?se=2011-10-12T11%3A53%3A40Z&sr=b&sp=r&sig=eVkH%2BFxxShel2hcN50ZUmgPAHk%2FmqRVeaBfyry%2BVacw%3D');

done();
});

test('GetBlobSharedUrlWithDuration', function (done) {
var containerName = 'container';
var blobName = 'blob';

var blobServiceassert = azure.createBlobService('storageAccount', 'storageAccessKey', 'host.com:80');

// Mock Date just to ensure a fixed signature
this.clock = sinon.useFakeTimers(0, "Date");

var sharedAccessPolicy = {
AccessPolicy: {
Expiry: azure.date.minutesFromNow(10)
}
};

this.clock.restore();

var blobUrl = blobServiceassert.getBlobUrl(containerName, blobName, sharedAccessPolicy);
assert.equal(blobUrl, 'http://host.com:80/' + containerName + '/' + blobName + '?se=1970-01-01T00%3A10%3A00Z&sr=b&sp=r&sig=LofuDUzdHPpiteauMetANWzDpzd0Vw%2BVMOHyXYCipAM%3D');


done(); done();
}); });
Expand Down
1 change: 1 addition & 0 deletions test/testlist.txt
Expand Up @@ -30,6 +30,7 @@ services/table/tableservice-tablequery-tests.js
services/table/tableservice-tests.js services/table/tableservice-tests.js
services/sqlAzure/sqlservice-tests.js services/sqlAzure/sqlservice-tests.js
util/atomhandler-tests.js util/atomhandler-tests.js
util/date-tests.js
util/iso8061date-tests.js util/iso8061date-tests.js
util/util-tests.js util/util-tests.js
util/validate-tests.js util/validate-tests.js
Expand Down
58 changes: 58 additions & 0 deletions test/util/date-tests.js
@@ -0,0 +1,58 @@
/**
* Copyright (c) Microsoft. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

var should = require('should');
var sinon = require('sinon');

var testutil = require('./util');
var azure = testutil.libRequire('azure');

suite('date-tests', function () {
setup(function () {
this.clock = sinon.useFakeTimers(0, "Date");
});

teardown(function () {
this.clock.restore();
});

test('daysFromNow', function () {
var daysInterval = 1;
var expectedDate = new Date('Jan 2, 1970 00:00:00 am GMT');

azure.date.daysFromNow(daysInterval).should.equal(expectedDate);
});

test('hoursFromNow', function () {
var hoursInterval = 3;
var expectedDate = new Date('Jan 1, 1970 03:00:00 am GMT');

azure.date.hoursFromNow(hoursInterval).should.equal(expectedDate);
});

test('minutesFromNow', function () {
var minutesInterval = 10;
var expectedDate = new Date('Jan 1, 1970 00:10:00 am GMT');

azure.date.minutesFromNow(minutesInterval).should.equal(expectedDate);
});

test('secondsFromNow', function () {
var secondsInterval = 20;
var expectedDate = new Date('Jan 1, 1970 00:00:20 am GMT');

azure.date.secondsFromNow(secondsInterval).should.equal(expectedDate);
});
});

0 comments on commit 2712ccb

Please sign in to comment.