@etsoo/appscript 1.1.63 → 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.
- package/__tests__/app/CoreApp.ts +124 -0
- package/lib/cjs/app/CoreApp.d.ts +20 -4
- package/lib/cjs/app/CoreApp.js +59 -11
- package/lib/cjs/i18n/en-US.json +1 -0
- package/lib/cjs/i18n/zh-CN.json +1 -0
- package/lib/cjs/i18n/zh-HK.json +1 -0
- package/lib/cjs/index.d.ts +1 -0
- package/lib/cjs/index.js +1 -0
- package/lib/cjs/result/InitCallResult.d.ts +22 -0
- package/lib/cjs/result/InitCallResult.js +2 -0
- package/lib/cjs/state/User.d.ts +4 -0
- package/lib/mjs/app/CoreApp.d.ts +20 -4
- package/lib/mjs/app/CoreApp.js +61 -13
- package/lib/mjs/i18n/en-US.json +1 -0
- package/lib/mjs/i18n/zh-CN.json +1 -0
- package/lib/mjs/i18n/zh-HK.json +1 -0
- package/lib/mjs/index.d.ts +1 -0
- package/lib/mjs/index.js +1 -0
- package/lib/mjs/result/InitCallResult.d.ts +22 -0
- package/lib/mjs/result/InitCallResult.js +1 -0
- package/lib/mjs/state/User.d.ts +4 -0
- package/package.json +7 -7
- package/src/app/CoreApp.ts +96 -20
- package/src/i18n/en-US.json +1 -0
- package/src/i18n/zh-CN.json +1 -0
- package/src/i18n/zh-HK.json +1 -0
- package/src/index.ts +1 -0
- package/src/result/InitCallResult.ts +26 -0
- package/src/state/User.ts +5 -0
|
@@ -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
|
+
});
|
package/lib/cjs/app/CoreApp.d.ts
CHANGED
|
@@ -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,16 +411,17 @@ 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
|
|
412
|
-
* @param
|
|
421
|
+
* @param timestamp Timestamp
|
|
413
422
|
* @returns Enhanced passphrase
|
|
414
423
|
*/
|
|
415
|
-
protected encryptionEnhance(passphrase: string,
|
|
424
|
+
protected encryptionEnhance(passphrase: string, timestamp: string): string;
|
|
416
425
|
/**
|
|
417
426
|
* Format date to string
|
|
418
427
|
* @param input Input date
|
|
@@ -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
|
package/lib/cjs/app/CoreApp.js
CHANGED
|
@@ -203,10 +203,25 @@ class CoreApp {
|
|
|
203
203
|
* @returns Pure text
|
|
204
204
|
*/
|
|
205
205
|
decrypt(messageEncrypted, passphrase) {
|
|
206
|
-
|
|
207
|
-
const
|
|
206
|
+
// Timestamp splitter
|
|
207
|
+
const pos = messageEncrypted.indexOf('+');
|
|
208
|
+
const timestamp = messageEncrypted.substring(0, pos);
|
|
208
209
|
const message = messageEncrypted.substring(pos + 1);
|
|
209
|
-
|
|
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,23 +260,43 @@ 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) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
266
|
+
encrypt(message, passphrase, iterations) {
|
|
267
|
+
// Default 1 * 1000
|
|
268
|
+
iterations !== null && iterations !== void 0 ? iterations : (iterations = 1);
|
|
269
|
+
// Timestamp
|
|
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);
|
|
279
|
+
return (timestamp +
|
|
280
|
+
'+' +
|
|
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
|
|
258
293
|
* @param passphrase Secret passphrase
|
|
259
|
-
* @param
|
|
294
|
+
* @param timestamp Timestamp
|
|
260
295
|
* @returns Enhanced passphrase
|
|
261
296
|
*/
|
|
262
|
-
encryptionEnhance(passphrase,
|
|
297
|
+
encryptionEnhance(passphrase, timestamp) {
|
|
263
298
|
var _a;
|
|
264
|
-
passphrase +=
|
|
299
|
+
passphrase += timestamp;
|
|
265
300
|
passphrase += passphrase.length.toString();
|
|
266
301
|
return passphrase + ((_a = this.passphrase) !== null && _a !== void 0 ? _a : '');
|
|
267
302
|
}
|
|
@@ -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
|
|
@@ -540,6 +587,7 @@ class CoreApp {
|
|
|
540
587
|
*/
|
|
541
588
|
userLogin(user, refreshToken, keep = false) {
|
|
542
589
|
this.userData = user;
|
|
590
|
+
this.passphrase = user.passphrase;
|
|
543
591
|
this.authorize(user.token, refreshToken, keep);
|
|
544
592
|
}
|
|
545
593
|
/**
|
package/lib/cjs/i18n/en-US.json
CHANGED
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"status": "Status",
|
|
60
60
|
"submit": "Submit",
|
|
61
61
|
"success": "Success",
|
|
62
|
+
"timeDifferenceInvalid": "The time difference between the device and the server is {0}, which exceeds the limit of {1} seconds. Please adjust the device time. If it is abnormal, please inform the administrator",
|
|
62
63
|
"tokenExpiry": "Your session is about to expire. Click the Cancel button to continue",
|
|
63
64
|
"yes": "Yes",
|
|
64
65
|
"unknownError": "Unknown Error",
|
package/lib/cjs/i18n/zh-CN.json
CHANGED
package/lib/cjs/i18n/zh-HK.json
CHANGED
package/lib/cjs/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export type { IApi, IApiPayload } from '@etsoo/restclient';
|
|
|
25
25
|
export * from './result/ActionResult';
|
|
26
26
|
export * from './result/ActionResultError';
|
|
27
27
|
export * from './result/IActionResult';
|
|
28
|
+
export * from './result/InitCallResult';
|
|
28
29
|
export * from './state/Culture';
|
|
29
30
|
export * from './state/State';
|
|
30
31
|
export * from './state/User';
|
package/lib/cjs/index.js
CHANGED
|
@@ -48,6 +48,7 @@ Object.defineProperty(exports, "createClient", { enumerable: true, get: function
|
|
|
48
48
|
__exportStar(require("./result/ActionResult"), exports);
|
|
49
49
|
__exportStar(require("./result/ActionResultError"), exports);
|
|
50
50
|
__exportStar(require("./result/IActionResult"), exports);
|
|
51
|
+
__exportStar(require("./result/InitCallResult"), exports);
|
|
51
52
|
// state
|
|
52
53
|
__exportStar(require("./state/Culture"), exports);
|
|
53
54
|
__exportStar(require("./state/State"), exports);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { IActionResult, IResultData } from './IActionResult';
|
|
2
|
+
/**
|
|
3
|
+
* Result data with id, follow this style to extend for specific model
|
|
4
|
+
*/
|
|
5
|
+
export interface InitCallResultData extends IResultData {
|
|
6
|
+
/**
|
|
7
|
+
* Secret passphrase
|
|
8
|
+
*/
|
|
9
|
+
passphrase: string;
|
|
10
|
+
/**
|
|
11
|
+
* Actual seconds gap
|
|
12
|
+
*/
|
|
13
|
+
seconds: number;
|
|
14
|
+
/**
|
|
15
|
+
* Valid seconds gap
|
|
16
|
+
*/
|
|
17
|
+
validSeconds: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Init call result
|
|
21
|
+
*/
|
|
22
|
+
export declare type InitCallResult = IActionResult<InitCallResultData>;
|
package/lib/cjs/state/User.d.ts
CHANGED
package/lib/mjs/app/CoreApp.d.ts
CHANGED
|
@@ -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,16 +411,17 @@ 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
|
|
412
|
-
* @param
|
|
421
|
+
* @param timestamp Timestamp
|
|
413
422
|
* @returns Enhanced passphrase
|
|
414
423
|
*/
|
|
415
|
-
protected encryptionEnhance(passphrase: string,
|
|
424
|
+
protected encryptionEnhance(passphrase: string, timestamp: string): string;
|
|
416
425
|
/**
|
|
417
426
|
* Format date to string
|
|
418
427
|
* @param input Input date
|
|
@@ -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
|
package/lib/mjs/app/CoreApp.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NotificationAlign, NotificationMessageType } from '@etsoo/notificationbase';
|
|
2
2
|
import { ApiDataError } from '@etsoo/restclient';
|
|
3
|
-
import { DateUtils, DomUtils, NumberUtils, StorageUtils } from '@etsoo/shared';
|
|
4
|
-
import { AES } from 'crypto-js';
|
|
3
|
+
import { DateUtils, DomUtils, NumberUtils, StorageUtils, Utils } from '@etsoo/shared';
|
|
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
|
-
|
|
204
|
-
const
|
|
203
|
+
// Timestamp splitter
|
|
204
|
+
const pos = messageEncrypted.indexOf('+');
|
|
205
|
+
const timestamp = messageEncrypted.substring(0, pos);
|
|
205
206
|
const message = messageEncrypted.substring(pos + 1);
|
|
206
|
-
|
|
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,23 +257,43 @@ 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) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
263
|
+
encrypt(message, passphrase, iterations) {
|
|
264
|
+
// Default 1 * 1000
|
|
265
|
+
iterations !== null && iterations !== void 0 ? iterations : (iterations = 1);
|
|
266
|
+
// Timestamp
|
|
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);
|
|
276
|
+
return (timestamp +
|
|
277
|
+
'+' +
|
|
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
|
|
255
290
|
* @param passphrase Secret passphrase
|
|
256
|
-
* @param
|
|
291
|
+
* @param timestamp Timestamp
|
|
257
292
|
* @returns Enhanced passphrase
|
|
258
293
|
*/
|
|
259
|
-
encryptionEnhance(passphrase,
|
|
294
|
+
encryptionEnhance(passphrase, timestamp) {
|
|
260
295
|
var _a;
|
|
261
|
-
passphrase +=
|
|
296
|
+
passphrase += timestamp;
|
|
262
297
|
passphrase += passphrase.length.toString();
|
|
263
298
|
return passphrase + ((_a = this.passphrase) !== null && _a !== void 0 ? _a : '');
|
|
264
299
|
}
|
|
@@ -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
|
|
@@ -537,6 +584,7 @@ export class CoreApp {
|
|
|
537
584
|
*/
|
|
538
585
|
userLogin(user, refreshToken, keep = false) {
|
|
539
586
|
this.userData = user;
|
|
587
|
+
this.passphrase = user.passphrase;
|
|
540
588
|
this.authorize(user.token, refreshToken, keep);
|
|
541
589
|
}
|
|
542
590
|
/**
|
package/lib/mjs/i18n/en-US.json
CHANGED
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"status": "Status",
|
|
60
60
|
"submit": "Submit",
|
|
61
61
|
"success": "Success",
|
|
62
|
+
"timeDifferenceInvalid": "The time difference between the device and the server is {0}, which exceeds the limit of {1} seconds. Please adjust the device time. If it is abnormal, please inform the administrator",
|
|
62
63
|
"tokenExpiry": "Your session is about to expire. Click the Cancel button to continue",
|
|
63
64
|
"yes": "Yes",
|
|
64
65
|
"unknownError": "Unknown Error",
|
package/lib/mjs/i18n/zh-CN.json
CHANGED
package/lib/mjs/i18n/zh-HK.json
CHANGED
package/lib/mjs/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export type { IApi, IApiPayload } from '@etsoo/restclient';
|
|
|
25
25
|
export * from './result/ActionResult';
|
|
26
26
|
export * from './result/ActionResultError';
|
|
27
27
|
export * from './result/IActionResult';
|
|
28
|
+
export * from './result/InitCallResult';
|
|
28
29
|
export * from './state/Culture';
|
|
29
30
|
export * from './state/State';
|
|
30
31
|
export * from './state/User';
|
package/lib/mjs/index.js
CHANGED
|
@@ -33,6 +33,7 @@ export { ApiAuthorizationScheme, createClient } from '@etsoo/restclient';
|
|
|
33
33
|
export * from './result/ActionResult';
|
|
34
34
|
export * from './result/ActionResultError';
|
|
35
35
|
export * from './result/IActionResult';
|
|
36
|
+
export * from './result/InitCallResult';
|
|
36
37
|
// state
|
|
37
38
|
export * from './state/Culture';
|
|
38
39
|
export * from './state/State';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { IActionResult, IResultData } from './IActionResult';
|
|
2
|
+
/**
|
|
3
|
+
* Result data with id, follow this style to extend for specific model
|
|
4
|
+
*/
|
|
5
|
+
export interface InitCallResultData extends IResultData {
|
|
6
|
+
/**
|
|
7
|
+
* Secret passphrase
|
|
8
|
+
*/
|
|
9
|
+
passphrase: string;
|
|
10
|
+
/**
|
|
11
|
+
* Actual seconds gap
|
|
12
|
+
*/
|
|
13
|
+
seconds: number;
|
|
14
|
+
/**
|
|
15
|
+
* Valid seconds gap
|
|
16
|
+
*/
|
|
17
|
+
validSeconds: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Init call result
|
|
21
|
+
*/
|
|
22
|
+
export declare type InitCallResult = IActionResult<InitCallResultData>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/mjs/state/User.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/appscript",
|
|
3
|
-
"version": "1.1.
|
|
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": "
|
|
26
|
+
"testEnvironment": "jsdom",
|
|
27
27
|
"transform": {
|
|
28
28
|
".+\\.jsx?$": "babel-jest",
|
|
29
29
|
".+\\.tsx?$": "ts-jest"
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@etsoo/notificationbase": "^1.0.94",
|
|
56
56
|
"@etsoo/restclient": "^1.0.62",
|
|
57
|
-
"@etsoo/shared": "^1.0.
|
|
57
|
+
"@etsoo/shared": "^1.0.76",
|
|
58
58
|
"@types/crypto-js": "^4.0.2",
|
|
59
59
|
"crypto-js": "^4.1.1"
|
|
60
60
|
},
|
|
@@ -65,13 +65,13 @@
|
|
|
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.
|
|
69
|
-
"@typescript-eslint/parser": "^5.
|
|
70
|
-
"eslint": "^8.4.
|
|
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",
|
|
74
|
-
"ts-jest": "^27.0
|
|
74
|
+
"ts-jest": "^27.1.0",
|
|
75
75
|
"typescript": "^4.5.2"
|
|
76
76
|
}
|
|
77
77
|
}
|
package/src/app/CoreApp.ts
CHANGED
|
@@ -12,9 +12,20 @@ import {
|
|
|
12
12
|
DateUtils,
|
|
13
13
|
DomUtils,
|
|
14
14
|
NumberUtils,
|
|
15
|
-
StorageUtils
|
|
15
|
+
StorageUtils,
|
|
16
|
+
Utils
|
|
16
17
|
} from '@etsoo/shared';
|
|
17
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
AES,
|
|
20
|
+
algo,
|
|
21
|
+
enc,
|
|
22
|
+
HmacSHA512,
|
|
23
|
+
lib,
|
|
24
|
+
mode,
|
|
25
|
+
pad,
|
|
26
|
+
PBKDF2,
|
|
27
|
+
SHA3
|
|
28
|
+
} from 'crypto-js';
|
|
18
29
|
import { AddressRegion } from '../address/AddressRegion';
|
|
19
30
|
import { AddressUtils } from '../address/AddressUtils';
|
|
20
31
|
import { ActionResultError } from '../result/ActionResultError';
|
|
@@ -183,9 +194,10 @@ export interface ICoreApp<
|
|
|
183
194
|
* Encrypt message
|
|
184
195
|
* @param message Message
|
|
185
196
|
* @param passphrase Secret passphrase
|
|
197
|
+
* @param iterations Iterations, 1000 times, 1 - 99
|
|
186
198
|
* @returns Result
|
|
187
199
|
*/
|
|
188
|
-
encrypt(message: string, passphrase: string): string;
|
|
200
|
+
encrypt(message: string, passphrase: string, iterations?: number): string;
|
|
189
201
|
|
|
190
202
|
/**
|
|
191
203
|
* Format date to string
|
|
@@ -289,6 +301,14 @@ export interface ICoreApp<
|
|
|
289
301
|
*/
|
|
290
302
|
getTimeZone(): string | undefined;
|
|
291
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
|
+
|
|
292
312
|
/**
|
|
293
313
|
* Check use has the specific role permission or not
|
|
294
314
|
* @param roles Roles to check
|
|
@@ -659,13 +679,33 @@ export abstract class CoreApp<
|
|
|
659
679
|
* @returns Pure text
|
|
660
680
|
*/
|
|
661
681
|
decrypt(messageEncrypted: string, passphrase: string) {
|
|
662
|
-
|
|
663
|
-
const
|
|
682
|
+
// Timestamp splitter
|
|
683
|
+
const pos = messageEncrypted.indexOf('+');
|
|
684
|
+
const timestamp = messageEncrypted.substring(0, pos);
|
|
664
685
|
const message = messageEncrypted.substring(pos + 1);
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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);
|
|
669
709
|
}
|
|
670
710
|
|
|
671
711
|
/**
|
|
@@ -712,28 +752,51 @@ export abstract class CoreApp<
|
|
|
712
752
|
* Encrypt message
|
|
713
753
|
* @param message Message
|
|
714
754
|
* @param passphrase Secret passphrase
|
|
755
|
+
* @param iterations Iterations, 1000 times, 1 - 99
|
|
715
756
|
* @returns Result
|
|
716
757
|
*/
|
|
717
|
-
encrypt(message: string, passphrase: string) {
|
|
718
|
-
|
|
758
|
+
encrypt(message: string, passphrase: string, iterations?: number) {
|
|
759
|
+
// Default 1 * 1000
|
|
760
|
+
iterations ??= 1;
|
|
761
|
+
|
|
762
|
+
// Timestamp
|
|
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
|
+
|
|
719
778
|
return (
|
|
720
|
-
|
|
721
|
-
'
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
779
|
+
timestamp +
|
|
780
|
+
'+' +
|
|
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
|
|
726
789
|
);
|
|
727
790
|
}
|
|
728
791
|
|
|
729
792
|
/**
|
|
730
793
|
* Enchance secret passphrase
|
|
731
794
|
* @param passphrase Secret passphrase
|
|
732
|
-
* @param
|
|
795
|
+
* @param timestamp Timestamp
|
|
733
796
|
* @returns Enhanced passphrase
|
|
734
797
|
*/
|
|
735
|
-
protected encryptionEnhance(passphrase: string,
|
|
736
|
-
passphrase +=
|
|
798
|
+
protected encryptionEnhance(passphrase: string, timestamp: string) {
|
|
799
|
+
passphrase += timestamp;
|
|
737
800
|
passphrase += passphrase.length.toString();
|
|
738
801
|
return passphrase + (this.passphrase ?? '');
|
|
739
802
|
}
|
|
@@ -896,6 +959,18 @@ export abstract class CoreApp<
|
|
|
896
959
|
return this.settings.timeZone ?? this.ipData?.timezone;
|
|
897
960
|
}
|
|
898
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
|
+
|
|
899
974
|
/**
|
|
900
975
|
* Check use has the specific role permission or not
|
|
901
976
|
* @param roles Roles to check
|
|
@@ -1064,6 +1139,7 @@ export abstract class CoreApp<
|
|
|
1064
1139
|
*/
|
|
1065
1140
|
userLogin(user: IUserData, refreshToken: string, keep: boolean = false) {
|
|
1066
1141
|
this.userData = user;
|
|
1142
|
+
this.passphrase = user.passphrase;
|
|
1067
1143
|
this.authorize(user.token, refreshToken, keep);
|
|
1068
1144
|
}
|
|
1069
1145
|
|
package/src/i18n/en-US.json
CHANGED
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"status": "Status",
|
|
60
60
|
"submit": "Submit",
|
|
61
61
|
"success": "Success",
|
|
62
|
+
"timeDifferenceInvalid": "The time difference between the device and the server is {0}, which exceeds the limit of {1} seconds. Please adjust the device time. If it is abnormal, please inform the administrator",
|
|
62
63
|
"tokenExpiry": "Your session is about to expire. Click the Cancel button to continue",
|
|
63
64
|
"yes": "Yes",
|
|
64
65
|
"unknownError": "Unknown Error",
|
package/src/i18n/zh-CN.json
CHANGED
package/src/i18n/zh-HK.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -42,6 +42,7 @@ export type { IApi, IApiPayload } from '@etsoo/restclient';
|
|
|
42
42
|
export * from './result/ActionResult';
|
|
43
43
|
export * from './result/ActionResultError';
|
|
44
44
|
export * from './result/IActionResult';
|
|
45
|
+
export * from './result/InitCallResult';
|
|
45
46
|
|
|
46
47
|
// state
|
|
47
48
|
export * from './state/Culture';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { IActionResult, IResultData } from './IActionResult';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Result data with id, follow this style to extend for specific model
|
|
5
|
+
*/
|
|
6
|
+
export interface InitCallResultData extends IResultData {
|
|
7
|
+
/**
|
|
8
|
+
* Secret passphrase
|
|
9
|
+
*/
|
|
10
|
+
passphrase: string;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Actual seconds gap
|
|
14
|
+
*/
|
|
15
|
+
seconds: number;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Valid seconds gap
|
|
19
|
+
*/
|
|
20
|
+
validSeconds: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Init call result
|
|
25
|
+
*/
|
|
26
|
+
export type InitCallResult = IActionResult<InitCallResultData>;
|