Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
icaliman committed Mar 27, 2015
0 parents commit 1227354
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store

**/node_modules

.idea
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# aws-sign

Simple module to calculate `Authorization` header for Amazon AWS REST requests, for Parse.com.


## Usage

1. Put 'aws-sign.js' file into your 'cloud/modules/' folder.
2. Require module in Cloud Code.

# Examples

```js
var AwsSign = require('cloud/modules/aws-sign.js');
var signer = new AwsSign({
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
});

var opts = {
method: 'PUT',
url: 'http://johnsmith.s3.amazonaws.com/photos/puppy.jpg',
headers: { ... },
... // Other request options, ignored by AwsSign.
body: {}
success: function() {},
error: function() {}
};
signer.sign(opts);

Parse.Cloud.httpRequest(opts);
```

The following keys are mandatory:

* `method`
* `url`

Others are optional. A date header (`headers.date`) will be added for you if it is not already set.
158 changes: 158 additions & 0 deletions aws-sign.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
var
crypto = require('crypto'),
url = require('url'),
querystring = require('querystring');


function AWSRestSigner(credentials) {
this.accessKeyId = credentials.accessKeyId;
this.secretAccessKey = credentials.secretAccessKey;
this.debug = false;
}

AWSRestSigner.subResources = ['acl', 'lifecycle', 'location', 'logging', 'notification', 'partNumber', 'policy', 'requestPayment', 'torrent', 'uploadId', 'uploads', 'versionId', 'versioning', 'versions', 'website'];

AWSRestSigner.prototype.canonizeAwzHeaders = function(xAmzHeaders) {
if (xAmzHeaders) {
var lcHeaders = {};
Object.keys(xAmzHeaders).forEach(function(header) {
var h = header.toLowerCase();
if (h!='x-amz-date') {
lcHeaders[h]=xAmzHeaders[header];
}
});

return Object.keys(lcHeaders)
.map(function(header) {
return header.toLowerCase();
})
.sort()
.map(function(header) {
return header+':'+lcHeaders[header]+"\n";
})
.join('');
} else {
return '';
}
}

AWSRestSigner.prototype.extractSubResources = function(queryString) {
var query = querystring.parse(queryString);

var subresources = [];
Object.keys(query).forEach(function(param) {
if (AWSRestSigner.subResources.indexOf(param)>=0) {
subresources.push(param);
}
});

if (subresources.length) {
subresources = subresources.sort();
var queryToSign = subresources.map(function(param) {
var result = param;
if (query[param]!='') {
result+="="+query[param];
}
return result;
});
return "?"+queryToSign.join("&")
}

return '';
}

AWSRestSigner.prototype.sign = function(opts) {
var urlData = url.parse(opts.url);
var
method = opts.method,
host = urlData.hostname || '',
path = urlData.pathname,
xAmzHeaders = {},
date, contentType, contentMd5,
bucket = "";



var _match = host.match(/^(.*)\.s3\.amazonaws\.com/);
if (_match) {
bucket = _match[1];
} else {
bucket = host;
}

if (!opts.headers) {
opts.headers = {};
}

Object.keys(opts.headers).forEach(function(key) {
var lcKey = key.toLowerCase();
switch(lcKey) {
case "date":
date = opts.headers[key];
break;
case "content-type":
contentType = opts.headers[key];
break;
case "content-md5":
contentMd5 = opts.headers[key];
break;
default:
if("x-amz-" === lcKey.slice(0, 6)) {
xAmzHeaders[lcKey] = opts.headers[key];
}
break;
}
});

if (!date) {
date = new Date().toUTCString();
opts.headers.date = date;
}

opts.headers["Authorization"] = this._sign(method, bucket, path, date, contentType, contentMd5, xAmzHeaders);
}


AWSRestSigner.prototype._sign = function(method, bucket, path, date, contentType, contentMd5, xAmzHeaders) {
var qPos = path.indexOf('?'), queryToSign='';
if (qPos>=0) {
var queryPart = path.substr(qPos+1, path.length);
path = path.substr(0,qPos);
queryToSign = this.extractSubResources(queryPart);
}

var canonicalizedAmzHeaders = this.canonizeAwzHeaders(xAmzHeaders);

var canonicalizedResource = '';
if (bucket!='') {
canonicalizedResource += '/'+bucket;
}
canonicalizedResource += path + queryToSign;

var stringToSign = method + "\n";
if (contentMd5) {
stringToSign += contentMd5;
}
stringToSign += "\n";

if (contentType) {
stringToSign += contentType;
}
stringToSign += "\n";

stringToSign +=
date + "\n" +
canonicalizedAmzHeaders +
canonicalizedResource;

if (this.debug) {
console.log("-----------")
console.log(stringToSign.replace(/\n/g, "\\n\n"));
console.log("-----------")
}

return 'AWS ' + this.accessKeyId + ':' + crypto.createHmac('sha1', this.secretAccessKey).update(stringToSign).digest('base64');
}

module.exports = AWSRestSigner;

0 comments on commit 1227354

Please sign in to comment.