node-vault-client 1.0.1 → 2.0.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.
@@ -1,263 +1,288 @@
1
- 'use strict';
2
-
3
- const _ = require('lodash');
4
- const Lease = require('./Lease');
5
- const errors = require('./errors');
6
- const VaultApiClient = require('./VaultApiClient');
7
- const VaultAppRoleAuth = require('./auth/VaultAppRoleAuth');
8
- const VaultTokenAuth = require('./auth/VaultTokenAuth');
9
- const VaultIAMAuth = require('./auth/VaultIAMAuth');
10
- const VaultNodeConfig = require('./VaultNodeConfig');
11
- const VaultKubernetesAuth = require('./auth/VaultKubernetesAuth');
12
- const vaultInstances = {};
13
-
14
- class VaultClient {
15
-
16
- /**
17
- * Client constructor function.
18
- *
19
- * @param {Object} options
20
- * @param {Object} options.api
21
- * @param {String} options.api.url - the url of the vault server
22
- * @param {String} [options.api.apiVersion='v1']
23
- * @param {Object} options.auth
24
- * @param {String} options.auth.type
25
- * @param {Object} options.auth.config - auth configuration variables
26
- * @param {Object|false} options.logger - Logger that supports "error", "info", "warn", "trace", "debug" methods. Uses `console` by default. Pass `false` to disable logging.
27
- */
28
- constructor(options) {
29
- this.__log = this.__setupLogger(options.logger);
30
-
31
- this.__api = new VaultApiClient(
32
- options.api,
33
- this.__log
34
- );
35
-
36
- /**
37
- * @type {VaultBaseAuth}
38
- * @private
39
- */
40
- this.__auth = this.__getAuthProvider(
41
- options.auth,
42
- this.__api
43
- );
44
-
45
- this.__namespace = options.auth.config.namespace;
46
- }
47
-
48
- /**
49
- * Boot an instance of Vault
50
- *
51
- * The instance will be stored in a local hash. Calling Vault.boot multiple
52
- * times with the same name will return the same instance.
53
- *
54
- * @param {String} name - Vault instance name
55
- * @param {Object} [options] - options for {@link Vault#constructor}.
56
- * @return Vault
57
- */
58
- static boot(name, options) {
59
- if (options === undefined) {
60
- throw new errors.InvalidArgumentsError('Options should be provided');
61
- }
62
-
63
- let instance = vaultInstances[name];
64
- if (instance === undefined) {
65
- vaultInstances[name] = instance = new VaultClient(options);
66
- }
67
- return instance;
68
- }
69
-
70
- /**
71
- * Get an instance of Vault
72
- *
73
- * The instance will be stored in a local hash. Calling Vault.pop multiple
74
- * times with the same name will return the same instance.
75
- *
76
- * @param {String} name - Vault instance name
77
- * @return Vault
78
- */
79
- static get(name) {
80
- let instance = vaultInstances[name];
81
-
82
- if (instance === undefined) {
83
- throw new errors.InvalidArgumentsError('Invalid instance name');
84
- }
85
-
86
- return instance;
87
- }
88
-
89
- /**
90
- * Clear named Vault instance
91
- *
92
- * If no name passed all named instances will be cleared.
93
- *
94
- * @param {String} [name] - Vault instance name, all instances will be cleared if no name were passed
95
- */
96
- static clear(name) {
97
- if (typeof name === 'string') {
98
- delete vaultInstances[name];
99
- } else {
100
- for (let k in vaultInstances) {
101
- if (vaultInstances.hasOwnProperty(k)) {
102
- delete vaultInstances[k];
103
- }
104
- }
105
- }
106
- }
107
-
108
- /**
109
- * @protected
110
- *
111
- * @param {Object} authConfig
112
- * @param {string} authConfig.type
113
- * @param {string} authConfig.mount
114
- * @param {Object} authConfig.config
115
- * @param {VaultApiClient} api
116
- * @return {VaultBaseAuth}
117
- * @private
118
- */
119
- __getAuthProvider(authConfig, api) {
120
- this.__log.debug('creating vault auth method: "%s"', authConfig.type);
121
-
122
- switch (authConfig.type) {
123
- case 'iam':
124
- return new VaultIAMAuth(
125
- api,
126
- this.__log,
127
- authConfig.config,
128
- authConfig.mount
129
- );
130
- case 'appRole':
131
- return new VaultAppRoleAuth(
132
- api,
133
- this.__log,
134
- authConfig.config,
135
- authConfig.mount
136
- );
137
- case 'token':
138
- return new VaultTokenAuth(
139
- api,
140
- this.__log,
141
- authConfig.config,
142
- authConfig.mount
143
- );
144
- case 'kubernetes':
145
- return new VaultKubernetesAuth(
146
- api,
147
- this.__log,
148
- authConfig.config,
149
- authConfig.mount
150
- );
151
- }
152
-
153
- throw new errors.InvalidArgumentsError('Unsupported auth method')
154
- }
155
-
156
- /**
157
- * Populates Vault's values to NPM "config" module
158
- */
159
- fillNodeConfig() {
160
- const vaultConf = new VaultNodeConfig(this);
161
-
162
- return vaultConf.populate();
163
- }
164
-
165
- getHeaders(token) {
166
- if (this.__namespace) {
167
- return {
168
- 'X-Vault-Token': token.getId(),
169
- 'X-Vault-Namespace': this.__namespace
170
- }
171
- }
172
-
173
- return {'X-Vault-Token': token.getId()}
174
- }
175
-
176
- /**
177
- * Read secret from Vault
178
- * @param {string} path - path to the secret
179
- * @returns {Promise<Lease>}
180
- */
181
- read(path) {
182
- this.__log.debug('read secret %s', path);
183
- return this.__auth.getAuthToken()
184
- .then(token => this.__api.makeRequest('GET', path, null, this.getHeaders(token)))
185
- .then(res => {
186
- this.__log.debug('receive secret %s', path);
187
- return Lease.fromResponse(res);
188
- })
189
- .catch((reason) => {
190
- this.__log.error('read secret failed: %s', reason.message);
191
- throw reason;
192
- });
193
- }
194
-
195
- /**
196
- * Retrieves secrets list
197
- *
198
- * @param {string} path - path to the secret
199
- * @returns {Promise<Lease>}
200
- */
201
- list(path) {
202
- this.__log.debug('list secrets %s', path);
203
- return this.__auth.getAuthToken()
204
- .then(token => this.__api.makeRequest('LIST', path, null, this.getHeaders(token)))
205
- .then(res => {
206
- this.__log.debug('got secrets list %s', path);
207
- return Lease.fromResponse(res);
208
- })
209
- .catch((reason) => {
210
- this.__log.error('list secrets failed: %s', reason.message);
211
- throw reason;
212
- });
213
- }
214
-
215
- /**
216
- * Writes data to Vault
217
- *
218
- * @param path - path used to write data
219
- * @param {object} data - data to write
220
- * @returns {Promise<T | never>}
221
- */
222
- write(path, data) {
223
- this.__log.debug('write secret %s', path);
224
- return this.__auth.getAuthToken()
225
- .then((token) => this.__api.makeRequest('POST', path, data, this.getHeaders(token)))
226
- .then((response) => {
227
- this.__log.debug('secret %s was written', path);
228
- return response;
229
- })
230
- .catch((reason) => {
231
- this.__log.error('write secret failed: %s', reason.message);
232
- throw reason;
233
- });
234
- }
235
-
236
- /**
237
- * @private
238
- */
239
- __setupLogger(logger) {
240
- if (logger === false) {
241
- return {
242
- error: _.noop,
243
- warn: _.noop,
244
- info: _.noop,
245
- debug: _.noop,
246
- trace: _.noop,
247
- }
248
- } else if (_.intersection(_.functionsIn(logger), ['error', 'warn', 'info', 'debug', 'trace']).length >= 5) {
249
- return logger
250
- } else {
251
- return {
252
- error: console.error,
253
- warn: console.warn,
254
- info: console.info,
255
- trace: console.trace,
256
- // avoid output sensitive information
257
- debug: _.noop
258
- };
259
- }
260
- }
261
- }
262
-
263
- module.exports = VaultClient;
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const Lease = require('./Lease');
5
+ const errors = require('./errors');
6
+ const VaultApiClient = require('./VaultApiClient');
7
+ const VaultAppRoleAuth = require('./auth/VaultAppRoleAuth');
8
+ const VaultTokenAuth = require('./auth/VaultTokenAuth');
9
+ const VaultIAMAuth = require('./auth/VaultIAMAuth');
10
+ const VaultNodeConfig = require('./VaultNodeConfig');
11
+ const VaultKubernetesAuth = require('./auth/VaultKubernetesAuth');
12
+ const vaultInstances = {};
13
+
14
+ class VaultClient {
15
+
16
+ /**
17
+ * Client constructor function.
18
+ *
19
+ * @param {Object} options
20
+ * @param {Object} options.api
21
+ * @param {String} options.api.url - the url of the vault server
22
+ * @param {String} [options.api.apiVersion='v1']
23
+ * @param {Object} [options.api.requestOptions] - extra options merged into every HTTP
24
+ * request (e.g. an undici `dispatcher` for a proxy/SOCKS agent or custom TLS/CA trust).
25
+ * See {@link VaultApiClient#constructor}.
26
+ * @param {Object} options.auth
27
+ * @param {String} options.auth.type
28
+ * @param {Object} options.auth.config - auth configuration variables
29
+ * @param {Object|false} options.logger - Logger that supports "error", "info", "warn", "trace", "debug" methods. Uses `console` by default. Pass `false` to disable logging.
30
+ */
31
+ constructor(options) {
32
+ this.__log = this.__setupLogger(options.logger);
33
+
34
+ this.__api = new VaultApiClient(
35
+ options.api,
36
+ this.__log
37
+ );
38
+
39
+ /**
40
+ * @type {VaultBaseAuth}
41
+ * @private
42
+ */
43
+ this.__auth = this.__getAuthProvider(
44
+ options.auth,
45
+ this.__api
46
+ );
47
+
48
+ this.__namespace = options.auth.config.namespace;
49
+ }
50
+
51
+ /**
52
+ * Boot an instance of Vault
53
+ *
54
+ * The instance will be stored in a local hash. Calling Vault.boot multiple
55
+ * times with the same name will return the same instance.
56
+ *
57
+ * @param {String} name - Vault instance name
58
+ * @param {Object} [options] - options for {@link VaultClient#constructor}.
59
+ * @return {VaultClient}
60
+ */
61
+ static boot(name, options) {
62
+ if (options === undefined) {
63
+ throw new errors.InvalidArgumentsError('Options should be provided');
64
+ }
65
+
66
+ let instance = vaultInstances[name];
67
+ if (instance === undefined) {
68
+ vaultInstances[name] = instance = new VaultClient(options);
69
+ }
70
+ return instance;
71
+ }
72
+
73
+ /**
74
+ * Get an instance of Vault
75
+ *
76
+ * The instance will be stored in a local hash. Calling Vault.pop multiple
77
+ * times with the same name will return the same instance.
78
+ *
79
+ * @param {String} name - Vault instance name
80
+ * @return {VaultClient}
81
+ */
82
+ static get(name) {
83
+ let instance = vaultInstances[name];
84
+
85
+ if (instance === undefined) {
86
+ throw new errors.InvalidArgumentsError('Invalid instance name');
87
+ }
88
+
89
+ return instance;
90
+ }
91
+
92
+ /**
93
+ * Clear named Vault instance
94
+ *
95
+ * If no name passed all named instances will be cleared.
96
+ *
97
+ * @param {String} [name] - Vault instance name, all instances will be cleared if no name were passed
98
+ */
99
+ static clear(name) {
100
+ if (typeof name === 'string') {
101
+ const instance = vaultInstances[name];
102
+ if (instance !== undefined) {
103
+ instance.close();
104
+ delete vaultInstances[name];
105
+ }
106
+ } else {
107
+ for (let k in vaultInstances) {
108
+ if (Object.hasOwn(vaultInstances, k)) {
109
+ vaultInstances[k].close();
110
+ delete vaultInstances[k];
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Release resources held by this client.
118
+ *
119
+ * Cancels the background auth-token refresh timer that keeps the Node.js event loop
120
+ * alive for renewable tokens. Call this once you are done with the client so a
121
+ * short-lived script can exit on its own. Safe to call when no timer is armed and
122
+ * safe to call multiple times. After calling `close()` the client may still be used;
123
+ * the next operation that fetches a renewable token will arm a new refresh timer.
124
+ *
125
+ * @returns {void}
126
+ */
127
+ close() {
128
+ if (this.__auth && typeof this.__auth.cancelTokenRefresh === 'function') {
129
+ this.__auth.cancelTokenRefresh();
130
+ }
131
+ }
132
+
133
+ /**
134
+ * @protected
135
+ *
136
+ * @param {Object} authConfig
137
+ * @param {string} authConfig.type
138
+ * @param {string} authConfig.mount
139
+ * @param {Object} authConfig.config
140
+ * @param {VaultApiClient} api
141
+ * @return {VaultBaseAuth}
142
+ * @private
143
+ */
144
+ __getAuthProvider(authConfig, api) {
145
+ this.__log.debug('creating vault auth method: "%s"', authConfig.type);
146
+
147
+ switch (authConfig.type) {
148
+ case 'iam':
149
+ return new VaultIAMAuth(
150
+ api,
151
+ this.__log,
152
+ authConfig.config,
153
+ authConfig.mount
154
+ );
155
+ case 'appRole':
156
+ return new VaultAppRoleAuth(
157
+ api,
158
+ this.__log,
159
+ authConfig.config,
160
+ authConfig.mount
161
+ );
162
+ case 'token':
163
+ return new VaultTokenAuth(
164
+ api,
165
+ this.__log,
166
+ authConfig.config,
167
+ authConfig.mount
168
+ );
169
+ case 'kubernetes':
170
+ return new VaultKubernetesAuth(
171
+ api,
172
+ this.__log,
173
+ authConfig.config,
174
+ authConfig.mount
175
+ );
176
+ }
177
+
178
+ throw new errors.InvalidArgumentsError('Unsupported auth method')
179
+ }
180
+
181
+ /**
182
+ * Populates Vault's values to NPM "config" module
183
+ */
184
+ fillNodeConfig() {
185
+ const vaultConf = new VaultNodeConfig(this);
186
+
187
+ return vaultConf.populate();
188
+ }
189
+
190
+ getHeaders(token) {
191
+ if (this.__namespace) {
192
+ return {
193
+ 'X-Vault-Token': token.getId(),
194
+ 'X-Vault-Namespace': this.__namespace
195
+ }
196
+ }
197
+
198
+ return {'X-Vault-Token': token.getId()}
199
+ }
200
+
201
+ /**
202
+ * Read secret from Vault
203
+ * @param {string} path - path to the secret
204
+ * @returns {Promise<Lease>}
205
+ */
206
+ read(path) {
207
+ this.__log.debug('read secret %s', path);
208
+ return this.__auth.getAuthToken()
209
+ .then(token => this.__api.makeRequest('GET', path, null, this.getHeaders(token)))
210
+ .then(res => {
211
+ this.__log.debug('receive secret %s', path);
212
+ return Lease.fromResponse(res);
213
+ })
214
+ .catch((reason) => {
215
+ this.__log.error('read secret failed: %s', reason.message);
216
+ throw reason;
217
+ });
218
+ }
219
+
220
+ /**
221
+ * Retrieves secrets list
222
+ *
223
+ * @param {string} path - path to the secret
224
+ * @returns {Promise<Lease>}
225
+ */
226
+ list(path) {
227
+ this.__log.debug('list secrets %s', path);
228
+ return this.__auth.getAuthToken()
229
+ .then(token => this.__api.makeRequest('LIST', path, null, this.getHeaders(token)))
230
+ .then(res => {
231
+ this.__log.debug('got secrets list %s', path);
232
+ return Lease.fromResponse(res);
233
+ })
234
+ .catch((reason) => {
235
+ this.__log.error('list secrets failed: %s', reason.message);
236
+ throw reason;
237
+ });
238
+ }
239
+
240
+ /**
241
+ * Writes data to Vault
242
+ *
243
+ * @param {string} path - path used to write data
244
+ * @param {object} data - data to write
245
+ * @returns {Promise<Object>} the parsed Vault response body
246
+ */
247
+ write(path, data) {
248
+ this.__log.debug('write secret %s', path);
249
+ return this.__auth.getAuthToken()
250
+ .then((token) => this.__api.makeRequest('POST', path, data, this.getHeaders(token)))
251
+ .then((response) => {
252
+ this.__log.debug('secret %s was written', path);
253
+ return response;
254
+ })
255
+ .catch((reason) => {
256
+ this.__log.error('write secret failed: %s', reason.message);
257
+ throw reason;
258
+ });
259
+ }
260
+
261
+ /**
262
+ * @private
263
+ */
264
+ __setupLogger(logger) {
265
+ if (logger === false) {
266
+ return {
267
+ error: _.noop,
268
+ warn: _.noop,
269
+ info: _.noop,
270
+ debug: _.noop,
271
+ trace: _.noop,
272
+ }
273
+ } else if (_.intersection(_.functionsIn(logger), ['error', 'warn', 'info', 'debug', 'trace']).length >= 5) {
274
+ return logger
275
+ } else {
276
+ return {
277
+ error: console.error,
278
+ warn: console.warn,
279
+ info: console.info,
280
+ trace: console.trace,
281
+ // avoid output sensitive information
282
+ debug: _.noop
283
+ };
284
+ }
285
+ }
286
+ }
287
+
288
+ module.exports = VaultClient;