@etsoo/appscript 1.1.67 → 1.1.68
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/lib/cjs/app/CoreApp.d.ts +49 -12
- package/lib/cjs/app/CoreApp.js +129 -7
- package/lib/cjs/dto/InitCallDto.d.ts +13 -0
- package/lib/cjs/dto/InitCallDto.js +2 -0
- package/lib/cjs/index.d.ts +1 -0
- package/lib/cjs/index.js +1 -0
- package/lib/cjs/result/IActionResult.d.ts +2 -2
- package/lib/cjs/result/InitCallResult.d.ts +12 -4
- package/lib/cjs/state/User.d.ts +0 -4
- package/lib/mjs/app/CoreApp.d.ts +49 -12
- package/lib/mjs/app/CoreApp.js +129 -7
- package/lib/mjs/dto/InitCallDto.d.ts +13 -0
- package/lib/mjs/dto/InitCallDto.js +1 -0
- package/lib/mjs/index.d.ts +1 -0
- package/lib/mjs/index.js +1 -0
- package/lib/mjs/result/IActionResult.d.ts +2 -2
- package/lib/mjs/result/InitCallResult.d.ts +12 -4
- package/lib/mjs/state/User.d.ts +0 -4
- package/package.json +1 -1
- package/src/app/CoreApp.ts +193 -20
- package/src/dto/InitCallDto.ts +14 -0
- package/src/index.ts +1 -0
- package/src/result/IActionResult.ts +2 -2
- package/src/result/InitCallResult.ts +14 -4
- package/src/state/User.ts +0 -5
package/lib/cjs/app/CoreApp.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { ApiDataError, IApi, IPData } from '@etsoo/restclient';
|
|
|
3
3
|
import { DataTypes, DateUtils } from '@etsoo/shared';
|
|
4
4
|
import { AddressRegion } from '../address/AddressRegion';
|
|
5
5
|
import { IActionResult } from '../result/IActionResult';
|
|
6
|
+
import { InitCallResultData } from '../result/InitCallResult';
|
|
6
7
|
import { IUserData } from '../state/User';
|
|
7
8
|
import { IAppSettings } from './AppSettings';
|
|
8
9
|
import { UserRole } from './UserRole';
|
|
@@ -87,10 +88,6 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
|
|
|
87
88
|
* User data
|
|
88
89
|
*/
|
|
89
90
|
userData?: IUserData;
|
|
90
|
-
/**
|
|
91
|
-
* Passphrase for encryption
|
|
92
|
-
*/
|
|
93
|
-
passphrase?: string;
|
|
94
91
|
/**
|
|
95
92
|
* Search input element
|
|
96
93
|
*/
|
|
@@ -121,9 +118,10 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
|
|
|
121
118
|
* Decrypt message
|
|
122
119
|
* @param messageEncrypted Encrypted message
|
|
123
120
|
* @param passphrase Secret passphrase
|
|
121
|
+
* @param durationSeconds Duration seconds, <= 12 will be considered as month
|
|
124
122
|
* @returns Pure text
|
|
125
123
|
*/
|
|
126
|
-
decrypt(messageEncrypted: string, passphrase
|
|
124
|
+
decrypt(messageEncrypted: string, passphrase?: string, durationSeconds?: number): string | undefined;
|
|
127
125
|
/**
|
|
128
126
|
* Detect IP data, call only one time
|
|
129
127
|
* @param callback Callback will be called when the IP is ready
|
|
@@ -136,7 +134,7 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
|
|
|
136
134
|
* @param iterations Iterations, 1000 times, 1 - 99
|
|
137
135
|
* @returns Result
|
|
138
136
|
*/
|
|
139
|
-
encrypt(message: string, passphrase
|
|
137
|
+
encrypt(message: string, passphrase?: string, iterations?: number): string;
|
|
140
138
|
/**
|
|
141
139
|
* Format date to string
|
|
142
140
|
* @param input Input date
|
|
@@ -224,6 +222,13 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
|
|
|
224
222
|
* @param passphrase Secret passphrase
|
|
225
223
|
*/
|
|
226
224
|
hash(message: string, passphrase?: string): string;
|
|
225
|
+
/**
|
|
226
|
+
* Hash message Hex, SHA3 or HmacSHA512, 512 as Base64
|
|
227
|
+
* https://cryptojs.gitbook.io/docs/
|
|
228
|
+
* @param message Message
|
|
229
|
+
* @param passphrase Secret passphrase
|
|
230
|
+
*/
|
|
231
|
+
hashHex(message: string, passphrase?: string): string;
|
|
227
232
|
/**
|
|
228
233
|
* Check use has the specific role permission or not
|
|
229
234
|
* @param roles Roles to check
|
|
@@ -335,10 +340,6 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
335
340
|
* User data
|
|
336
341
|
*/
|
|
337
342
|
userData?: IUserData;
|
|
338
|
-
/**
|
|
339
|
-
* Passphrase for encryption
|
|
340
|
-
*/
|
|
341
|
-
passphrase?: string;
|
|
342
343
|
/**
|
|
343
344
|
* Response token header field name
|
|
344
345
|
*/
|
|
@@ -363,6 +364,18 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
363
364
|
* Token refresh count down seed
|
|
364
365
|
*/
|
|
365
366
|
protected refreshCountdownSeed: number;
|
|
367
|
+
/**
|
|
368
|
+
* Device id field name
|
|
369
|
+
*/
|
|
370
|
+
protected deviceIdField: string;
|
|
371
|
+
/**
|
|
372
|
+
* Device id
|
|
373
|
+
*/
|
|
374
|
+
protected deviceId: string;
|
|
375
|
+
/**
|
|
376
|
+
* Passphrase for encryption
|
|
377
|
+
*/
|
|
378
|
+
protected passphrase: string;
|
|
366
379
|
/**
|
|
367
380
|
* Protected constructor
|
|
368
381
|
* @param settings Settings
|
|
@@ -372,6 +385,22 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
372
385
|
*/
|
|
373
386
|
protected constructor(settings: S, api: IApi, notifier: INotifier<N, C>, name: string);
|
|
374
387
|
protected setApi(api: IApi): void;
|
|
388
|
+
/**
|
|
389
|
+
* Init call
|
|
390
|
+
* @returns Result
|
|
391
|
+
*/
|
|
392
|
+
protected initCall(): Promise<void>;
|
|
393
|
+
/**
|
|
394
|
+
* Init call update
|
|
395
|
+
* @param data Result data
|
|
396
|
+
* @param timestamp Timestamp
|
|
397
|
+
*/
|
|
398
|
+
protected initCallUpdate(data: InitCallResultData, timestamp: number): void;
|
|
399
|
+
/**
|
|
400
|
+
* Init call update fields in local storage
|
|
401
|
+
* @returns Fields
|
|
402
|
+
*/
|
|
403
|
+
protected initCallUpdateFields(): string[];
|
|
375
404
|
/**
|
|
376
405
|
* Alert action result
|
|
377
406
|
* @param result Action result
|
|
@@ -398,9 +427,10 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
398
427
|
* Decrypt message
|
|
399
428
|
* @param messageEncrypted Encrypted message
|
|
400
429
|
* @param passphrase Secret passphrase
|
|
430
|
+
* @param durationSeconds Duration seconds, <= 12 will be considered as month
|
|
401
431
|
* @returns Pure text
|
|
402
432
|
*/
|
|
403
|
-
decrypt(messageEncrypted: string, passphrase
|
|
433
|
+
decrypt(messageEncrypted: string, passphrase?: string, durationSeconds?: number): string | undefined;
|
|
404
434
|
/**
|
|
405
435
|
* Detect IP data, call only one time
|
|
406
436
|
* @param callback Callback will be called when the IP is ready
|
|
@@ -414,7 +444,7 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
414
444
|
* @param iterations Iterations, 1000 times, 1 - 99
|
|
415
445
|
* @returns Result
|
|
416
446
|
*/
|
|
417
|
-
encrypt(message: string, passphrase
|
|
447
|
+
encrypt(message: string, passphrase?: string, iterations?: number): string;
|
|
418
448
|
/**
|
|
419
449
|
* Enchance secret passphrase
|
|
420
450
|
* @param passphrase Secret passphrase
|
|
@@ -504,6 +534,13 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
504
534
|
* @param passphrase Secret passphrase
|
|
505
535
|
*/
|
|
506
536
|
hash(message: string, passphrase?: string): string;
|
|
537
|
+
/**
|
|
538
|
+
* Hash message Hex, SHA3 or HmacSHA512, 512 as Base64
|
|
539
|
+
* https://cryptojs.gitbook.io/docs/
|
|
540
|
+
* @param message Message
|
|
541
|
+
* @param passphrase Secret passphrase
|
|
542
|
+
*/
|
|
543
|
+
hashHex(message: string, passphrase?: string): string;
|
|
507
544
|
/**
|
|
508
545
|
* Check use has the specific role permission or not
|
|
509
546
|
* @param roles Roles to check
|
package/lib/cjs/app/CoreApp.js
CHANGED
|
@@ -34,10 +34,19 @@ class CoreApp {
|
|
|
34
34
|
* Token refresh count down seed
|
|
35
35
|
*/
|
|
36
36
|
this.refreshCountdownSeed = 0;
|
|
37
|
+
/**
|
|
38
|
+
* Device id field name
|
|
39
|
+
*/
|
|
40
|
+
this.deviceIdField = 'SmartERPDeviceId';
|
|
41
|
+
/**
|
|
42
|
+
* Passphrase for encryption
|
|
43
|
+
*/
|
|
44
|
+
this.passphrase = '***';
|
|
37
45
|
this.settings = settings;
|
|
38
46
|
this.api = api;
|
|
39
47
|
this.notifier = notifier;
|
|
40
48
|
this.name = name;
|
|
49
|
+
this.deviceId = shared_1.StorageUtils.getLocalData(this.deviceIdField, '');
|
|
41
50
|
this.setApi(api);
|
|
42
51
|
const { currentCulture, currentRegion } = settings;
|
|
43
52
|
this.changeCulture(currentCulture);
|
|
@@ -108,6 +117,81 @@ class CoreApp {
|
|
|
108
117
|
}
|
|
109
118
|
};
|
|
110
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Init call
|
|
122
|
+
* @returns Result
|
|
123
|
+
*/
|
|
124
|
+
async initCall() {
|
|
125
|
+
var _a;
|
|
126
|
+
const data = {
|
|
127
|
+
timestamp: new Date().getTime(),
|
|
128
|
+
deviceId: this.deviceId === '' ? undefined : this.deviceId
|
|
129
|
+
};
|
|
130
|
+
const result = await this.api.put('Auth/WebInitCall', data);
|
|
131
|
+
if (result == null)
|
|
132
|
+
return;
|
|
133
|
+
if (result.data == null) {
|
|
134
|
+
this.notifier.alert(this.get('noData'));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (!result.ok) {
|
|
138
|
+
const seconds = result.data.seconds;
|
|
139
|
+
const validSeconds = result.data.validSeconds;
|
|
140
|
+
if (result.title === 'timeDifferenceInvalid' &&
|
|
141
|
+
seconds != null &&
|
|
142
|
+
validSeconds != null) {
|
|
143
|
+
const title = (_a = this.get('timeDifferenceInvalid')) === null || _a === void 0 ? void 0 : _a.format(seconds.toString(), validSeconds.toString());
|
|
144
|
+
this.notifier.alert(title);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
this.alertResult(result);
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
this.initCallUpdate(result.data, data.timestamp);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Init call update
|
|
155
|
+
* @param data Result data
|
|
156
|
+
* @param timestamp Timestamp
|
|
157
|
+
*/
|
|
158
|
+
initCallUpdate(data, timestamp) {
|
|
159
|
+
if (data.deviceId == null || data.passphrase == null)
|
|
160
|
+
return;
|
|
161
|
+
// Decrypt
|
|
162
|
+
// Should be done within 120 seconds after returning from the backend
|
|
163
|
+
const passphrase = this.decrypt(data.passphrase, timestamp.toString(), 120);
|
|
164
|
+
if (passphrase == null)
|
|
165
|
+
return;
|
|
166
|
+
// Update device id and cache it
|
|
167
|
+
this.deviceId = data.deviceId;
|
|
168
|
+
shared_1.StorageUtils.setLocalData(this.deviceIdField, this.deviceId);
|
|
169
|
+
// Current passphrase
|
|
170
|
+
this.passphrase = passphrase;
|
|
171
|
+
// Previous passphrase
|
|
172
|
+
if (data.previousPassphrase) {
|
|
173
|
+
const prev = this.decrypt(data.previousPassphrase, timestamp.toString(), 120);
|
|
174
|
+
// Update
|
|
175
|
+
const fields = this.initCallUpdateFields();
|
|
176
|
+
for (const field of fields) {
|
|
177
|
+
const currentValue = shared_1.StorageUtils.getLocalData(field, '');
|
|
178
|
+
if (currentValue === '' || currentValue.indexOf('+') === -1)
|
|
179
|
+
continue;
|
|
180
|
+
const newValueSource = this.decrypt(currentValue, prev, 12);
|
|
181
|
+
if (newValueSource == null)
|
|
182
|
+
continue;
|
|
183
|
+
const newValue = this.encrypt(newValueSource);
|
|
184
|
+
shared_1.StorageUtils.setLocalData(field, newValue);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Init call update fields in local storage
|
|
190
|
+
* @returns Fields
|
|
191
|
+
*/
|
|
192
|
+
initCallUpdateFields() {
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
111
195
|
/**
|
|
112
196
|
* Alert action result
|
|
113
197
|
* @param result Action result
|
|
@@ -200,19 +284,35 @@ class CoreApp {
|
|
|
200
284
|
* Decrypt message
|
|
201
285
|
* @param messageEncrypted Encrypted message
|
|
202
286
|
* @param passphrase Secret passphrase
|
|
287
|
+
* @param durationSeconds Duration seconds, <= 12 will be considered as month
|
|
203
288
|
* @returns Pure text
|
|
204
289
|
*/
|
|
205
|
-
decrypt(messageEncrypted, passphrase) {
|
|
290
|
+
decrypt(messageEncrypted, passphrase, durationSeconds) {
|
|
206
291
|
// Timestamp splitter
|
|
207
292
|
const pos = messageEncrypted.indexOf('+');
|
|
293
|
+
if (pos === -1 || messageEncrypted.length <= 66)
|
|
294
|
+
return undefined;
|
|
208
295
|
const timestamp = messageEncrypted.substring(0, pos);
|
|
209
296
|
const message = messageEncrypted.substring(pos + 1);
|
|
297
|
+
if (durationSeconds != null && durationSeconds > 0) {
|
|
298
|
+
const milseconds = shared_1.Utils.charsToNumber(timestamp);
|
|
299
|
+
if (isNaN(milseconds) || milseconds < 1)
|
|
300
|
+
return undefined;
|
|
301
|
+
const timespan = new Date().substract(new Date(milseconds));
|
|
302
|
+
if ((durationSeconds <= 12 &&
|
|
303
|
+
timespan.totalMonths > durationSeconds) ||
|
|
304
|
+
(durationSeconds > 12 &&
|
|
305
|
+
timespan.totalSeconds > durationSeconds))
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
210
308
|
// Iterations
|
|
211
309
|
const iterations = parseInt(message.substring(0, 2), 10);
|
|
310
|
+
if (isNaN(iterations))
|
|
311
|
+
return undefined;
|
|
212
312
|
const salt = crypto_js_1.enc.Hex.parse(message.substring(2, 34));
|
|
213
313
|
const iv = crypto_js_1.enc.Hex.parse(message.substring(34, 66));
|
|
214
314
|
const encrypted = message.substring(66);
|
|
215
|
-
const key = (0, crypto_js_1.PBKDF2)(this.encryptionEnhance(passphrase, timestamp), salt, {
|
|
315
|
+
const key = (0, crypto_js_1.PBKDF2)(this.encryptionEnhance(passphrase !== null && passphrase !== void 0 ? passphrase : this.passphrase, timestamp), salt, {
|
|
216
316
|
keySize: 8,
|
|
217
317
|
hasher: crypto_js_1.algo.SHA256,
|
|
218
318
|
iterations: 1000 * iterations
|
|
@@ -270,7 +370,7 @@ class CoreApp {
|
|
|
270
370
|
const timestamp = shared_1.Utils.numberToChars(new Date().getTime());
|
|
271
371
|
const bits = 16; // 128 / 8
|
|
272
372
|
const salt = crypto_js_1.lib.WordArray.random(bits);
|
|
273
|
-
const key = (0, crypto_js_1.PBKDF2)(this.encryptionEnhance(passphrase, timestamp), salt, {
|
|
373
|
+
const key = (0, crypto_js_1.PBKDF2)(this.encryptionEnhance(passphrase !== null && passphrase !== void 0 ? passphrase : this.passphrase, timestamp), salt, {
|
|
274
374
|
keySize: 8,
|
|
275
375
|
hasher: crypto_js_1.algo.SHA256,
|
|
276
376
|
iterations: 1000 * iterations
|
|
@@ -295,10 +395,9 @@ class CoreApp {
|
|
|
295
395
|
* @returns Enhanced passphrase
|
|
296
396
|
*/
|
|
297
397
|
encryptionEnhance(passphrase, timestamp) {
|
|
298
|
-
var _a;
|
|
299
398
|
passphrase += timestamp;
|
|
300
399
|
passphrase += passphrase.length.toString();
|
|
301
|
-
return passphrase
|
|
400
|
+
return passphrase;
|
|
302
401
|
}
|
|
303
402
|
/**
|
|
304
403
|
* Format date to string
|
|
@@ -360,7 +459,19 @@ class CoreApp {
|
|
|
360
459
|
* @param forceToLocal Force to local labels
|
|
361
460
|
*/
|
|
362
461
|
formatResult(result, forceToLocal) {
|
|
363
|
-
|
|
462
|
+
const title = result.title;
|
|
463
|
+
if (title && /^\w+$/.test(title)) {
|
|
464
|
+
const key = title.formatInitial(false);
|
|
465
|
+
const localTitle = this.get(key);
|
|
466
|
+
if (localTitle) {
|
|
467
|
+
result.title = localTitle;
|
|
468
|
+
// Hold the original title in type when type is null
|
|
469
|
+
if (result.type == null)
|
|
470
|
+
result.type = title;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
else if ((title == null || forceToLocal) && result.type != null) {
|
|
474
|
+
// Get label from type
|
|
364
475
|
const key = result.type.formatInitial(false);
|
|
365
476
|
result.title = this.get(key);
|
|
366
477
|
}
|
|
@@ -438,6 +549,18 @@ class CoreApp {
|
|
|
438
549
|
else
|
|
439
550
|
return (0, crypto_js_1.HmacSHA512)(message, passphrase).toString(crypto_js_1.enc.Base64);
|
|
440
551
|
}
|
|
552
|
+
/**
|
|
553
|
+
* Hash message Hex, SHA3 or HmacSHA512, 512 as Base64
|
|
554
|
+
* https://cryptojs.gitbook.io/docs/
|
|
555
|
+
* @param message Message
|
|
556
|
+
* @param passphrase Secret passphrase
|
|
557
|
+
*/
|
|
558
|
+
hashHex(message, passphrase) {
|
|
559
|
+
if (passphrase == null)
|
|
560
|
+
return (0, crypto_js_1.SHA3)(message, { outputLength: 512 }).toString(crypto_js_1.enc.Hex);
|
|
561
|
+
else
|
|
562
|
+
return (0, crypto_js_1.HmacSHA512)(message, passphrase).toString(crypto_js_1.enc.Hex);
|
|
563
|
+
}
|
|
441
564
|
/**
|
|
442
565
|
* Check use has the specific role permission or not
|
|
443
566
|
* @param roles Roles to check
|
|
@@ -587,7 +710,6 @@ class CoreApp {
|
|
|
587
710
|
*/
|
|
588
711
|
userLogin(user, refreshToken, keep = false) {
|
|
589
712
|
this.userData = user;
|
|
590
|
-
this.passphrase = user.passphrase;
|
|
591
713
|
this.authorize(user.token, refreshToken, keep);
|
|
592
714
|
}
|
|
593
715
|
/**
|
package/lib/cjs/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export * from './def/ListItem';
|
|
|
16
16
|
export * from './dto/IdDto';
|
|
17
17
|
export * from './dto/IdLabelDto';
|
|
18
18
|
export * from './dto/IdLabelPrimaryDto';
|
|
19
|
+
export * from './dto/InitCallDto';
|
|
19
20
|
export * from './dto/UpdateDto';
|
|
20
21
|
export * from './i18n/enUS';
|
|
21
22
|
export * from './i18n/zhCN';
|
package/lib/cjs/index.js
CHANGED
|
@@ -35,6 +35,7 @@ __exportStar(require("./def/ListItem"), exports);
|
|
|
35
35
|
__exportStar(require("./dto/IdDto"), exports);
|
|
36
36
|
__exportStar(require("./dto/IdLabelDto"), exports);
|
|
37
37
|
__exportStar(require("./dto/IdLabelPrimaryDto"), exports);
|
|
38
|
+
__exportStar(require("./dto/InitCallDto"), exports);
|
|
38
39
|
__exportStar(require("./dto/UpdateDto"), exports);
|
|
39
40
|
// i18n
|
|
40
41
|
__exportStar(require("./i18n/enUS"), exports);
|
|
@@ -48,11 +48,11 @@ export interface IActionResult<D extends IResultData = IResultData> {
|
|
|
48
48
|
/**
|
|
49
49
|
* Trace id
|
|
50
50
|
*/
|
|
51
|
-
|
|
51
|
+
traceId?: string;
|
|
52
52
|
/**
|
|
53
53
|
* Type
|
|
54
54
|
*/
|
|
55
|
-
|
|
55
|
+
type: string;
|
|
56
56
|
/**
|
|
57
57
|
* Success or not
|
|
58
58
|
*/
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import { IActionResult, IResultData } from './IActionResult';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Init call result data
|
|
4
4
|
*/
|
|
5
5
|
export interface InitCallResultData extends IResultData {
|
|
6
|
+
/**
|
|
7
|
+
* Device id
|
|
8
|
+
*/
|
|
9
|
+
deviceId?: string;
|
|
6
10
|
/**
|
|
7
11
|
* Secret passphrase
|
|
8
12
|
*/
|
|
9
|
-
passphrase
|
|
13
|
+
passphrase?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Previous secret passphrase
|
|
16
|
+
*/
|
|
17
|
+
previousPassphrase?: string;
|
|
10
18
|
/**
|
|
11
19
|
* Actual seconds gap
|
|
12
20
|
*/
|
|
13
|
-
seconds
|
|
21
|
+
seconds?: number;
|
|
14
22
|
/**
|
|
15
23
|
* Valid seconds gap
|
|
16
24
|
*/
|
|
17
|
-
validSeconds
|
|
25
|
+
validSeconds?: number;
|
|
18
26
|
}
|
|
19
27
|
/**
|
|
20
28
|
* Init call result
|
package/lib/cjs/state/User.d.ts
CHANGED
package/lib/mjs/app/CoreApp.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { ApiDataError, IApi, IPData } from '@etsoo/restclient';
|
|
|
3
3
|
import { DataTypes, DateUtils } from '@etsoo/shared';
|
|
4
4
|
import { AddressRegion } from '../address/AddressRegion';
|
|
5
5
|
import { IActionResult } from '../result/IActionResult';
|
|
6
|
+
import { InitCallResultData } from '../result/InitCallResult';
|
|
6
7
|
import { IUserData } from '../state/User';
|
|
7
8
|
import { IAppSettings } from './AppSettings';
|
|
8
9
|
import { UserRole } from './UserRole';
|
|
@@ -87,10 +88,6 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
|
|
|
87
88
|
* User data
|
|
88
89
|
*/
|
|
89
90
|
userData?: IUserData;
|
|
90
|
-
/**
|
|
91
|
-
* Passphrase for encryption
|
|
92
|
-
*/
|
|
93
|
-
passphrase?: string;
|
|
94
91
|
/**
|
|
95
92
|
* Search input element
|
|
96
93
|
*/
|
|
@@ -121,9 +118,10 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
|
|
|
121
118
|
* Decrypt message
|
|
122
119
|
* @param messageEncrypted Encrypted message
|
|
123
120
|
* @param passphrase Secret passphrase
|
|
121
|
+
* @param durationSeconds Duration seconds, <= 12 will be considered as month
|
|
124
122
|
* @returns Pure text
|
|
125
123
|
*/
|
|
126
|
-
decrypt(messageEncrypted: string, passphrase
|
|
124
|
+
decrypt(messageEncrypted: string, passphrase?: string, durationSeconds?: number): string | undefined;
|
|
127
125
|
/**
|
|
128
126
|
* Detect IP data, call only one time
|
|
129
127
|
* @param callback Callback will be called when the IP is ready
|
|
@@ -136,7 +134,7 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
|
|
|
136
134
|
* @param iterations Iterations, 1000 times, 1 - 99
|
|
137
135
|
* @returns Result
|
|
138
136
|
*/
|
|
139
|
-
encrypt(message: string, passphrase
|
|
137
|
+
encrypt(message: string, passphrase?: string, iterations?: number): string;
|
|
140
138
|
/**
|
|
141
139
|
* Format date to string
|
|
142
140
|
* @param input Input date
|
|
@@ -224,6 +222,13 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
|
|
|
224
222
|
* @param passphrase Secret passphrase
|
|
225
223
|
*/
|
|
226
224
|
hash(message: string, passphrase?: string): string;
|
|
225
|
+
/**
|
|
226
|
+
* Hash message Hex, SHA3 or HmacSHA512, 512 as Base64
|
|
227
|
+
* https://cryptojs.gitbook.io/docs/
|
|
228
|
+
* @param message Message
|
|
229
|
+
* @param passphrase Secret passphrase
|
|
230
|
+
*/
|
|
231
|
+
hashHex(message: string, passphrase?: string): string;
|
|
227
232
|
/**
|
|
228
233
|
* Check use has the specific role permission or not
|
|
229
234
|
* @param roles Roles to check
|
|
@@ -335,10 +340,6 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
335
340
|
* User data
|
|
336
341
|
*/
|
|
337
342
|
userData?: IUserData;
|
|
338
|
-
/**
|
|
339
|
-
* Passphrase for encryption
|
|
340
|
-
*/
|
|
341
|
-
passphrase?: string;
|
|
342
343
|
/**
|
|
343
344
|
* Response token header field name
|
|
344
345
|
*/
|
|
@@ -363,6 +364,18 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
363
364
|
* Token refresh count down seed
|
|
364
365
|
*/
|
|
365
366
|
protected refreshCountdownSeed: number;
|
|
367
|
+
/**
|
|
368
|
+
* Device id field name
|
|
369
|
+
*/
|
|
370
|
+
protected deviceIdField: string;
|
|
371
|
+
/**
|
|
372
|
+
* Device id
|
|
373
|
+
*/
|
|
374
|
+
protected deviceId: string;
|
|
375
|
+
/**
|
|
376
|
+
* Passphrase for encryption
|
|
377
|
+
*/
|
|
378
|
+
protected passphrase: string;
|
|
366
379
|
/**
|
|
367
380
|
* Protected constructor
|
|
368
381
|
* @param settings Settings
|
|
@@ -372,6 +385,22 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
372
385
|
*/
|
|
373
386
|
protected constructor(settings: S, api: IApi, notifier: INotifier<N, C>, name: string);
|
|
374
387
|
protected setApi(api: IApi): void;
|
|
388
|
+
/**
|
|
389
|
+
* Init call
|
|
390
|
+
* @returns Result
|
|
391
|
+
*/
|
|
392
|
+
protected initCall(): Promise<void>;
|
|
393
|
+
/**
|
|
394
|
+
* Init call update
|
|
395
|
+
* @param data Result data
|
|
396
|
+
* @param timestamp Timestamp
|
|
397
|
+
*/
|
|
398
|
+
protected initCallUpdate(data: InitCallResultData, timestamp: number): void;
|
|
399
|
+
/**
|
|
400
|
+
* Init call update fields in local storage
|
|
401
|
+
* @returns Fields
|
|
402
|
+
*/
|
|
403
|
+
protected initCallUpdateFields(): string[];
|
|
375
404
|
/**
|
|
376
405
|
* Alert action result
|
|
377
406
|
* @param result Action result
|
|
@@ -398,9 +427,10 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
398
427
|
* Decrypt message
|
|
399
428
|
* @param messageEncrypted Encrypted message
|
|
400
429
|
* @param passphrase Secret passphrase
|
|
430
|
+
* @param durationSeconds Duration seconds, <= 12 will be considered as month
|
|
401
431
|
* @returns Pure text
|
|
402
432
|
*/
|
|
403
|
-
decrypt(messageEncrypted: string, passphrase
|
|
433
|
+
decrypt(messageEncrypted: string, passphrase?: string, durationSeconds?: number): string | undefined;
|
|
404
434
|
/**
|
|
405
435
|
* Detect IP data, call only one time
|
|
406
436
|
* @param callback Callback will be called when the IP is ready
|
|
@@ -414,7 +444,7 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
414
444
|
* @param iterations Iterations, 1000 times, 1 - 99
|
|
415
445
|
* @returns Result
|
|
416
446
|
*/
|
|
417
|
-
encrypt(message: string, passphrase
|
|
447
|
+
encrypt(message: string, passphrase?: string, iterations?: number): string;
|
|
418
448
|
/**
|
|
419
449
|
* Enchance secret passphrase
|
|
420
450
|
* @param passphrase Secret passphrase
|
|
@@ -504,6 +534,13 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
504
534
|
* @param passphrase Secret passphrase
|
|
505
535
|
*/
|
|
506
536
|
hash(message: string, passphrase?: string): string;
|
|
537
|
+
/**
|
|
538
|
+
* Hash message Hex, SHA3 or HmacSHA512, 512 as Base64
|
|
539
|
+
* https://cryptojs.gitbook.io/docs/
|
|
540
|
+
* @param message Message
|
|
541
|
+
* @param passphrase Secret passphrase
|
|
542
|
+
*/
|
|
543
|
+
hashHex(message: string, passphrase?: string): string;
|
|
507
544
|
/**
|
|
508
545
|
* Check use has the specific role permission or not
|
|
509
546
|
* @param roles Roles to check
|
package/lib/mjs/app/CoreApp.js
CHANGED
|
@@ -31,10 +31,19 @@ export class CoreApp {
|
|
|
31
31
|
* Token refresh count down seed
|
|
32
32
|
*/
|
|
33
33
|
this.refreshCountdownSeed = 0;
|
|
34
|
+
/**
|
|
35
|
+
* Device id field name
|
|
36
|
+
*/
|
|
37
|
+
this.deviceIdField = 'SmartERPDeviceId';
|
|
38
|
+
/**
|
|
39
|
+
* Passphrase for encryption
|
|
40
|
+
*/
|
|
41
|
+
this.passphrase = '***';
|
|
34
42
|
this.settings = settings;
|
|
35
43
|
this.api = api;
|
|
36
44
|
this.notifier = notifier;
|
|
37
45
|
this.name = name;
|
|
46
|
+
this.deviceId = StorageUtils.getLocalData(this.deviceIdField, '');
|
|
38
47
|
this.setApi(api);
|
|
39
48
|
const { currentCulture, currentRegion } = settings;
|
|
40
49
|
this.changeCulture(currentCulture);
|
|
@@ -105,6 +114,81 @@ export class CoreApp {
|
|
|
105
114
|
}
|
|
106
115
|
};
|
|
107
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Init call
|
|
119
|
+
* @returns Result
|
|
120
|
+
*/
|
|
121
|
+
async initCall() {
|
|
122
|
+
var _a;
|
|
123
|
+
const data = {
|
|
124
|
+
timestamp: new Date().getTime(),
|
|
125
|
+
deviceId: this.deviceId === '' ? undefined : this.deviceId
|
|
126
|
+
};
|
|
127
|
+
const result = await this.api.put('Auth/WebInitCall', data);
|
|
128
|
+
if (result == null)
|
|
129
|
+
return;
|
|
130
|
+
if (result.data == null) {
|
|
131
|
+
this.notifier.alert(this.get('noData'));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (!result.ok) {
|
|
135
|
+
const seconds = result.data.seconds;
|
|
136
|
+
const validSeconds = result.data.validSeconds;
|
|
137
|
+
if (result.title === 'timeDifferenceInvalid' &&
|
|
138
|
+
seconds != null &&
|
|
139
|
+
validSeconds != null) {
|
|
140
|
+
const title = (_a = this.get('timeDifferenceInvalid')) === null || _a === void 0 ? void 0 : _a.format(seconds.toString(), validSeconds.toString());
|
|
141
|
+
this.notifier.alert(title);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
this.alertResult(result);
|
|
145
|
+
}
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
this.initCallUpdate(result.data, data.timestamp);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Init call update
|
|
152
|
+
* @param data Result data
|
|
153
|
+
* @param timestamp Timestamp
|
|
154
|
+
*/
|
|
155
|
+
initCallUpdate(data, timestamp) {
|
|
156
|
+
if (data.deviceId == null || data.passphrase == null)
|
|
157
|
+
return;
|
|
158
|
+
// Decrypt
|
|
159
|
+
// Should be done within 120 seconds after returning from the backend
|
|
160
|
+
const passphrase = this.decrypt(data.passphrase, timestamp.toString(), 120);
|
|
161
|
+
if (passphrase == null)
|
|
162
|
+
return;
|
|
163
|
+
// Update device id and cache it
|
|
164
|
+
this.deviceId = data.deviceId;
|
|
165
|
+
StorageUtils.setLocalData(this.deviceIdField, this.deviceId);
|
|
166
|
+
// Current passphrase
|
|
167
|
+
this.passphrase = passphrase;
|
|
168
|
+
// Previous passphrase
|
|
169
|
+
if (data.previousPassphrase) {
|
|
170
|
+
const prev = this.decrypt(data.previousPassphrase, timestamp.toString(), 120);
|
|
171
|
+
// Update
|
|
172
|
+
const fields = this.initCallUpdateFields();
|
|
173
|
+
for (const field of fields) {
|
|
174
|
+
const currentValue = StorageUtils.getLocalData(field, '');
|
|
175
|
+
if (currentValue === '' || currentValue.indexOf('+') === -1)
|
|
176
|
+
continue;
|
|
177
|
+
const newValueSource = this.decrypt(currentValue, prev, 12);
|
|
178
|
+
if (newValueSource == null)
|
|
179
|
+
continue;
|
|
180
|
+
const newValue = this.encrypt(newValueSource);
|
|
181
|
+
StorageUtils.setLocalData(field, newValue);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Init call update fields in local storage
|
|
187
|
+
* @returns Fields
|
|
188
|
+
*/
|
|
189
|
+
initCallUpdateFields() {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
108
192
|
/**
|
|
109
193
|
* Alert action result
|
|
110
194
|
* @param result Action result
|
|
@@ -197,19 +281,35 @@ export class CoreApp {
|
|
|
197
281
|
* Decrypt message
|
|
198
282
|
* @param messageEncrypted Encrypted message
|
|
199
283
|
* @param passphrase Secret passphrase
|
|
284
|
+
* @param durationSeconds Duration seconds, <= 12 will be considered as month
|
|
200
285
|
* @returns Pure text
|
|
201
286
|
*/
|
|
202
|
-
decrypt(messageEncrypted, passphrase) {
|
|
287
|
+
decrypt(messageEncrypted, passphrase, durationSeconds) {
|
|
203
288
|
// Timestamp splitter
|
|
204
289
|
const pos = messageEncrypted.indexOf('+');
|
|
290
|
+
if (pos === -1 || messageEncrypted.length <= 66)
|
|
291
|
+
return undefined;
|
|
205
292
|
const timestamp = messageEncrypted.substring(0, pos);
|
|
206
293
|
const message = messageEncrypted.substring(pos + 1);
|
|
294
|
+
if (durationSeconds != null && durationSeconds > 0) {
|
|
295
|
+
const milseconds = Utils.charsToNumber(timestamp);
|
|
296
|
+
if (isNaN(milseconds) || milseconds < 1)
|
|
297
|
+
return undefined;
|
|
298
|
+
const timespan = new Date().substract(new Date(milseconds));
|
|
299
|
+
if ((durationSeconds <= 12 &&
|
|
300
|
+
timespan.totalMonths > durationSeconds) ||
|
|
301
|
+
(durationSeconds > 12 &&
|
|
302
|
+
timespan.totalSeconds > durationSeconds))
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
207
305
|
// Iterations
|
|
208
306
|
const iterations = parseInt(message.substring(0, 2), 10);
|
|
307
|
+
if (isNaN(iterations))
|
|
308
|
+
return undefined;
|
|
209
309
|
const salt = enc.Hex.parse(message.substring(2, 34));
|
|
210
310
|
const iv = enc.Hex.parse(message.substring(34, 66));
|
|
211
311
|
const encrypted = message.substring(66);
|
|
212
|
-
const key = PBKDF2(this.encryptionEnhance(passphrase, timestamp), salt, {
|
|
312
|
+
const key = PBKDF2(this.encryptionEnhance(passphrase !== null && passphrase !== void 0 ? passphrase : this.passphrase, timestamp), salt, {
|
|
213
313
|
keySize: 8,
|
|
214
314
|
hasher: algo.SHA256,
|
|
215
315
|
iterations: 1000 * iterations
|
|
@@ -267,7 +367,7 @@ export class CoreApp {
|
|
|
267
367
|
const timestamp = Utils.numberToChars(new Date().getTime());
|
|
268
368
|
const bits = 16; // 128 / 8
|
|
269
369
|
const salt = lib.WordArray.random(bits);
|
|
270
|
-
const key = PBKDF2(this.encryptionEnhance(passphrase, timestamp), salt, {
|
|
370
|
+
const key = PBKDF2(this.encryptionEnhance(passphrase !== null && passphrase !== void 0 ? passphrase : this.passphrase, timestamp), salt, {
|
|
271
371
|
keySize: 8,
|
|
272
372
|
hasher: algo.SHA256,
|
|
273
373
|
iterations: 1000 * iterations
|
|
@@ -292,10 +392,9 @@ export class CoreApp {
|
|
|
292
392
|
* @returns Enhanced passphrase
|
|
293
393
|
*/
|
|
294
394
|
encryptionEnhance(passphrase, timestamp) {
|
|
295
|
-
var _a;
|
|
296
395
|
passphrase += timestamp;
|
|
297
396
|
passphrase += passphrase.length.toString();
|
|
298
|
-
return passphrase
|
|
397
|
+
return passphrase;
|
|
299
398
|
}
|
|
300
399
|
/**
|
|
301
400
|
* Format date to string
|
|
@@ -357,7 +456,19 @@ export class CoreApp {
|
|
|
357
456
|
* @param forceToLocal Force to local labels
|
|
358
457
|
*/
|
|
359
458
|
formatResult(result, forceToLocal) {
|
|
360
|
-
|
|
459
|
+
const title = result.title;
|
|
460
|
+
if (title && /^\w+$/.test(title)) {
|
|
461
|
+
const key = title.formatInitial(false);
|
|
462
|
+
const localTitle = this.get(key);
|
|
463
|
+
if (localTitle) {
|
|
464
|
+
result.title = localTitle;
|
|
465
|
+
// Hold the original title in type when type is null
|
|
466
|
+
if (result.type == null)
|
|
467
|
+
result.type = title;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
else if ((title == null || forceToLocal) && result.type != null) {
|
|
471
|
+
// Get label from type
|
|
361
472
|
const key = result.type.formatInitial(false);
|
|
362
473
|
result.title = this.get(key);
|
|
363
474
|
}
|
|
@@ -435,6 +546,18 @@ export class CoreApp {
|
|
|
435
546
|
else
|
|
436
547
|
return HmacSHA512(message, passphrase).toString(enc.Base64);
|
|
437
548
|
}
|
|
549
|
+
/**
|
|
550
|
+
* Hash message Hex, SHA3 or HmacSHA512, 512 as Base64
|
|
551
|
+
* https://cryptojs.gitbook.io/docs/
|
|
552
|
+
* @param message Message
|
|
553
|
+
* @param passphrase Secret passphrase
|
|
554
|
+
*/
|
|
555
|
+
hashHex(message, passphrase) {
|
|
556
|
+
if (passphrase == null)
|
|
557
|
+
return SHA3(message, { outputLength: 512 }).toString(enc.Hex);
|
|
558
|
+
else
|
|
559
|
+
return HmacSHA512(message, passphrase).toString(enc.Hex);
|
|
560
|
+
}
|
|
438
561
|
/**
|
|
439
562
|
* Check use has the specific role permission or not
|
|
440
563
|
* @param roles Roles to check
|
|
@@ -584,7 +707,6 @@ export class CoreApp {
|
|
|
584
707
|
*/
|
|
585
708
|
userLogin(user, refreshToken, keep = false) {
|
|
586
709
|
this.userData = user;
|
|
587
|
-
this.passphrase = user.passphrase;
|
|
588
710
|
this.authorize(user.token, refreshToken, keep);
|
|
589
711
|
}
|
|
590
712
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/mjs/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export * from './def/ListItem';
|
|
|
16
16
|
export * from './dto/IdDto';
|
|
17
17
|
export * from './dto/IdLabelDto';
|
|
18
18
|
export * from './dto/IdLabelPrimaryDto';
|
|
19
|
+
export * from './dto/InitCallDto';
|
|
19
20
|
export * from './dto/UpdateDto';
|
|
20
21
|
export * from './i18n/enUS';
|
|
21
22
|
export * from './i18n/zhCN';
|
package/lib/mjs/index.js
CHANGED
|
@@ -48,11 +48,11 @@ export interface IActionResult<D extends IResultData = IResultData> {
|
|
|
48
48
|
/**
|
|
49
49
|
* Trace id
|
|
50
50
|
*/
|
|
51
|
-
|
|
51
|
+
traceId?: string;
|
|
52
52
|
/**
|
|
53
53
|
* Type
|
|
54
54
|
*/
|
|
55
|
-
|
|
55
|
+
type: string;
|
|
56
56
|
/**
|
|
57
57
|
* Success or not
|
|
58
58
|
*/
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import { IActionResult, IResultData } from './IActionResult';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Init call result data
|
|
4
4
|
*/
|
|
5
5
|
export interface InitCallResultData extends IResultData {
|
|
6
|
+
/**
|
|
7
|
+
* Device id
|
|
8
|
+
*/
|
|
9
|
+
deviceId?: string;
|
|
6
10
|
/**
|
|
7
11
|
* Secret passphrase
|
|
8
12
|
*/
|
|
9
|
-
passphrase
|
|
13
|
+
passphrase?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Previous secret passphrase
|
|
16
|
+
*/
|
|
17
|
+
previousPassphrase?: string;
|
|
10
18
|
/**
|
|
11
19
|
* Actual seconds gap
|
|
12
20
|
*/
|
|
13
|
-
seconds
|
|
21
|
+
seconds?: number;
|
|
14
22
|
/**
|
|
15
23
|
* Valid seconds gap
|
|
16
24
|
*/
|
|
17
|
-
validSeconds
|
|
25
|
+
validSeconds?: number;
|
|
18
26
|
}
|
|
19
27
|
/**
|
|
20
28
|
* Init call result
|
package/lib/mjs/state/User.d.ts
CHANGED
package/package.json
CHANGED
package/src/app/CoreApp.ts
CHANGED
|
@@ -28,8 +28,10 @@ import {
|
|
|
28
28
|
} from 'crypto-js';
|
|
29
29
|
import { AddressRegion } from '../address/AddressRegion';
|
|
30
30
|
import { AddressUtils } from '../address/AddressUtils';
|
|
31
|
+
import { InitCallDto } from '../dto/InitCallDto';
|
|
31
32
|
import { ActionResultError } from '../result/ActionResultError';
|
|
32
33
|
import { IActionResult } from '../result/IActionResult';
|
|
34
|
+
import { InitCallResult, InitCallResultData } from '../result/InitCallResult';
|
|
33
35
|
import { IUserData } from '../state/User';
|
|
34
36
|
import { IAppSettings } from './AppSettings';
|
|
35
37
|
import { UserRole } from './UserRole';
|
|
@@ -140,11 +142,6 @@ export interface ICoreApp<
|
|
|
140
142
|
*/
|
|
141
143
|
userData?: IUserData;
|
|
142
144
|
|
|
143
|
-
/**
|
|
144
|
-
* Passphrase for encryption
|
|
145
|
-
*/
|
|
146
|
-
passphrase?: string;
|
|
147
|
-
|
|
148
145
|
/**
|
|
149
146
|
* Search input element
|
|
150
147
|
*/
|
|
@@ -180,9 +177,14 @@ export interface ICoreApp<
|
|
|
180
177
|
* Decrypt message
|
|
181
178
|
* @param messageEncrypted Encrypted message
|
|
182
179
|
* @param passphrase Secret passphrase
|
|
180
|
+
* @param durationSeconds Duration seconds, <= 12 will be considered as month
|
|
183
181
|
* @returns Pure text
|
|
184
182
|
*/
|
|
185
|
-
decrypt(
|
|
183
|
+
decrypt(
|
|
184
|
+
messageEncrypted: string,
|
|
185
|
+
passphrase?: string,
|
|
186
|
+
durationSeconds?: number
|
|
187
|
+
): string | undefined;
|
|
186
188
|
|
|
187
189
|
/**
|
|
188
190
|
* Detect IP data, call only one time
|
|
@@ -197,7 +199,7 @@ export interface ICoreApp<
|
|
|
197
199
|
* @param iterations Iterations, 1000 times, 1 - 99
|
|
198
200
|
* @returns Result
|
|
199
201
|
*/
|
|
200
|
-
encrypt(message: string, passphrase
|
|
202
|
+
encrypt(message: string, passphrase?: string, iterations?: number): string;
|
|
201
203
|
|
|
202
204
|
/**
|
|
203
205
|
* Format date to string
|
|
@@ -309,6 +311,14 @@ export interface ICoreApp<
|
|
|
309
311
|
*/
|
|
310
312
|
hash(message: string, passphrase?: string): string;
|
|
311
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Hash message Hex, SHA3 or HmacSHA512, 512 as Base64
|
|
316
|
+
* https://cryptojs.gitbook.io/docs/
|
|
317
|
+
* @param message Message
|
|
318
|
+
* @param passphrase Secret passphrase
|
|
319
|
+
*/
|
|
320
|
+
hashHex(message: string, passphrase?: string): string;
|
|
321
|
+
|
|
312
322
|
/**
|
|
313
323
|
* Check use has the specific role permission or not
|
|
314
324
|
* @param roles Roles to check
|
|
@@ -457,11 +467,6 @@ export abstract class CoreApp<
|
|
|
457
467
|
*/
|
|
458
468
|
userData?: IUserData;
|
|
459
469
|
|
|
460
|
-
/**
|
|
461
|
-
* Passphrase for encryption
|
|
462
|
-
*/
|
|
463
|
-
passphrase?: string;
|
|
464
|
-
|
|
465
470
|
/**
|
|
466
471
|
* Response token header field name
|
|
467
472
|
*/
|
|
@@ -499,6 +504,21 @@ export abstract class CoreApp<
|
|
|
499
504
|
*/
|
|
500
505
|
protected refreshCountdownSeed = 0;
|
|
501
506
|
|
|
507
|
+
/**
|
|
508
|
+
* Device id field name
|
|
509
|
+
*/
|
|
510
|
+
protected deviceIdField: string = 'SmartERPDeviceId';
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Device id
|
|
514
|
+
*/
|
|
515
|
+
protected deviceId: string;
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Passphrase for encryption
|
|
519
|
+
*/
|
|
520
|
+
protected passphrase: string = '***';
|
|
521
|
+
|
|
502
522
|
/**
|
|
503
523
|
* Protected constructor
|
|
504
524
|
* @param settings Settings
|
|
@@ -517,6 +537,11 @@ export abstract class CoreApp<
|
|
|
517
537
|
this.notifier = notifier;
|
|
518
538
|
this.name = name;
|
|
519
539
|
|
|
540
|
+
this.deviceId = StorageUtils.getLocalData<string>(
|
|
541
|
+
this.deviceIdField,
|
|
542
|
+
''
|
|
543
|
+
);
|
|
544
|
+
|
|
520
545
|
this.setApi(api);
|
|
521
546
|
|
|
522
547
|
const { currentCulture, currentRegion } = settings;
|
|
@@ -561,6 +586,108 @@ export abstract class CoreApp<
|
|
|
561
586
|
};
|
|
562
587
|
}
|
|
563
588
|
|
|
589
|
+
/**
|
|
590
|
+
* Init call
|
|
591
|
+
* @returns Result
|
|
592
|
+
*/
|
|
593
|
+
protected async initCall() {
|
|
594
|
+
const data: InitCallDto = {
|
|
595
|
+
timestamp: new Date().getTime(),
|
|
596
|
+
deviceId: this.deviceId === '' ? undefined : this.deviceId
|
|
597
|
+
};
|
|
598
|
+
const result = await this.api.put<InitCallResult>(
|
|
599
|
+
'Auth/WebInitCall',
|
|
600
|
+
data
|
|
601
|
+
);
|
|
602
|
+
if (result == null) return;
|
|
603
|
+
|
|
604
|
+
if (result.data == null) {
|
|
605
|
+
this.notifier.alert(this.get<string>('noData')!);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (!result.ok) {
|
|
610
|
+
const seconds = result.data.seconds;
|
|
611
|
+
const validSeconds = result.data.validSeconds;
|
|
612
|
+
if (
|
|
613
|
+
result.title === 'timeDifferenceInvalid' &&
|
|
614
|
+
seconds != null &&
|
|
615
|
+
validSeconds != null
|
|
616
|
+
) {
|
|
617
|
+
const title = this.get('timeDifferenceInvalid')?.format(
|
|
618
|
+
seconds.toString(),
|
|
619
|
+
validSeconds.toString()
|
|
620
|
+
);
|
|
621
|
+
this.notifier.alert(title!);
|
|
622
|
+
} else {
|
|
623
|
+
this.alertResult(result);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
this.initCallUpdate(result.data, data.timestamp);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Init call update
|
|
634
|
+
* @param data Result data
|
|
635
|
+
* @param timestamp Timestamp
|
|
636
|
+
*/
|
|
637
|
+
protected initCallUpdate(data: InitCallResultData, timestamp: number) {
|
|
638
|
+
if (data.deviceId == null || data.passphrase == null) return;
|
|
639
|
+
|
|
640
|
+
// Decrypt
|
|
641
|
+
// Should be done within 120 seconds after returning from the backend
|
|
642
|
+
const passphrase = this.decrypt(
|
|
643
|
+
data.passphrase,
|
|
644
|
+
timestamp.toString(),
|
|
645
|
+
120
|
|
646
|
+
);
|
|
647
|
+
if (passphrase == null) return;
|
|
648
|
+
|
|
649
|
+
// Update device id and cache it
|
|
650
|
+
this.deviceId = data.deviceId;
|
|
651
|
+
StorageUtils.setLocalData(this.deviceIdField, this.deviceId);
|
|
652
|
+
|
|
653
|
+
// Current passphrase
|
|
654
|
+
this.passphrase = passphrase;
|
|
655
|
+
|
|
656
|
+
// Previous passphrase
|
|
657
|
+
if (data.previousPassphrase) {
|
|
658
|
+
const prev = this.decrypt(
|
|
659
|
+
data.previousPassphrase,
|
|
660
|
+
timestamp.toString(),
|
|
661
|
+
120
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
// Update
|
|
665
|
+
const fields = this.initCallUpdateFields();
|
|
666
|
+
for (const field of fields) {
|
|
667
|
+
const currentValue = StorageUtils.getLocalData<string>(
|
|
668
|
+
field,
|
|
669
|
+
''
|
|
670
|
+
);
|
|
671
|
+
if (currentValue === '' || currentValue.indexOf('+') === -1)
|
|
672
|
+
continue;
|
|
673
|
+
|
|
674
|
+
const newValueSource = this.decrypt(currentValue, prev, 12);
|
|
675
|
+
if (newValueSource == null) continue;
|
|
676
|
+
|
|
677
|
+
const newValue = this.encrypt(newValueSource);
|
|
678
|
+
StorageUtils.setLocalData(field, newValue);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Init call update fields in local storage
|
|
685
|
+
* @returns Fields
|
|
686
|
+
*/
|
|
687
|
+
protected initCallUpdateFields(): string[] {
|
|
688
|
+
return [];
|
|
689
|
+
}
|
|
690
|
+
|
|
564
691
|
/**
|
|
565
692
|
* Alert action result
|
|
566
693
|
* @param result Action result
|
|
@@ -676,23 +803,44 @@ export abstract class CoreApp<
|
|
|
676
803
|
* Decrypt message
|
|
677
804
|
* @param messageEncrypted Encrypted message
|
|
678
805
|
* @param passphrase Secret passphrase
|
|
806
|
+
* @param durationSeconds Duration seconds, <= 12 will be considered as month
|
|
679
807
|
* @returns Pure text
|
|
680
808
|
*/
|
|
681
|
-
decrypt(
|
|
809
|
+
decrypt(
|
|
810
|
+
messageEncrypted: string,
|
|
811
|
+
passphrase?: string,
|
|
812
|
+
durationSeconds?: number
|
|
813
|
+
) {
|
|
682
814
|
// Timestamp splitter
|
|
683
815
|
const pos = messageEncrypted.indexOf('+');
|
|
816
|
+
if (pos === -1 || messageEncrypted.length <= 66) return undefined;
|
|
817
|
+
|
|
684
818
|
const timestamp = messageEncrypted.substring(0, pos);
|
|
685
819
|
const message = messageEncrypted.substring(pos + 1);
|
|
686
820
|
|
|
821
|
+
if (durationSeconds != null && durationSeconds > 0) {
|
|
822
|
+
const milseconds = Utils.charsToNumber(timestamp);
|
|
823
|
+
if (isNaN(milseconds) || milseconds < 1) return undefined;
|
|
824
|
+
const timespan = new Date().substract(new Date(milseconds));
|
|
825
|
+
if (
|
|
826
|
+
(durationSeconds <= 12 &&
|
|
827
|
+
timespan.totalMonths > durationSeconds) ||
|
|
828
|
+
(durationSeconds > 12 &&
|
|
829
|
+
timespan.totalSeconds > durationSeconds)
|
|
830
|
+
)
|
|
831
|
+
return undefined;
|
|
832
|
+
}
|
|
833
|
+
|
|
687
834
|
// Iterations
|
|
688
835
|
const iterations = parseInt(message.substring(0, 2), 10);
|
|
836
|
+
if (isNaN(iterations)) return undefined;
|
|
689
837
|
|
|
690
838
|
const salt = enc.Hex.parse(message.substring(2, 34));
|
|
691
839
|
const iv = enc.Hex.parse(message.substring(34, 66));
|
|
692
840
|
const encrypted = message.substring(66);
|
|
693
841
|
|
|
694
842
|
const key = PBKDF2(
|
|
695
|
-
this.encryptionEnhance(passphrase, timestamp),
|
|
843
|
+
this.encryptionEnhance(passphrase ?? this.passphrase, timestamp),
|
|
696
844
|
salt,
|
|
697
845
|
{
|
|
698
846
|
keySize: 8, // 256 / 32
|
|
@@ -755,7 +903,7 @@ export abstract class CoreApp<
|
|
|
755
903
|
* @param iterations Iterations, 1000 times, 1 - 99
|
|
756
904
|
* @returns Result
|
|
757
905
|
*/
|
|
758
|
-
encrypt(message: string, passphrase
|
|
906
|
+
encrypt(message: string, passphrase?: string, iterations?: number) {
|
|
759
907
|
// Default 1 * 1000
|
|
760
908
|
iterations ??= 1;
|
|
761
909
|
|
|
@@ -765,7 +913,7 @@ export abstract class CoreApp<
|
|
|
765
913
|
const bits = 16; // 128 / 8
|
|
766
914
|
const salt = lib.WordArray.random(bits);
|
|
767
915
|
const key = PBKDF2(
|
|
768
|
-
this.encryptionEnhance(passphrase, timestamp),
|
|
916
|
+
this.encryptionEnhance(passphrase ?? this.passphrase, timestamp),
|
|
769
917
|
salt,
|
|
770
918
|
{
|
|
771
919
|
keySize: 8, // 256 / 32
|
|
@@ -798,7 +946,7 @@ export abstract class CoreApp<
|
|
|
798
946
|
protected encryptionEnhance(passphrase: string, timestamp: string) {
|
|
799
947
|
passphrase += timestamp;
|
|
800
948
|
passphrase += passphrase.length.toString();
|
|
801
|
-
return passphrase
|
|
949
|
+
return passphrase;
|
|
802
950
|
}
|
|
803
951
|
|
|
804
952
|
/**
|
|
@@ -880,7 +1028,18 @@ export abstract class CoreApp<
|
|
|
880
1028
|
* @param forceToLocal Force to local labels
|
|
881
1029
|
*/
|
|
882
1030
|
formatResult(result: IActionResult, forceToLocal?: boolean) {
|
|
883
|
-
|
|
1031
|
+
const title = result.title;
|
|
1032
|
+
if (title && /^\w+$/.test(title)) {
|
|
1033
|
+
const key = title.formatInitial(false);
|
|
1034
|
+
const localTitle = this.get(key);
|
|
1035
|
+
if (localTitle) {
|
|
1036
|
+
result.title = localTitle;
|
|
1037
|
+
|
|
1038
|
+
// Hold the original title in type when type is null
|
|
1039
|
+
if (result.type == null) result.type = title;
|
|
1040
|
+
}
|
|
1041
|
+
} else if ((title == null || forceToLocal) && result.type != null) {
|
|
1042
|
+
// Get label from type
|
|
884
1043
|
const key = result.type.formatInitial(false);
|
|
885
1044
|
result.title = this.get(key);
|
|
886
1045
|
}
|
|
@@ -918,7 +1077,10 @@ export abstract class CoreApp<
|
|
|
918
1077
|
* @returns Cached token
|
|
919
1078
|
*/
|
|
920
1079
|
getCacheToken(): string | null {
|
|
921
|
-
let refreshToken = StorageUtils.getLocalData(
|
|
1080
|
+
let refreshToken = StorageUtils.getLocalData<string>(
|
|
1081
|
+
this.headerTokenField,
|
|
1082
|
+
''
|
|
1083
|
+
);
|
|
922
1084
|
if (refreshToken === '')
|
|
923
1085
|
refreshToken = StorageUtils.getSessionData(
|
|
924
1086
|
this.headerTokenField,
|
|
@@ -971,6 +1133,18 @@ export abstract class CoreApp<
|
|
|
971
1133
|
else return HmacSHA512(message, passphrase).toString(enc.Base64);
|
|
972
1134
|
}
|
|
973
1135
|
|
|
1136
|
+
/**
|
|
1137
|
+
* Hash message Hex, SHA3 or HmacSHA512, 512 as Base64
|
|
1138
|
+
* https://cryptojs.gitbook.io/docs/
|
|
1139
|
+
* @param message Message
|
|
1140
|
+
* @param passphrase Secret passphrase
|
|
1141
|
+
*/
|
|
1142
|
+
hashHex(message: string, passphrase?: string) {
|
|
1143
|
+
if (passphrase == null)
|
|
1144
|
+
return SHA3(message, { outputLength: 512 }).toString(enc.Hex);
|
|
1145
|
+
else return HmacSHA512(message, passphrase).toString(enc.Hex);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
974
1148
|
/**
|
|
975
1149
|
* Check use has the specific role permission or not
|
|
976
1150
|
* @param roles Roles to check
|
|
@@ -1139,7 +1313,6 @@ export abstract class CoreApp<
|
|
|
1139
1313
|
*/
|
|
1140
1314
|
userLogin(user: IUserData, refreshToken: string, keep: boolean = false) {
|
|
1141
1315
|
this.userData = user;
|
|
1142
|
-
this.passphrase = user.passphrase;
|
|
1143
1316
|
this.authorize(user.token, refreshToken, keep);
|
|
1144
1317
|
}
|
|
1145
1318
|
|
package/src/index.ts
CHANGED
|
@@ -56,12 +56,12 @@ export interface IActionResult<D extends IResultData = IResultData> {
|
|
|
56
56
|
/**
|
|
57
57
|
* Trace id
|
|
58
58
|
*/
|
|
59
|
-
|
|
59
|
+
traceId?: string;
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Type
|
|
63
63
|
*/
|
|
64
|
-
|
|
64
|
+
type: string;
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* Success or not
|
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
import { IActionResult, IResultData } from './IActionResult';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Init call result data
|
|
5
5
|
*/
|
|
6
6
|
export interface InitCallResultData extends IResultData {
|
|
7
|
+
/**
|
|
8
|
+
* Device id
|
|
9
|
+
*/
|
|
10
|
+
deviceId?: string;
|
|
11
|
+
|
|
7
12
|
/**
|
|
8
13
|
* Secret passphrase
|
|
9
14
|
*/
|
|
10
|
-
passphrase
|
|
15
|
+
passphrase?: string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Previous secret passphrase
|
|
19
|
+
*/
|
|
20
|
+
previousPassphrase?: string;
|
|
11
21
|
|
|
12
22
|
/**
|
|
13
23
|
* Actual seconds gap
|
|
14
24
|
*/
|
|
15
|
-
seconds
|
|
25
|
+
seconds?: number;
|
|
16
26
|
|
|
17
27
|
/**
|
|
18
28
|
* Valid seconds gap
|
|
19
29
|
*/
|
|
20
|
-
validSeconds
|
|
30
|
+
validSeconds?: number;
|
|
21
31
|
}
|
|
22
32
|
|
|
23
33
|
/**
|