@memberjunction/encryption 0.0.1 → 2.130.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.
Files changed (46) hide show
  1. package/README.md +391 -28
  2. package/dist/EncryptionEngine.d.ts +351 -0
  3. package/dist/EncryptionEngine.d.ts.map +1 -0
  4. package/dist/EncryptionEngine.js +683 -0
  5. package/dist/EncryptionEngine.js.map +1 -0
  6. package/dist/EncryptionKeySourceBase.d.ts +203 -0
  7. package/dist/EncryptionKeySourceBase.d.ts.map +1 -0
  8. package/dist/EncryptionKeySourceBase.js +133 -0
  9. package/dist/EncryptionKeySourceBase.js.map +1 -0
  10. package/dist/actions/EnableFieldEncryptionAction.d.ts +87 -0
  11. package/dist/actions/EnableFieldEncryptionAction.d.ts.map +1 -0
  12. package/dist/actions/EnableFieldEncryptionAction.js +308 -0
  13. package/dist/actions/EnableFieldEncryptionAction.js.map +1 -0
  14. package/dist/actions/RotateEncryptionKeyAction.d.ts +79 -0
  15. package/dist/actions/RotateEncryptionKeyAction.d.ts.map +1 -0
  16. package/dist/actions/RotateEncryptionKeyAction.js +343 -0
  17. package/dist/actions/RotateEncryptionKeyAction.js.map +1 -0
  18. package/dist/actions/index.d.ts +12 -0
  19. package/dist/actions/index.d.ts.map +1 -0
  20. package/dist/actions/index.js +17 -0
  21. package/dist/actions/index.js.map +1 -0
  22. package/dist/index.d.ts +66 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +81 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/interfaces.d.ts +216 -0
  27. package/dist/interfaces.d.ts.map +1 -0
  28. package/dist/interfaces.js +15 -0
  29. package/dist/interfaces.js.map +1 -0
  30. package/dist/providers/AWSKMSKeySource.d.ts +110 -0
  31. package/dist/providers/AWSKMSKeySource.d.ts.map +1 -0
  32. package/dist/providers/AWSKMSKeySource.js +245 -0
  33. package/dist/providers/AWSKMSKeySource.js.map +1 -0
  34. package/dist/providers/AzureKeyVaultKeySource.d.ts +109 -0
  35. package/dist/providers/AzureKeyVaultKeySource.d.ts.map +1 -0
  36. package/dist/providers/AzureKeyVaultKeySource.js +268 -0
  37. package/dist/providers/AzureKeyVaultKeySource.js.map +1 -0
  38. package/dist/providers/ConfigFileKeySource.d.ts +173 -0
  39. package/dist/providers/ConfigFileKeySource.d.ts.map +1 -0
  40. package/dist/providers/ConfigFileKeySource.js +310 -0
  41. package/dist/providers/ConfigFileKeySource.js.map +1 -0
  42. package/dist/providers/EnvVarKeySource.d.ts +152 -0
  43. package/dist/providers/EnvVarKeySource.d.ts.map +1 -0
  44. package/dist/providers/EnvVarKeySource.js +251 -0
  45. package/dist/providers/EnvVarKeySource.js.map +1 -0
  46. package/package.json +65 -6
