@etsoo/appscript 1.1.66 → 1.1.67

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.
@@ -0,0 +1,124 @@
1
+ import {
2
+ INotificaseBase,
3
+ INotification,
4
+ Notification,
5
+ NotificationCallProps,
6
+ NotificationContainer,
7
+ NotificationRenderProps
8
+ } from '@etsoo/notificationbase';
9
+ import { ApiAuthorizationScheme, createClient } from '@etsoo/restclient';
10
+ import { DataTypes, DomUtils, Utils } from '@etsoo/shared';
11
+ import { AddressUtils } from '../../src/address/AddressUtils';
12
+ import { IAppSettings } from '../../src/app/AppSettings';
13
+ import { CoreApp } from '../../src/app/CoreApp';
14
+ import { zhCN } from '../../src/i18n/zhCN';
15
+
16
+ // Detected country or region
17
+ const { detectedCountry } = DomUtils;
18
+
19
+ // Detected culture
20
+ const { detectedCulture } = DomUtils;
21
+
22
+ // Supported cultures
23
+ const supportedCultures: DataTypes.CultureDefinition[] = [zhCN({})];
24
+
25
+ // Supported regions
26
+ const supportedRegions = ['CN'];
27
+
28
+ // Class implementation for tests
29
+ class NotificationTest extends Notification<any, NotificationCallProps> {
30
+ render(props: NotificationRenderProps, className?: string, options?: any) {
31
+ throw new Error('Method not implemented.');
32
+ }
33
+ }
34
+
35
+ class NotificationContainerTest extends NotificationContainer<
36
+ any,
37
+ NotificationCallProps
38
+ > {
39
+ protected addRaw(
40
+ data: INotificaseBase<any, NotificationCallProps>
41
+ ): INotification<any, NotificationCallProps> {
42
+ throw new Error('Method not implemented.');
43
+ }
44
+ }
45
+
46
+ // Container
47
+ var container = new NotificationContainerTest((update) => {});
48
+
49
+ // Arrange
50
+ class CoreAppTest extends CoreApp<IAppSettings, {}, NotificationCallProps> {
51
+ /**
52
+ * Constructor
53
+ * @param settings Settings
54
+ * @param name Application name
55
+ */
56
+ constructor() {
57
+ super(
58
+ {
59
+ /**
60
+ * Endpoint of the API service
61
+ */
62
+ endpoint: 'http://{hostname}/com.etsoo.SmartERPApi/api/',
63
+
64
+ /**
65
+ * App root url
66
+ */
67
+ homepage: '',
68
+
69
+ /**
70
+ * Web url of the cloud
71
+ */
72
+ webUrl: 'http://localhost',
73
+
74
+ // Authorization scheme
75
+ authScheme: ApiAuthorizationScheme.Bearer,
76
+
77
+ // Detected culture
78
+ detectedCulture,
79
+
80
+ // Supported cultures
81
+ cultures: supportedCultures,
82
+
83
+ // Supported regions
84
+ regions: supportedRegions,
85
+
86
+ // Browser's time zone
87
+ timeZone: Utils.getTimeZone(),
88
+
89
+ // Current country or region
90
+ currentRegion: AddressUtils.getRegion(
91
+ supportedRegions,
92
+ detectedCountry,
93
+ detectedCulture
94
+ ),
95
+
96
+ // Current culture
97
+ currentCulture: DomUtils.getCulture(
98
+ supportedCultures,
99
+ detectedCulture
100
+ )!
101
+ },
102
+ createClient(),
103
+ container,
104
+ 'SmartERP'
105
+ );
106
+ }
107
+
108
+ freshCountdownUI(callback?: () => PromiseLike<unknown>): void {
109
+ throw new Error('Method not implemented.');
110
+ }
111
+ }
112
+
113
+ const app = new CoreAppTest();
114
+
115
+ test('Tests for encrypt / decrypt', () => {
116
+ // Arrange
117
+ const input = 'Hello, world!';
118
+ const passphrase = 'My password';
119
+
120
+ // Act
121
+ const encrypted = app.encrypt(input, passphrase);
122
+ const plain = app.decrypt(encrypted, passphrase);
123
+ expect(plain).toEqual(input);
124
+ });
@@ -133,9 +133,10 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
133
133
  * Encrypt message
