@wavo-cloud/aws-secrets-manager-helper 0.1.8 → 0.1.11

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.
@@ -7,6 +7,13 @@ const client = new AWS.SecretsManager({
7
7
  region: secretsManagerRegion,
8
8
  })
9
9
 
10
+ /**
11
+ * Right now, we have client lists that tell our systems where a clients secrets are
12
+ * We should be using tags (ie all production client lists have a tag saying its a production client
13
+ * or test client etc)
14
+ * These tags will also hold the organization name so that client secrets can still be found by name
15
+ */
16
+
10
17
  /**
11
18
  * Attempts to parse secret
12
19
  * @param {String} secretString - The stringified secret
@@ -70,6 +77,28 @@ async function getSecretValue(secretId) {
70
77
  }
71
78
  }
72
79
 
80
+ /**
81
+ * Gets secret tags from aws secrets manager
82
+ * Returns Array of tags
83
+ * @param {String} secretId - The ID of an existing secret
84
+ */
85
+ async function getSecretTags(secretId) {
86
+ let promiseError
87
+ const data = await client
88
+ .describeSecret({ SecretId: secretId })
89
+ .promise()
90
+ .catch(error => {
91
+ promiseError = error
92
+ })
93
+
94
+ if (promiseError) {
95
+ handleAWSPromiseError(promiseError)
96
+ } else if ('Tags' in data) {
97
+ return data.Tags
98
+ }
99
+ return []
100
+ }
101
+
73
102
  /**
74
103
  * Gets the secret keying where client secrets are stored
75
104
  * Returns JSON where JSON is the secret
@@ -92,8 +121,13 @@ async function getClientSecret(clientId, clientIdsSecretIdOverride = null) {
92
121
  * Gets all client secrets holding their API keys
93
122
  * Returns [{ clientId: String, secretId: String, ...secret }]
94
123
  * Skips all that are missing a region or organization
124
+ * If activeOnly flag, it only returns active client secrets
125
+ * This is true by default
95
126
  */
