@nymphjs/tilmeld 1.0.0-beta.80 → 1.0.0-beta.82
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/CHANGELOG.md +10 -0
- package/README.md +1 -1
- package/dist/AbleObject.js +2 -5
- package/dist/AbleObject.js.map +1 -1
- package/dist/Group.d.ts +3 -3
- package/dist/Group.js +42 -44
- package/dist/Group.js.map +1 -1
- package/dist/Group.test.js +11 -16
- package/dist/Group.test.js.map +1 -1
- package/dist/Tilmeld.d.ts +3 -4
- package/dist/Tilmeld.js +70 -76
- package/dist/Tilmeld.js.map +1 -1
- package/dist/Tilmeld.types.d.ts +2 -2
- package/dist/Tilmeld.types.js +1 -2
- package/dist/User.d.ts +4 -4
- package/dist/User.js +95 -89
- package/dist/User.js.map +1 -1
- package/dist/User.test.js +10 -15
- package/dist/User.test.js.map +1 -1
- package/dist/conf/d.d.ts +6 -4
- package/dist/conf/d.js +1 -2
- package/dist/conf/defaults.d.ts +1 -1
- package/dist/conf/defaults.js +59 -63
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +2 -2
- package/dist/conf/index.js +2 -8
- package/dist/conf/index.js.map +1 -1
- package/dist/enforceTilmeld.d.ts +1 -1
- package/dist/enforceTilmeld.js +4 -8
- package/dist/enforceTilmeld.js.map +1 -1
- package/dist/errors/AccessControlError.js +2 -6
- package/dist/errors/AccessControlError.js.map +1 -1
- package/dist/errors/BadDataError.js +2 -6
- package/dist/errors/BadDataError.js.map +1 -1
- package/dist/errors/BadEmailError.js +2 -6
- package/dist/errors/BadEmailError.js.map +1 -1
- package/dist/errors/BadUsernameError.js +2 -6
- package/dist/errors/BadUsernameError.js.map +1 -1
- package/dist/errors/CouldNotChangeDefaultPrimaryGroupError.js +1 -5
- package/dist/errors/CouldNotChangeDefaultPrimaryGroupError.js.map +1 -1
- package/dist/errors/EmailChangeRateLimitExceededError.js +1 -5
- package/dist/errors/EmailChangeRateLimitExceededError.js.map +1 -1
- package/dist/errors/index.d.ts +6 -6
- package/dist/errors/index.js +6 -22
- package/dist/errors/index.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +16 -36
- package/dist/index.js.map +1 -1
- package/dist/tilmeldJoiProps.js +13 -18
- package/dist/tilmeldJoiProps.js.map +1 -1
- package/jest.config.js +11 -2
- package/package.json +27 -28
- package/src/Group.test.ts +2 -2
- package/src/Group.ts +12 -8
- package/src/Tilmeld.ts +9 -9
- package/src/Tilmeld.types.ts +2 -2
- package/src/User.test.ts +2 -2
- package/src/User.ts +77 -65
- package/src/conf/d.ts +6 -4
- package/src/conf/defaults.ts +7 -5
- package/src/conf/index.ts +2 -2
- package/src/enforceTilmeld.ts +1 -1
- package/src/errors/index.ts +6 -6
- package/src/index.ts +11 -11
- package/src/tilmeldJoiProps.ts +2 -2
- package/src/type/locutus.d.ts +3 -0
- package/tsconfig.json +3 -1
package/dist/User.js
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const md5_1 = __importDefault(require("crypto-js/md5"));
|
|
11
|
-
const lodash_1 = require("lodash");
|
|
12
|
-
const otpauth_1 = require("otpauth");
|
|
13
|
-
const qrcode_1 = require("qrcode");
|
|
14
|
-
const enforceTilmeld_1 = require("./enforceTilmeld");
|
|
15
|
-
const AbleObject_1 = __importDefault(require("./AbleObject"));
|
|
16
|
-
const errors_1 = require("./errors");
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { humanSecret, nanoid } from '@nymphjs/guid';
|
|
3
|
+
import strtotime from 'locutus/php/datetime/strtotime.js';
|
|
4
|
+
import { difference } from 'lodash-es';
|
|
5
|
+
import { TOTP, Secret } from 'otpauth';
|
|
6
|
+
import { toDataURL } from 'qrcode';
|
|
7
|
+
import { enforceTilmeld } from './enforceTilmeld.js';
|
|
8
|
+
import AbleObject from './AbleObject.js';
|
|
9
|
+
import { BadDataError, BadEmailError, BadUsernameError, EmailChangeRateLimitExceededError, } from './errors/index.js';
|
|
17
10
|
/**
|
|
18
11
|
* A user data model.
|
|
19
12
|
*
|
|
@@ -23,7 +16,7 @@ const errors_1 = require("./errors");
|
|
|
23
16
|
* @copyright SciActive Inc
|
|
24
17
|
* @see http://nymph.io/
|
|
25
18
|
*/
|
|
26
|
-
class User extends
|
|
19
|
+
export default class User extends AbleObject {
|
|
27
20
|
/**
|
|
28
21
|
* The instance of Tilmeld to use for queries.
|
|
29
22
|
*/
|
|
@@ -128,7 +121,7 @@ class User extends AbleObject_1.default {
|
|
|
128
121
|
return entity;
|
|
129
122
|
}
|
|
130
123
|
static current(returnObjectIfNotExist) {
|
|
131
|
-
const tilmeld =
|
|
124
|
+
const tilmeld = enforceTilmeld(this);
|
|
132
125
|
if (tilmeld.currentUser == null) {
|
|
133
126
|
return returnObjectIfNotExist ? this.factorySync() : null;
|
|
134
127
|
}
|
|
@@ -141,7 +134,7 @@ class User extends AbleObject_1.default {
|
|
|
141
134
|
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
142
135
|
*/
|
|
143
136
|
static async sendRecovery(data) {
|
|
144
|
-
const tilmeld =
|
|
137
|
+
const tilmeld = enforceTilmeld(this);
|
|
145
138
|
if (!tilmeld.config.pwRecovery) {
|
|
146
139
|
return {
|
|
147
140
|
result: false,
|
|
@@ -183,7 +176,7 @@ class User extends AbleObject_1.default {
|
|
|
183
176
|
};
|
|
184
177
|
}
|
|
185
178
|
// Create a unique secret.
|
|
186
|
-
getUser.recoverSecret =
|
|
179
|
+
getUser.recoverSecret = humanSecret();
|
|
187
180
|
getUser.recoverSecretDate = Date.now();
|
|
188
181
|
if (!(await getUser.$saveSkipAC())) {
|
|
189
182
|
return { result: false, message: "Couldn't save recovery secret." };
|
|
@@ -225,7 +218,7 @@ class User extends AbleObject_1.default {
|
|
|
225
218
|
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
226
219
|
*/
|
|
227
220
|
static async recover(data) {
|
|
228
|
-
const tilmeld =
|
|
221
|
+
const tilmeld = enforceTilmeld(this);
|
|
229
222
|
if (!tilmeld.config.pwRecovery) {
|
|
230
223
|
return {
|
|
231
224
|
result: false,
|
|
@@ -236,7 +229,7 @@ class User extends AbleObject_1.default {
|
|
|
236
229
|
if (user.guid == null ||
|
|
237
230
|
user.recoverSecret == null ||
|
|
238
231
|
data.secret !== user.recoverSecret ||
|
|
239
|
-
(
|
|
232
|
+
strtotime('+' + tilmeld.config.pwRecoveryTimeLimit, Math.floor((user.recoverSecretDate ?? 0) / 1000)) *
|
|
240
233
|
1000 <
|
|
241
234
|
Date.now()) {
|
|
242
235
|
return {
|
|
@@ -262,7 +255,7 @@ class User extends AbleObject_1.default {
|
|
|
262
255
|
}
|
|
263
256
|
}
|
|
264
257
|
static getClientConfig() {
|
|
265
|
-
const tilmeld =
|
|
258
|
+
const tilmeld = enforceTilmeld(this);
|
|
266
259
|
return {
|
|
267
260
|
regFields: tilmeld.config.regFields,
|
|
268
261
|
userFields: tilmeld.config.userFields,
|
|
@@ -275,7 +268,7 @@ class User extends AbleObject_1.default {
|
|
|
275
268
|
};
|
|
276
269
|
}
|
|
277
270
|
static async loginUser(data) {
|
|
278
|
-
const tilmeld =
|
|
271
|
+
const tilmeld = enforceTilmeld(this);
|
|
279
272
|
if (!('username' in data) || !data.username.length) {
|
|
280
273
|
return { result: false, message: 'Incorrect login/password.' };
|
|
281
274
|
}
|
|
@@ -310,7 +303,7 @@ class User extends AbleObject_1.default {
|
|
|
310
303
|
}
|
|
311
304
|
}
|
|
312
305
|
async $getUniques() {
|
|
313
|
-
const tilmeld =
|
|
306
|
+
const tilmeld = enforceTilmeld(this);
|
|
314
307
|
const uniques = [`u:${this.$data.username}`];
|
|
315
308
|
if (!tilmeld.config.emailUsernames &&
|
|
316
309
|
tilmeld.config.userFields.includes('email')) {
|
|
@@ -322,7 +315,7 @@ class User extends AbleObject_1.default {
|
|
|
322
315
|
return uniques;
|
|
323
316
|
}
|
|
324
317
|
async $login(data) {
|
|
325
|
-
const tilmeld =
|
|
318
|
+
const tilmeld = enforceTilmeld(this);
|
|
326
319
|
if (this.guid == null) {
|
|
327
320
|
return { result: false, message: 'Incorrect login/password.' };
|
|
328
321
|
}
|
|
@@ -378,7 +371,7 @@ class User extends AbleObject_1.default {
|
|
|
378
371
|
return { result: true, message: 'You are logged in.' };
|
|
379
372
|
}
|
|
380
373
|
async $switchUser(data) {
|
|
381
|
-
const tilmeld =
|
|
374
|
+
const tilmeld = enforceTilmeld(this);
|
|
382
375
|
if (this.guid == null) {
|
|
383
376
|
return { result: false, message: 'Incorrect login/password.' };
|
|
384
377
|
}
|
|
@@ -440,7 +433,7 @@ class User extends AbleObject_1.default {
|
|
|
440
433
|
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
441
434
|
*/
|
|
442
435
|
async $logout() {
|
|
443
|
-
const tilmeld =
|
|
436
|
+
const tilmeld = enforceTilmeld(this);
|
|
444
437
|
try {
|
|
445
438
|
for (let callback of this.constructor
|
|
446
439
|
.beforeLogoutCallbacks) {
|
|
@@ -473,7 +466,11 @@ class User extends AbleObject_1.default {
|
|
|
473
466
|
return 'https://secure.gravatar.com/avatar/?d=mm&s=40';
|
|
474
467
|
}
|
|
475
468
|
return ('https://secure.gravatar.com/avatar/' +
|
|
476
|
-
|
|
469
|
+
crypto
|
|
470
|
+
.createHash('sha256')
|
|
471
|
+
.update(this.$data.email.trim().toLowerCase())
|
|
472
|
+
.digest('hex')
|
|
473
|
+
.toLowerCase() +
|
|
477
474
|
'?d=identicon&s=40');
|
|
478
475
|
}
|
|
479
476
|
$getGid() {
|
|
@@ -508,55 +505,55 @@ class User extends AbleObject_1.default {
|
|
|
508
505
|
return this.$descendantGroups;
|
|
509
506
|
}
|
|
510
507
|
$jsonAcceptData(input, allowConflict = false) {
|
|
511
|
-
const tilmeld =
|
|
508
|
+
const tilmeld = enforceTilmeld(this);
|
|
512
509
|
this.$check();
|
|
513
510
|
if ('abilities' in input.data &&
|
|
514
511
|
input.data.abilities?.includes('system/admin') !==
|
|
515
512
|
this.$data.abilities?.includes('system/admin') &&
|
|
516
513
|
(!tilmeld.currentUser ||
|
|
517
514
|
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
518
|
-
throw new
|
|
515
|
+
throw new BadDataError("You don't have the authority to grant or revoke system/admin.");
|
|
519
516
|
}
|
|
520
517
|
if ('abilities' in input.data &&
|
|
521
518
|
input.data.abilities?.includes('tilmeld/admin') !==
|
|
522
519
|
this.$data.abilities?.includes('tilmeld/admin') &&
|
|
523
520
|
(!tilmeld.currentUser ||
|
|
524
521
|
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
525
|
-
throw new
|
|
522
|
+
throw new BadDataError("You don't have the authority to grant or revoke tilmeld/admin.");
|
|
526
523
|
}
|
|
527
524
|
if ('abilities' in input.data &&
|
|
528
525
|
input.data.abilities?.includes('tilmeld/switch') !==
|
|
529
526
|
this.$data.abilities?.includes('tilmeld/switch') &&
|
|
530
527
|
(!tilmeld.currentUser ||
|
|
531
528
|
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
532
|
-
throw new
|
|
529
|
+
throw new BadDataError("You don't have the authority to grant or revoke tilmeld/switch.");
|
|
533
530
|
}
|
|
534
531
|
this.$originalEmail = this.$data.email;
|
|
535
532
|
super.$jsonAcceptData(input, allowConflict);
|
|
536
533
|
}
|
|
537
534
|
$jsonAcceptPatch(patch, allowConflict = false) {
|
|
538
|
-
const tilmeld =
|
|
535
|
+
const tilmeld = enforceTilmeld(this);
|
|
539
536
|
this.$check();
|
|
540
537
|
if ('abilities' in patch.set &&
|
|
541
538
|
patch.set.abilities?.includes('system/admin') !==
|
|
542
539
|
this.$data.abilities?.includes('system/admin') &&
|
|
543
540
|
(!tilmeld.currentUser ||
|
|
544
541
|
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
545
|
-
throw new
|
|
542
|
+
throw new BadDataError("You don't have the authority to grant or revoke system/admin.");
|
|
546
543
|
}
|
|
547
544
|
if ('abilities' in patch.set &&
|
|
548
545
|
patch.set.abilities?.includes('tilmeld/admin') !==
|
|
549
546
|
this.$data.abilities?.includes('tilmeld/admin') &&
|
|
550
547
|
(!tilmeld.currentUser ||
|
|
551
548
|
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
552
|
-
throw new
|
|
549
|
+
throw new BadDataError("You don't have the authority to grant or revoke tilmeld/admin.");
|
|
553
550
|
}
|
|
554
551
|
if ('abilities' in patch.set &&
|
|
555
552
|
patch.set.abilities?.includes('tilmeld/switch') !==
|
|
556
553
|
this.$data.abilities?.includes('tilmeld/switch') &&
|
|
557
554
|
(!tilmeld.currentUser ||
|
|
558
555
|
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
559
|
-
throw new
|
|
556
|
+
throw new BadDataError("You don't have the authority to grant or revoke tilmeld/switch.");
|
|
560
557
|
}
|
|
561
558
|
this.$originalEmail = this.$data.email;
|
|
562
559
|
super.$jsonAcceptPatch(patch, allowConflict);
|
|
@@ -571,7 +568,7 @@ class User extends AbleObject_1.default {
|
|
|
571
568
|
* @param givenUser User to update protection for. If undefined, will use the currently logged in user.
|
|
572
569
|
*/
|
|
573
570
|
$updateDataProtection(givenUser) {
|
|
574
|
-
const tilmeld =
|
|
571
|
+
const tilmeld = enforceTilmeld(this);
|
|
575
572
|
let user = givenUser ?? tilmeld.User.current();
|
|
576
573
|
this.$clientEnabledMethods = [...User.DEFAULT_CLIENT_ENABLED_METHODS];
|
|
577
574
|
this.$privateData = [...User.DEFAULT_PRIVATE_DATA];
|
|
@@ -689,7 +686,7 @@ class User extends AbleObject_1.default {
|
|
|
689
686
|
*/
|
|
690
687
|
async $gatekeeper(ability) {
|
|
691
688
|
await this.$wake();
|
|
692
|
-
const tilmeld =
|
|
689
|
+
const tilmeld = enforceTilmeld(this);
|
|
693
690
|
if (ability == null) {
|
|
694
691
|
return tilmeld.User.current()?.$is(this) ?? false;
|
|
695
692
|
}
|
|
@@ -733,7 +730,7 @@ class User extends AbleObject_1.default {
|
|
|
733
730
|
* @returns True on success, false on failure.
|
|
734
731
|
*/
|
|
735
732
|
async $sendEmailVerification() {
|
|
736
|
-
const tilmeld =
|
|
733
|
+
const tilmeld = enforceTilmeld(this);
|
|
737
734
|
if (this.guid == null) {
|
|
738
735
|
return false;
|
|
739
736
|
}
|
|
@@ -822,16 +819,20 @@ class User extends AbleObject_1.default {
|
|
|
822
819
|
* @returns True if the passwords match, otherwise false.
|
|
823
820
|
*/
|
|
824
821
|
$checkPassword(password) {
|
|
825
|
-
const tilmeld =
|
|
822
|
+
const tilmeld = enforceTilmeld(this);
|
|
826
823
|
switch (tilmeld.config.pwMethod) {
|
|
827
824
|
case 'plain':
|
|
828
825
|
return this.$data.password === password;
|
|
829
826
|
case 'digest':
|
|
830
|
-
return this.$data.password ===
|
|
827
|
+
return (this.$data.password ===
|
|
828
|
+
crypto.createHash('sha256').update(password).digest('base64'));
|
|
831
829
|
case 'salt':
|
|
832
830
|
default:
|
|
833
831
|
return (this.$data.password ===
|
|
834
|
-
|
|
832
|
+
crypto
|
|
833
|
+
.createHash('sha256')
|
|
834
|
+
.update(password + this.$data.salt)
|
|
835
|
+
.digest('base64'));
|
|
835
836
|
}
|
|
836
837
|
}
|
|
837
838
|
/**
|
|
@@ -918,7 +919,7 @@ class User extends AbleObject_1.default {
|
|
|
918
919
|
}
|
|
919
920
|
if (await this.$save()) {
|
|
920
921
|
if (data.revokeCurrentTokens) {
|
|
921
|
-
const tilmeld =
|
|
922
|
+
const tilmeld = enforceTilmeld(this);
|
|
922
923
|
await tilmeld.login(this, true);
|
|
923
924
|
}
|
|
924
925
|
return { result: true, message: 'Your password has been changed.' };
|
|
@@ -934,7 +935,7 @@ class User extends AbleObject_1.default {
|
|
|
934
935
|
* @returns The resulting password or hash which is stored in the entity.
|
|
935
936
|
*/
|
|
936
937
|
$password(password) {
|
|
937
|
-
const tilmeld =
|
|
938
|
+
const tilmeld = enforceTilmeld(this);
|
|
938
939
|
switch (tilmeld.config.pwMethod) {
|
|
939
940
|
case 'plain':
|
|
940
941
|
delete this.$data.salt;
|
|
@@ -942,12 +943,18 @@ class User extends AbleObject_1.default {
|
|
|
942
943
|
break;
|
|
943
944
|
case 'digest':
|
|
944
945
|
delete this.$data.salt;
|
|
945
|
-
this.$data.password =
|
|
946
|
+
this.$data.password = crypto
|
|
947
|
+
.createHash('sha256')
|
|
948
|
+
.update(password)
|
|
949
|
+
.digest('base64');
|
|
946
950
|
break;
|
|
947
951
|
case 'salt':
|
|
948
952
|
default:
|
|
949
|
-
this.$data.salt =
|
|
950
|
-
this.$data.password =
|
|
953
|
+
this.$data.salt = nanoid();
|
|
954
|
+
this.$data.password = crypto
|
|
955
|
+
.createHash('sha256')
|
|
956
|
+
.update(password + this.$data.salt)
|
|
957
|
+
.digest('base64');
|
|
951
958
|
break;
|
|
952
959
|
}
|
|
953
960
|
return this.$data.password;
|
|
@@ -964,7 +971,7 @@ class User extends AbleObject_1.default {
|
|
|
964
971
|
}
|
|
965
972
|
this.$data.revokeTokenDate = Date.now();
|
|
966
973
|
if (await this.$save()) {
|
|
967
|
-
const tilmeld =
|
|
974
|
+
const tilmeld = enforceTilmeld(this);
|
|
968
975
|
await tilmeld.login(this, true);
|
|
969
976
|
return {
|
|
970
977
|
result: true,
|
|
@@ -988,8 +995,8 @@ class User extends AbleObject_1.default {
|
|
|
988
995
|
if (this.$data.totpSecret == null) {
|
|
989
996
|
return false;
|
|
990
997
|
}
|
|
991
|
-
const tilmeld =
|
|
992
|
-
const totp = new
|
|
998
|
+
const tilmeld = enforceTilmeld(this);
|
|
999
|
+
const totp = new TOTP({
|
|
993
1000
|
issuer: tilmeld.config.appName,
|
|
994
1001
|
label: this.$data.username,
|
|
995
1002
|
algorithm: 'SHA1',
|
|
@@ -1005,12 +1012,12 @@ class User extends AbleObject_1.default {
|
|
|
1005
1012
|
* @returns An object with 'uri', 'qrcode', and 'secret'.
|
|
1006
1013
|
*/
|
|
1007
1014
|
async $getNewTOTPSecret() {
|
|
1008
|
-
const tilmeld =
|
|
1015
|
+
const tilmeld = enforceTilmeld(this);
|
|
1009
1016
|
if (this.$data.totpSecret != null) {
|
|
1010
|
-
throw new
|
|
1017
|
+
throw new BadDataError('You already have a 2FA secret.');
|
|
1011
1018
|
}
|
|
1012
|
-
const secret = new
|
|
1013
|
-
const totp = new
|
|
1019
|
+
const secret = new Secret();
|
|
1020
|
+
const totp = new TOTP({
|
|
1014
1021
|
issuer: tilmeld.config.appName,
|
|
1015
1022
|
label: this.$data.username,
|
|
1016
1023
|
algorithm: 'SHA1',
|
|
@@ -1019,7 +1026,7 @@ class User extends AbleObject_1.default {
|
|
|
1019
1026
|
secret,
|
|
1020
1027
|
});
|
|
1021
1028
|
const uri = totp.toString();
|
|
1022
|
-
const qrcode = await
|
|
1029
|
+
const qrcode = await toDataURL(uri);
|
|
1023
1030
|
return {
|
|
1024
1031
|
uri,
|
|
1025
1032
|
qrcode,
|
|
@@ -1049,8 +1056,8 @@ class User extends AbleObject_1.default {
|
|
|
1049
1056
|
data.code.length !== 6) {
|
|
1050
1057
|
return { result: false, message: '2FA code is invalid.' };
|
|
1051
1058
|
}
|
|
1052
|
-
const tilmeld =
|
|
1053
|
-
const totp = new
|
|
1059
|
+
const tilmeld = enforceTilmeld(this);
|
|
1060
|
+
const totp = new TOTP({
|
|
1054
1061
|
issuer: tilmeld.config.appName,
|
|
1055
1062
|
label: this.$data.username,
|
|
1056
1063
|
algorithm: 'SHA1',
|
|
@@ -1077,7 +1084,7 @@ class User extends AbleObject_1.default {
|
|
|
1077
1084
|
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
1078
1085
|
*/
|
|
1079
1086
|
async $removeTOTPSecret(data) {
|
|
1080
|
-
const tilmeld =
|
|
1087
|
+
const tilmeld = enforceTilmeld(this);
|
|
1081
1088
|
if (data) {
|
|
1082
1089
|
if (!this.$checkPassword(data.password ?? '')) {
|
|
1083
1090
|
return { result: false, message: 'Incorrect password.' };
|
|
@@ -1090,7 +1097,7 @@ class User extends AbleObject_1.default {
|
|
|
1090
1097
|
if (this.$data.totpSecret == null) {
|
|
1091
1098
|
return { result: false, message: "You don't have a 2FA secret." };
|
|
1092
1099
|
}
|
|
1093
|
-
const totp = new
|
|
1100
|
+
const totp = new TOTP({
|
|
1094
1101
|
issuer: tilmeld.config.appName,
|
|
1095
1102
|
label: this.$data.username,
|
|
1096
1103
|
algorithm: 'SHA1',
|
|
@@ -1125,7 +1132,7 @@ class User extends AbleObject_1.default {
|
|
|
1125
1132
|
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
1126
1133
|
*/
|
|
1127
1134
|
async $checkUsername() {
|
|
1128
|
-
const tilmeld =
|
|
1135
|
+
const tilmeld = enforceTilmeld(this);
|
|
1129
1136
|
this.$nymph.config.debugInfo('tilmeld', `Username check "${this.$data.username}".`);
|
|
1130
1137
|
if (!tilmeld.config.emailUsernames) {
|
|
1131
1138
|
if (this.$data.username == null || !this.$data.username.length) {
|
|
@@ -1147,7 +1154,7 @@ class User extends AbleObject_1.default {
|
|
|
1147
1154
|
' characters.',
|
|
1148
1155
|
};
|
|
1149
1156
|
}
|
|
1150
|
-
if (
|
|
1157
|
+
if (difference(this.$data.username.split(''), tilmeld.config.validChars.split('')).length) {
|
|
1151
1158
|
return {
|
|
1152
1159
|
result: false,
|
|
1153
1160
|
message: tilmeld.config.validCharsNotice,
|
|
@@ -1219,7 +1226,7 @@ class User extends AbleObject_1.default {
|
|
|
1219
1226
|
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
1220
1227
|
*/
|
|
1221
1228
|
async $checkEmail() {
|
|
1222
|
-
const tilmeld =
|
|
1229
|
+
const tilmeld = enforceTilmeld(this);
|
|
1223
1230
|
this.$nymph.config.debugInfo('tilmeld', `Email check "${this.$data.email}".`);
|
|
1224
1231
|
if (this.$data.email == null || !this.$data.email.length) {
|
|
1225
1232
|
if (tilmeld.config.verifyEmail) {
|
|
@@ -1260,7 +1267,7 @@ class User extends AbleObject_1.default {
|
|
|
1260
1267
|
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
1261
1268
|
*/
|
|
1262
1269
|
async $checkPhone() {
|
|
1263
|
-
const tilmeld =
|
|
1270
|
+
const tilmeld = enforceTilmeld(this);
|
|
1264
1271
|
this.$nymph.config.debugInfo('tilmeld', `Phone check "${this.$data.phone}".`);
|
|
1265
1272
|
if (this.$data.phone == null || !this.$data.phone.length) {
|
|
1266
1273
|
return { result: false, message: 'Please specify a phone number.' };
|
|
@@ -1289,7 +1296,7 @@ class User extends AbleObject_1.default {
|
|
|
1289
1296
|
};
|
|
1290
1297
|
}
|
|
1291
1298
|
async $register(data) {
|
|
1292
|
-
let tilmeld =
|
|
1299
|
+
let tilmeld = enforceTilmeld(this);
|
|
1293
1300
|
if (!tilmeld.config.allowRegistration) {
|
|
1294
1301
|
return {
|
|
1295
1302
|
result: false,
|
|
@@ -1329,11 +1336,11 @@ class User extends AbleObject_1.default {
|
|
|
1329
1336
|
this.$data.email = this.$data.username;
|
|
1330
1337
|
}
|
|
1331
1338
|
// Start transaction.
|
|
1332
|
-
const transaction = 'tilmeld-register-' +
|
|
1339
|
+
const transaction = 'tilmeld-register-' + nanoid();
|
|
1333
1340
|
const nymph = this.$nymph;
|
|
1334
1341
|
const tnymph = await this.$nymph.startTransaction(transaction);
|
|
1335
1342
|
this.$setNymph(tnymph);
|
|
1336
|
-
tilmeld =
|
|
1343
|
+
tilmeld = enforceTilmeld(this);
|
|
1337
1344
|
let message = '';
|
|
1338
1345
|
let loggedin = false;
|
|
1339
1346
|
try {
|
|
@@ -1472,7 +1479,7 @@ class User extends AbleObject_1.default {
|
|
|
1472
1479
|
};
|
|
1473
1480
|
}
|
|
1474
1481
|
async $save() {
|
|
1475
|
-
let tilmeld =
|
|
1482
|
+
let tilmeld = enforceTilmeld(this);
|
|
1476
1483
|
if (this.$data.username == null || !this.$data.username.trim().length) {
|
|
1477
1484
|
return false;
|
|
1478
1485
|
}
|
|
@@ -1480,7 +1487,7 @@ class User extends AbleObject_1.default {
|
|
|
1480
1487
|
tilmeld.gatekeeper('tilmeld/admin') &&
|
|
1481
1488
|
!tilmeld.gatekeeper('system/admin') &&
|
|
1482
1489
|
this.$data.abilities?.includes('system/admin')) {
|
|
1483
|
-
throw new
|
|
1490
|
+
throw new BadDataError("You don't have the authority to modify system admins.");
|
|
1484
1491
|
}
|
|
1485
1492
|
let sendVerification = false;
|
|
1486
1493
|
// Formatting.
|
|
@@ -1568,13 +1575,13 @@ class User extends AbleObject_1.default {
|
|
|
1568
1575
|
// Verification.
|
|
1569
1576
|
const unCheck = await this.$checkUsername();
|
|
1570
1577
|
if (!unCheck.result) {
|
|
1571
|
-
throw new
|
|
1578
|
+
throw new BadUsernameError(unCheck.message);
|
|
1572
1579
|
}
|
|
1573
1580
|
if (!tilmeld.config.emailUsernames &&
|
|
1574
1581
|
tilmeld.config.userFields.includes('email')) {
|
|
1575
1582
|
const emCheck = await this.$checkEmail();
|
|
1576
1583
|
if (!emCheck.result) {
|
|
1577
|
-
throw new
|
|
1584
|
+
throw new BadEmailError(emCheck.message);
|
|
1578
1585
|
}
|
|
1579
1586
|
}
|
|
1580
1587
|
// Email changes.
|
|
@@ -1588,7 +1595,7 @@ class User extends AbleObject_1.default {
|
|
|
1588
1595
|
// If this is the first user, they'll be made an admin, so they don't
|
|
1589
1596
|
// need to verify.
|
|
1590
1597
|
if (!this.$data.abilities?.includes('system/admin')) {
|
|
1591
|
-
this.$data.secret =
|
|
1598
|
+
this.$data.secret = nanoid();
|
|
1592
1599
|
sendVerification = true;
|
|
1593
1600
|
}
|
|
1594
1601
|
}
|
|
@@ -1598,9 +1605,9 @@ class User extends AbleObject_1.default {
|
|
|
1598
1605
|
if (tilmeld.config.emailRateLimit !== '' &&
|
|
1599
1606
|
this.$data.emailChangeDate != null &&
|
|
1600
1607
|
this.$data.emailChangeDate >=
|
|
1601
|
-
(
|
|
1602
|
-
throw new
|
|
1603
|
-
new Date((
|
|
1608
|
+
strtotime('-' + tilmeld.config.emailRateLimit) * 1000) {
|
|
1609
|
+
throw new EmailChangeRateLimitExceededError('You already changed your email address recently. Please wait until ' +
|
|
1610
|
+
new Date(strtotime('+' + tilmeld.config.emailRateLimit, Math.floor(this.$data.emailChangeDate / 1000)) * 1000).toString() +
|
|
1604
1611
|
' to change your email address again.');
|
|
1605
1612
|
}
|
|
1606
1613
|
else {
|
|
@@ -1610,10 +1617,10 @@ class User extends AbleObject_1.default {
|
|
|
1610
1617
|
// an email change.
|
|
1611
1618
|
(this.$data.emailChangeDate == null ||
|
|
1612
1619
|
this.$data.emailChangeDate <
|
|
1613
|
-
(
|
|
1620
|
+
strtotime('-' + tilmeld.config.emailRateLimit) * 1000)) {
|
|
1614
1621
|
// Save the old email in case the cancel change link is clicked.
|
|
1615
1622
|
this.$data.cancelEmailAddress = this.$originalEmail;
|
|
1616
|
-
this.$data.cancelEmailSecret =
|
|
1623
|
+
this.$data.cancelEmailSecret = nanoid();
|
|
1617
1624
|
this.$data.emailChangeDate = Date.now();
|
|
1618
1625
|
}
|
|
1619
1626
|
// Save the new address and reset to the old one (until they verify
|
|
@@ -1623,7 +1630,7 @@ class User extends AbleObject_1.default {
|
|
|
1623
1630
|
if (tilmeld.config.emailUsernames) {
|
|
1624
1631
|
this.$data.username = this.$data.email;
|
|
1625
1632
|
}
|
|
1626
|
-
this.$data.newEmailSecret =
|
|
1633
|
+
this.$data.newEmailSecret = nanoid();
|
|
1627
1634
|
sendVerification = true;
|
|
1628
1635
|
}
|
|
1629
1636
|
}
|
|
@@ -1635,11 +1642,11 @@ class User extends AbleObject_1.default {
|
|
|
1635
1642
|
// email change.
|
|
1636
1643
|
(this.$data.emailChangeDate == null ||
|
|
1637
1644
|
this.$data.emailChangeDate <
|
|
1638
|
-
(
|
|
1645
|
+
strtotime('-' + tilmeld.config.emailRateLimit) * 1000)) {
|
|
1639
1646
|
// The user doesn't need to verify their new email address, but should
|
|
1640
1647
|
// be able to cancel the email change from their old address.
|
|
1641
1648
|
this.$data.cancelEmailAddress = this.$originalEmail;
|
|
1642
|
-
this.$data.cancelEmailSecret =
|
|
1649
|
+
this.$data.cancelEmailSecret = nanoid();
|
|
1643
1650
|
sendVerification = true;
|
|
1644
1651
|
}
|
|
1645
1652
|
}
|
|
@@ -1648,14 +1655,14 @@ class User extends AbleObject_1.default {
|
|
|
1648
1655
|
}
|
|
1649
1656
|
delete this.$data.passwordTemp;
|
|
1650
1657
|
if (this.$data.password == null) {
|
|
1651
|
-
throw new
|
|
1658
|
+
throw new BadDataError('A password is required.');
|
|
1652
1659
|
}
|
|
1653
1660
|
// Start transaction.
|
|
1654
|
-
const transaction = 'tilmeld-save-' +
|
|
1661
|
+
const transaction = 'tilmeld-save-' + nanoid();
|
|
1655
1662
|
const nymph = this.$nymph;
|
|
1656
1663
|
const tnymph = await this.$nymph.startTransaction(transaction);
|
|
1657
1664
|
this.$setNymph(tnymph);
|
|
1658
|
-
tilmeld =
|
|
1665
|
+
tilmeld = enforceTilmeld(this);
|
|
1659
1666
|
// If the primary group is generated now, the user assigned to it won't have
|
|
1660
1667
|
// a guid yet, so it needs to be assigned after the user is saved.
|
|
1661
1668
|
let saveGeneratedPrimaryGroupAgain = false;
|
|
@@ -1746,7 +1753,7 @@ class User extends AbleObject_1.default {
|
|
|
1746
1753
|
tilmeld.config.validatorUser(tilmeld, this);
|
|
1747
1754
|
}
|
|
1748
1755
|
catch (e) {
|
|
1749
|
-
throw new
|
|
1756
|
+
throw new BadDataError(e?.message);
|
|
1750
1757
|
}
|
|
1751
1758
|
}
|
|
1752
1759
|
catch (e) {
|
|
@@ -1794,7 +1801,7 @@ class User extends AbleObject_1.default {
|
|
|
1794
1801
|
this.$descendantGroups = undefined;
|
|
1795
1802
|
this.$gatekeeperCache = undefined;
|
|
1796
1803
|
await tnymph.commit(transaction);
|
|
1797
|
-
const tilmeld =
|
|
1804
|
+
const tilmeld = enforceTilmeld(nymph);
|
|
1798
1805
|
if (tilmeld.User.current(true).$is(this)) {
|
|
1799
1806
|
// Update the user in the session cache.
|
|
1800
1807
|
await tilmeld.fillSession(this);
|
|
@@ -1821,14 +1828,14 @@ class User extends AbleObject_1.default {
|
|
|
1821
1828
|
return false;
|
|
1822
1829
|
}
|
|
1823
1830
|
async $delete() {
|
|
1824
|
-
const tilmeld =
|
|
1831
|
+
const tilmeld = enforceTilmeld(this);
|
|
1825
1832
|
if (!this.$skipAcWhenDeleting && !tilmeld.gatekeeper('tilmeld/admin')) {
|
|
1826
|
-
throw new
|
|
1833
|
+
throw new BadDataError("You don't have the authority to delete users.");
|
|
1827
1834
|
}
|
|
1828
1835
|
if (!this.$skipAcWhenDeleting &&
|
|
1829
1836
|
!tilmeld.gatekeeper('system/admin') &&
|
|
1830
1837
|
this.$data.abilities?.includes('system/admin')) {
|
|
1831
|
-
throw new
|
|
1838
|
+
throw new BadDataError("You don't have the authority to delete system admins.");
|
|
1832
1839
|
}
|
|
1833
1840
|
if (tilmeld.User.current(true).$is(this)) {
|
|
1834
1841
|
await this.$logout();
|
|
@@ -1872,5 +1879,4 @@ class User extends AbleObject_1.default {
|
|
|
1872
1879
|
return true;
|
|
1873
1880
|
}
|
|
1874
1881
|
}
|
|
1875
|
-
exports.default = User;
|
|
1876
1882
|
//# sourceMappingURL=User.js.map
|