Skip to content

Commit 39cacc1

Browse files
Arthur Cinaderflovilmart
authored andcommitted
Use default AWS credential provider. (#15)
* Use default AWS credential provider. * Make key and secret optional * Alter spec for optional credentials * Update README * conform tests to current, not really right configuration order * make key and secret optional, but don't break backward compatibility - add unit test to cover - move arg processing to own lib module to facilitate unit testing. * More doc changes. * Make it explicit that using the default provider for credentials is preferred. * Fix one spot where the doc order for arguments were wrong. * change test to expect a specific error * Make readme less ambiguous
1 parent 3405674 commit 39cacc1

File tree

4 files changed

+145
-72
lines changed

4 files changed

+145
-72
lines changed

README.md

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ parse-server adapter for AWS S3
88

99
`npm install --save parse-server-s3-adapter`
1010

11+
# aws credentials
12+
13+
Although it is not recommended, AWS credentials can be explicitly configured through an options
14+
object, constructor string arguments or environment variables ([see below](#using-a-config-file)).
15+
This option is provided for backward compatibility.
16+
17+
The preferred method is to use the default AWS credentials pattern. If no AWS credentials are explicitly configured, the AWS SDK will look for credentials in the standard locations used by all AWS SDKs and the AWS CLI. More info can be found in [the docs](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#config-settings-and-precedence). For more information on AWS best practices, see [IAM Best Practices User Guide](http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html).
18+
1119
# usage with parse-server
1220

1321
### using a config file
@@ -20,10 +28,10 @@ parse-server adapter for AWS S3
2028
"filesAdapter": {
2129
"module": "parse-server-s3-adapter",
2230
"options": {
23-
"accessKey": "accessKey",
24-
"secretKey": "secretKey",
2531
"bucket": "my_bucket",
2632
// optional:
33+
"accessKey": "accessKey",
34+
"secretKey": "secretKey",
2735
"region": 'us-east-1', // default value
2836
"bucketPrefix": '', // default value
2937
"directAccess": false, // default value
@@ -40,10 +48,15 @@ parse-server adapter for AWS S3
4048

4149
Set your environment variables:
4250

51+
```
52+
S3_BUCKET=bucketName
53+
```
54+
55+
the following optional configurations can be set by environment variables too:
56+
4357
```
4458
S3_ACCESS_KEY=accessKey
4559
S3_SECRET_KEY=secretKey
46-
S3_BUCKET=bucketName
4760
S3_SIGNATURE_VERSION=v4
4861
```
4962

@@ -60,13 +73,11 @@ And update your config / options
6073

6174

6275
### passing as an instance
63-
6476
```
6577
var S3Adapter = require('parse-server-s3-adapter');
6678
6779
var s3Adapter = new S3Adapter('accessKey',
68-
'secretKey',
69-
'bucket' , {
80+
'secretKey', bucket, {
7081
region: 'us-east-1'
7182
bucketPrefix: '',
7283
directAccess: false,
@@ -81,21 +92,30 @@ var api = new ParseServer({
8192
filesAdapter: s3adapter
8293
})
8394
```
95+
**Note:** there are a few ways you can pass arguments:
96+
97+
```
98+
S3Adapter("bucket")
99+
S3Adapter("bucket", options)
100+
S3Adapter("key", "secret", "bucket")
101+
S3Adapter("key", "secret", "bucket", options)
102+
S3Adapter(options) // where options must contain bucket.
103+
```
84104

85105
or with an options hash
86106

87107
```
88108
var S3Adapter = require('parse-server-s3-adapter');
89109
90110
var s3Options = {
91-
"accessKey": "accessKey",
92-
"secretKey": "secretKey",
93111
"bucket": "my_bucket",
94112
// optional:
113+
"accessKey": null, // default value
114+
"secretKey": null, // default value
95115
"region": 'us-east-1', // default value
96116
"bucketPrefix": '', // default value
97117
"directAccess": false, // default value
98-
"baseUrl": null // default value,
118+
"baseUrl": null // default value
99119
"signatureVersion": 'v4', // default value
100120
"globalCacheControl": null // default value. Or 'public, max-age=86400000' for 24 hrs Cache-Control
101121
}

index.js

Lines changed: 8 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,7 @@
44
// Stores Parse files in AWS S3.
55

66
var AWS = require('aws-sdk');
7-
const DEFAULT_S3_REGION = "us-east-1";
8-
9-
function requiredOrFromEnvironment(options, key, env) {
10-
options[key] = options[key] || process.env[env];
11-
if (!options[key]) {
12-
throw `S3Adapter requires option '${key}' or env. variable ${env}`;
13-
}
14-
return options;
15-
}
16-
17-
function fromEnvironmentOrDefault(options, key, env, defaultValue) {
18-
options[key] = options[key] || process.env[env] || defaultValue;
19-
return options;
20-
}
21-
22-
function optionsFromArguments(args) {
23-
let options = {};
24-
let accessKeyOrOptions = args[0];
25-
if (typeof accessKeyOrOptions == 'string') {
26-
options.accessKey = accessKeyOrOptions;
27-
options.secretKey = args[1];
28-
options.bucket = args[2];
29-
let otherOptions = args[3];
30-
if (otherOptions) {
31-
options.bucketPrefix = otherOptions.bucketPrefix;
32-
options.directAccess = otherOptions.directAccess;
33-
options.baseUrl = otherOptions.baseUrl;
34-
options.baseUrlDirect = otherOptions.baseUrlDirect;
35-
options.signatureVersion = otherOptions.signatureVersion;
36-
options.globalCacheControl = otherOptions.globalCacheControl;
37-
}
38-
} else {
39-
options = accessKeyOrOptions || {};
40-
}
41-
options = requiredOrFromEnvironment(options, 'accessKey', 'S3_ACCESS_KEY');
42-
options = requiredOrFromEnvironment(options, 'secretKey', 'S3_SECRET_KEY');
43-
options = requiredOrFromEnvironment(options, 'bucket', 'S3_BUCKET');
44-
options = fromEnvironmentOrDefault(options, 'bucketPrefix', 'S3_BUCKET_PREFIX', '');
45-
options = fromEnvironmentOrDefault(options, 'region', 'S3_REGION', DEFAULT_S3_REGION);
46-
options = fromEnvironmentOrDefault(options, 'directAccess', 'S3_DIRECT_ACCESS', false);
47-
options = fromEnvironmentOrDefault(options, 'baseUrl', 'S3_BASE_URL', null);
48-
options = fromEnvironmentOrDefault(options, 'baseUrlDirect', 'S3_BASE_URL_DIRECT', false);
49-
options = fromEnvironmentOrDefault(options, 'signatureVersion', 'S3_SIGNATURE_VERSION', 'v4');
50-
options = fromEnvironmentOrDefault(options, 'globalCacheControl', 'S3_GLOBAL_CACHE_CONTROL', null);
51-
52-
return options;
53-
}
7+
var optionsFromArguments = require('./lib/optionsFromArguments');
548

559
// Creates an S3 session.
5610
// Providing AWS access, secret keys and bucket are mandatory
@@ -67,13 +21,17 @@ function S3Adapter() {
6721
this._globalCacheControl = options.globalCacheControl;
6822

6923
let s3Options = {
70-
accessKeyId: options.accessKey,
71-
secretAccessKey: options.secretKey,
7224
params: { Bucket: this._bucket },
7325
region: this._region,
7426
signatureVersion: this._signatureVersion,
7527
globalCacheControl: this._globalCacheControl
7628
};
29+
30+
if (options.accessKey && options.secretKey) {
31+
options.accessKeyId = options.accessKey;
32+
options.secretAccessKey = options.secretKey;
33+
}
34+
7735
this._s3Client = new AWS.S3(s3Options);
7836
this._hasBucket = false;
7937
}
@@ -147,7 +105,7 @@ S3Adapter.prototype.getFileData = function(filename) {
147105
if (err !== null) {
148106
return reject(err);
149107
}
150-
// Something happend here...
108+
// Something happened here...
151109
if (data && !data.Body) {
152110
return reject(data);
153111
}

lib/optionsFromArguments.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
'use strict';
2+
3+
const DEFAULT_S3_REGION = 'us-east-1';
4+
5+
function requiredOrFromEnvironment(options, key, env) {
6+
options[key] = options[key] || process.env[env];
7+
if (!options[key]) {
8+
throw `S3Adapter requires option '${key}' or env. variable ${env}`;
9+
}
10+
return options;
11+
}
12+
13+
function fromEnvironmentOrDefault(options, key, env, defaultValue) {
14+
options[key] = options[key] || process.env[env] || defaultValue;
15+
return options;
16+
}
17+
18+
const optionsFromArguments = function optionsFromArguments(args) {
19+
const stringOrOptions = args[0];
20+
let options = {};
21+
let otherOptions;
22+
23+
if (typeof stringOrOptions == 'string') {
24+
if (args.length == 1) {
25+
options.bucket = stringOrOptions;
26+
} else if (args.length == 2) {
27+
options.bucket = stringOrOptions;
28+
if (typeof args[1] != 'object') {
29+
throw new Error('Failed to configure S3Adapter. Arguments don\'t make sense');
30+
}
31+
otherOptions = args[1];
32+
} else if (args.length > 2) {
33+
if (typeof args[1] != 'string' || typeof args[2] != 'string') {
34+
throw new Error('Failed to configure S3Adapter. Arguments don\'t make sense');
35+
}
36+
options.accessKey = args[0];
37+
options.secretKey = args[1];
38+
options.bucket = args[2];
39+
otherOptions = args[3];
40+
}
41+
42+
if (otherOptions) {
43+
options.bucketPrefix = otherOptions.bucketPrefix;
44+
options.directAccess = otherOptions.directAccess;
45+
options.baseUrl = otherOptions.baseUrl;
46+
options.baseUrlDirect = otherOptions.baseUrlDirect;
47+
options.signatureVersion = otherOptions.signatureVersion;
48+
options.globalCacheControl = otherOptions.globalCacheControl;
49+
}
50+
} else {
51+
options = stringOrOptions || {};
52+
}
53+
options = requiredOrFromEnvironment(options, 'bucket', 'S3_BUCKET');
54+
options = fromEnvironmentOrDefault(options, 'accessKey', 'S3_ACCESS_KEY', null);
55+
options = fromEnvironmentOrDefault(options, 'secretKey', 'S3_SECRET_KEY', null);
56+
options = fromEnvironmentOrDefault(options, 'bucketPrefix', 'S3_BUCKET_PREFIX', '');
57+
options = fromEnvironmentOrDefault(options, 'region', 'S3_REGION', DEFAULT_S3_REGION);
58+
options = fromEnvironmentOrDefault(options, 'directAccess', 'S3_DIRECT_ACCESS', false);
59+
options = fromEnvironmentOrDefault(options, 'baseUrl', 'S3_BASE_URL', null);
60+
options = fromEnvironmentOrDefault(options, 'baseUrlDirect', 'S3_BASE_URL_DIRECT', false);
61+
options = fromEnvironmentOrDefault(options, 'signatureVersion', 'S3_SIGNATURE_VERSION', 'v4');
62+
options = fromEnvironmentOrDefault(
63+
options, 'globalCacheControl', 'S3_GLOBAL_CACHE_CONTROL', null);
64+
65+
return options;
66+
}
67+
68+
module.exports = optionsFromArguments;

spec/test.spec.js

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,67 @@
22
let filesAdapterTests = require('parse-server-conformance-tests').files;
33

44
let S3Adapter = require('../index.js');
5+
let optionsFromArguments = require('../lib/optionsFromArguments');
56

67
describe('S3Adapter tests', () => {
78

89
it('should throw when not initialized properly', () => {
910
expect(() => {
1011
var s3 = new S3Adapter();
11-
}).toThrow("S3Adapter requires option 'accessKey' or env. variable S3_ACCESS_KEY")
12-
13-
expect(() => {
14-
var s3 = new S3Adapter('accessKey');
15-
}).toThrow("S3Adapter requires option 'secretKey' or env. variable S3_SECRET_KEY")
16-
17-
expect(() => {
18-
var s3 = new S3Adapter('accessKey', 'secretKey');
1912
}).toThrow("S3Adapter requires option 'bucket' or env. variable S3_BUCKET")
2013

21-
expect(() => {
22-
var s3 = new S3Adapter({ accessKey: 'accessKey'});
23-
}).toThrow("S3Adapter requires option 'secretKey' or env. variable S3_SECRET_KEY")
14+
expect(() =>  {
15+
var s3 = new S3Adapter('accessKey', 'secretKey', {});
16+
}).toThrow(new Error('Failed to configure S3Adapter. Arguments don\'t make sense'));
17+
2418
expect(() => {
2519
var s3 = new S3Adapter({ accessKey: 'accessKey' , secretKey: 'secretKey'});
2620
}).toThrow("S3Adapter requires option 'bucket' or env. variable S3_BUCKET")
2721
})
2822

2923
it('should not throw when initialized properly', () => {
3024
expect(() => {
31-
var s3 = new S3Adapter('accessKey', 'secretKey', 'bucket');
25+
var s3 = new S3Adapter('bucket');
3226
}).not.toThrow()
3327

3428
expect(() => {
35-
var s3 = new S3Adapter({ accessKey: 'accessKey' , secretKey: 'secretKey', bucket: 'bucket'});
29+
var s3 = new S3Adapter({ bucket: 'bucket'});
3630
}).not.toThrow()
3731
});
3832

33+
describe('to find the right arg in the right place', () => {
34+
it('should accept just bucket as first string arg', () => {
35+
var args = ['bucket'];
36+
var options = optionsFromArguments(args);
37+
expect(options.bucket).toEqual('bucket');
38+
});
39+
40+
it('should accept bucket and options', () => {
41+
var confObj = { bucketPrefix: 'test/' };
42+
var args = ['bucket', confObj];
43+
var options = optionsFromArguments(args);
44+
expect(options.bucket).toEqual('bucket');
45+
expect(options.bucketPrefix).toEqual('test/');
46+
});
47+
48+
it('should accept key, secret, and bucket as args', () => {
49+
var args = ['key', 'secret', 'bucket'];
50+
var options = optionsFromArguments(args);
51+
expect(options.accessKey).toEqual('key');
52+
expect(options.secretKey).toEqual('secret');
53+
expect(options.bucket).toEqual('bucket');
54+
});
55+
56+
it('should accept key, secret, bucket, and options object as args', () => {
57+
var confObj = { bucketPrefix: 'test/' };
58+
var args = ['key', 'secret', 'bucket', confObj];
59+
var options = optionsFromArguments(args);
60+
expect(options.accessKey).toEqual('key');
61+
expect(options.secretKey).toEqual('secret');
62+
expect(options.bucket).toEqual('bucket');
63+
expect(options.bucketPrefix).toEqual('test/');
64+
});
65+
});
3966

4067
describe('getFileLocation', () => {
4168
var config = {

0 commit comments

Comments
 (0)