96
- async function getAllClientSecrets(clientIdsSecretIdOverride = null) {
127
+ async function getAllClientSecrets(
128
+ clientIdsSecretIdOverride = null,
129
+ activeOnly = true
130
+ ) {
97
131
  const clientSecretIds = await getClientSecretIds(clientIdsSecretIdOverride)
98
132
  if (!clientSecretIds) return []
99
133
  const parsedClientSecrets = await Promise.all(
@@ -101,11 +135,21 @@ async function getAllClientSecrets(clientIdsSecretIdOverride = null) {
101
135
  const result = await getSecretValue(clientSecretIds[clientId])
102
136
  result.id = clientId
103
137
  result.secretId = clientSecretIds[clientId]
138
+ const tags = await getSecretTags(clientSecretIds[clientId])
139
+ const isActiveOrganizationTag = tags.find(
140
+ tag => tag.Key === 'is_active_organization'
141
+ ) || { Value: 'false' }
142
+ result.isActiveOrganization = isActiveOrganizationTag.Value
104
143
  return result
105
144
  })
106
145
  )
107
146
  return parsedClientSecrets.filter(clientSecret => {
108
- if (clientSecret.organization && clientSecret.region && !clientSecret.error)
147
+ if (
148
+ clientSecret.organization &&
149
+ clientSecret.region &&
150
+ !clientSecret.error &&
151
+ (!activeOnly || clientSecret.isActiveOrganization)
152
+ )
109
153
  return true
110
154
  return false
111
155
  })
@@ -117,7 +161,7 @@ async function getAllClientSecrets(clientIdsSecretIdOverride = null) {
117
161
  * @param {String} secretDescription - The description of the secret
118
162
  * @param {String} secretValue - The JSON string secret value
119
163
  */
120
- async function createSecret(secretName, secretDescription, secretValue) {
164
+ async function createSecret(secretName, secretDescription, secretValue, tags) {
121
165
  let promiseError
122
166
  if (typeof secretValue !== 'string')
123
167
  throw new Error('Secret value must be a JSON string.')
@@ -131,6 +175,7 @@ async function createSecret(secretName, secretDescription, secretValue) {
131
175
  Name: secretName,
132
176
  Description: secretDescription,
133
177
  SecretString: secretValue,
178
+ Tags: tags,
134
179
  })
135
180
  .promise()
136
181
  .catch(error => {
@@ -164,6 +209,50 @@ async function editSecret(secretName, secretValue) {
164
209
  else return data
165
210
  }
166
211
 
212
+ /**
213
+ * Removes a set of tags from a secret
214
+ * @param {String} secretName - The friendly name of the secret
215
+ * @param {Object} tagNames - An array of strings representing tags to remove
216
+ */
217
+ async function removeTags(secretName, tagNames) {
218
+ let promiseError
219
+ const data = await client
220
+ .untagResource({
221
+ SecretId: secretName,
222
+ TagKeys: tagNames,
223
+ })
224
+ .promise()
225
+ .catch(error => {
226
+ promiseError = error
227
+ })
228
+
229
+ if (promiseError) handleAWSPromiseError(promiseError)
230
+ else return data
231
+ }
232
+
233
+ /**
234
+ * Appends a set of tags to a secret
235
+ * @param {String} secretName - The friendly name of the secret
236
+ * @param {Object} tags - An array of objects representing tags to add
237
+ * Object structure:
238
+ * { Key: 'key', Value: 'value' }
239
+ */
240
+ async function appendTags(secretName, tags) {
241
+ let promiseError
242
+ const data = await client
243
+ .tagResource({
244
+ SecretId: secretName,
245
+ Tags: tags,
246
+ })
247
+ .promise()
248
+ .catch(error => {
249
+ promiseError = error
250
+ })
251
+
252
+ if (promiseError) handleAWSPromiseError(promiseError)
253
+ else return data
254
+ }
255
+
167
256
  /**
168
257
  * Adds a client to the client list and does data validation
169
258
  * @param {String} clientId - The ID of the client
@@ -172,14 +261,16 @@ async function editSecret(secretName, secretValue) {
172
261
  * @param {String} region - The sub location of the organization's data in S3
173
262
  * @param {Object} keyValuePairs - The secret to store for the client
174
263
  */
175
- async function createClient(
264
+ async function createClient({
176
265
  clientId,
177
266
  clientName,
178
267
  organization,
179
268
  region,
180
269
  keyValuePairs,
181
- clientIdsSecretIdOverride = null
182
- ) {
270
+ clientIdsSecretIdOverride = null,
271
+ isSelfServe = false,
272
+ isActiveOrganization = false,
273
+ }) {
183
274
  if (!clientId || !clientName || !organization || !region)
184
275
  throw new Error('Client ID, name, organization, and region are required.')
185
276
  const regex = new RegExp('^[a-z0-9-_]*$')
@@ -203,7 +294,17 @@ async function createClient(
203
294
  organization,
204
295
  region,
205
296
  ...keyValuePairs,
206
- })
297
+ }),
298
+ [
299
+ {
300
+ Key: 'self_serve',
301
+ Value: isSelfServe.toString(),
302
+ },
303
+ {
304
+ Key: 'is_active_organization',
305
+ Value: isActiveOrganization.toString(),
306
+ },
307
+ ]
207
308
  )
208
309
  // add to client list
209
310
  const editClientListResults = await editSecret(
@@ -225,14 +326,14 @@ async function createClient(
225
326
  * @param {String} region - The sub location of the organization's data in S3
226
327
  * @param {Object} keyValuePairs - The secret to store for the client
227
328
  */
228
- async function setClient(
329
+ async function setClient({
229
330
  clientId,
230
331
  clientName,
231
332
  organization,
232
333
  region,
233
334
  keyValuePairs,
234
- clientIdsSecretIdOverride = null
235
- ) {
335
+ clientIdsSecretIdOverride = null,
336
+ }) {
236
337
  if (!clientId || !clientName || !organization || !region)
237
338
  throw new Error('Client ID, name, organization, and region are required.')
238
339
  const regex = new RegExp('^[a-z0-9-_]*$')
@@ -271,12 +372,12 @@ async function setClient(
271
372
  * @param {Object} keyValuePairsToAdd
272
373
  * @param {Array} keysToDelete
273
374
  */
274
- async function editClient(
375
+ async function editClient({
275
376
  clientId,
276
377
  keyValuePairsToAdd,
277
378
  keysToDelete,
278
- clientIdsSecretIdOverride
279
- ) {
379
+ clientIdsSecretIdOverride,
380
+ }) {
280
381
  if (!clientId) throw new Error('Client ID is required.')
281
382
  if (
282
383
  keysToDelete.some(key =>
@@ -313,12 +414,36 @@ async function editClient(
313
414
  return { editClientResults }
314
415
  }
315
416
 
417
+ async function setClientActiveStatus({
418
+ clientId,
419
+ newActiveStatus,
420
+ clientIdsSecretIdOverride,
421
+ }) {
422
+ if (!clientId) throw new Error('Client ID is required.')
423
+ if (newActiveStatus === null || newActiveStatus === undefined)
424
+ throw new Error('New active status is required.')
425
+ if (typeof newActiveStatus !== 'boolean')
426
+ throw new Error('New active status must be a boolean')
427
+ // get client secret location
428
+ const currentClientList = await getClientSecretIds(clientIdsSecretIdOverride)
429
+ const clientSecretId = currentClientList[clientId]
430
+ await removeTags(clientSecretId, ['is_active_organization'])
431
+ await appendTags(clientSecretId, [
432
+ {
433
+ Key: 'is_active_organization',
434
+ Value: newActiveStatus.toString(),
435
+ },
436
+ ])
437
+ }
438
+
316
439
  module.exports = {
317
440
  getClientSecretIds,
318
441
  getClientSecret,
319
442
  getAllClientSecrets,
320
443
  getSecretValue,
444
+ getSecretTags,
321
445
  createClient,
322
446
  setClient,
323
447
  editClient,
448
+ setClientActiveStatus,
324
449
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wavo-cloud/aws-secrets-manager-helper",
3
- "version": "0.1.8",
3
+ "version": "0.1.11",
4
4
  "description": "Wavo Cloud Infallible AWS Secrets Manager Helper",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
package/test/aws.test.js CHANGED
@@ -3,9 +3,11 @@ const {
3
3
  getAllClientSecrets,
4
4
  getClientSecretIds,
5
5
  getSecretValue,
6
+ getSecretTags,
6
7
  createClient,
7
8
  setClient,
8
9
  editClient,
10
+ setClientActiveStatus,
9
11
  } = require('../app/utils/awsSecretsManager')
10
12
  const { deleteClient } = require('../app/utils/awsSecretsManagerDeleteHelper')
11
13
  const expect = require('chai').expect
@@ -25,33 +27,56 @@ describe('Test Secrets Manager Helper', async () => {
25
27
  })
26
28
 
27
29
  it('should create a new client', async () => {
28
- const createClientResult = await createClient(
29
- 'test_client_id',
30
- 'test_client_name',
31
- 'test_organization',
32
- 'test_region',
33
- { test_secret_key: 'test_secret_value' },
34
- clientIdsSecretId
35
- )
30
+ const createClientResult = await createClient({
31
+ clientId: 'test_client_id',
32
+ clientName: 'test_client_name',
33
+ organization: 'test_organization',
34
+ region: 'test_region',
35
+ keyValuePairs: { test_secret_key: 'test_secret_value' },
36
+ clientIdsSecretIdOverride: clientIdsSecretId,
37
+ isSelfServe: true,
38
+ isActiveOrganization: true,
39
+ })
36
40
  expect(createClientResult.createSecretResults.Name).to.equal(
37
41
  'test_organization/ad_platforms/api'
38
42
  )
39
43
 
44
+ const newClientTags = await getSecretTags('test_organization/ad_platforms/api')
45
+ const isActiveOrganizationTag = newClientTags.find(
46
+ tag => tag.Key === 'is_active_organization'
47
+ )
48
+ expect(isActiveOrganizationTag.Value === 'true')
49
+
40
50
  const clientList = await getClientSecretIds(clientIdsSecretId)
41
51
  expect(clientList['test_client_id']).to.equal(
42
52
  'test_organization/ad_platforms/api'
43
53
  )
44
54
  })
45
55
 
46
- it('should set a client', async () => {
47
- await setClient(
48
- 'test_client_id',
49
- 'test_client_name_2',
50
- 'test_organization',
51
- 'test_region_new',
52
- { test_secret_key_new: 'test_secret_value_new', to_delete: 'to_delete' },
53
- clientIdsSecretId
56
+ it('should change the clients active status to false', async () => {
57
+ await setClientActiveStatus({
58
+ clientId: 'test_client_id',
59
+ newActiveStatus: false,
60
+ clientIdsSecretIdOverride: clientIdsSecretId,
61
+ })
62
+
63
+ const clientTags = await getSecretTags('test_organization/ad_platforms/api')
64
+ const isActiveOrganizationTag = clientTags.find(
65
+ tag => tag.Key === 'is_active_organization'
54
66
  )
67
+ expect(isActiveOrganizationTag.Value === 'falsey')
68
+
69
+ })
70
+
71
+ it('should set a client', async () => {
72
+ await setClient({
73
+ clientId: 'test_client_id',
74
+ clientName: 'test_client_name_2',
75
+ organization: 'test_organization',
76
+ region: 'test_region_new',
77
+ keyValuePairs: { test_secret_key_new: 'test_secret_value_new', to_delete: 'to_delete' },
78
+ clientIdsSecretIdOverride: clientIdsSecretId
79
+ })
55
80
 
56
81
  const testSecret = await getSecretValue(
57
82
  'test_organization/ad_platforms/api'
@@ -65,16 +90,16 @@ describe('Test Secrets Manager Helper', async () => {
65
90
  })
66
91
 
67
92
  it('should edit, add, and delete a client key/value secret', async () => {
68
- await editClient(
69
- 'test_client_id',
70
- {
93
+ await editClient({
94
+ clientId: 'test_client_id',
95
+ keyValuePairsToAdd: {
71
96
  test_secret_key_new: 'edit',
72
97
  new_key: 'add',
73
98
  organization: 'test_organization_new'
74
99
  },
75
- ['to_delete'],
76
- clientIdsSecretId
77
- )
100
+ keysToDelete: ['to_delete'],
101
+ clientIdsSecretIdOverride: clientIdsSecretId
102
+ })
78
103
 
79
104
  const testSecret = await getSecretValue(
80
105
  'test_organization/ad_platforms/api'