134
134
  * @param message Message
135
135
  * @param passphrase Secret passphrase
136
+ * @param iterations Iterations, 1000 times, 1 - 99
136
137
  * @returns Result
137
138
  */
138
- encrypt(message: string, passphrase: string): string;
139
+ encrypt(message: string, passphrase: string, iterations?: number): string;
139
140
  /**
140
141
  * Format date to string
141
142
  * @param input Input date
@@ -216,6 +217,13 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
216
217
  * @returns Time zone
217
218
  */
218
219
  getTimeZone(): string | undefined;
220
+ /**
221
+ * Hash message, SHA3 or HmacSHA512, 512 as Base64
222
+ * https://cryptojs.gitbook.io/docs/
223
+ * @param message Message
224
+ * @param passphrase Secret passphrase
225
+ */
226
+ hash(message: string, passphrase?: string): string;
219
227
  /**
220
228
  * Check use has the specific role permission or not
221
229
  * @param roles Roles to check
@@ -403,9 +411,10 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
403
411
  * Encrypt message
404
412
  * @param message Message
405
413
  * @param passphrase Secret passphrase
414
+ * @param iterations Iterations, 1000 times, 1 - 99
406
415
  * @returns Result
407
416
  */
408
- encrypt(message: string, passphrase: string): string;
417
+ encrypt(message: string, passphrase: string, iterations?: number): string;
409
418
  /**
410
419
  * Enchance secret passphrase
411
420
  * @param passphrase Secret passphrase
@@ -488,6 +497,13 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
488
497
  * @returns Time zone
489
498
  */
490
499
  getTimeZone(): string | undefined;
500
+ /**
501
+ * Hash message, SHA3 or HmacSHA512, 512 as Base64
502
+ * https://cryptojs.gitbook.io/docs/
503
+ * @param message Message
504
+ * @param passphrase Secret passphrase
505
+ */
506
+ hash(message: string, passphrase?: string): string;
491
507
  /**
492
508
  * Check use has the specific role permission or not
493
509
  * @param roles Roles to check
@@ -203,10 +203,25 @@ class CoreApp {
203
203
  * @returns Pure text
204
204
  */
205
205
  decrypt(messageEncrypted, passphrase) {
206
+ // Timestamp splitter
206
207
  const pos = messageEncrypted.indexOf('+');
207
208
  const timestamp = messageEncrypted.substring(0, pos);
208
209
  const message = messageEncrypted.substring(pos + 1);
209
- return crypto_js_1.AES.decrypt(message, this.encryptionEnhance(passphrase, timestamp)).toString();
210
+ // Iterations
211
+ const iterations = parseInt(message.substring(0, 2), 10);
212
+ const salt = crypto_js_1.enc.Hex.parse(message.substring(2, 34));
213
+ const iv = crypto_js_1.enc.Hex.parse(message.substring(34, 66));
214
+ const encrypted = message.substring(66);
215
+ const key = (0, crypto_js_1.PBKDF2)(this.encryptionEnhance(passphrase, timestamp), salt, {
216
+ keySize: 8,
217
+ hasher: crypto_js_1.algo.SHA256,
218
+ iterations: 1000 * iterations
219
+ });
220
+ return crypto_js_1.AES.decrypt(encrypted, key, {
221
+ iv,
222
+ padding: crypto_js_1.pad.Pkcs7,
223
+ mode: crypto_js_1.mode.CBC
224
+ }).toString(crypto_js_1.enc.Utf8);
210
225
  }
211
226
  /**
212
227
  * Detect IP data, call only one time
@@ -245,13 +260,33 @@ class CoreApp {
245
260
  * Encrypt message
246
261
  * @param message Message
247
262
  * @param passphrase Secret passphrase
263
+ * @param iterations Iterations, 1000 times, 1 - 99
248
264
  * @returns Result
249
265
  */
