Skip to content

Commit 9896c6c

Browse files
sethpollackLevelbossMike
authored andcommitted
Add Server Side Encryption (#67)
* Add Server Side Encryption * dont pass serverSideEncryption if not set * remove serverSideEncryption from options * add tests for serverSideEncryption * pass serverSideEncryption to activate
1 parent 31a4087 commit 9896c6c

File tree

5 files changed

+141
-39
lines changed

5 files changed

+141
-39
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ The client specified MUST implement functions called `getObject` and `putObject`
138138

139139
*Default:* the default S3 library is `aws-sdk`
140140

141+
### serverSideEncryption
142+
143+
The Server-side encryption algorithm used when storing this object in S3 (e.g., AES256, aws:kms). Possible values include:
144+
- "AES256"
145+
- "aws:kms"
146+
141147
### How do I activate a revision?
142148

143149
A user can activate a revision by either:
@@ -239,7 +245,7 @@ You can deploy your Ember application to S3 and still use the history-api for pr
239245
### With Cloudfront
240246
A Cloudfront Custom Error Response can handle catching the 404 error that occurs when a request is made to a pretty URL and can allow that request to be handled by index.html and in turn Ember.
241247

242-
A Custom Error Response can be created for your CloudFront distrubution in the AWS console by navigating to:
248+
A Custom Error Response can be created for your CloudFront distrubution in the AWS console by navigating to:
243249

244250
Cloudfront > `Distribution ID` > Error Pages > Create Custom Error Response.
245251

index.js

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,17 @@ module.exports = {
3636
requiredConfig: ['bucket', 'region'],
3737

3838
upload: function(/* context */) {
39-
var bucket = this.readConfig('bucket');
40-
var prefix = this.readConfig('prefix');
41-
var acl = this.readConfig('acl');
42-
var cacheControl = this.readConfig('cacheControl');
43-
var revisionKey = this.readConfig('revisionKey');
44-
var distDir = this.readConfig('distDir');
45-
var filePattern = this.readConfig('filePattern');
46-
var gzippedFiles = this.readConfig('gzippedFiles');
47-
var allowOverwrite = this.readConfig('allowOverwrite');
48-
var filePath = joinUriSegments(distDir, filePattern);
39+
var bucket = this.readConfig('bucket');
40+
var prefix = this.readConfig('prefix');
41+
var acl = this.readConfig('acl');
42+
var cacheControl = this.readConfig('cacheControl');
43+
var revisionKey = this.readConfig('revisionKey');
44+
var distDir = this.readConfig('distDir');
45+
var filePattern = this.readConfig('filePattern');
46+
var gzippedFiles = this.readConfig('gzippedFiles');
47+
var allowOverwrite = this.readConfig('allowOverwrite');
48+
var serverSideEncryption = this.readConfig('serverSideEncryption');
49+
var filePath = joinUriSegments(distDir, filePattern);
4950

5051
var options = {
5152
bucket: bucket,
@@ -59,18 +60,23 @@ module.exports = {
5960
allowOverwrite: allowOverwrite
6061
};
6162

63+
if (serverSideEncryption) {
64+
options.serverSideEncryption = serverSideEncryption;
65+
}
66+
6267
this.log('preparing to upload revision to S3 bucket `' + bucket + '`', { verbose: true });
6368

6469
var s3 = new this.S3({ plugin: this });
6570
return s3.upload(options);
6671
},
6772

6873
activate: function(/* context */) {
69-
var bucket = this.readConfig('bucket');
70-
var prefix = this.readConfig('prefix');
71-
var acl = this.readConfig('acl');
72-
var revisionKey = this.readConfig('revisionKey');
73-
var filePattern = this.readConfig('filePattern');
74+
var bucket = this.readConfig('bucket');
75+
var prefix = this.readConfig('prefix');
76+
var acl = this.readConfig('acl');
77+
var revisionKey = this.readConfig('revisionKey');
78+
var filePattern = this.readConfig('filePattern');
79+
var serverSideEncryption = this.readConfig('serverSideEncryption');
7480

7581
var options = {
7682
bucket: bucket,
@@ -80,6 +86,10 @@ module.exports = {
8086
revisionKey: revisionKey,
8187
};
8288

89+
if (serverSideEncryption) {
90+
options.serverSideEncryption = serverSideEncryption;
91+
}
92+
8393
this.log('preparing to activate `' + revisionKey + '`', { verbose: true });
8494

8595
var s3 = new this.S3({ plugin: this });

lib/s3.js

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,18 @@ module.exports = CoreObject.extend({
3535
},
3636

3737
upload: function(options) {
38-
var client = this._client;
39-
var plugin = this._plugin;
40-
var bucket = options.bucket;
41-
var acl = options.acl;
42-
var cacheControl = options.cacheControl;
43-
var allowOverwrite = options.allowOverwrite;
44-
var key = options.filePattern + ":" + options.revisionKey;
45-
var revisionKey = joinUriSegments(options.prefix, key);
46-
var putObject = Promise.denodeify(client.putObject.bind(client));
47-
var gzippedFilePaths = options.gzippedFilePaths || [];
48-
var isGzipped = gzippedFilePaths.indexOf(options.filePattern) !== -1;
38+
var client = this._client;
39+
var plugin = this._plugin;
40+
var bucket = options.bucket;
41+
var acl = options.acl;
42+
var cacheControl = options.cacheControl;
43+
var allowOverwrite = options.allowOverwrite;
44+
var key = options.filePattern + ":" + options.revisionKey;
45+
var revisionKey = joinUriSegments(options.prefix, key);
46+
var putObject = Promise.denodeify(client.putObject.bind(client));
47+
var gzippedFilePaths = options.gzippedFilePaths || [];
48+
var isGzipped = gzippedFilePaths.indexOf(options.filePattern) !== -1;
49+
var serverSideEncryption = options.serverSideEncryption;
4950

5051
var params = {
5152
Bucket: bucket,
@@ -55,6 +56,10 @@ module.exports = CoreObject.extend({
5556
CacheControl: cacheControl
5657
};
5758

59+
if (serverSideEncryption) {
60+
params.ServerSideEncryption = serverSideEncryption;
61+
}
62+
5863
if (isGzipped) {
5964
params.ContentEncoding = 'gzip';
6065
}
@@ -77,18 +82,19 @@ module.exports = CoreObject.extend({
7782
},
7883

7984
activate: function(options) {
80-
var plugin = this._plugin;
81-
var client = this._client;
82-
var bucket = options.bucket;
83-
var acl = options.acl;
84-
var prefix = options.prefix;
85-
var filePattern = options.filePattern;
86-
var key = filePattern + ":" + options.revisionKey;
87-
88-
var revisionKey = joinUriSegments(prefix, key);
89-
var indexKey = joinUriSegments(prefix, filePattern);
90-
var copySource = encodeURIComponent([bucket, revisionKey].join('/'));
91-
var copyObject = Promise.denodeify(client.copyObject.bind(client));
85+
var plugin = this._plugin;
86+
var client = this._client;
87+
var bucket = options.bucket;
88+
var acl = options.acl;
89+
var prefix = options.prefix;
90+
var filePattern = options.filePattern;
91+
var key = filePattern + ":" + options.revisionKey;
92+
var serverSideEncryption = options.serverSideEncryption;
93+
94+
var revisionKey = joinUriSegments(prefix, key);
95+
var indexKey = joinUriSegments(prefix, filePattern);
96+
var copySource = encodeURIComponent([bucket, revisionKey].join('/'));
97+
var copyObject = Promise.denodeify(client.copyObject.bind(client));
9298

9399
var params = {
94100
Bucket: bucket,
@@ -97,6 +103,10 @@ module.exports = CoreObject.extend({
97103
ACL: acl,
98104
};
99105

106+
if (serverSideEncryption) {
107+
params.ServerSideEncryption = serverSideEncryption;
108+
}
109+
100110
return this.fetchRevisions(options).then(function(revisions) {
101111
var found = revisions.map(function(element) { return element.revision; }).indexOf(options.revisionKey);
102112
if (found >= 0) {

tests/unit/index-nodetest.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,25 @@ describe('s3-index plugin', function() {
125125
});
126126
});
127127

128+
it('detects serverSideEncryption when defined', function() {
129+
context.config['s3-index'].serverSideEncryption = 'AES256';
130+
var promise = plugin.upload(context);
131+
132+
return assert.isFulfilled(promise)
133+
.then(function() {
134+
assert.equal(s3Options.serverSideEncryption, 'AES256', 'serverSideEncryption passed correctly');
135+
});
136+
});
137+
138+
it('filters serverSideEncryption when not defined', function() {
139+
var promise = plugin.upload(context);
140+
141+
return assert.isFulfilled(promise)
142+
.then(function() {
143+
assert.equal(s3Options.hasOwnProperty('serverSideEncryption'), false, 'serverSideEncryption filtered correctly');
144+
});
145+
});
146+
128147
it('passes cacheControl options based on the cacheControl option to the s3-abstraction', function() {
129148
var cacheControl = 'max-age=3600';
130149
context.config['s3-index'].cacheControl = cacheControl;
@@ -192,6 +211,25 @@ describe('s3-index plugin', function() {
192211
assert.deepEqual(s3Options, expected);
193212
});
194213
});
214+
215+
it('detects serverSideEncryption when defined', function() {
216+
context.config['s3-index'].serverSideEncryption = 'AES256';
217+
var promise = plugin.activate(context);
218+
219+
return assert.isFulfilled(promise)
220+
.then(function() {
221+
assert.equal(s3Options.serverSideEncryption, 'AES256', 'serverSideEncryption passed correctly');
222+
});
223+
});
224+
225+
it('filters serverSideEncryption when not defined', function() {
226+
var promise = plugin.activate(context);
227+
228+
return assert.isFulfilled(promise)
229+
.then(function() {
230+
assert.equal(s3Options.hasOwnProperty('serverSideEncryption'), false, 'serverSideEncryption filtered correctly');
231+
});
232+
});
195233
});
196234

197235
describe('#fetchInitialRevisions', function() {

tests/unit/lib/s3-nodetest.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,25 @@ describe('s3', function() {
125125
});
126126
});
127127

128+
it('detects serverSideEncryption when defined', function() {
129+
options.serverSideEncryption = 'AES256';
130+
var promise = subject.upload(options);
131+
132+
return assert.isFulfilled(promise)
133+
.then(function() {
134+
assert.equal(s3Params.ServerSideEncryption, 'AES256', 'serverSideEncryption passed correctly');
135+
});
136+
});
137+
138+
it('filters serverSideEncryption when not defined', function() {
139+
var promise = subject.upload(options);
140+
141+
return assert.isFulfilled(promise)
142+
.then(function() {
143+
assert.equal(s3Params.hasOwnProperty('serverSideEncryption'), false, 'serverSideEncryption filtered correctly');
144+
});
145+
});
146+
128147
it('detects `filePattern` other than `index.html` in order to customize ContentType', function() {
129148
var filePath = 'tests/unit/fixtures/test.tar';
130149

@@ -346,6 +365,25 @@ describe('s3', function() {
346365
assert.equal(copyParams.ACL, acl);
347366
});
348367
});
368+
369+
it('detects serverSideEncryption when defined', function() {
370+
options.serverSideEncryption = 'AES256';
371+
var promise = subject.activate(options);
372+
373+
return assert.isFulfilled(promise)
374+
.then(function() {
375+
assert.equal(copyParams.ServerSideEncryption, 'AES256', 'serverSideEncryption passed correctly');
376+
});
377+
});
378+
379+
it('filters serverSideEncryption when not defined', function() {
380+
var promise = subject.activate(options);
381+
382+
return assert.isFulfilled(promise)
383+
.then(function() {
384+
assert.equal(copyParams.hasOwnProperty('serverSideEncryption'), false, 'serverSideEncryption filtered correctly');
385+
});
386+
});
349387
});
350388

351389
describe('with an invalid revision key', function() {

0 commit comments

Comments
 (0)