@memberjunction/encryption 0.0.1 → 2.129.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/README.md +391 -28
- package/dist/EncryptionEngine.d.ts +351 -0
- package/dist/EncryptionEngine.d.ts.map +1 -0
- package/dist/EncryptionEngine.js +683 -0
- package/dist/EncryptionEngine.js.map +1 -0
- package/dist/EncryptionKeySourceBase.d.ts +203 -0
- package/dist/EncryptionKeySourceBase.d.ts.map +1 -0
- package/dist/EncryptionKeySourceBase.js +133 -0
- package/dist/EncryptionKeySourceBase.js.map +1 -0
- package/dist/actions/EnableFieldEncryptionAction.d.ts +87 -0
- package/dist/actions/EnableFieldEncryptionAction.d.ts.map +1 -0
- package/dist/actions/EnableFieldEncryptionAction.js +308 -0
- package/dist/actions/EnableFieldEncryptionAction.js.map +1 -0
- package/dist/actions/RotateEncryptionKeyAction.d.ts +79 -0
- package/dist/actions/RotateEncryptionKeyAction.d.ts.map +1 -0
- package/dist/actions/RotateEncryptionKeyAction.js +343 -0
- package/dist/actions/RotateEncryptionKeyAction.js.map +1 -0
- package/dist/actions/index.d.ts +12 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +17 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +81 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces.d.ts +216 -0
- package/dist/interfaces.d.ts.map +1 -0
- package/dist/interfaces.js +15 -0
- package/dist/interfaces.js.map +1 -0
- package/dist/providers/AWSKMSKeySource.d.ts +110 -0
- package/dist/providers/AWSKMSKeySource.d.ts.map +1 -0
- package/dist/providers/AWSKMSKeySource.js +245 -0
- package/dist/providers/AWSKMSKeySource.js.map +1 -0
- package/dist/providers/AzureKeyVaultKeySource.d.ts +109 -0
- package/dist/providers/AzureKeyVaultKeySource.d.ts.map +1 -0
- package/dist/providers/AzureKeyVaultKeySource.js +268 -0
- package/dist/providers/AzureKeyVaultKeySource.js.map +1 -0
- package/dist/providers/ConfigFileKeySource.d.ts +173 -0
- package/dist/providers/ConfigFileKeySource.d.ts.map +1 -0
- package/dist/providers/ConfigFileKeySource.js +310 -0
- package/dist/providers/ConfigFileKeySource.js.map +1 -0
- package/dist/providers/EnvVarKeySource.d.ts +152 -0
- package/dist/providers/EnvVarKeySource.d.ts.map +1 -0
- package/dist/providers/EnvVarKeySource.js +251 -0
- package/dist/providers/EnvVarKeySource.js.map +1 -0
- package/package.json +65 -6
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Configuration file key source provider.
|
|
4
|
+
*
|
|
5
|
+
* This provider retrieves encryption keys from MemberJunction's
|
|
6
|
+
* configuration file (mj.config.cjs or other cosmiconfig-compatible formats).
|
|
7
|
+
*
|
|
8
|
+
* ## Usage
|
|
9
|
+
*
|
|
10
|
+
* 1. Add keys to your mj.config.cjs:
|
|
11
|
+
* ```javascript
|
|
12
|
+
* module.exports = {
|
|
13
|
+
* encryptionKeys: {
|
|
14
|
+
* pii_master: 'K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=',
|
|
15
|
+
* api_secrets: 'aW5kZXhfbmV3X2tleV9mb3JfdjJfcm90YXRpb24='
|
|
16
|
+
* }
|
|
17
|
+
* };
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* 2. Configure in database with:
|
|
21
|
+
* - EncryptionKeySourceID pointing to 'Configuration File' source
|
|
22
|
+
* - KeyLookupValue = 'pii_master' (the key name in the config)
|
|
23
|
+
*
|
|
24
|
+
* ## Key Format
|
|
25
|
+
*
|
|
26
|
+
* Keys must be base64-encoded strings. Generate with:
|
|
27
|
+
* ```bash
|
|
28
|
+
* openssl rand -base64 32 # For AES-256
|
|
29
|
+
* openssl rand -base64 16 # For AES-128
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* ## Security Considerations
|
|
33
|
+
*
|
|
34
|
+
* - Config files should have restricted file permissions (600 or 640)
|
|
35
|
+
* - Don't commit config files with keys to source control
|
|
36
|
+
* - Consider using .gitignore for mj.config.cjs
|
|
37
|
+
* - For production, prefer environment variables or secrets managers
|
|
38
|
+
*
|
|
39
|
+
* ## Key Rotation
|
|
40
|
+
*
|
|
41
|
+
* Add the new key with a versioned name:
|
|
42
|
+
* ```javascript
|
|
43
|
+
* encryptionKeys: {
|
|
44
|
+
* pii_master: '<current-key>',
|
|
45
|
+
* pii_master_v2: '<new-key>'
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @module @memberjunction/encryption
|
|
50
|
+
*/
|
|
51
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
52
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
53
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
54
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
55
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
56
|
+
};
|
|
57
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
|
+
exports.ConfigFileKeySource = void 0;
|
|
59
|
+
const global_1 = require("@memberjunction/global");
|
|
60
|
+
const EncryptionKeySourceBase_1 = require("../EncryptionKeySourceBase");
|
|
61
|
+
/**
|
|
62
|
+
* Encryption key source that retrieves keys from configuration files.
|
|
63
|
+
*
|
|
64
|
+
* Uses cosmiconfig to locate configuration in standard locations:
|
|
65
|
+
* - mj.config.cjs (recommended)
|
|
66
|
+
* - mj.config.js
|
|
67
|
+
* - .mjrc.json
|
|
68
|
+
* - .mjrc.yaml
|
|
69
|
+
*
|
|
70
|
+
* Keys are read from the `encryptionKeys` section of the configuration.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* // Configuration file (mj.config.cjs):
|
|
75
|
+
* module.exports = {
|
|
76
|
+
* encryptionKeys: {
|
|
77
|
+
* pii_master: 'base64-encoded-key-here',
|
|
78
|
+
* financial_data: 'another-base64-key'
|
|
79
|
+
* }
|
|
80
|
+
* };
|
|
81
|
+
*
|
|
82
|
+
* // Usage (typically automatic via ClassFactory):
|
|
83
|
+
* const source = new ConfigFileKeySource();
|
|
84
|
+
* await source.Initialize(); // Load the config file
|
|
85
|
+
*
|
|
86
|
+
* const key = await source.GetKey('pii_master');
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
let ConfigFileKeySource = class ConfigFileKeySource extends EncryptionKeySourceBase_1.EncryptionKeySourceBase {
|
|
90
|
+
/**
|
|
91
|
+
* Loaded configuration from the config file.
|
|
92
|
+
* Set during Initialize(), null if not yet loaded.
|
|
93
|
+
*
|
|
94
|
+
* @private
|
|
95
|
+
*/
|
|
96
|
+
_loadedConfig = null;
|
|
97
|
+
/**
|
|
98
|
+
* Path to the loaded config file (for error messages).
|
|
99
|
+
*
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
_configFilePath = null;
|
|
103
|
+
/**
|
|
104
|
+
* Human-readable name for this source.
|
|
105
|
+
*/
|
|
106
|
+
get SourceName() {
|
|
107
|
+
return 'Configuration File';
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Initializes the key source by loading the configuration file.
|
|
111
|
+
*
|
|
112
|
+
* Uses cosmiconfig to search for configuration in standard locations.
|
|
113
|
+
* Must be called before any key operations.
|
|
114
|
+
*
|
|
115
|
+
* @throws Error if cosmiconfig module cannot be loaded
|
|
116
|
+
*/
|
|
117
|
+
async Initialize() {
|
|
118
|
+
try {
|
|
119
|
+
// Dynamic import to avoid bundling cosmiconfig if not used
|
|
120
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
121
|
+
const { cosmiconfigSync } = require('cosmiconfig');
|
|
122
|
+
const explorer = cosmiconfigSync('mj', {
|
|
123
|
+
searchStrategy: 'global'
|
|
124
|
+
});
|
|
125
|
+
const result = explorer.search();
|
|
126
|
+
if (result?.config?.encryptionKeys) {
|
|
127
|
+
const keys = result.config.encryptionKeys;
|
|
128
|
+
// Validate that encryptionKeys is an object with string values
|
|
129
|
+
if (typeof keys !== 'object' || keys === null) {
|
|
130
|
+
throw new Error('Invalid encryptionKeys configuration: expected an object with string values');
|
|
131
|
+
}
|
|
132
|
+
// Type check all values
|
|
133
|
+
for (const [keyName, keyValue] of Object.entries(keys)) {
|
|
134
|
+
if (typeof keyValue !== 'string') {
|
|
135
|
+
throw new Error(`Invalid key "${keyName}" in encryptionKeys: ` +
|
|
136
|
+
`expected base64 string, got ${typeof keyValue}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
this._loadedConfig = keys;
|
|
140
|
+
this._configFilePath = result.filepath;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
// If cosmiconfig is not available or fails, log but don't crash
|
|
145
|
+
// The ValidateConfiguration check will fail appropriately
|
|
146
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
147
|
+
console.warn(`ConfigFileKeySource: Failed to load configuration: ${message}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Validates that the configuration file was loaded successfully.
|
|
152
|
+
*
|
|
153
|
+
* @returns `true` if the config file was loaded and contains encryptionKeys
|
|
154
|
+
*/
|
|
155
|
+
ValidateConfiguration() {
|
|
156
|
+
return this._loadedConfig !== null;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Checks if a key exists in the configuration file.
|
|
160
|
+
*
|
|
161
|
+
* @param lookupValue - The key name to check
|
|
162
|
+
* @returns Promise resolving to `true` if the key exists
|
|
163
|
+
*/
|
|
164
|
+
async KeyExists(lookupValue) {
|
|
165
|
+
if (!this._loadedConfig) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
if (!lookupValue || typeof lookupValue !== 'string') {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
// Validate key name format
|
|
172
|
+
if (!this.isValidKeyName(lookupValue)) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
return this._loadedConfig[lookupValue] !== undefined;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Retrieves key material from the configuration file.
|
|
179
|
+
*
|
|
180
|
+
* Keys should be stored as base64-encoded strings in the encryptionKeys
|
|
181
|
+
* section of the configuration file.
|
|
182
|
+
*
|
|
183
|
+
* For versioned keys, the version is appended with an underscore:
|
|
184
|
+
* - `key_name` for version 1 (default)
|
|
185
|
+
* - `key_name_v2` for version 2
|
|
186
|
+
*
|
|
187
|
+
* @param lookupValue - The key name in the configuration
|
|
188
|
+
* @param keyVersion - Optional version number (defaults to '1')
|
|
189
|
+
* @returns Promise resolving to the decoded key bytes
|
|
190
|
+
*
|
|
191
|
+
* @throws Error if Initialize() was not called
|
|
192
|
+
* @throws Error if the key is not found
|
|
193
|
+
* @throws Error if the value is not valid base64
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```typescript
|
|
197
|
+
* // Config file has: encryptionKeys: { pii_master: '...' }
|
|
198
|
+
* const key = await source.GetKey('pii_master');
|
|
199
|
+
*
|
|
200
|
+
* // For versioned keys during rotation:
|
|
201
|
+
* // Config has: pii_master, pii_master_v2
|
|
202
|
+
* const newKey = await source.GetKey('pii_master', '2');
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
async GetKey(lookupValue, keyVersion) {
|
|
206
|
+
// Check if initialized
|
|
207
|
+
if (!this._loadedConfig) {
|
|
208
|
+
const configPath = this._configFilePath || 'mj.config.cjs';
|
|
209
|
+
throw new Error(`Configuration file not loaded. Ensure Initialize() was called and ` +
|
|
210
|
+
`the configuration file (${configPath}) exists with an "encryptionKeys" section. ` +
|
|
211
|
+
`Example:\n` +
|
|
212
|
+
`module.exports = {\n` +
|
|
213
|
+
` encryptionKeys: {\n` +
|
|
214
|
+
` your_key_name: 'base64-encoded-key'\n` +
|
|
215
|
+
` }\n` +
|
|
216
|
+
`};`);
|
|
217
|
+
}
|
|
218
|
+
// Validate lookup value
|
|
219
|
+
if (!lookupValue || typeof lookupValue !== 'string') {
|
|
220
|
+
throw new Error('Invalid lookup value: key name is required. ' +
|
|
221
|
+
'Provide the name of the key as defined in the encryptionKeys section.');
|
|
222
|
+
}
|
|
223
|
+
// Validate key name format
|
|
224
|
+
if (!this.isValidKeyName(lookupValue)) {
|
|
225
|
+
throw new Error(`Invalid key name: "${lookupValue}". ` +
|
|
226
|
+
'Key names must start with a letter or underscore and contain only ' +
|
|
227
|
+
'letters, numbers, and underscores.');
|
|
228
|
+
}
|
|
229
|
+
// Build the full key name with version
|
|
230
|
+
const keyName = this.buildKeyName(lookupValue, keyVersion);
|
|
231
|
+
// Retrieve the value
|
|
232
|
+
const keyValue = this._loadedConfig[keyName];
|
|
233
|
+
if (keyValue === undefined || keyValue === null) {
|
|
234
|
+
throw new Error(`Encryption key "${keyName}" not found in configuration file. ` +
|
|
235
|
+
`Add it to the "encryptionKeys" section of ${this._configFilePath || 'mj.config.cjs'}. ` +
|
|
236
|
+
`Example:\n` +
|
|
237
|
+
`encryptionKeys: {\n` +
|
|
238
|
+
` "${keyName}": "base64-encoded-key"\n` +
|
|
239
|
+
`}\n\n` +
|
|
240
|
+
`Generate a key with: openssl rand -base64 32`);
|
|
241
|
+
}
|
|
242
|
+
if (keyValue.trim() === '') {
|
|
243
|
+
throw new Error(`Encryption key "${keyName}" in configuration file is empty. ` +
|
|
244
|
+
'The value must be a non-empty base64-encoded key.');
|
|
245
|
+
}
|
|
246
|
+
// Decode from base64
|
|
247
|
+
try {
|
|
248
|
+
const keyBytes = Buffer.from(keyValue, 'base64');
|
|
249
|
+
if (keyBytes.length === 0) {
|
|
250
|
+
throw new Error('Decoded key is empty');
|
|
251
|
+
}
|
|
252
|
+
return keyBytes;
|
|
253
|
+
}
|
|
254
|
+
catch (err) {
|
|
255
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
256
|
+
throw new Error(`Invalid base64 encoding for key "${keyName}" in configuration file. ` +
|
|
257
|
+
`The value must be a valid base64-encoded string. Error: ${message}. ` +
|
|
258
|
+
'Generate a valid key with: openssl rand -base64 32');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Cleans up resources (no-op for config file source).
|
|
263
|
+
*/
|
|
264
|
+
async Dispose() {
|
|
265
|
+
this._loadedConfig = null;
|
|
266
|
+
this._configFilePath = null;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Builds the full key name with optional version suffix.
|
|
270
|
+
*
|
|
271
|
+
* @param baseName - The base key name
|
|
272
|
+
* @param keyVersion - Optional version number
|
|
273
|
+
* @returns The full key name
|
|
274
|
+
*
|
|
275
|
+
* @private
|
|
276
|
+
*/
|
|
277
|
+
buildKeyName(baseName, keyVersion) {
|
|
278
|
+
// No version or version '1' = use base name
|
|
279
|
+
if (!keyVersion || keyVersion === '1') {
|
|
280
|
+
return baseName;
|
|
281
|
+
}
|
|
282
|
+
// For other versions, append _v{version} (lowercase for config files)
|
|
283
|
+
return `${baseName}_v${keyVersion}`;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Validates that a string is a valid configuration key name.
|
|
287
|
+
*
|
|
288
|
+
* @param name - The name to validate
|
|
289
|
+
* @returns `true` if the name is valid
|
|
290
|
+
*
|
|
291
|
+
* @private
|
|
292
|
+
*/
|
|
293
|
+
isValidKeyName(name) {
|
|
294
|
+
if (!name || name.length === 0) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
if (name.length > 256) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
// Allow alphanumeric, underscores, and hyphens
|
|
301
|
+
// Must start with letter or underscore
|
|
302
|
+
const keyNamePattern = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
303
|
+
return keyNamePattern.test(name);
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
exports.ConfigFileKeySource = ConfigFileKeySource;
|
|
307
|
+
exports.ConfigFileKeySource = ConfigFileKeySource = __decorate([
|
|
308
|
+
(0, global_1.RegisterClass)(EncryptionKeySourceBase_1.EncryptionKeySourceBase, 'ConfigFileKeySource')
|
|
309
|
+
], ConfigFileKeySource);
|
|
310
|
+
//# sourceMappingURL=ConfigFileKeySource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigFileKeySource.js","sourceRoot":"","sources":["../../src/providers/ConfigFileKeySource.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;;;;;;;;;AAEH,mDAAuD;AACvD,wEAAqE;AAYrE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEI,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,iDAAuB;IAC5D;;;;;OAKG;IACK,aAAa,GAAkC,IAAI,CAAC;IAE5D;;;;OAIG;IACK,eAAe,GAAkB,IAAI,CAAC;IAE9C;;OAEG;IACH,IAAI,UAAU;QACV,OAAO,oBAAoB,CAAC;IAChC,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,UAAU;QACZ,IAAI,CAAC;YACD,2DAA2D;YAC3D,iEAAiE;YACjE,MAAM,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;YAEnD,MAAM,QAAQ,GAAwB,eAAe,CAAC,IAAI,EAAE;gBACxD,cAAc,EAAE,QAAQ;aAC3B,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;YAEjC,IAAI,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;gBACjC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC;gBAE1C,+DAA+D;gBAC/D,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;oBAC5C,MAAM,IAAI,KAAK,CACX,6EAA6E,CAChF,CAAC;gBACN,CAAC;gBAED,wBAAwB;gBACxB,KAAK,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC,EAAE,CAAC;oBAChF,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;wBAC/B,MAAM,IAAI,KAAK,CACX,gBAAgB,OAAO,uBAAuB;4BAC9C,+BAA+B,OAAO,QAAQ,EAAE,CACnD,CAAC;oBACN,CAAC;gBACL,CAAC;gBAED,IAAI,CAAC,aAAa,GAAG,IAA8B,CAAC;gBACpD,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC;YAC3C,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,gEAAgE;YAChE,0DAA0D;YAC1D,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,CAAC,IAAI,CACR,sDAAsD,OAAO,EAAE,CAClE,CAAC;QACN,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,qBAAqB;QACjB,OAAO,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,SAAS,CAAC,WAAmB;QAC/B,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACtB,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,IAAI,CAAC,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;YAClD,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,2BAA2B;QAC3B,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;YACpC,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,OAAO,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,KAAK,SAAS,CAAC;IACzD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,KAAK,CAAC,MAAM,CAAC,WAAmB,EAAE,UAAmB;QACjD,uBAAuB;QACvB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACtB,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,IAAI,eAAe,CAAC;YAC3D,MAAM,IAAI,KAAK,CACX,oEAAoE;gBACpE,2BAA2B,UAAU,6CAA6C;gBAClF,YAAY;gBACZ,sBAAsB;gBACtB,uBAAuB;gBACvB,2CAA2C;gBAC3C,OAAO;gBACP,IAAI,CACP,CAAC;QACN,CAAC;QAED,wBAAwB;QACxB,IAAI,CAAC,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CACX,8CAA8C;gBAC9C,uEAAuE,CAC1E,CAAC;QACN,CAAC;QAED,2BAA2B;QAC3B,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACX,sBAAsB,WAAW,KAAK;gBACtC,oEAAoE;gBACpE,oCAAoC,CACvC,CAAC;QACN,CAAC;QAED,uCAAuC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QAE3D,qBAAqB;QACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAE7C,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CACX,mBAAmB,OAAO,qCAAqC;gBAC/D,6CAA6C,IAAI,CAAC,eAAe,IAAI,eAAe,IAAI;gBACxF,YAAY;gBACZ,qBAAqB;gBACrB,MAAM,OAAO,2BAA2B;gBACxC,OAAO;gBACP,8CAA8C,CACjD,CAAC;QACN,CAAC;QAED,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACX,mBAAmB,OAAO,oCAAoC;gBAC9D,mDAAmD,CACtD,CAAC;QACN,CAAC;QAED,qBAAqB;QACrB,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAEjD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;YAC5C,CAAC;YAED,OAAO,QAAQ,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,MAAM,IAAI,KAAK,CACX,oCAAoC,OAAO,2BAA2B;gBACtE,2DAA2D,OAAO,IAAI;gBACtE,oDAAoD,CACvD,CAAC;QACN,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACT,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;IAChC,CAAC;IAED;;;;;;;;OAQG;IACK,YAAY,CAAC,QAAgB,EAAE,UAAmB;QACtD,4CAA4C;QAC5C,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,GAAG,EAAE,CAAC;YACpC,OAAO,QAAQ,CAAC;QACpB,CAAC;QAED,sEAAsE;QACtE,OAAO,GAAG,QAAQ,KAAK,UAAU,EAAE,CAAC;IACxC,CAAC;IAED;;;;;;;OAOG;IACK,cAAc,CAAC,IAAY;QAC/B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YACpB,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,+CAA+C;QAC/C,uCAAuC;QACvC,MAAM,cAAc,GAAG,2BAA2B,CAAC;QACnD,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;CACJ,CAAA;AAtQY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,sBAAa,EAAC,iDAAuB,EAAE,qBAAqB,CAAC;GACjD,mBAAmB,CAsQ/B"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Environment variable key source provider.
|
|
3
|
+
*
|
|
4
|
+
* This provider retrieves encryption keys from environment variables.
|
|
5
|
+
* It's the simplest and most commonly used key source for development
|
|
6
|
+
* and containerized deployments.
|
|
7
|
+
*
|
|
8
|
+
* ## Usage
|
|
9
|
+
*
|
|
10
|
+
* 1. Generate a key: `openssl rand -base64 32`
|
|
11
|
+
* 2. Set environment variable: `export MJ_ENCRYPTION_KEY_PII="<base64-key>"`
|
|
12
|
+
* 3. Configure in database with KeyLookupValue = 'MJ_ENCRYPTION_KEY_PII'
|
|
13
|
+
*
|
|
14
|
+
* ## Key Format
|
|
15
|
+
*
|
|
16
|
+
* Keys must be base64-encoded. For AES-256, generate with:
|
|
17
|
+
* ```bash
|
|
18
|
+
* openssl rand -base64 32
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* For AES-128:
|
|
22
|
+
* ```bash
|
|
23
|
+
* openssl rand -base64 16
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* ## Key Rotation
|
|
27
|
+
*
|
|
28
|
+
* During rotation, store the new key in a separate variable:
|
|
29
|
+
* ```bash
|
|
30
|
+
* export MJ_ENCRYPTION_KEY_PII="<current-key>"
|
|
31
|
+
* export MJ_ENCRYPTION_KEY_PII_NEW="<new-key>"
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* After rotation completes, remove the old key and optionally
|
|
35
|
+
* rename the new key to the original variable name.
|
|
36
|
+
*
|
|
37
|
+
* ## Security Considerations
|
|
38
|
+
*
|
|
39
|
+
* - Environment variables may be logged or visible to child processes
|
|
40
|
+
* - Consider using secrets managers for production deployments
|
|
41
|
+
* - Ensure proper access controls on the runtime environment
|
|
42
|
+
* - Never commit keys to source control
|
|
43
|
+
*
|
|
44
|
+
* @module @memberjunction/encryption
|
|
45
|
+
*/
|
|
46
|
+
/// <reference types="node" />
|
|
47
|
+
import { EncryptionKeySourceBase } from '../EncryptionKeySourceBase';
|
|
48
|
+
/**
|
|
49
|
+
* Encryption key source that retrieves keys from environment variables.
|
|
50
|
+
*
|
|
51
|
+
* This is the default and recommended key source for:
|
|
52
|
+
* - Development environments
|
|
53
|
+
* - Docker/Kubernetes deployments with secret injection
|
|
54
|
+
* - Serverless functions with environment configuration
|
|
55
|
+
*
|
|
56
|
+
* Keys are expected to be base64-encoded strings in the environment.
|
|
57
|
+
* The provider decodes them to raw bytes for crypto operations.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* // The provider is automatically instantiated by ClassFactory
|
|
62
|
+
* // based on database configuration. For manual usage:
|
|
63
|
+
*
|
|
64
|
+
* import { EnvVarKeySource } from '@memberjunction/encryption';
|
|
65
|
+
*
|
|
66
|
+
* const source = new EnvVarKeySource();
|
|
67
|
+
*
|
|
68
|
+
* // Check if key exists
|
|
69
|
+
* if (await source.KeyExists('MJ_ENCRYPTION_KEY_PII')) {
|
|
70
|
+
* const keyBytes = await source.GetKey('MJ_ENCRYPTION_KEY_PII');
|
|
71
|
+
* console.log(`Key length: ${keyBytes.length} bytes`);
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare class EnvVarKeySource extends EncryptionKeySourceBase {
|
|
76
|
+
/**
|
|
77
|
+
* Human-readable name for this source.
|
|
78
|
+
*/
|
|
79
|
+
get SourceName(): string;
|
|
80
|
+
/**
|
|
81
|
+
* Validates the source configuration.
|
|
82
|
+
*
|
|
83
|
+
* For environment variables, configuration is always valid as
|
|
84
|
+
* keys are validated at lookup time. This allows the source
|
|
85
|
+
* to be used dynamically for any environment variable.
|
|
86
|
+
*
|
|
87
|
+
* @returns Always returns `true`
|
|
88
|
+
*/
|
|
89
|
+
ValidateConfiguration(): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Checks if an environment variable containing a key exists.
|
|
92
|
+
*
|
|
93
|
+
* @param lookupValue - The environment variable name to check
|
|
94
|
+
* @returns Promise resolving to `true` if the variable is defined
|
|
95
|
+
*/
|
|
96
|
+
KeyExists(lookupValue: string): Promise<boolean>;
|
|
97
|
+
/**
|
|
98
|
+
* Retrieves key material from an environment variable.
|
|
99
|
+
*
|
|
100
|
+
* The environment variable should contain a base64-encoded key.
|
|
101
|
+
* For versioned keys, the version is appended with an underscore:
|
|
102
|
+
* - `KEY_NAME` for version 1 (default)
|
|
103
|
+
* - `KEY_NAME_V2` for version 2
|
|
104
|
+
* - etc.
|
|
105
|
+
*
|
|
106
|
+
* @param lookupValue - The environment variable name
|
|
107
|
+
* @param keyVersion - Optional version number (defaults to '1')
|
|
108
|
+
* @returns Promise resolving to the decoded key bytes
|
|
109
|
+
*
|
|
110
|
+
* @throws Error if the environment variable is not set
|
|
111
|
+
* @throws Error if the value is not valid base64
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* // Get current key
|
|
116
|
+
* const key = await source.GetKey('MJ_ENCRYPTION_KEY_PII');
|
|
117
|
+
*
|
|
118
|
+
* // Get specific version during rotation
|
|
119
|
+
* const oldKey = await source.GetKey('MJ_ENCRYPTION_KEY_PII', '1');
|
|
120
|
+
* const newKey = await source.GetKey('MJ_ENCRYPTION_KEY_PII', '2');
|
|
121
|
+
* // The above looks for MJ_ENCRYPTION_KEY_PII_V2
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
GetKey(lookupValue: string, keyVersion?: string): Promise<Buffer>;
|
|
125
|
+
/**
|
|
126
|
+
* Builds the full environment variable name with optional version suffix.
|
|
127
|
+
*
|
|
128
|
+
* @param baseName - The base environment variable name
|
|
129
|
+
* @param keyVersion - Optional version number
|
|
130
|
+
* @returns The full environment variable name
|
|
131
|
+
*
|
|
132
|
+
* @private
|
|
133
|
+
*/
|
|
134
|
+
private buildEnvVarName;
|
|
135
|
+
/**
|
|
136
|
+
* Validates that a string is a valid environment variable name.
|
|
137
|
+
*
|
|
138
|
+
* Valid names:
|
|
139
|
+
* - Start with a letter (A-Z, a-z) or underscore (_)
|
|
140
|
+
* - Contain only letters, numbers, and underscores
|
|
141
|
+
*
|
|
142
|
+
* This prevents injection attacks where malicious lookupValues
|
|
143
|
+
* could be crafted to access unintended variables.
|
|
144
|
+
*
|
|
145
|
+
* @param name - The name to validate
|
|
146
|
+
* @returns `true` if the name is valid
|
|
147
|
+
*
|
|
148
|
+
* @private
|
|
149
|
+
*/
|
|
150
|
+
private isValidEnvVarName;
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=EnvVarKeySource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EnvVarKeySource.d.ts","sourceRoot":"","sources":["../../src/providers/EnvVarKeySource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;;AAGH,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAErE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBACa,eAAgB,SAAQ,uBAAuB;IACxD;;OAEG;IACH,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED;;;;;;;;OAQG;IACH,qBAAqB,IAAI,OAAO;IAMhC;;;;;OAKG;IACG,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAatD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAsEvE;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;IAUvB;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,iBAAiB;CAiB5B"}
|