Skip to content

Commit

Permalink
Added functionality to use S3 SSE
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom Hudson committed Mar 9, 2017
1 parent 40508a3 commit 2fed8d2
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 2 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,29 @@ var upload = multer({
})
```

## Using Server-Side Encryption

*An overview of S3's server-side encryption can be found in the [S3 Docs] (http://docs.aws.amazon.com/AmazonS3/latest/dev/serv-side-encryption.html); be advised that customer-managed keys (SSE-C) is not implemented at this time.*

You may use the S3 server-side encryption functionality via the optional `sse` and `sseKms` parameters. Full documentation of these parameters in relation to the S3 API can be found [here] (http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property) and [here] (http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingServerSideEncryption.html).

`sse` has two valid values: 'AES256' and 'aws:kms'. 'AES256' utilizes the S3-managed key system, while 'aws:kms' utilizes the AWS KMS system and accepts the optional `sseKms` parameter to specify the key ID of the key you wish to use. leaving `sseKms` blank when 'aws:kms' is specified will use the default KMS key.

```javascript
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'some-bucket',
acl: 'authenticated-read',
contentDisposition: 'attachment',
sse: 'AES256',
key: function(req, file, cb) {
cb(null, Date.now().toString())
}
})
})
```

## Testing

The tests mock all access to S3 and can be run completely offline.
Expand Down
27 changes: 25 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ var defaultMetadata = staticValue(null)
var defaultCacheControl = staticValue(null)
var defaultContentDisposition = staticValue(null)
var defaultStorageClass = staticValue('STANDARD')
var defaultSSE = staticValue(null)
var defaultSSEKMS = staticValue(null)

function defaultKey (req, file, cb) {
crypto.randomBytes(16, function (err, raw) {
Expand Down Expand Up @@ -44,7 +46,9 @@ function collect (storage, req, file, cb) {
storage.getMetadata.bind(storage, req, file),
storage.getCacheControl.bind(storage, req, file),
storage.getContentDisposition.bind(storage, req, file),
storage.getStorageClass.bind(storage, req, file)
storage.getStorageClass.bind(storage, req, file),
storage.getSSE.bind(storage, req, file),
storage.getSSEKMS.bind(storage, req, file)
], function (err, values) {
if (err) return cb(err)

Expand All @@ -60,7 +64,9 @@ function collect (storage, req, file, cb) {
contentDisposition: values[5],
storageClass: values[6],
contentType: contentType,
replacementStream: replacementStream
replacementStream: replacementStream,
sse: values[7],
sseKms: values[8]
})
})
})
Expand Down Expand Up @@ -124,6 +130,20 @@ function S3Storage (opts) {
case 'undefined': this.getStorageClass = defaultStorageClass; break
default: throw new TypeError('Expected opts.storageClass to be undefined, string or function')
}

switch (typeof opts.sse) {
case 'function': this.getSSE = opts.sse; break
case 'string': this.getSSE = staticValue(opts.sse); break
case 'undefined': this.getSSE = defaultSSE; break
default: throw new TypeError('Expected opts.sse to be undefined, string or function')
}

switch (typeof opts.sseKms) {
case 'function': this.getSSEKMS = opts.sseKms; break
case 'string': this.getSSEKMS = opts.sseKms; break
case 'undefined': this.getSSEKMS = defaultSSEKMS; break
default: throw new TypeError('Expected opts.sseKms to be undefined, string, or function')
}
}

S3Storage.prototype._handleFile = function (req, file, cb) {
Expand All @@ -140,6 +160,8 @@ S3Storage.prototype._handleFile = function (req, file, cb) {
ContentType: opts.contentType,
Metadata: opts.metadata,
StorageClass: opts.storageClass,
ServerSideEncryption: opts.sse,
SSEKMSKeyId: opts.sseKms,
Body: (opts.replacementStream || file.stream)
}

Expand All @@ -164,6 +186,7 @@ S3Storage.prototype._handleFile = function (req, file, cb) {
contentType: opts.contentType,
contentDisposition: opts.contentDisposition,
storageClass: opts.storageClass,
serverSideEncryption: opts.sse,
metadata: opts.metadata,
location: result.Location,
etag: result.ETag
Expand Down
56 changes: 56 additions & 0 deletions test/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,60 @@ describe('Multer S3', function () {
done()
})
})

it('uploads file with AES256 server-side encryption', function (done) {
var s3 = mockS3()
var form = new FormData()
var storage = multerS3({ s3: s3, bucket: 'test', sse: 'AES256' })
var upload = multer({ storage: storage })
var parser = upload.single('image')
var image = fs.createReadStream(path.join(__dirname, 'files', 'ffffff.png'))

form.append('name', 'Multer')
form.append('image', image)

submitForm(parser, form, function (err, req) {
assert.ifError(err)

assert.equal(req.body.name, 'Multer')

assert.equal(req.file.fieldname, 'image')
assert.equal(req.file.originalname, 'ffffff.png')
assert.equal(req.file.size, 68)
assert.equal(req.file.bucket, 'test')
assert.equal(req.file.etag, 'mock-etag')
assert.equal(req.file.location, 'mock-location')
assert.equal(req.file.serverSideEncryption, 'AES256')

done()
})
})

it('uploads file with AWS KMS-managed server-side encryption', function (done) {
var s3 = mockS3()
var form = new FormData()
var storage = multerS3({ s3: s3, bucket: 'test', sse: 'aws:kms' })
var upload = multer({ storage: storage })
var parser = upload.single('image')
var image = fs.createReadStream(path.join(__dirname, 'files', 'ffffff.png'))

form.append('name', 'Multer')
form.append('image', image)

submitForm(parser, form, function (err, req) {
assert.ifError(err)

assert.equal(req.body.name, 'Multer')

assert.equal(req.file.fieldname, 'image')
assert.equal(req.file.originalname, 'ffffff.png')
assert.equal(req.file.size, 68)
assert.equal(req.file.bucket, 'test')
assert.equal(req.file.etag, 'mock-etag')
assert.equal(req.file.location, 'mock-location')
assert.equal(req.file.serverSideEncryption, 'aws:kms')

done()
})
})
})

0 comments on commit 2fed8d2

Please sign in to comment.