@@ -0,0 +1,308 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Action for enabling encryption on an existing entity field.
4
+ *
5
+ * When encryption is enabled on a field that already has data, this action:
6
+ * 1. Verifies the encryption key is valid and accessible
7
+ * 2. Loads existing records in batches
8
+ * 3. Encrypts all non-null values
9
+ * 4. Saves the encrypted values back to the database
10
+ *
11
+ * ## Usage
12
+ *
13
+ * This action is typically invoked after updating the EntityField metadata
14
+ * to enable encryption:
15
+ *
16
+ * ```typescript
17
+ * // First, update the EntityField to enable encryption
18
+ * entityField.Encrypt = true;
19
+ * entityField.EncryptionKeyID = 'key-uuid';
20
+ * await entityField.Save();
21
+ *
22
+ * // Then, encrypt existing data
23
+ * const result = await actionEngine.RunAction({
24
+ * ActionName: 'Enable Field Encryption',
25
+ * Params: [
26
+ * { Name: 'EntityFieldID', Value: entityField.ID },
27
+ * { Name: 'BatchSize', Value: 100 }
28
+ * ],
29
+ * ContextUser: currentUser
30
+ * });
31
+ * ```
32
+ *
33
+ * ## Security Considerations
34
+ *
35
+ * - This is a one-way operation - plaintext is replaced with ciphertext
36
+ * - Ensure backups exist before running
37
+ * - Values that are already encrypted are skipped
38
+ * - Empty/null values are not encrypted
39
+ *
40
+ * @module @memberjunction/encryption
41
+ */
42
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
43
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
44
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
45
+ 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;
46
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
47
+ };
48
+ Object.defineProperty(exports, "__esModule", { value: true });
49
+ exports.EnableFieldEncryptionAction = void 0;
50
+ const global_1 = require("@memberjunction/global");
51
+ const core_1 = require("@memberjunction/core");
52
+ const EncryptionEngine_1 = require("../EncryptionEngine");
53
+ /**
54
+ * Action for encrypting existing data when encryption is enabled on a field.
55
+ *
56
+ * This action handles the initial encryption of existing plaintext data
57
+ * after the Encrypt flag is set on an EntityField.
58
+ *
59
+ * ## Process
60
+ *
61
+ * 1. Loads the EntityField metadata to get encryption settings
62
+ * 2. Validates the encryption key is accessible
63
+ * 3. Queries for all records where the field is not null
64
+ * 4. For each record:
65
+ * - Skip if already encrypted
66
+ * - Encrypt the plaintext value
67
+ * - Save the encrypted value
68
+ * 5. Returns statistics on encrypted/skipped records
69
+ *
70
+ * ## Batch Processing
71
+ *
72
+ * Records are processed in configurable batches to manage memory
73
+ * and allow for progress tracking.
74
+ *
75
+ * @security This is a privileged operation that modifies data.
76
+ * Should be restricted to administrators.
77
+ */
78
+ let EnableFieldEncryptionAction = class EnableFieldEncryptionAction {
79
+ /**
80
+ * Executes the field encryption operation.
81
+ *
82
+ * @param params - Action parameters including EntityFieldID and optional BatchSize
83
+ * @returns Result with counts of encrypted and skipped records
84
+ */
85
+ async Run(params) {
86
+ const { Params, ContextUser } = params;
87
+ // Extract parameters
88
+ const entityFieldId = this.getParamValue(Params, 'EntityFieldID');
89
+ const batchSize = this.getParamValue(Params, 'BatchSize') || 100;
90
+ // Validate required parameters
91
+ if (!entityFieldId) {
92
+ return {
93
+ Success: false,
94
+ ResultCode: 'INVALID_PARAMS',
95
+ Message: 'EntityFieldID is required',
96
+ Params
97
+ };
98
+ }
99
+ try {
100
+ const result = await this.enableFieldEncryption({
101
+ entityFieldId: String(entityFieldId),
102
+ batchSize: Number(batchSize)
103
+ }, ContextUser);
104
+ // Update output parameters
105
+ const outputParams = [...Params];
106
+ const encryptedParam = outputParams.find(p => p.Name === 'RecordsEncrypted');
107
+ if (encryptedParam)
108
+ encryptedParam.Value = result.recordsEncrypted;
109
+ const skippedParam = outputParams.find(p => p.Name === 'RecordsSkipped');
110
+ if (skippedParam)
111
+ skippedParam.Value = result.recordsSkipped;
112
+ if (result.success) {
113
+ return {
114
+ Success: true,
115
+ ResultCode: 'SUCCESS',
116
+ Message: `Field encryption completed. Encrypted ${result.recordsEncrypted} records, skipped ${result.recordsSkipped} already encrypted.`,
117
+ Params: outputParams
118
+ };
119
+ }
120
+ else {
121
+ return {
122
+ Success: false,
123
+ ResultCode: 'ENCRYPTION_FAILED',
124
+ Message: result.error || 'Field encryption failed',
125
+ Params: outputParams
126
+ };
127
+ }
128
+ }
129
+ catch (error) {
130
+ const message = error instanceof Error ? error.message : String(error);
131
+ (0, core_1.LogError)(`Enable field encryption failed: ${message}`);
132
+ return {
133
+ Success: false,
134
+ ResultCode: 'ERROR',
135
+ Message: `Enable field encryption failed: ${message}`,
136
+ Params
137
+ };
138
+ }
139
+ }
140
+ /**
141
+ * Performs the actual field encryption operation.
142
+ *
143
+ * @private
144
+ */
145
+ async enableFieldEncryption(params, contextUser) {
146
+ const { entityFieldId, batchSize = 100 } = params;
147
+ const engine = EncryptionEngine_1.EncryptionEngine.Instance;
148
+ await engine.Config(false, contextUser);
149
+ const md = new core_1.Metadata();
150
+ const rv = new core_1.RunView();
151
+ let recordsEncrypted = 0;
152
+ let recordsSkipped = 0;
153
+ try {
154
+ // Step 1: Load the EntityField metadata
155
+ const fieldResult = await rv.RunView({
156
+ EntityName: 'Entity Fields',
157
+ ExtraFilter: `ID = '${entityFieldId}'`,
158
+ ResultType: 'simple'
159
+ }, contextUser);
160
+ if (!fieldResult.Success || fieldResult.Results.length === 0) {
161
+ return {
162
+ success: false,
163
+ recordsEncrypted: 0,
164
+ recordsSkipped: 0,
165
+ error: `EntityField not found: ${entityFieldId}`
166
+ };
167
+ }
168
+ const fieldInfo = fieldResult.Results[0];
169
+ const entityName = fieldInfo.Entity;
170
+ const fieldName = fieldInfo.Name;
171
+ const encryptionKeyId = fieldInfo.EncryptionKeyID;
172
+ // Validate that encryption is enabled
173
+ if (!fieldInfo.Encrypt) {
174
+ return {
175
+ success: false,
176
+ recordsEncrypted: 0,
177
+ recordsSkipped: 0,
178
+ error: `Field ${entityName}.${fieldName} does not have encryption enabled. Set Encrypt=true first.`
179
+ };
180
+ }
181
+ if (!encryptionKeyId) {
182
+ return {
183
+ success: false,
184
+ recordsEncrypted: 0,
185
+ recordsSkipped: 0,
186
+ error: `Field ${entityName}.${fieldName} does not have an EncryptionKeyID set.`
187
+ };
188
+ }
189
+ (0, core_1.LogStatus)(`Enabling encryption for field: ${entityName}.${fieldName}`);
190
+ // Step 2: Validate the encryption key is accessible
191
+ // This will throw if the key is not valid
192
+ await engine.ValidateKeyMaterial(fieldInfo.KeyLookupValue || fieldInfo.EncryptionKeyID, // fallback to key ID if no lookup value in field
193
+ encryptionKeyId, contextUser);
194
+ (0, core_1.LogStatus)('Encryption key validated successfully');
195
+ // Step 3: Get entity info for proper querying
196
+ const entityInfo = md.Entities.find(e => e.Name === entityName);
197
+ if (!entityInfo) {
198
+ return {
199
+ success: false,
200
+ recordsEncrypted: 0,
201
+ recordsSkipped: 0,
202
+ error: `Entity not found: ${entityName}`
203
+ };
204
+ }
205
+ // Step 4: Process records in batches
206
+ let hasMore = true;
207
+ let offset = 0;
208
+ while (hasMore) {
209
+ // Query for records where field is not null and not already encrypted
210
+ // We check for NOT LIKE '$ENC$%' to find unencrypted values
211
+ const batchResult = await rv.RunView({
212
+ EntityName: entityName,
213
+ ExtraFilter: `${fieldName} IS NOT NULL AND ${fieldName} NOT LIKE '$ENC$%'`,
214
+ OrderBy: entityInfo.PrimaryKeys.map(pk => pk.Name).join(', '),
215
+ MaxRows: batchSize,
216
+ ResultType: 'entity_object'
217
+ }, contextUser);
218
+ if (!batchResult.Success) {
219
+ (0, core_1.LogError)(`Failed to query records for ${entityName}.${fieldName}`);
220
+ hasMore = false;
221
+ continue;
222
+ }
223
+ const records = batchResult.Results;
224
+ hasMore = records.length === batchSize;
225
+ if (records.length === 0) {
226
+ break;
227
+ }
228
+ (0, core_1.LogStatus)(`Processing batch of ${records.length} records (offset: ${offset})`);
229
+ // Encrypt each record
230
+ for (const record of records) {
231
+ const plainValue = record.Get(fieldName);
232
+ // Double-check: skip null/empty values
233
+ if (plainValue === null || plainValue === undefined || plainValue === '') {
234
+ continue;
235
+ }
236
+ // Double-check: skip if somehow already encrypted
237
+ if (typeof plainValue === 'string' && engine.IsEncrypted(plainValue)) {
238
+ recordsSkipped++;
239
+ continue;
240
+ }
241
+ try {
242
+ // Convert to string if needed
243
+ const stringValue = typeof plainValue === 'string'
244
+ ? plainValue
245
+ : JSON.stringify(plainValue);
246
+ // Encrypt the value
247
+ const encryptedValue = await engine.Encrypt(stringValue, encryptionKeyId, contextUser);
248
+ // Update and save the record
249
+ record.Set(fieldName, encryptedValue);
250
+ const saveResult = await record.Save();
251
+ if (saveResult) {
252
+ recordsEncrypted++;
253
+ }
254
+ else {
255
+ (0, core_1.LogError)(`Failed to save encrypted record in ${entityName}.${fieldName}`);
256
+ }
257
+ }
258
+ catch (recordError) {
259
+ const msg = recordError instanceof Error ? recordError.message : String(recordError);
260
+ (0, core_1.LogError)(`Failed to encrypt record in ${entityName}.${fieldName}: ${msg}`);
261
+ }
262
+ }
263
+ offset += batchSize;
264
+ }
265
+ // Also count records that were already encrypted (query separately)
266
+ const alreadyEncryptedResult = await rv.RunView({
267
+ EntityName: entityName,
268
+ ExtraFilter: `${fieldName} LIKE '$ENC$%'`,
269
+ MaxRows: 1, // Just checking count
270
+ ResultType: 'simple'
271
+ }, contextUser);
272
+ if (alreadyEncryptedResult.Success) {
273
+ // The query itself would return count, but for simplicity we note there are some
274
+ (0, core_1.LogStatus)(`Some records in ${entityName}.${fieldName} were already encrypted`);
275
+ }
276
+ (0, core_1.LogStatus)(`Field encryption completed for ${entityName}.${fieldName}. ` +
277
+ `Encrypted: ${recordsEncrypted}, Skipped: ${recordsSkipped}`);
278
+ return {
279
+ success: true,
280
+ recordsEncrypted,
281
+ recordsSkipped
282
+ };
283
+ }
284
+ catch (error) {
285
+ const message = error instanceof Error ? error.message : String(error);
286
+ (0, core_1.LogError)(`Enable field encryption error: ${message}`);
287
+ return {
288
+ success: false,
289
+ recordsEncrypted,
290
+ recordsSkipped,
291
+ error: message
292
+ };
293
+ }
294
+ }
295
+ /**
296
+ * Helper to extract parameter value by name.
297
+ * @private
298
+ */
299
+ getParamValue(params, name) {
300
+ const param = params.find(p => p.Name === name);
301
+ return param?.Value ?? null;
302
+ }
303
+ };
304
+ exports.EnableFieldEncryptionAction = EnableFieldEncryptionAction;
305
+ exports.EnableFieldEncryptionAction = EnableFieldEncryptionAction = __decorate([
306
+ (0, global_1.RegisterClass)(Object, 'Enable Field Encryption')
307
+ ], EnableFieldEncryptionAction);
308
+ //# sourceMappingURL=EnableFieldEncryptionAction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EnableFieldEncryptionAction.js","sourceRoot":"","sources":["../../src/actions/EnableFieldEncryptionAction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @fileoverview Action for rotating encryption keys with full data re-encryption.
3
+ *
4
+ * Key rotation is a critical security operation that:
5
+ * 1. Validates the new key material is accessible
6
+ * 2. Decrypts all data encrypted with the old key
7
+ * 3. Re-encrypts with the new key in a transactional manner
8
+ * 4. Updates the key metadata to point to the new key material
9
+ *
10
+ * ## Usage
11
+ *
12
+ * This action is typically invoked via the MemberJunction Actions framework:
13
+ *
14
+ * ```typescript
15
+ * const result = await actionEngine.RunAction({
16
+ * ActionName: 'Rotate Encryption Key',
17
+ * Params: [
18
+ * { Name: 'EncryptionKeyID', Value: 'key-uuid' },
19
+ * { Name: 'NewKeyLookupValue', Value: 'MJ_ENCRYPTION_KEY_PII_V2' },
20
+ * { Name: 'BatchSize', Value: 100 }
21
+ * ],
22
+ * ContextUser: currentUser
23
+ * });
24
+ * ```
25
+ *
26
+ * ## Security Considerations
27
+ *
28
+ * - The new key must be accessible before starting rotation
29
+ * - Rotation is transactional - all or nothing
30
+ * - Key status is set to 'Rotating' during the operation
31
+ * - On success, key version is incremented
32
+ * - On failure, original key continues to work
33
+ *
34
+ * @module @memberjunction/encryption
35
+ */
36
+ import { ActionResultSimple, RunActionParams } from '@memberjunction/actions-base';
37
+ /**
38
+ * Action for rotating encryption keys with full re-encryption of affected data.
39
+ *
40
+ * Key rotation involves:
41
+ * 1. Validating the new key is accessible
42
+ * 2. Setting key status to 'Rotating'
43
+ * 3. For each entity field using this key:
44
+ * - Loading all records in batches
45
+ * - Decrypting with old key
46
+ * - Re-encrypting with new key
47
+ * - Updating records
48
+ * 4. Updating key metadata (lookup value, version)
49
+ * 5. Setting key status back to 'Active'
50
+ *
51
+ * ## Transaction Safety
52
+ *
53
+ * - Each batch is processed within a transaction
54
+ * - On batch failure, the entire rotation is rolled back
55
+ * - Key status is reset to 'Active' on failure
56
+ *
57
+ * @security This is a privileged operation that should be restricted to administrators.
58
+ */
59
+ export declare class RotateEncryptionKeyAction {
60
+ /**
61
+ * Executes the key rotation operation.
62
+ *
63
+ * @param params - Action parameters including EncryptionKeyID and NewKeyLookupValue
64
+ * @returns Result with success status and details about rotated fields/records
65
+ */
66
+ Run(params: RunActionParams): Promise<ActionResultSimple>;
67
+ /**
68
+ * Performs the actual key rotation operation.
69
+ *
70
+ * @private
71
+ */
72
+ private rotateKey;
73
+ /**
74
+ * Helper to extract parameter value by name.
75
+ * @private
76
+ */
77
+ private getParamValue;
78
+ }
79
+ //# sourceMappingURL=RotateEncryptionKeyAction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RotateEncryptionKeyAction.d.ts","sourceRoot":"","sources":["../../src/actions/RotateEncryptionKeyAction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAe,MAAM,8BAA8B,CAAC;AAKhG;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBACa,yBAAyB;IAClC;;;;;OAKG;IACU,GAAG,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAyEtE;;;;OAIG;YACW,SAAS;IAyNvB;;;OAGG;IACH,OAAO,CAAC,aAAa;CAIxB"}