250
- encrypt(message, passphrase) {
266
+ encrypt(message, passphrase, iterations) {
267
+ // Default 1 * 1000
268
+ iterations !== null && iterations !== void 0 ? iterations : (iterations = 1);
269
+ // Timestamp
251
270
  const timestamp = shared_1.Utils.numberToChars(new Date().getTime());
271
+ const bits = 16; // 128 / 8
272
+ const salt = crypto_js_1.lib.WordArray.random(bits);
273
+ const key = (0, crypto_js_1.PBKDF2)(this.encryptionEnhance(passphrase, timestamp), salt, {
274
+ keySize: 8,
275
+ hasher: crypto_js_1.algo.SHA256,
276
+ iterations: 1000 * iterations
277
+ });
278
+ const iv = crypto_js_1.lib.WordArray.random(bits);
252
279
  return (timestamp +
253
280
  '+' +
254
- crypto_js_1.AES.encrypt(message, this.encryptionEnhance(passphrase, timestamp)).toString());
281
+ iterations.toString().padStart(2, '0') +
282
+ salt.toString(crypto_js_1.enc.Hex) +
283
+ iv.toString(crypto_js_1.enc.Hex) +
284
+ crypto_js_1.AES.encrypt(message, key, {
285
+ iv,
286
+ padding: crypto_js_1.pad.Pkcs7,
287
+ mode: crypto_js_1.mode.CBC
288
+ }).toString() // enc.Base64
289
+ );
255
290
  }
256
291
  /**
257
292
  * Enchance secret passphrase
@@ -391,6 +426,18 @@ class CoreApp {
391
426
  // settings.timeZone = Utils.getTimeZone()
392
427
  return (_a = this.settings.timeZone) !== null && _a !== void 0 ? _a : (_b = this.ipData) === null || _b === void 0 ? void 0 : _b.timezone;
393
428
  }
429
+ /**
430
+ * Hash message, SHA3 or HmacSHA512, 512 as Base64
431
+ * https://cryptojs.gitbook.io/docs/
432
+ * @param message Message
433
+ * @param passphrase Secret passphrase
434
+ */
435
+ hash(message, passphrase) {
436
+ if (passphrase == null)
437
+ return (0, crypto_js_1.SHA3)(message, { outputLength: 512 }).toString(crypto_js_1.enc.Base64);
438
+ else
439
+ return (0, crypto_js_1.HmacSHA512)(message, passphrase).toString(crypto_js_1.enc.Base64);
440
+ }
394
441
  /**
395
442
  * Check use has the specific role permission or not
396
443
  * @param roles Roles to check
@@ -133,9 +133,10 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
133
133
  * Encrypt message
134
134
  * @param message Message
135
135
  * @param passphrase Secret passphrase
136
+ * @param iterations Iterations, 1000 times, 1 - 99
136
137
  * @returns Result
137
138
  */
138
- encrypt(message: string, passphrase: string): string;
139
+ encrypt(message: string, passphrase: string, iterations?: number): string;
139
140
  /**
140
141
  * Format date to string
141
142
  * @param input Input date
@@ -216,6 +217,13 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
216
217
  * @returns Time zone
217
218
  */
218
219
  getTimeZone(): string | undefined;
220
+ /**
221
+ * Hash message, SHA3 or HmacSHA512, 512 as Base64
222
+ * https://cryptojs.gitbook.io/docs/
223
+ * @param message Message
224
+ * @param passphrase Secret passphrase
225
+ */
226
+ hash(message: string, passphrase?: string): string;
219
227
  /**
220
228
  * Check use has the specific role permission or not
221
229
  * @param roles Roles to check
@@ -403,9 +411,10 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
403
411
  * Encrypt message
404
412
  * @param message Message
405
413
  * @param passphrase Secret passphrase
414
+ * @param iterations Iterations, 1000 times, 1 - 99
406
415
  * @returns Result
407
416
  */
408
- encrypt(message: string, passphrase: string): string;
417
+ encrypt(message: string, passphrase: string, iterations?: number): string;
409
418
  /**
410
419
  * Enchance secret passphrase
411
420
  * @param passphrase Secret passphrase
@@ -488,6 +497,13 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
488
497
  * @returns Time zone
489
498
  */
