ac-awssecrets 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.acsemver.js ADDED
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ repository: {
3
+ url: 'https://github.com/mmpro/ac-awssecrets'
4
+ },
5
+ changelogFile: __dirname + '/CHANGELOG.md',
6
+ sections: [
7
+ {name: 'App' }
8
+ ],
9
+ }
package/.eslintrc.js ADDED
@@ -0,0 +1,26 @@
1
+ const config = {
2
+ 'env': {
3
+ 'commonjs': true,
4
+ 'es6': true,
5
+ 'node': true
6
+ },
7
+ 'extends': 'eslint:recommended',
8
+ "rules": {
9
+ "space-before-function-paren": 0,
10
+ "no-extra-semi": 0,
11
+ "object-curly-spacing": ["error", "always"],
12
+ "brace-style": ["error", "stroustrup", { "allowSingleLine": true }],
13
+ "no-useless-escape": 0,
14
+ "standard/no-callback-literal": 0,
15
+ "new-cap": 0
16
+ },
17
+ globals: {
18
+ describe: true,
19
+ it: true
20
+ },
21
+ 'parserOptions': {
22
+ 'ecmaVersion': 2018
23
+ },
24
+ }
25
+
26
+ module.exports = config
package/CHANGELOG.md ADDED
@@ -0,0 +1,68 @@
1
+ <a name="1.1.3"></a>
2
+
3
+ ## [1.1.3](https://github.com/mmpro/ac-awssecrets/compare/v1.1.2..v1.1.3) (2021-09-22 11:13:39)
4
+
5
+
6
+ ### Bug Fix
7
+
8
+ * **App:** Package updates | MP | [82f4a90c24c4c94279bf23621dd92805e01ca4d7](https://github.com/mmpro/ac-awssecrets/commit/82f4a90c24c4c94279bf23621dd92805e01ca4d7)
9
+ Package updates
10
+ <a name="1.1.2"></a>
11
+
12
+ ## [1.1.2](https://github.com/mmpro/ac-awssecrets/compare/v1.1.1..v1.1.2) (2021-05-31 06:17:24)
13
+
14
+
15
+ ### Bug Fix
16
+
17
+ * **App:** Package updates | MP | [466bc49d1d270b784cc35c0ab3e476b3fd6e3587](https://github.com/mmpro/ac-awssecrets/commit/466bc49d1d270b784cc35c0ab3e476b3fd6e3587)
18
+ Package updates
19
+ <a name="1.1.1"></a>
20
+
21
+ ## [1.1.1](https://github.com/mmpro/ac-awssecrets/compare/v1.1.0..v1.1.1) (2020-03-29 14:10:49)
22
+
23
+
24
+ ### Bug Fix
25
+
26
+ * **App:** Prepare repository for AC semantic release | MP | [f8f652bc09e1581e2ec35b3be6d51045bb905576](https://github.com/mmpro/ac-awssecrets/commit/f8f652bc09e1581e2ec35b3be6d51045bb905576)
27
+ Cleaned up repository and use ac-semantic-release
28
+ ### Chores
29
+
30
+ * **Misc:** Updated packages | MP | [83244d834fd80f34fbaf450fdefcdfa5b78f91f2](https://github.com/mmpro/ac-awssecrets/commit/83244d834fd80f34fbaf450fdefcdfa5b78f91f2)
31
+ Updated packages
32
+ <a name="1.1.0"></a>
33
+ # [1.1.0](https://github.com/mmpro/ac-awssecrets/compare/v1.0.1...v1.1.0) (2020-02-16 18:42)
34
+
35
+
36
+ ### Features
37
+
38
+ * **Misc:** The servers parameter now support more flexibility | mp ([01f0a798f543108ecef72771de5705764f7db82f](https://github.com/mmpro/ac-awssecrets/commit/01f0a798f543108ecef72771de5705764f7db82f))
39
+ You can set custom identifiers for array of objects - see README
40
+
41
+
42
+
43
+ <a name="1.0.1"></a>
44
+ ## [1.0.1](https://github.com/mmpro/ac-awssecrets/compare/v1.0.0...v1.0.1) (2019-07-24 19:43)
45
+
46
+
47
+ ### Bug Fixes
48
+
49
+ * **Misc:** Force version bump | MP ([fbfb1ae](https://github.com/mmpro/ac-awssecrets/commit/fbfb1ae))
50
+ Force version bump
51
+
52
+
53
+
54
+ <a name="1.0.0"></a>
55
+ # 1.0.0 (2019-07-24 19:42)
56
+
57
+
58
+ ### Bug Fixes
59
+
60
+ * **Misc:** Package update | MP ([dbb4c23](https://github.com/mmpro/ac-awssecrets/commit/dbb4c23))
61
+ Package update and some minor adjustments for corp-release/semver
62
+ * **Misc:** Package update | MP ([8224bf7](https://github.com/mmpro/ac-awssecrets/commit/8224bf7))
63
+ Package update and some minor adjustments for corp-release/semver
64
+ * **Misc:** Package update | MP ([14e79bc](https://github.com/mmpro/ac-awssecrets/commit/14e79bc))
65
+ Package update and some minor adjustments for corp-release/semver
66
+
67
+
68
+
package/Makefile ADDED
@@ -0,0 +1,16 @@
1
+ MOCHA_OPTS= --slow 0 -A
2
+ REPORTER = spec
3
+
4
+ lint-fix:
5
+ ./node_modules/.bin/eslint --fix index.js test/test.js
6
+
7
+ lint-check:
8
+ ./node_modules/.bin/eslint index.js test/test.js
9
+
10
+ commit:
11
+ @node ./node_modules/ac-semantic-release/lib/commit.js
12
+
13
+ release:
14
+ @node ./node_modules/ac-semantic-release/lib/release.js
15
+
16
+ .PHONY: check
package/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # AC AWS Secrets
2
+ Reads secrets from AWS secrets manager and adds them to the configuration of the embedding app.
3
+
4
+ ## Usage
5
+
6
+ ### Simple example
7
+ Lets assume we have the following configuration and secret
8
+
9
+ ```
10
+ let existingConfig = {
11
+ redis: {
12
+ host: 'localhost'
13
+ }
14
+ }
15
+
16
+ // Stored under name "redis.cacheServer" in AWS
17
+ let secret = {
18
+ host: 'my-secret-server'
19
+ }
20
+ ```
21
+
22
+ The following setup will replace the existing configuration and redis.host will be "my-secret-server"
23
+ ```
24
+ const secretParams = {
25
+ aws: {
26
+ accessKeyId: 'accessKeyId',
27
+ secretAccessKey: 'secretAccessKey',
28
+ region: 'eu-central-1'
29
+ },
30
+ secrets: [
31
+ { key: 'redis', name: 'redis.cacheServer', ignoreInTestMode: true }
32
+ ],
33
+ config: existingConfig,
34
+ environment: 'development'
35
+ }
36
+
37
+ awsSecrets.loadSecrets(secretParams, (err, result) => {
38
+ if (err) return cb(err)
39
+ _.forEach(result, (item) => {
40
+ console.log('Setting secret for', _.padEnd(_.get(item, 'key'), 25), '->', _.get(item, 'name'))
41
+ })
42
+ return cb()
43
+ })
44
+ ```
45
+
46
+ ### Example with array of objects
47
+
48
+ ```
49
+ let existingConfig = {
50
+ redis: {
51
+ databases: [
52
+ { db: 0, name: 'cache' },
53
+ { db: 1, name: 'auth' }
54
+ ]
55
+ }
56
+ }
57
+
58
+ // secret stored under "redis.cacheServer"
59
+ let secret = {
60
+ host: 'my-secret-server'
61
+
62
+ // secert storend under "redis.authServer"
63
+ let secret = {
64
+ host: 'my-auth-server
65
+ }
66
+
67
+ // now use the function
68
+ const secretParams = {
69
+ aws: {
70
+ accessKeyId: 'accessKeyId',
71
+ secretAccessKey: 'secretAccessKey',
72
+ region: 'eu-central-1'
73
+ },
74
+ secrets: [
75
+ { key: 'redis.databases', name: 'redis.cacheServer', servers: { identifier: 'name', value: 'cache' } }
76
+ { key: 'redis.databases', name: 'redis.authServer', servers: { identifier: 'name', value: 'auth' } }
77
+ ],
78
+ config: existingConfig,
79
+ environment: 'development'
80
+ }
81
+
82
+ awsSecrets.loadSecrets(secretParams, (err, result) => {
83
+ // now
84
+ redis.databases: [
85
+ { db: 0, name: 'cache', host: 'my-secret-server' },
86
+ { db: 1, name: 'auth', host: 'my-auth-server' }
87
+ ]
88
+ })
89
+
90
+ ```
91
+
92
+ ## Parameters
93
+ + aws - object with accessKeyId, secretAccessKey and region (IAM user must have permission to read the secrets)
94
+ + secrets - array of secrets to fetch
95
+ + config - the current config (secrets will be merged into it)
96
+ + environment - the current node environment (e.g. test, production, ... defaults to development)
97
+
98
+ ## Links
99
+ - [Website](https://www.admiralcloud.com/)
100
+ - [Twitter (@admiralcloud)](https://twitter.com/admiralcloud)
101
+ - [Facebook](https://www.facebook.com/MediaAssetManagement/)
102
+
103
+ ## License
104
+ [MIT License](https://opensource.org/licenses/MIT) Copyright © 2009-present, AdmiralCloud, Mark Poepping
package/index.js ADDED
@@ -0,0 +1,185 @@
1
+ const _ = require('lodash')
2
+ const async = require('async')
3
+ const AWS = require('aws-sdk')
4
+
5
+ /**
6
+ * @param aws OBJ object with aws configuration data
7
+ */
8
+
9
+ const awsSecrets = () => {
10
+ const loadSecrets = (params, cb) => {
11
+ const multiSecrets = _.get(params, 'multisecrets', [])
12
+ const secrets = _.get(params, 'secrets', [])
13
+ let config = _.get(params, 'config', {}) // the config object -> will be changed by reference
14
+ const environment = _.get(config, 'environment', 'development')
15
+
16
+ const region = _.get(params, 'aws.region', 'eu-central-1')
17
+ const endpoint = _.find(awsEndpoints, { region })
18
+ const client = new AWS.SecretsManager({
19
+ accessKeyId: _.get(params, 'aws.accessKeyId'),
20
+ secretAccessKey: _.get(params, 'aws.secretAccessKey'),
21
+ region: region,
22
+ endpoint: _.get(endpoint, 'endpoint')
23
+ })
24
+
25
+ let result = []
26
+ async.series({
27
+ fetchPlacholders: (done) => {
28
+ if (!_.size(multiSecrets)) return done()
29
+ // some keys can have multiple entries (e.g. cloudfrontCOnfigs can have 1 - n entries)
30
+ // we have to fetch them first from a secret and add them to the secrets to fetch
31
+
32
+ async.each(multiSecrets, (secret, itDone) => {
33
+ let secretName = (config.environment === 'test' ? 'test.' : '') + _.get(secret, 'name')
34
+
35
+ client.getSecretValue({ SecretId: secretName }, function(err, data) {
36
+ if (err) {
37
+ if (_.get(secret, 'ignoreInTestMode')) return itDone()
38
+ if (_.get(secret, 'ignoreIfMissing')) return itDone() // this is an optional key
39
+
40
+ console.error('Fetching secret %s failed', secretName, err)
41
+ return itDone({ message: err, additionalInfo: { key: secretName } })
42
+ }
43
+ if (!_.get(data, 'SecretString')) {
44
+ console.warn('Secret %s NOT avaialble', secretName, _.get(data, 'SecretString'))
45
+ return itDone()
46
+ }
47
+
48
+ let value
49
+ try {
50
+ value = JSON.parse(_.get(data, 'SecretString'))
51
+ }
52
+ catch (e) {
53
+ value = _.get(data, 'SecretString.values')
54
+ }
55
+
56
+ try {
57
+ value = JSON.parse(_.get(value, 'values'))
58
+ }
59
+ catch (e) {
60
+ return done({ message: 'placeHolderSecrets_valuesInvalid', additionalInfo: { key: secret.key } })
61
+ }
62
+
63
+ // value should be an array of keys
64
+ _.forEach(value, (item) => {
65
+ secrets.push({
66
+ key: secret.key,
67
+ name: item,
68
+ type: 'arrayObject'
69
+ })
70
+ })
71
+ return itDone()
72
+ })
73
+ }, done)
74
+ },
75
+ fetchSecrets: (done) => {
76
+ if (!_.size(secrets)) return done()
77
+ async.each(secrets, (secret, itDone) => {
78
+ if (environment === 'test' && _.get(secret, 'ignoreInTestMode')) return itDone()
79
+ // key is the local configuration path
80
+ let key = _.get(secret, 'key')
81
+ // secret name is the name used to fetch the secret
82
+ let secretName = (config.environment === 'test' ? 'test.' : '') + _.get(secret, 'name') + (_.get(secret, 'suffix') ? '.' + _.get(secret, 'suffix') : '')
83
+
84
+ client.getSecretValue({ SecretId: secretName }, function(err, data) {
85
+ if (err) {
86
+ if (_.get(secret, 'ignoreIfMissing')) return itDone() // this is an optional key
87
+
88
+ console.error('Fetching secret %s failed', secretName, _.get(err, 'message', err))
89
+ return itDone({ message: _.get(err, 'message', err), additionalInfo: { key: secretName } })
90
+ }
91
+ if (!_.get(data, 'SecretString')) {
92
+ console.warn('Secret %s NOT avaialble', secretName, _.get(data, 'SecretString'))
93
+ return itDone()
94
+ }
95
+
96
+ let value
97
+ try {
98
+ value = JSON.parse(_.get(data, 'SecretString'))
99
+
100
+ // if value is prefixed with JSON -> parse the value
101
+ if (_.get(value, 'valueHasJSON')) {
102
+ _.forEach(value, (val, key) => {
103
+ if (_.startsWith(val, 'JSON:')) {
104
+ try {
105
+ _.set(value, key, JSON.parse(val.substr(5)))
106
+ }
107
+ catch (e) {
108
+ throw e
109
+ }
110
+ }
111
+ })
112
+ // remove that entry
113
+ _.unset(value, 'valueHasJSON')
114
+ }
115
+ }
116
+ catch (e) {
117
+ value = _.get(data, 'SecretString')
118
+ }
119
+
120
+ // make sure boolean values are converted (from string)
121
+ value = _.mapValues(value, (val) => {
122
+ if (val === 'true') return true
123
+ else if (val === 'false') return false
124
+ else return val
125
+ })
126
+ let existingValue = _.get(config, key, {})
127
+
128
+ if (secret.servers) {
129
+ if (_.isBoolean(secret.servers)) {
130
+ // LEGACY SUPPORT FOR OLD NOTATION - DEPRECATED - DO NOT USE ANY LONGER
131
+ existingValue = _.find(_.get(config, key + '.servers', []), { server: secret.serverName })
132
+ }
133
+ else {
134
+ // NEW NOTATION AS OBJECT
135
+ let match = {}
136
+ _.set(match, _.get(secret.servers, 'identifier'), _.get(secret.servers, 'value'))
137
+ existingValue = _.find(_.get(config, key, []), match)
138
+ }
139
+ }
140
+ if (_.get(secret, 'type') === 'array') {
141
+ let array = []
142
+ _.forEach(value, (val) => {
143
+ array.push(val)
144
+ })
145
+ value = _.concat(existingValue, array)
146
+ }
147
+
148
+ if (_.get(secret, 'type') === 'arrayObject') {
149
+ existingValue.push(value)
150
+ }
151
+ else {
152
+ let setFresh
153
+ if (_.isEmpty(existingValue)) setFresh = true
154
+ _.merge(existingValue, value)
155
+ // setFresh -> this property/path has never existed
156
+ if (setFresh) _.set(config, key, existingValue)
157
+ }
158
+
159
+ if (_.get(secret, 'log')) {
160
+ console.log(_.repeat('.', 90))
161
+ console.warn(key, existingValue)
162
+ console.warn(_.get(config, key))
163
+ console.warn(_.repeat('.', 90))
164
+ }
165
+
166
+ result.push({ key, name: _.get(secret, 'name', '-') })
167
+ return itDone()
168
+ })
169
+ }, done)
170
+ }
171
+ }, (err) => {
172
+ return cb(err, _.orderBy(result, 'key'))
173
+ })
174
+ }
175
+
176
+ const awsEndpoints = [
177
+ { region: 'eu-central-1', endpoint: 'https://secretsmanager.eu-central-1.amazonaws.com' }
178
+ ]
179
+
180
+ return {
181
+ loadSecrets
182
+ }
183
+ }
184
+
185
+ module.exports = awsSecrets()
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "ac-awssecrets",
3
+ "author": "Mark Poepping (https://www.mmpro.de)",
4
+ "license": "MIT",
5
+ "repository": "mmpro/ac-awssecrets",
6
+ "version": "1.1.3",
7
+ "dependencies": {
8
+ "async": "^3.2.1",
9
+ "aws-sdk": "^2.992.0",
10
+ "lodash": "^4.17.21"
11
+ },
12
+ "devDependencies": {
13
+ "ac-semantic-release": "^0.2.6",
14
+ "eslint": "7.x",
15
+ "mocha": "^9.1.1"
16
+ },
17
+ "scripts": {
18
+ "test": "mocha --reporter spec"
19
+ },
20
+ "engines": {
21
+ "node": ">=8.0.0"
22
+ }
23
+ }
package/test/test.js ADDED
@@ -0,0 +1,7 @@
1
+ //const awsSecrets = require('../index')
2
+
3
+ describe('Tests', () => {
4
+ it('Should work', done => {
5
+ return done()
6
+ })
7
+ })