@rockcarver/frodo-lib 0.11.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 (112) hide show
  1. package/.eslintrc +32 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. package/.github/README.md +121 -0
  5. package/.github/workflows/pipeline.yml +287 -0
  6. package/.prettierrc +6 -0
  7. package/CHANGELOG.md +512 -0
  8. package/CODE_OF_CONDUCT.md +128 -0
  9. package/LICENSE +21 -0
  10. package/README.md +8 -0
  11. package/docs/CONTRIBUTE.md +96 -0
  12. package/docs/PIPELINE.md +169 -0
  13. package/docs/images/npm_versioning_guidelines.png +0 -0
  14. package/docs/images/release_pipeline.png +0 -0
  15. package/jsconfig.json +6 -0
  16. package/package.json +95 -0
  17. package/resources/sampleEntitiesFile.json +8 -0
  18. package/resources/sampleEnvFile.env +2 -0
  19. package/src/api/AuthenticateApi.js +33 -0
  20. package/src/api/BaseApi.js +242 -0
  21. package/src/api/CirclesOfTrustApi.js +87 -0
  22. package/src/api/EmailTemplateApi.js +37 -0
  23. package/src/api/IdmConfigApi.js +88 -0
  24. package/src/api/LogApi.js +45 -0
  25. package/src/api/ManagedObjectApi.js +62 -0
  26. package/src/api/OAuth2ClientApi.js +69 -0
  27. package/src/api/OAuth2OIDCApi.js +73 -0
  28. package/src/api/OAuth2ProviderApi.js +32 -0
  29. package/src/api/RealmApi.js +99 -0
  30. package/src/api/Saml2Api.js +176 -0
  31. package/src/api/ScriptApi.js +84 -0
  32. package/src/api/SecretsApi.js +151 -0
  33. package/src/api/ServerInfoApi.js +41 -0
  34. package/src/api/SocialIdentityProvidersApi.js +114 -0
  35. package/src/api/StartupApi.js +45 -0
  36. package/src/api/ThemeApi.js +181 -0
  37. package/src/api/TreeApi.js +207 -0
  38. package/src/api/VariablesApi.js +104 -0
  39. package/src/api/utils/ApiUtils.js +77 -0
  40. package/src/api/utils/ApiUtils.test.js +96 -0
  41. package/src/api/utils/Base64.js +62 -0
  42. package/src/index.js +32 -0
  43. package/src/index.test.js +13 -0
  44. package/src/ops/AdminOps.js +901 -0
  45. package/src/ops/AuthenticateOps.js +342 -0
  46. package/src/ops/CirclesOfTrustOps.js +350 -0
  47. package/src/ops/ConnectionProfileOps.js +254 -0
  48. package/src/ops/EmailTemplateOps.js +326 -0
  49. package/src/ops/IdmOps.js +227 -0
  50. package/src/ops/IdpOps.js +342 -0
  51. package/src/ops/JourneyOps.js +2026 -0
  52. package/src/ops/LogOps.js +357 -0
  53. package/src/ops/ManagedObjectOps.js +34 -0
  54. package/src/ops/OAuth2ClientOps.js +151 -0
  55. package/src/ops/OrganizationOps.js +85 -0
  56. package/src/ops/RealmOps.js +139 -0
  57. package/src/ops/SamlOps.js +541 -0
  58. package/src/ops/ScriptOps.js +211 -0
  59. package/src/ops/SecretsOps.js +288 -0
  60. package/src/ops/StartupOps.js +114 -0
  61. package/src/ops/ThemeOps.js +379 -0
  62. package/src/ops/VariablesOps.js +185 -0
  63. package/src/ops/templates/OAuth2ClientTemplate.json +270 -0
  64. package/src/ops/templates/OrgModelUserAttributesTemplate.json +149 -0
  65. package/src/ops/templates/cloud/GenericExtensionAttributesTemplate.json +392 -0
  66. package/src/ops/templates/cloud/managed.json +4119 -0
  67. package/src/ops/utils/Console.js +434 -0
  68. package/src/ops/utils/DataProtection.js +92 -0
  69. package/src/ops/utils/DataProtection.test.js +28 -0
  70. package/src/ops/utils/ExportImportUtils.js +146 -0
  71. package/src/ops/utils/ExportImportUtils.test.js +119 -0
  72. package/src/ops/utils/OpsUtils.js +76 -0
  73. package/src/ops/utils/Wordwrap.js +11 -0
  74. package/src/storage/SessionStorage.js +45 -0
  75. package/src/storage/StaticStorage.js +15 -0
  76. package/test/e2e/journey/baseline/ForgottenUsername.journey.json +216 -0
  77. package/test/e2e/journey/baseline/Login.journey.json +205 -0
  78. package/test/e2e/journey/baseline/PasswordGrant.journey.json +139 -0
  79. package/test/e2e/journey/baseline/ProgressiveProfile.journey.json +198 -0
  80. package/test/e2e/journey/baseline/Registration.journey.json +249 -0
  81. package/test/e2e/journey/baseline/ResetPassword.journey.json +268 -0
  82. package/test/e2e/journey/baseline/UpdatePassword.journey.json +323 -0
  83. package/test/e2e/journey/baseline/allAlphaJourneys.journeys.json +1520 -0
  84. package/test/e2e/journey/delete/ForgottenUsername.journey.json +216 -0
  85. package/test/e2e/journey/delete/Login.journey.json +205 -0
  86. package/test/e2e/journey/delete/PasswordGrant.journey.json +139 -0
  87. package/test/e2e/journey/delete/ProgressiveProfile.journey.json +198 -0
  88. package/test/e2e/journey/delete/Registration.journey.json +249 -0
  89. package/test/e2e/journey/delete/ResetPassword.journey.json +268 -0
  90. package/test/e2e/journey/delete/UpdatePassword.journey.json +323 -0
  91. package/test/e2e/journey/delete/deleteMe.journey.json +230 -0
  92. package/test/e2e/journey/list/Disabled.journey.json +43 -0
  93. package/test/e2e/journey/list/ForgottenUsername.journey.json +216 -0
  94. package/test/e2e/journey/list/Login.journey.json +205 -0
  95. package/test/e2e/journey/list/PasswordGrant.journey.json +139 -0
  96. package/test/e2e/journey/list/ProgressiveProfile.journey.json +198 -0
  97. package/test/e2e/journey/list/Registration.journey.json +249 -0
  98. package/test/e2e/journey/list/ResetPassword.journey.json +268 -0
  99. package/test/e2e/journey/list/UpdatePassword.journey.json +323 -0
  100. package/test/e2e/setup.js +107 -0
  101. package/test/e2e/theme/baseline/Contrast.theme.json +95 -0
  102. package/test/e2e/theme/baseline/Highlander.theme.json +95 -0
  103. package/test/e2e/theme/baseline/Robroy.theme.json +95 -0
  104. package/test/e2e/theme/baseline/Starter-Theme.theme.json +94 -0
  105. package/test/e2e/theme/baseline/Zardoz.theme.json +95 -0
  106. package/test/e2e/theme/import/Contrast.theme.json +95 -0
  107. package/test/e2e/theme/import/Highlander.theme.json +95 -0
  108. package/test/e2e/theme/import/Robroy.theme.json +95 -0
  109. package/test/e2e/theme/import/Starter-Theme.theme.json +94 -0
  110. package/test/e2e/theme/import/Zardoz.default.theme.json +95 -0
  111. package/test/fs_tmp/.gitkeep +2 -0
  112. package/test/global/setup.js +65 -0