490
499
  getTimeZone(): string | undefined;
500
+ /**
501
+ * Hash message, SHA3 or HmacSHA512, 512 as Base64
502
+ * https://cryptojs.gitbook.io/docs/
503
+ * @param message Message
504
+ * @param passphrase Secret passphrase
505
+ */
506
+ hash(message: string, passphrase?: string): string;
491
507
  /**
492
508
  * Check use has the specific role permission or not
493
509
  * @param roles Roles to check
@@ -1,7 +1,7 @@
1
1
  import { NotificationAlign, NotificationMessageType } from '@etsoo/notificationbase';
2
2
  import { ApiDataError } from '@etsoo/restclient';
3
3
  import { DateUtils, DomUtils, NumberUtils, StorageUtils, Utils } from '@etsoo/shared';
4
- import { AES } from 'crypto-js';
4
+ import { AES, algo, enc, HmacSHA512, lib, mode, pad, PBKDF2, SHA3 } from 'crypto-js';
5
5
  import { AddressRegion } from '../address/AddressRegion';
6
6
  import { AddressUtils } from '../address/AddressUtils';
7
7
  import { ActionResultError } from '../result/ActionResultError';
@@ -200,10 +200,25 @@ export class CoreApp {
200
200
  * @returns Pure text
201
201
  */
202
202
  decrypt(messageEncrypted, passphrase) {
203
+ // Timestamp splitter
203
204
  const pos = messageEncrypted.indexOf('+');
204
205
  const timestamp = messageEncrypted.substring(0, pos);
205
206
  const message = messageEncrypted.substring(pos + 1);
206
- return AES.decrypt(message, this.encryptionEnhance(passphrase, timestamp)).toString();
207
+ // Iterations
208
+ const iterations = parseInt(message.substring(0, 2), 10);
209
+ const salt = enc.Hex.parse(message.substring(2, 34));
210
+ const iv = enc.Hex.parse(message.substring(34, 66));
211
+ const encrypted = message.substring(66);
212
+ const key = PBKDF2(this.encryptionEnhance(passphrase, timestamp), salt, {
213
+ keySize: 8,
214
+ hasher: algo.SHA256,
215
+ iterations: 1000 * iterations
216
+ });
217
+ return AES.decrypt(encrypted, key, {
218
+ iv,
219
+ padding: pad.Pkcs7,
220
+ mode: mode.CBC
221
+ }).toString(enc.Utf8);
207
222
  }
208
223
  /**
209
224
  * Detect IP data, call only one time
@@ -242,13 +257,33 @@ export class CoreApp {
242
257
  * Encrypt message
243
258
  * @param message Message
244
259
  * @param passphrase Secret passphrase
260
+ * @param iterations Iterations, 1000 times, 1 - 99
245
261
  * @returns Result
246
262
  */
247
- encrypt(message, passphrase) {
263
+ encrypt(message, passphrase, iterations) {
264
+ // Default 1 * 1000
265
+ iterations !== null && iterations !== void 0 ? iterations : (iterations = 1);
266
+ // Timestamp
248
267
  const timestamp = Utils.numberToChars(new Date().getTime());
268
+ const bits = 16; // 128 / 8
269
+ const salt = lib.WordArray.random(bits);
270
+ const key = PBKDF2(this.encryptionEnhance(passphrase, timestamp), salt, {
271
+ keySize: 8,
272
+ hasher: algo.SHA256,
273
+ iterations: 1000 * iterations
274
+ });
275
+ const iv = lib.WordArray.random(bits);
249
276
  return (timestamp +
250
277
  '+' +
251
- AES.encrypt(message, this.encryptionEnhance(passphrase, timestamp)).toString());
278
+ iterations.toString().padStart(2, '0') +
279
+ salt.toString(enc.Hex) +
280
+ iv.toString(enc.Hex) +
281
+ AES.encrypt(message, key, {
282
+ iv,
283
+ padding: pad.Pkcs7,
284
+ mode: mode.CBC
285
+ }).toString() // enc.Base64
286
+ );
252
287
  }
