Skip to content
Browse files

first commit

  • Loading branch information...
0 parents commit 920615c6470137ea74b74ee8c2e26ae76868bd74 @andrewschaaf committed
Showing with 264 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +6 −0 Cakefile
  3. +23 −0 LICENSE-MIT.txt
  4. +37 −0 README.md
  5. +73 −0 lib/s3-post.js
  6. +20 −0 package.json
  7. +71 −0 src/s3-post.coffee
  8. +31 −0 test.coffee
3 .gitignore
@@ -0,0 +1,3 @@
+.DS_Store
+node_modules
+localsettings.coffee
6 Cakefile
@@ -0,0 +1,6 @@
+
+{noisyExec} = require "tafa-misc-util"
+
+
+task 'dev', () ->
+ noisyExec "coffee -cwo lib src"
23 LICENSE-MIT.txt
@@ -0,0 +1,23 @@
+
+Copyright (c) 2011+ node-s3-post contributors
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
37 README.md
@@ -0,0 +1,37 @@
+
+If you have a signed policy, Amazon S3 lets you POST without requiring the account's secret key.
+
+
+## Signing a policy
+<pre>
+{signPolicy} = require 's3-post'
+
+{signature64, policy64} = signPolicy secretKey, {
+"expiration": "2999-12-30T12:00:00.000Z",
+ "conditions": [
+ {"bucket": "takin-mah-bukket"},
+ ["starts-with", "$key", ""]
+ ]
+}
+</pre>
+
+
+## POSTing
+<pre>
+{postToS3} = require 's3-post'
+
+postToS3 {
+ AWSAccessKeyId: "..."
+ policy64: "..."
+ signature64: "..."
+ bucket: "..."
+ key: "..."
+ data: fs.readFileSync "..."
+}, (e) ->
+ if e
+ console.log "OMG NOES!!!"
+ console.log e.responseStatus
+ console.log e.responseText
+ else
+ console.log "Yay!"
+</pre>
73 lib/s3-post.js
@@ -0,0 +1,73 @@
+(function() {
+ var crypto, https, joinBuffers, postToS3, signPolicy;
+ https = require('https');
+ crypto = require('crypto');
+ joinBuffers = require('tafa-misc-util').joinBuffers;
+ signPolicy = function(secretKey, policy) {
+ var data, hmac, json, key, policy64, signature64;
+ json = JSON.stringify(policy);
+ policy64 = new Buffer(json).toString('base64');
+ data = new Buffer(policy64, 'utf-8');
+ key = new Buffer(secretKey, 'utf-8');
+ hmac = crypto.createHmac('sha1', key);
+ hmac.update(data);
+ signature64 = hmac.digest('base64');
+ return {
+ signature64: signature64,
+ policy64: policy64
+ };
+ };
+ postToS3 = function(_arg, callback) {
+ var AWSAccessKeyId, addParam, arr, boundary, bucket, buf, data, key, options, policy64, req, req_body, signature64;
+ AWSAccessKeyId = _arg.AWSAccessKeyId, policy64 = _arg.policy64, signature64 = _arg.signature64, bucket = _arg.bucket, key = _arg.key, data = _arg.data, boundary = _arg.boundary;
+ boundary || (boundary = '----------R46EARkAg4SAXSjufGsb6m');
+ buf = function(x) {
+ return new Buffer(x);
+ };
+ arr = [];
+ addParam = function(k, v) {
+ arr.push(buf('--' + boundary + '\r\n'));
+ arr.push(buf('Content-Disposition: form-data; name="' + k + '"\r\n\r\n'));
+ return arr.push(buf(v), buf('\r\n'));
+ };
+ addParam('AWSAccessKeyId', AWSAccessKeyId);
+ addParam('key', key);
+ addParam('signature', signature64);
+ addParam('policy', policy64);
+ arr.push(buf('--' + boundary + '\r\n'));
+ arr.push(buf('Content-Disposition: form-data; name="file"; filename="data"\r\n'));
+ arr.push(buf("Content-Length: " + data.length + "\r\n"));
+ arr.push(buf('Content-Transfer-Encoding: binary\r\n\r\n'));
+ arr.push(data, buf('\r\n'));
+ arr.push(buf('--' + boundary + '--'));
+ req_body = joinBuffers(arr);
+ options = {
+ host: "" + bucket + ".s3.amazonaws.com",
+ path: '/',
+ method: 'POST',
+ headers: {
+ 'Host': "" + bucket + ".s3.amazonaws.com",
+ 'Content-Type': 'multipart/form-data; boundary=' + boundary,
+ 'Content-Length': req_body.length
+ }
+ };
+ req = https.request(options, function(res) {
+ var _ref;
+ if ((200 <= (_ref = res.statusCode) && _ref < 300)) {
+ return callback(null);
+ } else {
+ return readText(res, function(text) {
+ return callback({
+ responseCode: res.statusCode,
+ responseText: text
+ });
+ });
+ }
+ });
+ return req.end(req_body);
+ };
+ module.exports = {
+ postToS3: postToS3,
+ signPolicy: signPolicy
+ };
+}).call(this);
20 package.json
@@ -0,0 +1,20 @@
+{
+ "name": "s3-post",
+ "version": "0.0.1",
+
+ "description": "Functions for S3 POSTing and policy signing.",
+ "url": "https://github.com/andrewschaaf/node-s3-post",
+
+ "main": "lib/s3-post.js",
+
+ "dependencies": {
+ "tafa-misc-util": "=0.1.3"
+ },
+
+ "author": "Andrew Schaaf <andrew@andrewschaaf.com> (http://andrewschaaf.com)",
+
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/andrewschaaf/node-s3-post.git"
+ }
+}
71 src/s3-post.coffee
@@ -0,0 +1,71 @@
+
+https = require 'https'
+crypto = require 'crypto'
+{joinBuffers} = require 'tafa-misc-util'
+
+
+signPolicy = (secretKey, policy) ->
+ json = JSON.stringify policy
+ policy64 = new Buffer(json).toString('base64')
+ data = new Buffer policy64, 'utf-8'
+ key = new Buffer secretKey, 'utf-8'
+ hmac = crypto.createHmac 'sha1', key
+ hmac.update data
+ signature64 = hmac.digest 'base64'
+ {
+ signature64: signature64
+ policy64: policy64
+ }
+
+
+postToS3 = ({AWSAccessKeyId, policy64, signature64, bucket, key, data, boundary}, callback) ->
+
+ boundary or= '----------R46EARkAg4SAXSjufGsb6m' # chosen by fair dice roll.
+ # guaranteed to be random.
+
+ #### encode req_body
+ buf = (x) ->
+ new Buffer x
+ arr = []
+ addParam = (k, v) ->
+ arr.push buf('--' + boundary + '\r\n')
+ arr.push buf('Content-Disposition: form-data; name="' + k + '"\r\n\r\n')
+ arr.push buf(v), buf('\r\n')
+ addParam 'AWSAccessKeyId', AWSAccessKeyId
+ addParam 'key', key
+ addParam 'signature', signature64
+ addParam 'policy', policy64
+ arr.push buf('--' + boundary + '\r\n')
+ arr.push buf 'Content-Disposition: form-data; name="file"; filename="data"\r\n'
+ arr.push buf "Content-Length: #{data.length}\r\n"
+ arr.push buf 'Content-Transfer-Encoding: binary\r\n\r\n'
+ arr.push data, buf('\r\n')
+ arr.push buf('--' + boundary + '--')
+ req_body = joinBuffers arr
+
+ #### POST
+ options = {
+ host: "#{bucket}.s3.amazonaws.com"
+ path: '/'
+ method: 'POST'
+ headers: {
+ 'Host': "#{bucket}.s3.amazonaws.com"
+ 'Content-Type': ('multipart/form-data; boundary=' + boundary)
+ 'Content-Length': req_body.length
+ }
+ }
+ req = https.request options, (res) ->
+ if 200 <= res.statusCode < 300
+ callback null
+ else
+ readText res, (text) ->
+ callback {
+ responseCode: res.statusCode
+ responseText: text
+ }
+ req.end req_body
+
+
+module.exports =
+ postToS3: postToS3
+ signPolicy: signPolicy
31 test.coffee
@@ -0,0 +1,31 @@
+
+{signPolicy, postToS3} = require './lib/s3-post'
+
+{AWSAccessKeyId, secret, bucket} = require './localsettings'
+
+
+{signature64, policy64} = signPolicy secret, {
+ "expiration": "2999-12-30T12:00:00.000Z",
+ "conditions": [
+ {"bucket": bucket},
+ ["starts-with", "$key", ""]
+ ]
+}
+
+
+postToS3 {
+ AWSAccessKeyId: AWSAccessKeyId
+ policy64: policy64
+ signature64: signature64
+ bucket: bucket
+ key: "key-ms-#{new Date().getTime()}"
+ data: new Buffer "OH HAI #{new Date().getTime()}\n"
+}, (e) ->
+ if e
+ console.log "OMG NOES!!!"
+ console.log e.responseStatus
+ console.log e.responseText
+ else
+ console.log "Yay!"
+
+

0 comments on commit 920615c

Please sign in to comment.
Something went wrong with that request. Please try again.