@middy/ssm 2.5.1 → 3.0.0-alpha.0

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2017-2021 Luciano Mammino, will Farrell and the [Middy team](https://github.com/middyjs/middy/graphs/contributors)
3
+ Copyright (c) 2017-2022 Luciano Mammino, will Farrell and the [Middy team](https://github.com/middyjs/middy/graphs/contributors)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -29,11 +29,11 @@ This middleware fetches parameters from [AWS Systems Manager Parameter Store](ht
29
29
 
30
30
  Parameters to fetch can be defined by path and by name (not mutually exclusive). See AWS docs [here](https://aws.amazon.com/blogs/mt/organize-parameters-by-hierarchy-tags-or-amazon-cloudwatch-events-with-amazon-ec2-systems-manager-parameter-store/).
31
31
 
32
- Parameters can be assigned to the Node.js `process.env` object by setting the `setToEnv` flag to `true`. They can also be assigned to the function handler's `context` object by setting the `setToContext` flag to `true`. By default all parameters are added with uppercase names.
32
+ Parameters can be assigned to the function handler's `context` object by setting the `setToContext` flag to `true`. By default all parameters are added with uppercase names.
33
33
 
34
34
  The Middleware makes a single API request to fetch all the parameters defined by name, but must make an additional request per specified path. This is because the AWS SDK currently doesn't expose a method to retrieve parameters from multiple paths.
35
35
 
36
- For each parameter defined by name, you also provide the name under which its value should be added to `process.env` or `context`. For each path, you instead provide a prefix, and by default the value import each parameter returned from that path will be added to `process.env` or `context` with a name equal to what's left of the parameter's full name _after_ the defined path, with the prefix prepended. If the prefix is an empty string, nothing is prepended. You can override this behaviour by providing your own mapping function with the `getParamNameFromPath` config option.
36
+ For each parameter defined by name, you also provide the name under which its value should be added to `context`. For each path, you instead provide a prefix, and by default the value import each parameter returned from that path will be added to `context` with a name equal to what's left of the parameter's full name _after_ the defined path, with the prefix prepended. If the prefix is an empty string, nothing is prepended. You can override this behaviour by providing your own mapping function with the `getParamNameFromPath` config option.
37
37
 
38
38
 
39
39
  ## Install
@@ -47,22 +47,19 @@ npm install --save @middy/ssm
47
47
 
48
48
  ## Options
49
49
 
50
- - `AwsClient` (object) (default `AWS.SSM`): AWS.SSM class constructor (e.g. that has been instrumented with AWS XRay). Must be from `aws-sdk` v2.
50
+ - `AwsClient` (object) (default `AWS.SSM`): AWS.SSM class constructor (e.g. that has been instrumented with AWS X-Ray). Must be from `aws-sdk` v2.
51
51
  - `awsClientOptions` (object) (optional): Options to pass to AWS.SSM class constructor.
52
52
  - `awsClientAssumeRole` (string) (optional): Internal key where role tokens are stored. See [@middy/sts](/packages/sts/README.md) on to set this.
53
- - `awsClientCapture` (function) (optional): Enable XRay by passing `captureAWSClient` from `aws-xray-sdk` in.
53
+ - `awsClientCapture` (function) (optional): Enable AWS X-Ray by passing `captureAWSClient` from `aws-xray-sdk` in.
54
54
  - `fetchData` (object) (required): Mapping of internal key name to API request parameter `Names`/`Path`. `SecureString` are automatically decrypted.
55
55
  - `disablePrefetch` (boolean) (default `false`): On cold start requests will trigger early if they can. Setting `awsClientAssumeRole` disables prefetch.
56
56
  - `cacheKey` (string) (default `ssm`): Cache key for the fetched data responses. Must be unique across all middleware.
57
57
  - `cacheExpiry` (number) (default `-1`): How long fetch data responses should be cached for. `-1`: cache forever, `0`: never cache, `n`: cache for n ms.
58
- - `setToEnv` (boolean) (default `false`): Store role tokens to `process.env`. **Storing secrets in `process.env` is considered security bad practice**
59
- - `setToContext` (boolean) (default `false`): Store role tokes to `request.context`.
58
+ - `setToContext` (boolean) (default `false`): Store role tokens to `request.context`.
60
59
 
61
60
  NOTES:
62
61
  - Lambda is required to have IAM permission for `ssm:GetParameters` and/or `ssm:GetParametersByPath` depending on what you're requesting.
63
62
  - `SSM` has [throughput limitations](https://docs.aws.amazon.com/general/latest/gr/ssm.html). Switching to Advanced Parameter type or increasing `maxRetries` and `retryDelayOptions.base` in `awsClientOptions` may be required.
64
- - `setToEnv` and `setToContext` are included for legacy support and should be avoided for performance and security reasons. See main documentation for best practices.
65
- - `setToEnv` can only assign secrets of type string
66
63
 
67
64
  ## Sample usage
68
65
 
@@ -79,7 +76,7 @@ handler
79
76
  .use(ssm({
80
77
  fetchData: {
81
78
  accessToken: '/dev/service_name/access_token', // single value
82
- dbParams: '/dev/service_name/database/', // object of values, key for each path
79
+ dbParams: '/dev/service_name/database/', // object of values, key for each path
83
80
  defaults: '/dev/defaults'
84
81
  },
85
82
  setToContext: true
@@ -109,7 +106,7 @@ handler
109
106
  .use(ssm({
110
107
  fetchData: {
111
108
  accessToken: '/dev/service_name/access_token', // single value
112
- dbParams: '/dev/service_name/database/', // object of values, key for each path
109
+ dbParams: '/dev/service_name/database/', // object of values, key for each path
113
110
  },
114
111
  cacheExpiry: 15*60*1000,
115
112
  cacheKey: 'ssm-secrets'
@@ -134,7 +131,7 @@ Everyone is very welcome to contribute to this repository. Feel free to [raise i
134
131
 
135
132
  ## License
136
133
 
137
- Licensed under [MIT License](LICENSE). Copyright (c) 2017-2021 Luciano Mammino, will Farrell, and the [Middy team](https://github.com/middyjs/middy/graphs/contributors).
134
+ Licensed under [MIT License](LICENSE). Copyright (c) 2017-2022 Luciano Mammino, will Farrell, and the [Middy team](https://github.com/middyjs/middy/graphs/contributors).
138
135
 
139
136
  <a href="https://app.fossa.io/projects/git%2Bgithub.com%2Fmiddyjs%2Fmiddy?ref=badge_large">
140
137
  <img src="https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmiddyjs%2Fmiddy.svg?type=large" alt="FOSSA Status" style="max-width:100%;">
package/index.d.ts CHANGED
@@ -1,19 +1,8 @@
1
1
  import { SSM } from 'aws-sdk'
2
- import { captureAWSClient } from 'aws-xray-sdk'
2
+ import { Options as MiddyOptions } from '@middy/util'
3
3
  import middy from '@middy/core'
4
4
 
5
- interface Options<S = SSM> {
6
- AwsClient?: new() => S
7
- awsClientOptions?: Partial<SSM.Types.ClientConfiguration>
8
- awsClientAssumeRole?: string
9
- awsClientCapture?: typeof captureAWSClient
10
- fetchData?: { [key: string]: string }
11
- disablePrefetch?: boolean
12
- cacheKey?: string
13
- cacheExpiry?: number
14
- setToEnv?: boolean
15
- setToContext?: boolean
16
- }
5
+ interface Options<S = SSM> extends MiddyOptions<S, SSM.Types.ClientConfiguration> {}
17
6
 
18
7
  declare function ssm (options?: Options): middy.MiddlewareObj
19
8
 
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@middy/ssm",
3
- "version": "2.5.1",
3
+ "version": "3.0.0-alpha.0",
4
4
  "description": "SSM (EC2 Systems Manager) parameters middleware for the middy framework",
5
- "type": "commonjs",
5
+ "type": "module",
6
6
  "engines": {
7
- "node": ">=12"
7
+ "node": ">=14"
8
8
  },
9
9
  "engineStrict": true,
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },
13
- "main": "index.js",
13
+ "exports": "./index.js",
14
14
  "types": "index.d.ts",
15
15
  "files": [
16
16
  "index.d.ts"
@@ -46,12 +46,12 @@
46
46
  },
47
47
  "homepage": "https://github.com/middyjs/middy#readme",
48
48
  "dependencies": {
49
- "@middy/util": "^2.5.1"
49
+ "@middy/util": "^3.0.0-alpha.0"
50
50
  },
51
51
  "devDependencies": {
52
- "@middy/core": "^2.5.1",
52
+ "@middy/core": "^3.0.0-alpha.0",
53
53
  "aws-sdk": "^2.939.0",
54
54
  "aws-xray-sdk": "^3.3.3"
55
55
  },
56
- "gitHead": "df18e5eff7d73492a96a2ca4780a2eae45d1cedb"
56
+ "gitHead": "c533f62841c8a39d061d7b94f30ba178f002c8db"
57
57
  }
package/index.js DELETED
@@ -1,187 +0,0 @@
1
- "use strict";
2
-
3
- const {
4
- canPrefetch,
5
- createPrefetchClient,
6
- createClient,
7
- processCache,
8
- getCache,
9
- modifyCache,
10
- jsonSafeParse,
11
- getInternal,
12
- sanitizeKey
13
- } = require('@middy/util');
14
-
15
- const SSM = require('aws-sdk/clients/ssm'); // v2
16
- // const { SSM } = require('@aws-sdk/client-ssm') // v3
17
-
18
-
19
- const awsRequestLimit = 10;
20
- const defaults = {
21
- AwsClient: SSM,
22
- // Allow for XRay
23
- awsClientOptions: {},
24
- awsClientAssumeRole: undefined,
25
- awsClientCapture: undefined,
26
- fetchData: {},
27
- // { contextKey: fetchKey, contextPrefix: fetchPath/ }
28
- disablePrefetch: false,
29
- cacheKey: 'ssm',
30
- cacheExpiry: -1,
31
- setToEnv: false,
32
- setToContext: false
33
- };
34
-
35
- const ssmMiddleware = (opts = {}) => {
36
- const options = { ...defaults,
37
- ...opts
38
- };
39
-
40
- const fetch = (request, cachedValues) => {
41
- return { ...fetchSingle(request, cachedValues),
42
- ...fetchByPath(request, cachedValues)
43
- };
44
- };
45
-
46
- const fetchSingle = (request, cachedValues = {}) => {
47
- const values = {};
48
- let batchReq = null;
49
- let batchInternalKeys = [];
50
- let batchFetchKeys = [];
51
- const internalKeys = Object.keys(options.fetchData);
52
- const fetchKeys = Object.values(options.fetchData);
53
-
54
- for (const [idx, internalKey] of internalKeys.entries()) {
55
- if (cachedValues[internalKey]) continue;
56
- const fetchKey = options.fetchData[internalKey];
57
- if (fetchKey.substr(-1) === '/') continue; // Skip path passed in
58
-
59
- batchInternalKeys.push(internalKey);
60
- batchFetchKeys.push(fetchKey); // from the first to the batch size skip, unless it's the last entry
61
-
62
- if ((!idx || (idx + 1) % awsRequestLimit !== 0) && !(idx + 1 === internalKeys.length)) {
63
- continue;
64
- }
65
-
66
- batchReq = client.getParameters({
67
- Names: batchFetchKeys,
68
- WithDecryption: true
69
- }).promise() // Required for aws-sdk v2
70
- .then(resp => {
71
- var _resp$InvalidParamete, _resp$Parameters;
72
-
73
- // Don't sanitize key, mapped to set value in options
74
- return Object.assign(...((_resp$InvalidParamete = resp.InvalidParameters) !== null && _resp$InvalidParamete !== void 0 ? _resp$InvalidParamete : []).map(fetchKey => {
75
- return {
76
- [fetchKey]: new Promise(() => {
77
- var _getCache$value, _getCache;
78
-
79
- const internalKey = internalKeys[fetchKeys.indexOf(fetchKey)];
80
- const value = (_getCache$value = (_getCache = getCache(options.cacheKey)) === null || _getCache === void 0 ? void 0 : _getCache.value) !== null && _getCache$value !== void 0 ? _getCache$value : {};
81
- value[internalKey] = undefined;
82
- modifyCache(options.cacheKey, value);
83
- throw new Error('ssm.InvalidParameter ' + fetchKey);
84
- })
85
- };
86
- }), ...((_resp$Parameters = resp.Parameters) !== null && _resp$Parameters !== void 0 ? _resp$Parameters : []).map(param => {
87
- return {
88
- [param.Name]: parseValue(param)
89
- };
90
- }));
91
- });
92
-
93
- for (const internalKey of batchInternalKeys) {
94
- values[internalKey] = batchReq.then(params => {
95
- return params[options.fetchData[internalKey]];
96
- });
97
- }
98
-
99
- batchInternalKeys = [];
100
- batchFetchKeys = [];
101
- batchReq = null;
102
- }
103
-
104
- return values;
105
- };
106
-
107
- const fetchByPath = (request, cachedValues = {}) => {
108
- const values = {};
109
-
110
- for (const internalKey in options.fetchData) {
111
- if (cachedValues[internalKey]) continue;
112
- const fetchKey = options.fetchData[internalKey];
113
- if (fetchKey.substr(-1) !== '/') continue; // Skip not path passed in
114
-
115
- values[internalKey] = fetchPath(fetchKey).catch(e => {
116
- var _getCache$value2, _getCache2;
117
-
118
- const value = (_getCache$value2 = (_getCache2 = getCache(options.cacheKey)) === null || _getCache2 === void 0 ? void 0 : _getCache2.value) !== null && _getCache$value2 !== void 0 ? _getCache$value2 : {};
119
- value[internalKey] = undefined;
120
- modifyCache(options.cacheKey, value);
121
- throw e;
122
- });
123
- }
124
-
125
- return values;
126
- };
127
-
128
- const fetchPath = (path, nextToken, values = {}) => {
129
- return client.getParametersByPath({
130
- Path: path,
131
- NextToken: nextToken,
132
- Recursive: true,
133
- WithDecryption: true
134
- }).promise() // Required for aws-sdk v2
135
- .then(resp => {
136
- Object.assign(values, ...resp.Parameters.map(param => {
137
- return {
138
- [sanitizeKey(param.Name.replace(path, ''))]: parseValue(param)
139
- };
140
- }));
141
- if (resp.NextToken) return fetchPath(path, resp.NextToken, values);
142
- return values;
143
- });
144
- };
145
-
146
- const parseValue = param => {
147
- if (param.Type === 'StringList') {
148
- return param.Value.split(',');
149
- }
150
-
151
- return jsonSafeParse(param.Value);
152
- };
153
-
154
- let prefetch, client;
155
-
156
- if (canPrefetch(options)) {
157
- client = createPrefetchClient(options);
158
- prefetch = processCache(options, fetch);
159
- }
160
-
161
- const ssmMiddlewareBefore = async request => {
162
- var _prefetch;
163
-
164
- if (!client) {
165
- client = await createClient(options, request);
166
- }
167
-
168
- const {
169
- value
170
- } = (_prefetch = prefetch) !== null && _prefetch !== void 0 ? _prefetch : processCache(options, fetch, request);
171
- Object.assign(request.internal, value);
172
-
173
- if (options.setToContext || options.setToEnv) {
174
- const data = await getInternal(Object.keys(options.fetchData), request);
175
- if (options.setToEnv) Object.assign(process.env, data);
176
- if (options.setToContext) Object.assign(request.context, data);
177
- }
178
-
179
- prefetch = null;
180
- };
181
-
182
- return {
183
- before: ssmMiddlewareBefore
184
- };
185
- };
186
-
187
- module.exports = ssmMiddleware;