253
288
  /**
254
289
  * Enchance secret passphrase
@@ -388,6 +423,18 @@ export class CoreApp {
388
423
  // settings.timeZone = Utils.getTimeZone()
389
424
  return (_a = this.settings.timeZone) !== null && _a !== void 0 ? _a : (_b = this.ipData) === null || _b === void 0 ? void 0 : _b.timezone;
390
425
  }
426
+ /**
427
+ * Hash message, SHA3 or HmacSHA512, 512 as Base64
428
+ * https://cryptojs.gitbook.io/docs/
429
+ * @param message Message
430
+ * @param passphrase Secret passphrase
431
+ */
432
+ hash(message, passphrase) {
433
+ if (passphrase == null)
434
+ return SHA3(message, { outputLength: 512 }).toString(enc.Base64);
435
+ else
436
+ return HmacSHA512(message, passphrase).toString(enc.Base64);
437
+ }
391
438
  /**
392
439
  * Check use has the specific role permission or not
393
440
  * @param roles Roles to check
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/appscript",
3
- "version": "1.1.66",
3
+ "version": "1.1.67",
4
4
  "description": "Applications shared TypeScript framework",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",
@@ -23,7 +23,7 @@
23
23
  "testMatch": [
24
24
  "<rootDir>/__tests__/**/*.ts"
25
25
  ],
26
- "testEnvironment": "node",
26
+ "testEnvironment": "jsdom",
27
27
  "transform": {
28
28
  ".+\\.jsx?$": "babel-jest",
29
29
  ".+\\.tsx?$": "ts-jest"
@@ -65,9 +65,9 @@
65
65
  "@babel/preset-env": "^7.16.4",
66
66
  "@babel/runtime-corejs3": "^7.16.3",
67
67
  "@types/jest": "^27.0.3",
68
- "@typescript-eslint/eslint-plugin": "^5.5.0",
69
- "@typescript-eslint/parser": "^5.5.0",
70
- "eslint": "^8.4.0",
68
+ "@typescript-eslint/eslint-plugin": "^5.6.0",
69
+ "@typescript-eslint/parser": "^5.6.0",
70
+ "eslint": "^8.4.1",
71
71
  "eslint-config-airbnb-base": "^15.0.0",
72
72
  "eslint-plugin-import": "^2.25.3",
73
73
  "jest": "^27.4.3",
@@ -15,7 +15,17 @@ import {
15
15
  StorageUtils,
16
16
  Utils
17
17
  } from '@etsoo/shared';
18
- import { AES } from 'crypto-js';
18
+ import {
19
+ AES,
20
+ algo,
21
+ enc,
22
+ HmacSHA512,
23
+ lib,
24
+ mode,
25
+ pad,
26
+ PBKDF2,
27
+ SHA3
28
+ } from 'crypto-js';
19
29
  import { AddressRegion } from '../address/AddressRegion';
20
30
  import { AddressUtils } from '../address/AddressUtils';
21
31
  import { ActionResultError } from '../result/ActionResultError';
@@ -184,9 +194,10 @@ export interface ICoreApp<
184
194
  * Encrypt message
185
195
  * @param message Message
186
196
  * @param passphrase Secret passphrase
197
+ * @param iterations Iterations, 1000 times, 1 - 99
187
198
  * @returns Result
188
199
  */
189
- encrypt(message: string, passphrase: string): string;
200
+ encrypt(message: string, passphrase: string, iterations?: number): string;
190
201
 
191
202
  /**
192
203
  * Format date to string
@@ -290,6 +301,14 @@ export interface ICoreApp<
290
301
  */
291
302
  getTimeZone(): string | undefined;
292
303
 
304
+ /**
305
+ * Hash message, SHA3 or HmacSHA512, 512 as Base64
306
+ * https://cryptojs.gitbook.io/docs/
307
+ * @param message Message
308
+ * @param passphrase Secret passphrase
309
+ */
310
+ hash(message: string, passphrase?: string): string;
311
+
293
312
  /**
294
313
  * Check use has the specific role permission or not
295
314
  * @param roles Roles to check
@@ -660,13 +679,33 @@ export abstract class CoreApp<
660
679
  * @returns Pure text
661
680
  */
662
681
  decrypt(messageEncrypted: string, passphrase: string) {
682
+ // Timestamp splitter
663
683
  const pos = messageEncrypted.indexOf('+');
664
684
  const timestamp = messageEncrypted.substring(0, pos);
665
685
  const message = messageEncrypted.substring(pos + 1);
666
- return AES.decrypt(
667
- message,
668
- this.encryptionEnhance(passphrase, timestamp)
669
- ).toString();
686
+
687
+ // Iterations
688
+ const iterations = parseInt(message.substring(0, 2), 10);
689
+
690
+ const salt = enc.Hex.parse(message.substring(2, 34));
691
+ const iv = enc.Hex.parse(message.substring(34, 66));
692
+ const encrypted = message.substring(66);
693
+
694
+ const key = PBKDF2(
695
+ this.encryptionEnhance(passphrase, timestamp),
696
+ salt,
697
+ {
698
+ keySize: 8, // 256 / 32
699
+ hasher: algo.SHA256,
700
+ iterations: 1000 * iterations
701
+ }
702
+ );
703
+
704
+ return AES.decrypt(encrypted, key, {
705
+ iv,
706
+ padding: pad.Pkcs7,
707
+ mode: mode.CBC
708
+ }).toString(enc.Utf8);
670
709
  }
671
710
 
672
711
  /**
@@ -713,17 +752,40 @@ export abstract class CoreApp<
713
752
  * Encrypt message
714
753
  * @param message Message
715
754
  * @param passphrase Secret passphrase
755
+ * @param iterations Iterations, 1000 times, 1 - 99
716
756
  * @returns Result
717
757
  */
718
- encrypt(message: string, passphrase: string) {
758
+ encrypt(message: string, passphrase: string, iterations?: number) {
759
+ // Default 1 * 1000
760
+ iterations ??= 1;
761
+
762
+ // Timestamp
719
763
  const timestamp = Utils.numberToChars(new Date().getTime());
764
+
765
+ const bits = 16; // 128 / 8
766
+ const salt = lib.WordArray.random(bits);
767
+ const key = PBKDF2(
768
+ this.encryptionEnhance(passphrase, timestamp),
769
+ salt,
770
+ {
771
+ keySize: 8, // 256 / 32
772
+ hasher: algo.SHA256,
773
+ iterations: 1000 * iterations
774
+ }
775
+ );
776
+ const iv = lib.WordArray.random(bits);
777
+
720
778
  return (
721
779
  timestamp +
722
780
  '+' +
723
- AES.encrypt(
724
- message,
725
- this.encryptionEnhance(passphrase, timestamp)
726
- ).toString()
781
+ iterations.toString().padStart(2, '0') +
782
+ salt.toString(enc.Hex) +
783
+ iv.toString(enc.Hex) +
784
+ AES.encrypt(message, key, {
785
+ iv,
786
+ padding: pad.Pkcs7,
787
+ mode: mode.CBC
788
+ }).toString() // enc.Base64
727
789
  );
728
790
  }
729
791
 
@@ -897,6 +959,18 @@ export abstract class CoreApp<
897
959
  return this.settings.timeZone ?? this.ipData?.timezone;
898
960
  }
899
961
 
962
+ /**
963
+ * Hash message, SHA3 or HmacSHA512, 512 as Base64
964
+ * https://cryptojs.gitbook.io/docs/
965
+ * @param message Message
966
+ * @param passphrase Secret passphrase
967
+ */
968
+ hash(message: string, passphrase?: string) {
969
+ if (passphrase == null)
970
+ return SHA3(message, { outputLength: 512 }).toString(enc.Base64);
971
+ else return HmacSHA512(message, passphrase).toString(enc.Base64);
972
+ }
973
+
900
974
  /**
901
975
  * Check use has the specific role permission or not
902
976
  * @param roles Roles to check