@@ -0,0 +1,434 @@
1
+ import { SingleBar, Presets } from 'cli-progress';
2
+ import { createSpinner } from 'nanospinner';
3
+ import Table from 'cli-table3';
4
+ // eslint-disable-next-line no-unused-vars
5
+ import * as colors from '@colors/colors';
6
+ import storage from '../../storage/SessionStorage.js';
7
+
8
+ /**
9
+ * Output a data message
10
+ * @param {Object} message the message
11
+ */
12
+ function data(message) {
13
+ switch (typeof message) {
14
+ case 'object':
15
+ console.dir(message, { depth: 3 });
16
+ break;
17
+ default:
18
+ console.log(message);
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Output a text message
24
+ * @param {Object} message the message
25
+ */
26
+ function text(message) {
27
+ switch (typeof message) {
28
+ case 'object':
29
+ console.dir(message, { depth: 3 });
30
+ break;
31
+ default:
32
+ console.error(message);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Output an info message
38
+ * @param {Object} message the message
39
+ */
40
+ function info(message) {
41
+ switch (typeof message) {
42
+ case 'object':
43
+ console.dir(message, { depth: 3 });
44
+ break;
45
+ default:
46
+ console.error(message.brightCyan);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Output a warn message
52
+ * @param {Object} message the message
53
+ */
54
+ function warn(message) {
55
+ switch (typeof message) {
56
+ case 'object':
57
+ console.dir(message, { depth: 3 });
58
+ break;
59
+ default:
60
+ console.error(message.yellow);
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Output an error message
66
+ * @param {Object} message the message
67
+ */
68
+ function error(message) {
69
+ switch (typeof message) {
70
+ case 'object':
71
+ console.dir(message, { depth: 4 });
72
+ break;
73
+ default:
74
+ console.error(message.brightRed);
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Prints a string message to console
80
+ *
81
+ * @param {string} message The string message to print
82
+ * @param {string} [type=text] "text", "info", "warn", "error" or "data". All but
83
+ * type="data" will be written to stderr.
84
+ * @param {boolean} [newline=true] Whether to add a new at the end of message
85
+ *
86
+ */
87
+ export function printMessage(message, type = 'text', newline = true) {
88
+ // if (storage.session.getItem('scriptFriendly')) {
89
+ switch (type) {
90
+ case 'data':
91
+ if (newline) {
92
+ data(message);
93
+ } else {
94
+ process.stdout.write(message);
95
+ }
96
+ break;
97
+ case 'text':
98
+ if (newline) {
99
+ text(message);
100
+ } else {
101
+ process.stderr.write(message);
102
+ }
103
+ break;
104
+ case 'info':
105
+ if (newline) {
106
+ info(message);
107
+ } else {
108
+ process.stderr.write(message.brightCyan);
109
+ }
110
+ break;
111
+ case 'warn':
112
+ if (newline) {
113
+ warn(message);
114
+ } else {
115
+ process.stderr.write(message.yellow);
116
+ }
117
+ break;
118
+ case 'error':
119
+ if (newline) {
120
+ error(message);
121
+ } else {
122
+ process.stderr.write(message.brightRed);
123
+ }
124
+ break;
125
+ default:
126
+ if (newline) {
127
+ error(message);
128
+ } else {
129
+ process.stderr.write(message.bgBrightRed);
130
+ }
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Creates a progress bar on stderr
136
+ *
137
+ * Example:
138
+ * [========================================] 100% | 49/49 | Analyzing journey - transactional_auth
139
+ *
140
+ * @param {Number} total The total number of entries to track progress for
141
+ * @param {String} message optional progress bar message
142
+ * @param {Object} options progress bar configuration options
143
+ *
144
+ */
145
+ export function createProgressBar(
146
+ total,
147
+ message = null,
148
+ options = {
149
+ format: '[{bar}] {percentage}% | {value}/{total} | {data}',
150
+ noTTYOutput: true,
151
+ }
152
+ ) {
153
+ let opt = options;
154
+ if (message == null) {
155
+ opt = {
156
+ format: '[{bar}] {percentage}% | {value}/{total}',
157
+ noTTYOutput: true,
158
+ };
159
+ }
160
+ let pBar = storage.session.getItem('progressBar');
161
+ if (!pBar) pBar = new SingleBar(opt, Presets.legacy); // create only when needed
162
+ pBar.start(total, 0, {
163
+ data: message,
164
+ });
165
+ storage.session.setItem('progressBar', pBar);
166
+ }
167
+
168
+ /**
169
+ * Updates the progress bar by 1
170
+ * @param {string} message optional message to update the progress bar
171
+ *
172
+ */
173
+ export function updateProgressBar(message = null) {
174
+ const pBar = storage.session.getItem('progressBar');
175
+ if (message)
176
+ pBar.increment({
177
+ data: message,
178
+ });
179
+ else pBar.increment();
180
+ }
181
+
182
+ /**
183
+ * Stop and hide the progress bar
184
+ * @param {*} message optional message to update the progress bar
185
+ */
186
+ export function stopProgressBar(message = null) {
187
+ const pBar = storage.session.getItem('progressBar');
188
+ if (message)
189
+ pBar.update({
190
+ data: message,
191
+ });
192
+ pBar.stop();
193
+ }
194
+
195
+ /**
196
+ * Create the spinner
197
+ * @param {String} message optional spinner message
198
+ */
199
+ export function showSpinner(message) {
200
+ const spinner = createSpinner(message).start();
201
+ storage.session.setItem('Spinner', spinner);
202
+ }
203
+
204
+ /**
205
+ * Stop the spinner
206
+ * @param {String} message optional message to update the spinner
207
+ */
208
+ export function stopSpinner(message = null) {
209
+ const spinner = storage.session.getItem('Spinner');
210
+ if (spinner) {
211
+ let options = {};
212
+ if (message) options = { text: message };
213
+ spinner.stop(options);
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Succeed the spinner. Stop and render success checkmark with optional message.
219
+ * @param {String} message optional message to update the spinner
220
+ */
221
+ export function succeedSpinner(message = null) {
222
+ const spinner = storage.session.getItem('Spinner');
223
+ if (spinner) {
224
+ let options = {};
225
+ if (message) options = { text: message };
226
+ spinner.success(options);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Warn the spinner
232
+ * @param {String} message optional message to update the spinner
233
+ */
234
+ export function warnSpinner(message = null) {
235
+ const spinner = storage.session.getItem('Spinner');
236
+ if (spinner) {
237
+ let options = {};
238
+ if (message) options = { text: message };
239
+ spinner.warn(options);
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Fail the spinner
245
+ * @param {String} message optional message to update the spinner
246
+ */
247
+ export function failSpinner(message = null) {
248
+ const spinner = storage.session.getItem('Spinner');
249
+ if (spinner) {
250
+ let options = {};
251
+ if (message) options = { text: message };
252
+ spinner.error(options);
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Spin the spinner
258
+ * @param {String} message optional message to update the spinner
259
+ */
260
+ export function spinSpinner(message = null) {
261
+ const spinner = storage.session.getItem('Spinner');
262
+ if (spinner) {
263
+ let options = {};
264
+ if (message) options = { text: message };
265
+ spinner.update(options);
266
+ spinner.spin();
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Create an empty table
272
+ * @param {[String]} head header row as an array of strings
273
+ * @returns {CliTable3} an empty table
274
+ */
275
+ export function createTable(head) {
276
+ return new Table({
277
+ head,
278
+ chars: {
279
+ top: '',
280
+ 'top-mid': '',
281
+ 'top-left': '',
282
+ 'top-right': '',
283
+ bottom: '',
284
+ 'bottom-mid': '',
285
+ 'bottom-left': '',
286
+ 'bottom-right': '',
287
+ left: '',
288
+ 'left-mid': '',
289
+ mid: '',
290
+ 'mid-mid': '',
291
+ right: '',
292
+ 'right-mid': '',
293
+ },
294
+ style: { 'padding-left': 0, 'padding-right': 0, head: ['brightCyan'] },
295
+ });
296
+ }
297
+
298
+ /**
299
+ * Create a new key/value table
300
+ * @returns {CliTable3} an empty key/value table
301
+ */
302
+ export function createKeyValueTable() {
303
+ return new Table({
304
+ chars: {
305
+ top: '',
306
+ 'top-mid': '',
307
+ 'top-left': '',
308
+ 'top-right': '',
309
+ bottom: '',
310
+ 'bottom-mid': '',
311
+ 'bottom-left': '',
312
+ 'bottom-right': '',
313
+ left: '',
314
+ 'left-mid': '',
315
+ mid: '',
316
+ 'mid-mid': '',
317
+ right: '',
318
+ 'right-mid': '',
319
+ },
320
+ style: { 'padding-left': 0, 'padding-right': 0 },
321
+ wordWrap: true,
322
+ });
323
+ }
324
+
325
+ /**
326
+ * Helper function to determine the total depth of an object
327
+ * @param {Object} object input object
328
+ * @returns {Number} total depth of the input object
329
+ */
330
+ function getObjectDepth(object) {
331
+ return Object(object) === object
332
+ ? 1 + Math.max(-1, ...Object.values(object).map(getObjectDepth))
333
+ : 0;
334
+ }
335
+
336
+ /**
337
+ * Helper function to determine if an object has values
338
+ * @param {Object} object input object
339
+ * @returns {boolean} true of the object or any of its sub-objects contain values, false otherwise
340
+ */
341
+ function hasValues(object) {
342
+ let has = false;
343
+ const keys = Object.keys(object);
344
+ for (const key of keys) {
345
+ if (Object(object[key]) !== object[key]) {
346
+ return true;
347
+ }
348
+ has = has || hasValues(object[key]);
349
+ }
350
+ return has;
351
+ }
352
+
353
+ /**
354
+ * Helper function (recursive) to add rows to an object table
355
+ * @param {Object} object object to render
356
+ * @param {Number} depth total depth of initial object
357
+ * @param {Number} level current level
358
+ * @param {CliTable3} table the object table to add the rows to
359
+ * @returns the updated object table
360
+ */
361
+ function addRows(object, depth, level, table, keyMap) {
362
+ const space = ' ';
363
+ const keys = Object.keys(object);
364
+ for (const key of keys) {
365
+ if (Object(object[key]) !== object[key]) {
366
+ if (level === 1) {
367
+ table.push([
368
+ keyMap[key] ? keyMap[key].brightCyan : key.brightCyan,
369
+ object[key],
370
+ ]);
371
+ } else {
372
+ table.push([
373
+ {
374
+ hAlign: 'right',
375
+ content: keyMap[key] ? keyMap[key].gray : key.gray,
376
+ },
377
+ object[key],
378
+ ]);
379
+ }
380
+ }
381
+ }
382
+ for (const key of keys) {
383
+ if (Object(object[key]) === object[key]) {
384
+ // only print header if there are any values below
385
+ if (hasValues(object[key])) {
386
+ let indention = new Array(level).fill(space).join('');
387
+ if (level < 3) indention = `\n${indention}`;
388
+ table.push([
389
+ indention.concat(
390
+ keyMap[key] ? keyMap[key].brightCyan : key.brightCyan
391
+ ),
392
+ '',
393
+ ]);
394
+ }
395
+ // eslint-disable-next-line no-param-reassign
396
+ table = addRows(object[key], depth, level + 1, table, keyMap);
397
+ }
398
+ }
399
+ return table;
400
+ }
401
+
402
+ /**
403
+ * Create and populate an object table from any JSON object. Use for describe commands.
404
+ * @param {Object} object JSON object to create
405
+ * @returns {CliTable3} a table that can be printed to the console
406
+ */
407
+ export function createObjectTable(object, keyMap = {}) {
408
+ // eslint-disable-next-line no-param-reassign
409
+ const depth = getObjectDepth(object);
410
+ // eslint-disable-next-line no-param-reassign
411
+ const level = 0;
412
+ // eslint-disable-next-line no-param-reassign
413
+ const table = new Table({
414
+ chars: {
415
+ top: '',
416
+ 'top-mid': '',
417
+ 'top-left': '',
418
+ 'top-right': '',
419
+ bottom: '',
420
+ 'bottom-mid': '',
421
+ 'bottom-left': '',
422
+ 'bottom-right': '',
423
+ left: '',
424
+ 'left-mid': '',
425
+ mid: '',
426
+ 'mid-mid': '',
427
+ right: '',
428
+ 'right-mid': '',
429
+ },
430
+ style: { 'padding-left': 0, 'padding-right': 0, head: ['brightCyan'] },
431
+ });
432
+ addRows(object, depth, level + 1, table, keyMap);
433
+ return table;
434
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Data is stored in base64 format. Initially it was binary data
3
+ * Format used in this encryption module.
4
+ * inspired by AndiDittrich
5
+ * +--------------------+-----------------------+----------------+----------------+
6
+ * | SALT | Initialization Vector | Auth Tag | Payload |
7
+ * | Used to derive key | AES GCM XOR Init | Data Integrity | Encrypted Data |
8
+ * | 64 Bytes, random | 16 Bytes, random | 16 Bytes | (N-96) Bytes |
9
+ * +--------------------+-----------------------+----------------+----------------+
10
+ * This module doesn't take care of data persistence, it's assumed the consuming method/class/package will do so.
11
+ */
12
+ import fs, { promises as fsp } from 'fs';
13
+ import crypto from 'crypto';
14
+ import { homedir } from 'os';
15
+ import { promisify } from 'util';
16
+ import { printMessage } from './Console.js';
17
+
18
+ const scrypt = promisify(crypto.scrypt);
19
+ // using WeakMaps for added security since it gets garbage collected
20
+ const _masterKey = new WeakMap();
21
+ const _nonce = new WeakMap();
22
+ const _salt = new WeakMap();
23
+ const _key = new WeakMap();
24
+ const _encrypt = new WeakMap();
25
+
26
+ class DataProtection {
27
+ constructor() {
28
+ const masterKeyPath = () => `${homedir()}/.frodo/masterkey.key`;
29
+ // Generates a master key in base64 format. This master key will be used to derive the key for encryption. this key should be protected by an HSM or KMS
30
+ _masterKey.set(this, async () => {
31
+ try {
32
+ if (!fs.existsSync(masterKeyPath())) {
33
+ const masterKey = crypto.randomBytes(32).toString('base64');
34
+ await fsp.writeFile(masterKeyPath(), masterKey);
35
+ }
36
+ return await fsp.readFile(masterKeyPath(), 'utf8');
37
+ } catch (err) {
38
+ printMessage(err.message, 'error');
39
+ return '';
40
+ }
41
+ });
42
+
43
+ // the nonce for AES GCM
44
+ _nonce.set(this, () => crypto.randomBytes(16));
45
+
46
+ // The salt
47
+ _salt.set(this, () => crypto.randomBytes(64));
48
+
49
+ // The function that derives the key, this supports sync and async operations
50
+ _key.set(
51
+ this,
52
+ // eslint-disable-next-line no-return-await
53
+ async (masterKey, salt) => await scrypt(masterKey, salt, 32)
54
+ );
55
+
56
+ // private method to encrypt and return encrypted data. cleaner code
57
+ _encrypt.set(this, (key, nonce, data, salt) => {
58
+ const cipher = crypto.createCipheriv('aes-256-gcm', key, nonce);
59
+ const encrypted = Buffer.concat([
60
+ cipher.update(JSON.stringify(data), 'utf8'),
61
+ cipher.final(),
62
+ ]);
63
+ const tag = cipher.getAuthTag();
64
+ const buffer = Buffer.concat([salt, nonce, tag, encrypted]);
65
+ return buffer.toString('base64');
66
+ });
67
+ }
68
+
69
+ async encrypt(data) {
70
+ const nonce = _nonce.get(this)();
71
+ const salt = _salt.get(this)();
72
+ const masterKey = await _masterKey.get(this)();
73
+ const key = await _key.get(this)(masterKey, salt);
74
+ return _encrypt.get(this)(key, nonce, data, salt);
75
+ }
76
+
77
+ async decrypt(data) {
78
+ const buffer = Buffer.from(data.toString(), 'base64');
79
+ const salt = buffer.slice(0, 64);
80
+ const nonce = buffer.slice(64, 80);
81
+ const tag = buffer.slice(80, 96);
82
+ const encrypted = buffer.slice(96);
83
+ const masterKey = await _masterKey.get(this)();
84
+ const key = await _key.get(this)(masterKey, salt);
85
+ const decipher = crypto.createDecipheriv('aes-256-gcm', key, nonce);
86
+ decipher.setAuthTag(tag);
87
+ return JSON.parse(
88
+ decipher.update(encrypted, 'binary', 'utf8') + decipher.final('utf8')
89
+ );
90
+ }
91
+ }
92
+ export default DataProtection;
@@ -0,0 +1,28 @@
1
+ import DataProtection from './DataProtection.js';
2
+
3
+ test('DataProtection to encrypt', async () => {
4
+ // Note this test checks that encyption happned not that encryption is correct
5
+ // this test relys on other tests to proove the likelyhood of successful encryption
6
+ // Arrange
7
+ const dp = new DataProtection();
8
+ const EXPECTED =
9
+ 'aMLtCqK1b+d3d88DDKrmIV7A6pifP77IItLKX7N7/UTOPxf8YCQWHCpTrmNnM5wNXue8HllEFIS+sxXRb20oCb4HImpbQM0so5DrHIqcIlF5LYDKjvzBOz1PdlclhIuIV+Gr8M3GRbNkQxXJuUZ4th5ISLpOjM+k8bDAlnHsRx5LLlbLFnAKq8Pu9DaTYUkZABOCOjfkoTb6re1p9c7xE2pAe213';
10
+ const originalString =
11
+ 'Go not to the Elves for counsel, for they will say both no and yes.';
12
+ // Act
13
+ const RESULT = await dp.encrypt(originalString);
14
+ // Assert
15
+ expect(RESULT.length).toBe(EXPECTED.length);
16
+ });
17
+
18
+ test('DataProtection to decrypt', async () => {
19
+ // Arrange
20
+ const dp = new DataProtection();
21
+ const originalString =
22
+ 'Go not to the Elves for counsel, for they will say both no and yes.';
23
+ // Act
24
+ const encrypted = await dp.encrypt(originalString);
25
+ const RESULT = await dp.decrypt(encrypted);
26
+ // Assert
27
+ expect(RESULT).toBe(originalString);
28
+ });
@@ -0,0 +1,146 @@
1
+ import fs from 'fs';
2
+ import slugify from 'slugify';
3
+ import storage from '../../storage/SessionStorage.js';
4
+ import { FRODO_METADATA_ID } from '../../storage/StaticStorage.js';
5
+ import {
6
+ encode,
7
+ decode,
8
+ encodeBase64Url,
9
+ decodeBase64Url,
10
+ } from '../../api/utils/Base64.js';
11
+ import { printMessage } from './Console.js';
12
+
13
+ export function getCurrentTimestamp() {
14
+ const ts = new Date();
15
+ return ts.toISOString();
16
+ }
17
+
18
+ function getMetadata() {
19
+ const metadata = {
20
+ origin: storage.session.getTenant(),
21
+ exportedBy: storage.session.getUsername(),
22
+ exportDate: getCurrentTimestamp(),
23
+ exportTool: FRODO_METADATA_ID,
24
+ exportToolVersion: storage.session.getFrodoVersion(),
25
+ };
26
+ return metadata;
27
+ }
28
+
29
+ /*
30
+ * Output str in title case
31
+ *
32
+ * e.g.: 'ALL UPPERCASE AND all lowercase' = 'All Uppercase And All Lowercase'
33
+ */
34
+ export function titleCase(input) {
35
+ const str = input.toString();
36
+ const splitStr = str.toLowerCase().split(' ');
37
+ for (let i = 0; i < splitStr.length; i += 1) {
38
+ splitStr[i] = splitStr[i].charAt(0).toUpperCase() + splitStr[i].slice(1);
39
+ }
40
+ return splitStr.join(' ');
41
+ }
42
+
43
+ export function getRealmString() {
44
+ const realm = storage.session.getRealm();
45
+ return realm
46
+ .split('/')
47
+ .reduce((result, item) => `${result}${titleCase(item)}`, '');
48
+ }
49
+
50
+ export function convertBase64TextToArray(b64text) {
51
+ let arrayOut = [];
52
+ let plainText = decode(b64text);
53
+ plainText = plainText.replace(/\t/g, ' ');
54
+ arrayOut = plainText.split('\n');
55
+ return arrayOut;
56
+ }
57
+
58
+ export function convertBase64UrlTextToArray(b64UTF8Text) {
59
+ let arrayOut = [];
60
+ let plainText = decodeBase64Url(b64UTF8Text);
61
+ plainText = plainText.replace(/\t/g, ' ');
62
+ arrayOut = plainText.split('\n');
63
+ return arrayOut;
64
+ }
65
+
66
+ export function convertTextArrayToBase64(textArray) {
67
+ const joinedText = textArray.join('\n');
68
+ const b64encodedScript = encode(joinedText);
69
+ return b64encodedScript;
70
+ }
71
+
72
+ export function convertTextArrayToBase64Url(textArray) {
73
+ const joinedText = textArray.join('\n');
74
+ const b64encodedScript = encodeBase64Url(joinedText);
75
+ return b64encodedScript;
76
+ }
77
+
78
+ // eslint-disable-next-line no-unused-vars
79
+ export function validateImport(metadata) {
80
+ return true;
81
+ }
82
+
83
+ // eslint-disable-next-line no-unused-vars
84
+ export function checkTargetCompatibility(type, source, target) {
85
+ // console.log(`source ${source}, target ${target}`);
86
+ // compatibilityKeys[type].forEach((element) => {
87
+ // if (source[element] != target[element]) {
88
+ // console.warn(`${element} in ${type} mismatch between source and target`);
89
+ // }
90
+ // });
91
+ }
92
+
93
+ export function getTypedFilename(name, type, suffix = 'json') {
94
+ const slug = slugify(name.replace(/^http(s?):\/\//, ''));
95
+ return `${slug}.${type}.${suffix}`;
96
+ }
97
+
98
+ export function saveToFile(type, data, identifier, filename) {
99
+ const exportData = {};
100
+ exportData.meta = getMetadata();
101
+ exportData[type] = {};
102
+ if (Array.isArray(data)) {
103
+ data.forEach((element) => {
104
+ exportData[type][element[identifier]] = element;
105
+ });
106
+ } else {
107
+ exportData[type][data[identifier]] = data;
108
+ }
109
+ fs.writeFile(filename, JSON.stringify(exportData, null, 2), (err) => {
110
+ if (err) {
111
+ return printMessage(`ERROR - can't save ${type} to file`, 'error');
112
+ }
113
+ return '';
114
+ });
115
+ }
116
+
117
+ /**
118
+ * Save JSON object to file
119
+ * @param {Object} data data object
120
+ * @param {String} filename file name
121
+ */
122
+ export function saveJsonToFile(data, filename) {
123
+ const exportData = data;
124
+ exportData.meta = getMetadata();
125
+ fs.writeFile(filename, JSON.stringify(exportData, null, 2), (err) => {
126
+ if (err) {
127
+ return printMessage(`ERROR - can't save ${filename}`, 'error');
128
+ }
129
+ return '';
130
+ });
131
+ }
132
+
133
+ /**
134
+ * Save text data to file
135
+ * @param {String} data text data
136
+ * @param {String} filename file name
137
+ */
138
+ export function saveTextToFile(data, filename) {
139
+ fs.writeFile(filename, data, (err) => {
140
+ if (err) {
141
+ printMessage(`ERROR - can't save ${filename}`, 'error');
142
+ return false;
143
+ }
144
+ return true;
145
+ });
146
+ }