Skip to content

Instantly share code, notes, and snippets.

@seriousben
Last active August 29, 2015 14:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seriousben/60ec66ef67805239f320 to your computer and use it in GitHub Desktop.
Save seriousben/60ec66ef67805239f320 to your computer and use it in GitHub Desktop.
Better Generation of S3 Policy with Tests (From: https://github.com/javiersuweijie/s3policy)
// Better https://github.com/javiersuweijie/s3policy
var crypto = require('crypto');
var path = require('path');
var _ = require('lodash');
var uuid = require('node-uuid');
var mimetype = require('mimetype');
var AWS = require('aws-sdk');
var debug = require('debug')('s3policy');
function getAwsConfig() {
if (!AWS.config.credentials) {
console.error('AWS Credentials are not configured correctly. Missing IAM Role or ENV variables.');
var e = new Error('Internal Error: Missing authentication information to generate policy.');
e.status = 500;
throw e;
}
return {
accessKey: AWS.config.credentials.accessKeyId,
secretKey: AWS.config.credentials.secretAccessKey
};
}
// Makes it easy to follow http://docs.aws.amazon.com/AmazonS3/latest/dev/request-rate-perf-considerations.html
function buildKey(context, filename) {
var environment = process.env.NODE_ENV || 'development';
var contextPath = _.isEmpty(context) ? '' : context + '/';
return path.join(environment + '/' + contextPath + uuid.v1() + '/' + filename);
}
var DEFAULT_WRITE_OPTIONS = {
expireInSec: 120,
maxSizeInMb: 10,
context: undefined,
acl: 'public-read'
};
module.exports = {
getWritePolicy: function(filename, bucket, redirectUrl, options, cb) {
try {
if (_.isFunction(options)) {
cb = options;
options = DEFAULT_WRITE_OPTIONS;
} else {
_.defaults(options, DEFAULT_WRITE_OPTIONS);
}
var awsConfig = getAwsConfig();
debug('Generating write policy:', options);
var key = buildKey(options.context, filename);
var duration = options.expireInSec;
var filesize = options.maxSizeInMb;
var acl = options.acl;
var dateObj = new Date();
var dateExp = new Date(dateObj.getTime() + duration * 1000);
var policy = {
'expiration': dateExp.toISOString(),
'conditions': [
{
bucket: bucket
},
{
key: key,
},
{
'success_action_redirect': redirectUrl
},
{
acl: acl
},
['content-length-range', 0, filesize * 1024 * 1024],
['starts-with', '$Content-Type', 'image']
]
};
var policyString = JSON.stringify(policy);
var policyBase64 = new Buffer(policyString).toString('base64');
var signature = crypto.createHmac('sha1', awsConfig.secretKey).update(policyBase64);
var s3Credentials = {
url: 'http://' + bucket + '.s3.amazonaws.com',
redirectUrl: redirectUrl,
key: key,
policy: policyBase64,
signature: signature.digest('base64'),
accessKey: awsConfig.accessKey,
acl: acl,
mimetype: mimetype.lookup(filename)
};
debug('Generated write policy:', s3Credentials);
cb(undefined, s3Credentials);
} catch (e) {
cb(e);
}
}
};
'use strict';
var path = require('path');
var fs = require('fs');
var request = require('supertest');
var FormData = require('form-data');
var s3policy = require('s3policy');
var expect = require('chai').expect;
var filename = 'avatar_1.jpg';
var pathToMedia = path.resolve(__dirname, './fixtures/media/', filename);
describe('Upload an image using UploadPolicy', function() {
it('upload', function(done) {
s3policy.getUploadPolicy(filename, function(err, policy) {
expect(err).to.not.exist();
expect(policy, 'policy required').to.exist();
var form = new FormData();
form.append('AWSAccessKeyId', policy.accessKey);
form.append('key', policy.key);
form.append('policy', policy.policy);
form.append('signature', policy.signature);
form.append('acl', policy.acl);
form.append('success_action_redirect', policy.redirectUrl);
form.append('Content-Type', policy.mimetype);
form.append('file', fs.createReadStream(pathToMedia));
form.submit(policy.url, function(err, res) {
if (err) {
return done(err);
}
expect(res.statusCode).to.equal(303);
if (res.statusCode === 303 && res.headers.location) {
expect(res.headers.location).to.include(helper.config.targetServer);
request(res.headers.location)
.get('').end(function(err, res) {
if (err) {
return done(err);
}
expect(res.body).to.be.an.instanceOf(Object);
expect(res.body).to.have.property('bucket');
done();
});
return;
}
// debugging information only! When failing!
var data = '';
res.on('data', function(chunk) {
data += chunk;
});
res.on('end', function() {
expect(data).to.not.be.empty();
expect(false).to.be.true();
done();
});
res.resume();
});
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment