@nymphjs/tilmeld 1.0.0-beta.131 → 1.0.0-beta.133
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 +12 -0
- package/dist/AbleObject.d.ts +6 -3
- package/dist/AbleObject.js +9 -9
- package/dist/AbleObject.js.map +1 -1
- package/dist/Group.d.ts +24 -23
- package/dist/Group.js +84 -92
- package/dist/Group.js.map +1 -1
- package/dist/Tilmeld.d.ts +9 -9
- package/dist/Tilmeld.js +16 -8
- package/dist/Tilmeld.js.map +1 -1
- package/dist/Tilmeld.types.d.ts +6 -6
- package/dist/User.d.ts +32 -31
- package/dist/User.js +280 -289
- package/dist/User.js.map +1 -1
- package/dist/conf/defaults.js +2 -2
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +8 -10
- package/dist/conf/index.js.map +1 -1
- package/dist/index.d.ts +2 -8
- package/dist/index.js +2 -8
- package/dist/index.js.map +1 -1
- package/dist/testArtifacts.d.ts +4 -2
- package/dist/testArtifacts.js.map +1 -1
- package/dist/tilmeldJoiProps.d.ts +1 -2
- package/dist/tilmeldJoiProps.js +3 -3
- package/dist/tilmeldJoiProps.js.map +1 -1
- package/package.json +5 -5
- package/src/AbleObject.ts +17 -11
- package/src/Group.ts +98 -122
- package/src/Tilmeld.ts +25 -20
- package/src/Tilmeld.types.ts +6 -6
- package/src/User.ts +303 -348
- package/src/conf/defaults.ts +2 -2
- package/src/conf/index.ts +8 -14
- package/src/index.ts +2 -8
- package/src/testArtifacts.ts +6 -4
- package/src/tilmeldJoiProps.ts +3 -3
package/dist/User.js
CHANGED
|
@@ -41,9 +41,18 @@ import { TOTP, Secret } from 'otpauth';
|
|
|
41
41
|
import { toDataURL } from 'qrcode';
|
|
42
42
|
import { splitn } from '@sciactive/splitn';
|
|
43
43
|
import { enforceTilmeld } from './enforceTilmeld.js';
|
|
44
|
-
import AbleObject from './AbleObject.js';
|
|
45
|
-
import GroupClass from './Group.js';
|
|
44
|
+
import { AbleObject } from './AbleObject.js';
|
|
45
|
+
import { Group as GroupClass } from './Group.js';
|
|
46
46
|
import { AccessControlError, BadDataError, BadEmailError, BadUsernameError, EmailChangeRateLimitExceededError, } from './errors/index.js';
|
|
47
|
+
/**
|
|
48
|
+
* A user data model.
|
|
49
|
+
*
|
|
50
|
+
* Written by Hunter Perrin for SciActive.
|
|
51
|
+
*
|
|
52
|
+
* @author Hunter Perrin <hperrin@port87.com>
|
|
53
|
+
* @copyright SciActive Inc
|
|
54
|
+
* @see http://nymph.io/
|
|
55
|
+
*/
|
|
47
56
|
let User = (() => {
|
|
48
57
|
let _classSuper = AbleObject;
|
|
49
58
|
let _instanceExtraInitializers = [];
|
|
@@ -369,20 +378,20 @@ let User = (() => {
|
|
|
369
378
|
constructor() {
|
|
370
379
|
super();
|
|
371
380
|
// Defaults.
|
|
372
|
-
this
|
|
373
|
-
this
|
|
374
|
-
this
|
|
375
|
-
this
|
|
381
|
+
this.enabled = true;
|
|
382
|
+
this.abilities = [];
|
|
383
|
+
this.groups = [];
|
|
384
|
+
this.inheritAbilities = true;
|
|
376
385
|
this.$updateDataProtection();
|
|
377
386
|
}
|
|
378
387
|
async $getUniques() {
|
|
379
388
|
const tilmeld = enforceTilmeld(this);
|
|
380
|
-
const uniques = [`u:${this
|
|
389
|
+
const uniques = [`u:${this.username}`];
|
|
381
390
|
if (!tilmeld.config.emailUsernames &&
|
|
382
391
|
tilmeld.config.userFields.includes('email')) {
|
|
383
|
-
uniques.push(`e:${this
|
|
384
|
-
if (this
|
|
385
|
-
uniques.push(`e:${this
|
|
392
|
+
uniques.push(`e:${this.email}`);
|
|
393
|
+
if (this.newEmailAddress != null) {
|
|
394
|
+
uniques.push(`e:${this.newEmailAddress}`);
|
|
386
395
|
}
|
|
387
396
|
}
|
|
388
397
|
return uniques;
|
|
@@ -397,13 +406,13 @@ let User = (() => {
|
|
|
397
406
|
await tilmeld.config.failedLoginAttempt(this, data);
|
|
398
407
|
return { result: false, message: 'Incorrect login/password.' };
|
|
399
408
|
}
|
|
400
|
-
if (!this
|
|
409
|
+
if (!this.enabled) {
|
|
401
410
|
return { result: false, message: 'Your account is disabled.' };
|
|
402
411
|
}
|
|
403
412
|
if (await this.$gatekeeper()) {
|
|
404
413
|
return { result: true, message: 'You are already logged in.' };
|
|
405
414
|
}
|
|
406
|
-
if (this
|
|
415
|
+
if (this.totpSecret != null) {
|
|
407
416
|
if (data.code == null ||
|
|
408
417
|
typeof data.code !== 'string' ||
|
|
409
418
|
data.code.length !== 6) {
|
|
@@ -429,7 +438,7 @@ let User = (() => {
|
|
|
429
438
|
catch (e) {
|
|
430
439
|
return { result: false, message: e.message };
|
|
431
440
|
}
|
|
432
|
-
this.$nymph.config.debugLog('tilmeld', `User login for "${this
|
|
441
|
+
this.$nymph.config.debugLog('tilmeld', `User login for "${this.username}".`);
|
|
433
442
|
// Authentication was successful, attempt to login.
|
|
434
443
|
if (!(await tilmeld.login(this, true))) {
|
|
435
444
|
return { result: false, message: 'Incorrect login/password.' };
|
|
@@ -437,7 +446,7 @@ let User = (() => {
|
|
|
437
446
|
for (let callback of this.constructor
|
|
438
447
|
.afterLoginCallbacks) {
|
|
439
448
|
if (callback) {
|
|
440
|
-
await callback(this
|
|
449
|
+
await callback(this);
|
|
441
450
|
}
|
|
442
451
|
}
|
|
443
452
|
// Login was successful.
|
|
@@ -456,7 +465,7 @@ let User = (() => {
|
|
|
456
465
|
if (isCurrentUser) {
|
|
457
466
|
return { result: false, message: "You can't switch to yourself." };
|
|
458
467
|
}
|
|
459
|
-
const toAbilities = this
|
|
468
|
+
const toAbilities = this.abilities || [];
|
|
460
469
|
const fromAbilities = user.abilities || [];
|
|
461
470
|
if (!fromAbilities.includes('tilmeld/switch') &&
|
|
462
471
|
!fromAbilities.includes('system/admin')) {
|
|
@@ -474,7 +483,7 @@ let User = (() => {
|
|
|
474
483
|
.beforeLoginCallbacks) {
|
|
475
484
|
if (callback) {
|
|
476
485
|
await callback(this, {
|
|
477
|
-
username: this
|
|
486
|
+
username: this.username || '',
|
|
478
487
|
password: '',
|
|
479
488
|
...(data || {}),
|
|
480
489
|
});
|
|
@@ -484,7 +493,7 @@ let User = (() => {
|
|
|
484
493
|
catch (e) {
|
|
485
494
|
return { result: false, message: e.message };
|
|
486
495
|
}
|
|
487
|
-
this.$nymph.config.debugLog('tilmeld', `User login switch for "${user.username}" to "${this
|
|
496
|
+
this.$nymph.config.debugLog('tilmeld', `User login switch for "${user.username}" to "${this.username}".`);
|
|
488
497
|
// Authentication was successful, attempt to login.
|
|
489
498
|
if (!(await tilmeld.loginSwitch(this, true))) {
|
|
490
499
|
return { result: false, message: 'Incorrect login/password.' };
|
|
@@ -492,7 +501,7 @@ let User = (() => {
|
|
|
492
501
|
for (let callback of this.constructor
|
|
493
502
|
.afterLoginCallbacks) {
|
|
494
503
|
if (callback) {
|
|
495
|
-
await callback(this
|
|
504
|
+
await callback(this);
|
|
496
505
|
}
|
|
497
506
|
}
|
|
498
507
|
// Login was successful.
|
|
@@ -508,47 +517,47 @@ let User = (() => {
|
|
|
508
517
|
for (let callback of this.constructor
|
|
509
518
|
.beforeLogoutCallbacks) {
|
|
510
519
|
if (callback) {
|
|
511
|
-
await callback(this
|
|
520
|
+
await callback(this);
|
|
512
521
|
}
|
|
513
522
|
}
|
|
514
523
|
}
|
|
515
524
|
catch (e) {
|
|
516
525
|
return { result: false, message: e.message };
|
|
517
526
|
}
|
|
518
|
-
this.$nymph.config.debugLog('tilmeld', `User logout for "${this
|
|
527
|
+
this.$nymph.config.debugLog('tilmeld', `User logout for "${this.username}".`);
|
|
519
528
|
await tilmeld.logout();
|
|
520
529
|
for (let callback of this.constructor
|
|
521
530
|
.afterLogoutCallbacks) {
|
|
522
531
|
if (callback) {
|
|
523
|
-
await callback(this
|
|
532
|
+
await callback(this);
|
|
524
533
|
}
|
|
525
534
|
}
|
|
526
535
|
return { result: true, message: 'You have been logged out.' };
|
|
527
536
|
}
|
|
528
537
|
$getAvatar() {
|
|
529
|
-
if (this
|
|
530
|
-
return this
|
|
538
|
+
if (this.avatar != null && this.avatar !== '') {
|
|
539
|
+
return this.avatar;
|
|
531
540
|
}
|
|
532
|
-
if (this
|
|
541
|
+
if (this.email == null || this.email === '') {
|
|
533
542
|
return 'https://secure.gravatar.com/avatar/?d=mm&s=40';
|
|
534
543
|
}
|
|
535
544
|
return ('https://secure.gravatar.com/avatar/' +
|
|
536
545
|
crypto
|
|
537
546
|
.createHash('sha256')
|
|
538
|
-
.update(this
|
|
547
|
+
.update(this.email.trim().toLowerCase())
|
|
539
548
|
.digest('hex')
|
|
540
549
|
.toLowerCase() +
|
|
541
550
|
'?d=identicon&s=40');
|
|
542
551
|
}
|
|
543
552
|
$getGid() {
|
|
544
|
-
if (this
|
|
545
|
-
return this
|
|
553
|
+
if (this.group) {
|
|
554
|
+
return this.group.guid;
|
|
546
555
|
}
|
|
547
556
|
return null;
|
|
548
557
|
}
|
|
549
558
|
$getGids() {
|
|
550
|
-
if (this
|
|
551
|
-
return this
|
|
559
|
+
if (this.groups) {
|
|
560
|
+
return this.groups
|
|
552
561
|
.map((group) => group.guid)
|
|
553
562
|
.filter((guid) => guid != null);
|
|
554
563
|
}
|
|
@@ -560,12 +569,12 @@ let User = (() => {
|
|
|
560
569
|
async $getDescendantGroups() {
|
|
561
570
|
if (this.$descendantGroups == null) {
|
|
562
571
|
this.$descendantGroups = [];
|
|
563
|
-
if (this
|
|
564
|
-
await this
|
|
565
|
-
this.$descendantGroups = await this
|
|
572
|
+
if (this.group != null) {
|
|
573
|
+
await this.group.$wake();
|
|
574
|
+
this.$descendantGroups = await this.group.$getDescendants();
|
|
566
575
|
}
|
|
567
|
-
await Promise.all(this
|
|
568
|
-
for (let curGroup of this
|
|
576
|
+
await Promise.all(this.groups?.map((e) => e.$wake()) || []);
|
|
577
|
+
for (let curGroup of this.groups ?? []) {
|
|
569
578
|
this.$descendantGroups = this.$descendantGroups?.concat(await curGroup.$getDescendants());
|
|
570
579
|
}
|
|
571
580
|
}
|
|
@@ -591,9 +600,8 @@ let User = (() => {
|
|
|
591
600
|
const isCurrentUser = user != null && user.$is(this);
|
|
592
601
|
// While loading, accessing abilities on the current user will infinitely
|
|
593
602
|
// loop, but if this is the current user, we can use its abilities.
|
|
594
|
-
const abilities = (isCurrentUser || user == null
|
|
595
|
-
|
|
596
|
-
: user?.abilities) ?? [];
|
|
603
|
+
const abilities = (isCurrentUser || user == null ? this.abilities : user?.abilities) ??
|
|
604
|
+
[];
|
|
597
605
|
const isNewUser = input.guid == null;
|
|
598
606
|
const isDomainUser = !isCurrentUser &&
|
|
599
607
|
user != null &&
|
|
@@ -622,24 +630,24 @@ let User = (() => {
|
|
|
622
630
|
throw new BadDataError('Abilities must be an array of strings.');
|
|
623
631
|
}
|
|
624
632
|
if (incoming.includes('system/admin') !==
|
|
625
|
-
this
|
|
633
|
+
this.abilities?.includes('system/admin') &&
|
|
626
634
|
(!tilmeld.currentUser ||
|
|
627
635
|
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
628
636
|
throw new BadDataError("You don't have the authority to grant or revoke system/admin.");
|
|
629
637
|
}
|
|
630
638
|
if (incoming.includes('tilmeld/admin') !==
|
|
631
|
-
this
|
|
639
|
+
this.abilities?.includes('tilmeld/admin') &&
|
|
632
640
|
(!tilmeld.currentUser ||
|
|
633
641
|
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
634
642
|
throw new BadDataError("You don't have the authority to grant or revoke tilmeld/admin.");
|
|
635
643
|
}
|
|
636
644
|
if (incoming.includes('tilmeld/switch') !==
|
|
637
|
-
this
|
|
645
|
+
this.abilities?.includes('tilmeld/switch') &&
|
|
638
646
|
(!tilmeld.currentUser ||
|
|
639
647
|
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
640
648
|
throw new BadDataError("You don't have the authority to grant or revoke tilmeld/switch.");
|
|
641
649
|
}
|
|
642
|
-
if (xor(incoming.filter((ability) => ability.startsWith('tilmeld/')), this
|
|
650
|
+
if (xor(incoming.filter((ability) => ability.startsWith('tilmeld/')), this.abilities?.filter((ability) => ability.startsWith('tilmeld/')) ?? []).length &&
|
|
643
651
|
(!tilmeld.currentUser ||
|
|
644
652
|
(!tilmeld.currentUser.abilities?.includes('system/admin') &&
|
|
645
653
|
!tilmeld.currentUser.abilities?.includes('tilmeld/admin')))) {
|
|
@@ -650,14 +658,14 @@ let User = (() => {
|
|
|
650
658
|
super.$putData(data, sdata, source);
|
|
651
659
|
// TODO: Fix these being added by the drivers.
|
|
652
660
|
// @ts-ignore
|
|
653
|
-
delete this
|
|
661
|
+
delete this.acRead;
|
|
654
662
|
// @ts-ignore
|
|
655
|
-
delete this
|
|
663
|
+
delete this.acWrite;
|
|
656
664
|
// @ts-ignore
|
|
657
|
-
delete this
|
|
665
|
+
delete this.acFull;
|
|
658
666
|
if (source === 'server') {
|
|
659
|
-
this.$originalEmail = this
|
|
660
|
-
this.$originalUsername = this
|
|
667
|
+
this.$originalEmail = this.email;
|
|
668
|
+
this.$originalUsername = this.username;
|
|
661
669
|
}
|
|
662
670
|
this.$updateDataProtection();
|
|
663
671
|
}
|
|
@@ -670,8 +678,8 @@ let User = (() => {
|
|
|
670
678
|
const tilmeld = enforceTilmeld(this);
|
|
671
679
|
let user = givenUser ?? tilmeld.User.current();
|
|
672
680
|
let [_username, domain] = tilmeld.config.domainSupport
|
|
673
|
-
? splitn(this.$originalUsername ?? this
|
|
674
|
-
: [this.$originalUsername ?? this
|
|
681
|
+
? splitn(this.$originalUsername ?? this.username ?? '', '@', 2)
|
|
682
|
+
: [this.$originalUsername ?? this.username, null];
|
|
675
683
|
// Lowercase the domain part.
|
|
676
684
|
if (domain) {
|
|
677
685
|
domain = domain.toLowerCase();
|
|
@@ -685,9 +693,7 @@ let User = (() => {
|
|
|
685
693
|
const isCurrentUser = user != null && user.$is(this);
|
|
686
694
|
// While loading, accessing abilities on the current user will infinitely
|
|
687
695
|
// loop, but if this is the current user, we can use its abilities.
|
|
688
|
-
const abilities = (isCurrentUser || user == null
|
|
689
|
-
? this.$data.abilities
|
|
690
|
-
: user?.abilities) ?? [];
|
|
696
|
+
const abilities = (isCurrentUser || user == null ? this.abilities : user?.abilities) ?? [];
|
|
691
697
|
const isNewUser = this.guid == null;
|
|
692
698
|
const isDomainUser = tilmeld.config.domainSupport &&
|
|
693
699
|
!isCurrentUser &&
|
|
@@ -831,10 +837,10 @@ let User = (() => {
|
|
|
831
837
|
}
|
|
832
838
|
}
|
|
833
839
|
$updateGroupDataProtection() {
|
|
834
|
-
if (this
|
|
835
|
-
this
|
|
840
|
+
if (this.group != null && !this.group.$asleep()) {
|
|
841
|
+
this.group.$updateDataProtection();
|
|
836
842
|
}
|
|
837
|
-
for (let group of this
|
|
843
|
+
for (let group of this.groups ?? []) {
|
|
838
844
|
if (!group.$asleep()) {
|
|
839
845
|
group.$updateDataProtection();
|
|
840
846
|
}
|
|
@@ -865,23 +871,23 @@ let User = (() => {
|
|
|
865
871
|
this.$gatekeeperCache = await this.$getGatekeeperCache();
|
|
866
872
|
}
|
|
867
873
|
return ((ability in this.$gatekeeperCache && !!this.$gatekeeperCache[ability]) ||
|
|
868
|
-
!!this
|
|
874
|
+
!!this.abilities?.includes('system/admin'));
|
|
869
875
|
}
|
|
870
876
|
/**
|
|
871
877
|
* Build a gatekeeper cache object.
|
|
872
878
|
*/
|
|
873
879
|
async $getGatekeeperCache() {
|
|
874
880
|
await this.$wake();
|
|
875
|
-
let abilities = this
|
|
876
|
-
if (this
|
|
877
|
-
await Promise.all(this
|
|
878
|
-
for (let curGroup of this
|
|
881
|
+
let abilities = this.abilities ?? [];
|
|
882
|
+
if (this.inheritAbilities) {
|
|
883
|
+
await Promise.all(this.groups?.map((e) => e.$wake()) || []);
|
|
884
|
+
for (let curGroup of this.groups ?? []) {
|
|
879
885
|
if (curGroup.enabled) {
|
|
880
886
|
abilities = abilities.concat(curGroup.abilities ?? []);
|
|
881
887
|
}
|
|
882
888
|
}
|
|
883
|
-
await this
|
|
884
|
-
const group = this
|
|
889
|
+
await this.group?.$wake();
|
|
890
|
+
const group = this.group;
|
|
885
891
|
if (group != null && group.cdate != null && group.enabled) {
|
|
886
892
|
abilities = abilities.concat(group.abilities ?? []);
|
|
887
893
|
}
|
|
@@ -906,56 +912,56 @@ let User = (() => {
|
|
|
906
912
|
}
|
|
907
913
|
let success = true;
|
|
908
914
|
const verifyUrl = `${tilmeld.config.appUrl.replace(/\/$/, () => '')}${tilmeld.config.setupPath}/verify`;
|
|
909
|
-
if (this
|
|
910
|
-
const link = `${verifyUrl}?action=verify&id=${encodeURIComponent(this.guid)}&secret=${encodeURIComponent(this
|
|
915
|
+
if (this.secret != null) {
|
|
916
|
+
const link = `${verifyUrl}?action=verify&id=${encodeURIComponent(this.guid)}&secret=${encodeURIComponent(this.secret)}`;
|
|
911
917
|
success =
|
|
912
918
|
success &&
|
|
913
919
|
(await tilmeld.config.sendEmail(tilmeld, {
|
|
914
920
|
template: 'VerifyEmail',
|
|
915
921
|
message: {
|
|
916
922
|
to: {
|
|
917
|
-
name: this
|
|
918
|
-
address: this
|
|
923
|
+
name: this.name ?? '',
|
|
924
|
+
address: this.email ?? '',
|
|
919
925
|
},
|
|
920
926
|
},
|
|
921
927
|
locals: { verifyLink: link },
|
|
922
928
|
}, this));
|
|
923
929
|
}
|
|
924
|
-
if (this
|
|
925
|
-
const link = `${verifyUrl}?action=verifychange&id=${encodeURIComponent(this.guid)}&secret=${encodeURIComponent(this
|
|
930
|
+
if (this.newEmailSecret != null) {
|
|
931
|
+
const link = `${verifyUrl}?action=verifychange&id=${encodeURIComponent(this.guid)}&secret=${encodeURIComponent(this.newEmailSecret)}`;
|
|
926
932
|
success =
|
|
927
933
|
success &&
|
|
928
934
|
(await tilmeld.config.sendEmail(tilmeld, {
|
|
929
935
|
template: 'VerifyEmailChange',
|
|
930
936
|
message: {
|
|
931
937
|
to: {
|
|
932
|
-
name: this
|
|
933
|
-
address: this
|
|
938
|
+
name: this.name ?? '',
|
|
939
|
+
address: this.newEmailAddress ?? '',
|
|
934
940
|
},
|
|
935
941
|
},
|
|
936
942
|
locals: {
|
|
937
943
|
verifyLink: link,
|
|
938
|
-
oldEmail: this
|
|
939
|
-
newEmail: this
|
|
944
|
+
oldEmail: this.cancelEmailAddress,
|
|
945
|
+
newEmail: this.newEmailAddress,
|
|
940
946
|
},
|
|
941
947
|
}, this));
|
|
942
948
|
}
|
|
943
|
-
if (this
|
|
944
|
-
const link = `${verifyUrl}?action=cancelchange&id=${encodeURIComponent(this.guid)}&secret=${encodeURIComponent(this
|
|
949
|
+
if (this.cancelEmailSecret != null) {
|
|
950
|
+
const link = `${verifyUrl}?action=cancelchange&id=${encodeURIComponent(this.guid)}&secret=${encodeURIComponent(this.cancelEmailSecret)}`;
|
|
945
951
|
success =
|
|
946
952
|
success &&
|
|
947
953
|
(await tilmeld.config.sendEmail(tilmeld, {
|
|
948
954
|
template: 'CancelEmailChange',
|
|
949
955
|
message: {
|
|
950
956
|
to: {
|
|
951
|
-
name: this
|
|
952
|
-
address: this
|
|
957
|
+
name: this.name ?? '',
|
|
958
|
+
address: this.cancelEmailAddress ?? '',
|
|
953
959
|
},
|
|
954
960
|
},
|
|
955
961
|
locals: {
|
|
956
962
|
cancelLink: link,
|
|
957
|
-
oldEmail: this
|
|
958
|
-
newEmail: this
|
|
963
|
+
oldEmail: this.cancelEmailAddress,
|
|
964
|
+
newEmail: this.newEmailAddress ?? this.email,
|
|
959
965
|
},
|
|
960
966
|
}, this));
|
|
961
967
|
}
|
|
@@ -968,14 +974,14 @@ let User = (() => {
|
|
|
968
974
|
* @returns True if the user is already in the group. The resulting array of groups if the user was not.
|
|
969
975
|
*/
|
|
970
976
|
async $addGroup(group) {
|
|
971
|
-
await Promise.all(this
|
|
972
|
-
let groups = this
|
|
977
|
+
await Promise.all(this.groups?.map((e) => e.$wake()) || []);
|
|
978
|
+
let groups = this.groups;
|
|
973
979
|
if (groups == null) {
|
|
974
980
|
groups = [];
|
|
975
981
|
}
|
|
976
982
|
if (!group.$inArray(groups)) {
|
|
977
983
|
groups.push(group);
|
|
978
|
-
this
|
|
984
|
+
this.groups = groups;
|
|
979
985
|
return groups;
|
|
980
986
|
}
|
|
981
987
|
return true;
|
|
@@ -990,16 +996,16 @@ let User = (() => {
|
|
|
990
996
|
const tilmeld = enforceTilmeld(this);
|
|
991
997
|
switch (tilmeld.config.pwMethod) {
|
|
992
998
|
case 'plain':
|
|
993
|
-
return this
|
|
999
|
+
return this.password === password;
|
|
994
1000
|
case 'digest':
|
|
995
|
-
return (this
|
|
1001
|
+
return (this.password ===
|
|
996
1002
|
crypto.createHash('sha256').update(password).digest('base64'));
|
|
997
1003
|
case 'salt':
|
|
998
1004
|
default:
|
|
999
|
-
return (this
|
|
1005
|
+
return (this.password ===
|
|
1000
1006
|
crypto
|
|
1001
1007
|
.createHash('sha256')
|
|
1002
|
-
.update(password + this
|
|
1008
|
+
.update(password + this.salt)
|
|
1003
1009
|
.digest('base64'));
|
|
1004
1010
|
}
|
|
1005
1011
|
}
|
|
@@ -1010,17 +1016,17 @@ let User = (() => {
|
|
|
1010
1016
|
* @returns True if the user wasn't in the group. The resulting array of groups if the user was.
|
|
1011
1017
|
*/
|
|
1012
1018
|
async $delGroup(group) {
|
|
1013
|
-
await Promise.all(this
|
|
1014
|
-
let groups = this
|
|
1015
|
-
if (this
|
|
1019
|
+
await Promise.all(this.groups?.map((e) => e.$wake()) || []);
|
|
1020
|
+
let groups = this.groups || [];
|
|
1021
|
+
if (this.groups != null && group.$inArray(groups)) {
|
|
1016
1022
|
const newGroups = [];
|
|
1017
1023
|
for (let curGroup of groups) {
|
|
1018
1024
|
if (!group.$is(curGroup)) {
|
|
1019
1025
|
newGroups.push(curGroup);
|
|
1020
1026
|
}
|
|
1021
1027
|
}
|
|
1022
|
-
this
|
|
1023
|
-
return this
|
|
1028
|
+
this.groups = newGroups;
|
|
1029
|
+
return this.groups;
|
|
1024
1030
|
}
|
|
1025
1031
|
return true;
|
|
1026
1032
|
}
|
|
@@ -1032,17 +1038,17 @@ let User = (() => {
|
|
|
1032
1038
|
*/
|
|
1033
1039
|
async $inGroup(group) {
|
|
1034
1040
|
if (typeof group === 'string') {
|
|
1035
|
-
if (this
|
|
1041
|
+
if (this.group?.guid === group) {
|
|
1036
1042
|
return true;
|
|
1037
1043
|
}
|
|
1038
|
-
for (let curGroup of this
|
|
1044
|
+
for (let curGroup of this.groups ?? []) {
|
|
1039
1045
|
if (curGroup.guid === group) {
|
|
1040
1046
|
return true;
|
|
1041
1047
|
}
|
|
1042
1048
|
}
|
|
1043
1049
|
return false;
|
|
1044
1050
|
}
|
|
1045
|
-
return
|
|
1051
|
+
return group.$is(this.group) || group.$inArray(this.groups ?? []);
|
|
1046
1052
|
}
|
|
1047
1053
|
/**
|
|
1048
1054
|
* Check whether the user is a descendant of a group.
|
|
@@ -1052,14 +1058,14 @@ let User = (() => {
|
|
|
1052
1058
|
*/
|
|
1053
1059
|
async $isDescendant(group) {
|
|
1054
1060
|
// Check to see if the user is in a descendant group of the given group.
|
|
1055
|
-
await this
|
|
1056
|
-
if (this
|
|
1057
|
-
this
|
|
1058
|
-
(await this
|
|
1061
|
+
await this.group?.$wake();
|
|
1062
|
+
if (this.group != null &&
|
|
1063
|
+
this.group?.cdate != null &&
|
|
1064
|
+
(await this.group?.$isDescendant(group))) {
|
|
1059
1065
|
return true;
|
|
1060
1066
|
}
|
|
1061
|
-
await Promise.all(this
|
|
1062
|
-
for (let curGroup of this
|
|
1067
|
+
await Promise.all(this.groups?.map((e) => e.$wake()) || []);
|
|
1068
|
+
for (let curGroup of this.groups ?? []) {
|
|
1063
1069
|
if (await curGroup.$isDescendant(group)) {
|
|
1064
1070
|
return true;
|
|
1065
1071
|
}
|
|
@@ -1073,17 +1079,17 @@ let User = (() => {
|
|
|
1073
1079
|
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
1074
1080
|
*/
|
|
1075
1081
|
async $changePassword(data) {
|
|
1076
|
-
this.$nymph.config.debugLog('tilmeld', `Password change request for "${this
|
|
1082
|
+
this.$nymph.config.debugLog('tilmeld', `Password change request for "${this.username}".`);
|
|
1077
1083
|
if (!('newPassword' in data) || !data.newPassword.length) {
|
|
1078
1084
|
return { result: false, message: 'Please specify a password.' };
|
|
1079
1085
|
}
|
|
1080
1086
|
if (!this.$checkPassword(data.currentPassword ?? '')) {
|
|
1081
|
-
this.$nymph.config.debugLog('tilmeld', `Incorrect password for change request for "${this
|
|
1087
|
+
this.$nymph.config.debugLog('tilmeld', `Incorrect password for change request for "${this.username}".`);
|
|
1082
1088
|
return { result: false, message: 'Incorrect password.' };
|
|
1083
1089
|
}
|
|
1084
|
-
this
|
|
1090
|
+
this.passwordTemp = data.newPassword;
|
|
1085
1091
|
if (data.revokeCurrentTokens) {
|
|
1086
|
-
this
|
|
1092
|
+
this.revokeTokenDate = Date.now();
|
|
1087
1093
|
}
|
|
1088
1094
|
try {
|
|
1089
1095
|
await this.$save();
|
|
@@ -1107,26 +1113,26 @@ let User = (() => {
|
|
|
1107
1113
|
const tilmeld = enforceTilmeld(this);
|
|
1108
1114
|
switch (tilmeld.config.pwMethod) {
|
|
1109
1115
|
case 'plain':
|
|
1110
|
-
delete this
|
|
1111
|
-
this
|
|
1116
|
+
delete this.salt;
|
|
1117
|
+
this.password = password;
|
|
1112
1118
|
break;
|
|
1113
1119
|
case 'digest':
|
|
1114
|
-
delete this
|
|
1115
|
-
this
|
|
1120
|
+
delete this.salt;
|
|
1121
|
+
this.password = crypto
|
|
1116
1122
|
.createHash('sha256')
|
|
1117
1123
|
.update(password)
|
|
1118
1124
|
.digest('base64');
|
|
1119
1125
|
break;
|
|
1120
1126
|
case 'salt':
|
|
1121
1127
|
default:
|
|
1122
|
-
this
|
|
1123
|
-
this
|
|
1128
|
+
this.salt = nanoid();
|
|
1129
|
+
this.password = crypto
|
|
1124
1130
|
.createHash('sha256')
|
|
1125
|
-
.update(password + this
|
|
1131
|
+
.update(password + this.salt)
|
|
1126
1132
|
.digest('base64');
|
|
1127
1133
|
break;
|
|
1128
1134
|
}
|
|
1129
|
-
return this
|
|
1135
|
+
return this.password;
|
|
1130
1136
|
}
|
|
1131
1137
|
/**
|
|
1132
1138
|
* A frontend accessible method to revoke all currently issued tokens.
|
|
@@ -1138,7 +1144,7 @@ let User = (() => {
|
|
|
1138
1144
|
if (!this.$checkPassword(data.password ?? '')) {
|
|
1139
1145
|
return { result: false, message: 'Incorrect password.' };
|
|
1140
1146
|
}
|
|
1141
|
-
this
|
|
1147
|
+
this.revokeTokenDate = Date.now();
|
|
1142
1148
|
try {
|
|
1143
1149
|
await this.$save();
|
|
1144
1150
|
const tilmeld = enforceTilmeld(this);
|
|
@@ -1153,7 +1159,7 @@ let User = (() => {
|
|
|
1153
1159
|
}
|
|
1154
1160
|
}
|
|
1155
1161
|
$hasTOTPSecret() {
|
|
1156
|
-
return this
|
|
1162
|
+
return this.totpSecret != null;
|
|
1157
1163
|
}
|
|
1158
1164
|
/**
|
|
1159
1165
|
* Check the given code against the user's TOTP secret.
|
|
@@ -1162,17 +1168,17 @@ let User = (() => {
|
|
|
1162
1168
|
* @returns True if the code is valid, otherwise false.
|
|
1163
1169
|
*/
|
|
1164
1170
|
$checkTOTPCode(code) {
|
|
1165
|
-
if (this
|
|
1171
|
+
if (this.totpSecret == null) {
|
|
1166
1172
|
return false;
|
|
1167
1173
|
}
|
|
1168
1174
|
const tilmeld = enforceTilmeld(this);
|
|
1169
1175
|
const totp = new TOTP({
|
|
1170
1176
|
issuer: tilmeld.config.appName,
|
|
1171
|
-
label: this
|
|
1177
|
+
label: this.username,
|
|
1172
1178
|
algorithm: 'SHA1',
|
|
1173
1179
|
digits: 6,
|
|
1174
1180
|
period: 30,
|
|
1175
|
-
secret: this
|
|
1181
|
+
secret: this.totpSecret,
|
|
1176
1182
|
});
|
|
1177
1183
|
return totp.validate({ token: code, window: 2 }) != null;
|
|
1178
1184
|
}
|
|
@@ -1183,13 +1189,13 @@ let User = (() => {
|
|
|
1183
1189
|
*/
|
|
1184
1190
|
async $getNewTOTPSecret() {
|
|
1185
1191
|
const tilmeld = enforceTilmeld(this);
|
|
1186
|
-
if (this
|
|
1192
|
+
if (this.totpSecret != null) {
|
|
1187
1193
|
throw new BadDataError('You already have a 2FA secret.');
|
|
1188
1194
|
}
|
|
1189
1195
|
const secret = new Secret();
|
|
1190
1196
|
const totp = new TOTP({
|
|
1191
1197
|
issuer: tilmeld.config.appName,
|
|
1192
|
-
label: this
|
|
1198
|
+
label: this.username,
|
|
1193
1199
|
algorithm: 'SHA1',
|
|
1194
1200
|
digits: 6,
|
|
1195
1201
|
period: 30,
|
|
@@ -1209,7 +1215,7 @@ let User = (() => {
|
|
|
1209
1215
|
if (!this.$checkPassword(data.password ?? '')) {
|
|
1210
1216
|
return { result: false, message: 'Incorrect password.' };
|
|
1211
1217
|
}
|
|
1212
|
-
if (this
|
|
1218
|
+
if (this.totpSecret != null) {
|
|
1213
1219
|
return { result: false, message: 'You already have a 2FA secret.' };
|
|
1214
1220
|
}
|
|
1215
1221
|
if (data.secret == null ||
|
|
@@ -1225,7 +1231,7 @@ let User = (() => {
|
|
|
1225
1231
|
const tilmeld = enforceTilmeld(this);
|
|
1226
1232
|
const totp = new TOTP({
|
|
1227
1233
|
issuer: tilmeld.config.appName,
|
|
1228
|
-
label: this
|
|
1234
|
+
label: this.username,
|
|
1229
1235
|
algorithm: 'SHA1',
|
|
1230
1236
|
digits: 6,
|
|
1231
1237
|
period: 30,
|
|
@@ -1234,7 +1240,7 @@ let User = (() => {
|
|
|
1234
1240
|
if (totp.validate({ token: data.code, window: 2 }) == null) {
|
|
1235
1241
|
return { result: false, message: '2FA code is incorrect.' };
|
|
1236
1242
|
}
|
|
1237
|
-
this
|
|
1243
|
+
this.totpSecret = data.secret;
|
|
1238
1244
|
try {
|
|
1239
1245
|
await this.$save();
|
|
1240
1246
|
return { result: true, message: 'Your two factor secret is now set.' };
|
|
@@ -1261,16 +1267,16 @@ let User = (() => {
|
|
|
1261
1267
|
data.code.length !== 6) {
|
|
1262
1268
|
return { result: false, message: '2FA code is invalid.' };
|
|
1263
1269
|
}
|
|
1264
|
-
if (this
|
|
1270
|
+
if (this.totpSecret == null) {
|
|
1265
1271
|
return { result: false, message: "You don't have a 2FA secret." };
|
|
1266
1272
|
}
|
|
1267
1273
|
const totp = new TOTP({
|
|
1268
1274
|
issuer: tilmeld.config.appName,
|
|
1269
|
-
label: this
|
|
1275
|
+
label: this.username,
|
|
1270
1276
|
algorithm: 'SHA1',
|
|
1271
1277
|
digits: 6,
|
|
1272
1278
|
period: 30,
|
|
1273
|
-
secret: this
|
|
1279
|
+
secret: this.totpSecret,
|
|
1274
1280
|
});
|
|
1275
1281
|
if (totp.validate({ token: data.code, window: 2 }) == null) {
|
|
1276
1282
|
return { result: false, message: '2FA code is incorrect.' };
|
|
@@ -1282,7 +1288,7 @@ let User = (() => {
|
|
|
1282
1288
|
message: 'You must provide your password and 2FA code.',
|
|
1283
1289
|
};
|
|
1284
1290
|
}
|
|
1285
|
-
delete this
|
|
1291
|
+
delete this.totpSecret;
|
|
1286
1292
|
try {
|
|
1287
1293
|
await this.$save();
|
|
1288
1294
|
return { result: true, message: 'Two factor secret has been removed.' };
|
|
@@ -1298,18 +1304,18 @@ let User = (() => {
|
|
|
1298
1304
|
*/
|
|
1299
1305
|
async $checkUsername() {
|
|
1300
1306
|
const tilmeld = enforceTilmeld(this);
|
|
1301
|
-
this.$nymph.config.debugInfo('tilmeld', `Username check "${this
|
|
1307
|
+
this.$nymph.config.debugInfo('tilmeld', `Username check "${this.username}".`);
|
|
1302
1308
|
if (!tilmeld.config.emailUsernames) {
|
|
1303
1309
|
let [username, domain] = tilmeld.config.domainSupport
|
|
1304
|
-
? splitn(this
|
|
1305
|
-
: [this
|
|
1310
|
+
? splitn(this.username ?? '', '@', 2)
|
|
1311
|
+
: [this.username, null];
|
|
1306
1312
|
// Lowercase the domain part.
|
|
1307
1313
|
if (domain) {
|
|
1308
1314
|
domain = domain.toLowerCase();
|
|
1309
|
-
this
|
|
1315
|
+
this.username = `${username}@${domain}`;
|
|
1310
1316
|
}
|
|
1311
|
-
if (this
|
|
1312
|
-
!this
|
|
1317
|
+
if (this.username == null ||
|
|
1318
|
+
!this.username.length ||
|
|
1313
1319
|
username == null ||
|
|
1314
1320
|
!username.length) {
|
|
1315
1321
|
return { result: false, message: 'Please specify a username.' };
|
|
@@ -1357,7 +1363,7 @@ let User = (() => {
|
|
|
1357
1363
|
type: '&',
|
|
1358
1364
|
ilike: [
|
|
1359
1365
|
'username',
|
|
1360
|
-
this
|
|
1366
|
+
this.username.replace(/([\\%_])/g, (s) => `\\${s}`),
|
|
1361
1367
|
],
|
|
1362
1368
|
};
|
|
1363
1369
|
if (this.guid != null) {
|
|
@@ -1371,7 +1377,7 @@ let User = (() => {
|
|
|
1371
1377
|
.checkUsernameCallbacks) {
|
|
1372
1378
|
if (callback) {
|
|
1373
1379
|
const result = await callback(this, {
|
|
1374
|
-
username: this
|
|
1380
|
+
username: this.username,
|
|
1375
1381
|
});
|
|
1376
1382
|
if (!result.result) {
|
|
1377
1383
|
return { message: 'Username is not available.', ...result };
|
|
@@ -1384,11 +1390,11 @@ let User = (() => {
|
|
|
1384
1390
|
};
|
|
1385
1391
|
}
|
|
1386
1392
|
else {
|
|
1387
|
-
this
|
|
1388
|
-
if (this
|
|
1393
|
+
this.email = this.username;
|
|
1394
|
+
if (this.username == null || !this.username.length) {
|
|
1389
1395
|
return { result: false, message: 'Please specify an email.' };
|
|
1390
1396
|
}
|
|
1391
|
-
if (this
|
|
1397
|
+
if (this.username.length < tilmeld.config.minUsernameLength) {
|
|
1392
1398
|
return {
|
|
1393
1399
|
result: false,
|
|
1394
1400
|
message: 'Emails must be at least ' +
|
|
@@ -1396,7 +1402,7 @@ let User = (() => {
|
|
|
1396
1402
|
' characters.',
|
|
1397
1403
|
};
|
|
1398
1404
|
}
|
|
1399
|
-
if (this
|
|
1405
|
+
if (this.username.length > tilmeld.config.maxUsernameLength) {
|
|
1400
1406
|
return {
|
|
1401
1407
|
result: false,
|
|
1402
1408
|
message: 'Emails must not exceed ' +
|
|
@@ -1414,8 +1420,8 @@ let User = (() => {
|
|
|
1414
1420
|
*/
|
|
1415
1421
|
async $checkEmail() {
|
|
1416
1422
|
const tilmeld = enforceTilmeld(this);
|
|
1417
|
-
this.$nymph.config.debugInfo('tilmeld', `Email check "${this
|
|
1418
|
-
if (this
|
|
1423
|
+
this.$nymph.config.debugInfo('tilmeld', `Email check "${this.email}".`);
|
|
1424
|
+
if (this.email == null || !this.email.length) {
|
|
1419
1425
|
if (tilmeld.config.verifyEmail) {
|
|
1420
1426
|
return { result: false, message: 'Please specify an email.' };
|
|
1421
1427
|
}
|
|
@@ -1423,12 +1429,12 @@ let User = (() => {
|
|
|
1423
1429
|
return { result: true, message: '' };
|
|
1424
1430
|
}
|
|
1425
1431
|
}
|
|
1426
|
-
if (!tilmeld.config.validEmailRegex.test(this
|
|
1432
|
+
if (!tilmeld.config.validEmailRegex.test(this.email)) {
|
|
1427
1433
|
return { result: false, message: tilmeld.config.validEmailRegexNotice };
|
|
1428
1434
|
}
|
|
1429
1435
|
const selector = {
|
|
1430
1436
|
type: '&',
|
|
1431
|
-
ilike: ['email', this
|
|
1437
|
+
ilike: ['email', this.email.replace(/([\\%_])/g, (s) => `\\${s}`)],
|
|
1432
1438
|
};
|
|
1433
1439
|
if (this.guid != null) {
|
|
1434
1440
|
selector['!guid'] = this.guid;
|
|
@@ -1452,11 +1458,11 @@ let User = (() => {
|
|
|
1452
1458
|
*/
|
|
1453
1459
|
async $checkPhone() {
|
|
1454
1460
|
const tilmeld = enforceTilmeld(this);
|
|
1455
|
-
this.$nymph.config.debugInfo('tilmeld', `Phone check "${this
|
|
1456
|
-
if (this
|
|
1461
|
+
this.$nymph.config.debugInfo('tilmeld', `Phone check "${this.phone}".`);
|
|
1462
|
+
if (this.phone == null || !this.phone.length) {
|
|
1457
1463
|
return { result: false, message: 'Please specify a phone number.' };
|
|
1458
1464
|
}
|
|
1459
|
-
const stripToDigits = this
|
|
1465
|
+
const stripToDigits = this.phone.replace(/\D/g, () => '');
|
|
1460
1466
|
if (!/\d{10}/.test(stripToDigits)) {
|
|
1461
1467
|
return {
|
|
1462
1468
|
result: false,
|
|
@@ -1508,16 +1514,16 @@ let User = (() => {
|
|
|
1508
1514
|
}
|
|
1509
1515
|
this.$password(data.password);
|
|
1510
1516
|
if (tilmeld.config.regFields.includes('name')) {
|
|
1511
|
-
this
|
|
1512
|
-
this
|
|
1513
|
-
(this
|
|
1514
|
-
(this
|
|
1515
|
-
if (this
|
|
1516
|
-
this
|
|
1517
|
+
this.name =
|
|
1518
|
+
this.nameFirst +
|
|
1519
|
+
(this.nameMiddle == null ? '' : ' ' + this.nameMiddle) +
|
|
1520
|
+
(this.nameLast == null ? '' : ' ' + this.nameLast);
|
|
1521
|
+
if (this.name === '') {
|
|
1522
|
+
this.name = this.username;
|
|
1517
1523
|
}
|
|
1518
1524
|
}
|
|
1519
1525
|
if (tilmeld.config.emailUsernames) {
|
|
1520
|
-
this
|
|
1526
|
+
this.email = this.username;
|
|
1521
1527
|
}
|
|
1522
1528
|
try {
|
|
1523
1529
|
const result = await transaction(this.$nymph, 'tilmeld-register-' + nanoid(), async (nymph) => {
|
|
@@ -1535,11 +1541,11 @@ let User = (() => {
|
|
|
1535
1541
|
tilmeld.config.verifyEmail) {
|
|
1536
1542
|
// The user will be enabled after verifying their e-mail address.
|
|
1537
1543
|
if (!tilmeld.config.unverifiedAccess) {
|
|
1538
|
-
this
|
|
1544
|
+
this.enabled = false;
|
|
1539
1545
|
}
|
|
1540
1546
|
}
|
|
1541
1547
|
else {
|
|
1542
|
-
this
|
|
1548
|
+
this.enabled = true;
|
|
1543
1549
|
}
|
|
1544
1550
|
// If createAdmin is true and there are no other users, grant
|
|
1545
1551
|
// "system/admin".
|
|
@@ -1557,11 +1563,11 @@ let User = (() => {
|
|
|
1557
1563
|
}
|
|
1558
1564
|
if (!otherUsers.length) {
|
|
1559
1565
|
this.$grant('system/admin');
|
|
1560
|
-
this
|
|
1566
|
+
this.enabled = true;
|
|
1561
1567
|
madeAdmin = true;
|
|
1562
1568
|
}
|
|
1563
1569
|
}
|
|
1564
|
-
this.$nymph.config.debugLog('tilmeld', `Registering new user "${this
|
|
1570
|
+
this.$nymph.config.debugLog('tilmeld', `Registering new user "${this.username}".`);
|
|
1565
1571
|
await this.$saveSkipAC();
|
|
1566
1572
|
// Finish up.
|
|
1567
1573
|
if (tilmeld.config.verifyEmail &&
|
|
@@ -1569,7 +1575,7 @@ let User = (() => {
|
|
|
1569
1575
|
!madeAdmin &&
|
|
1570
1576
|
tilmeld.config.userFields.includes('email')) {
|
|
1571
1577
|
message +=
|
|
1572
|
-
`Almost there. An email has been sent to ${this
|
|
1578
|
+
`Almost there. An email has been sent to ${this.email} ` +
|
|
1573
1579
|
'with a verification link for you to finish registration.';
|
|
1574
1580
|
}
|
|
1575
1581
|
else if (tilmeld.config.verifyEmail &&
|
|
@@ -1581,7 +1587,7 @@ let User = (() => {
|
|
|
1581
1587
|
}
|
|
1582
1588
|
message +=
|
|
1583
1589
|
"You're now logged in! An email has been sent to " +
|
|
1584
|
-
`${this
|
|
1590
|
+
`${this.email} with a verification link for you to finish ` +
|
|
1585
1591
|
'registration.';
|
|
1586
1592
|
loggedin = true;
|
|
1587
1593
|
}
|
|
@@ -1609,19 +1615,19 @@ let User = (() => {
|
|
|
1609
1615
|
template: 'UserRegistered',
|
|
1610
1616
|
message: { to: tilmeld.config.userRegisteredRecipient },
|
|
1611
1617
|
locals: {
|
|
1612
|
-
userUsername: this
|
|
1613
|
-
userName: this
|
|
1614
|
-
userFirstName: this
|
|
1615
|
-
userLastName: this
|
|
1616
|
-
userEmail: this
|
|
1617
|
-
userPhone: this
|
|
1618
|
+
userUsername: this.username,
|
|
1619
|
+
userName: this.name,
|
|
1620
|
+
userFirstName: this.nameFirst,
|
|
1621
|
+
userLastName: this.nameLast,
|
|
1622
|
+
userEmail: this.email,
|
|
1623
|
+
userPhone: this.phone,
|
|
1618
1624
|
},
|
|
1619
1625
|
}, this))) {
|
|
1620
1626
|
throw new Error('Failed to send.');
|
|
1621
1627
|
}
|
|
1622
1628
|
}
|
|
1623
1629
|
catch (e) {
|
|
1624
|
-
this.$nymph.config.debugError('tilmeld', `Couldn't send new user registered email for "${this
|
|
1630
|
+
this.$nymph.config.debugError('tilmeld', `Couldn't send new user registered email for "${this.username}": ${e.message}`);
|
|
1625
1631
|
}
|
|
1626
1632
|
}
|
|
1627
1633
|
if (result.loggedin) {
|
|
@@ -1644,24 +1650,24 @@ let User = (() => {
|
|
|
1644
1650
|
}
|
|
1645
1651
|
async $save() {
|
|
1646
1652
|
let tilmeld = enforceTilmeld(this);
|
|
1647
|
-
if (this
|
|
1653
|
+
if (this.username == null || !this.username.trim().length) {
|
|
1648
1654
|
throw new BadUsernameError('Username must not be empty.');
|
|
1649
1655
|
}
|
|
1650
1656
|
if (!this.$skipAcWhenSaving &&
|
|
1651
1657
|
tilmeld.gatekeeper('tilmeld/admin') &&
|
|
1652
1658
|
!tilmeld.gatekeeper('system/admin') &&
|
|
1653
|
-
this
|
|
1659
|
+
this.abilities?.includes('system/admin')) {
|
|
1654
1660
|
throw new AccessControlError("You don't have the authority to modify system admins.");
|
|
1655
1661
|
}
|
|
1656
1662
|
// Lowercase the domain part.
|
|
1657
1663
|
if (tilmeld.config.domainSupport) {
|
|
1658
|
-
const [username, domain] = splitn(this
|
|
1664
|
+
const [username, domain] = splitn(this.username ?? '', '@', 2);
|
|
1659
1665
|
if (domain) {
|
|
1660
|
-
this
|
|
1666
|
+
this.username = `${username}@${domain.toLowerCase()}`;
|
|
1661
1667
|
}
|
|
1662
1668
|
}
|
|
1663
1669
|
if (tilmeld.config.domainSupport) {
|
|
1664
|
-
const [_username, domain] = splitn(this
|
|
1670
|
+
const [_username, domain] = splitn(this.username ?? '', '@', 2);
|
|
1665
1671
|
const [_originalUsername, originalDomain] = splitn(this.$originalUsername ?? '', '@', 2);
|
|
1666
1672
|
if (this.guid != null &&
|
|
1667
1673
|
((originalDomain == null && domain != null) ||
|
|
@@ -1686,86 +1692,80 @@ let User = (() => {
|
|
|
1686
1692
|
}
|
|
1687
1693
|
let sendVerification = false;
|
|
1688
1694
|
// Formatting.
|
|
1689
|
-
this
|
|
1695
|
+
this.username = this.username.trim();
|
|
1690
1696
|
if (tilmeld.config.emailUsernames) {
|
|
1691
|
-
this
|
|
1692
|
-
}
|
|
1693
|
-
this
|
|
1694
|
-
this
|
|
1695
|
-
this
|
|
1696
|
-
this
|
|
1697
|
-
this
|
|
1698
|
-
this
|
|
1699
|
-
(this
|
|
1700
|
-
(this
|
|
1701
|
-
this
|
|
1697
|
+
this.email = this.username;
|
|
1698
|
+
}
|
|
1699
|
+
this.nameFirst = (this.nameFirst ?? '').trim();
|
|
1700
|
+
this.nameMiddle = (this.nameMiddle ?? '').trim();
|
|
1701
|
+
this.nameLast = (this.nameLast ?? '').trim();
|
|
1702
|
+
this.phone = (this.phone ?? '').trim();
|
|
1703
|
+
this.name =
|
|
1704
|
+
this.nameFirst +
|
|
1705
|
+
(this.nameMiddle ? ' ' + this.nameMiddle : '') +
|
|
1706
|
+
(this.nameLast ? ' ' + this.nameLast : '');
|
|
1707
|
+
this.enabled = !!this.enabled;
|
|
1702
1708
|
// Clear empty values.
|
|
1703
|
-
if (this
|
|
1704
|
-
|
|
1705
|
-
delete this.$data.nameFirst;
|
|
1709
|
+
if (this.nameFirst === '' || !tilmeld.config.userFields.includes('name')) {
|
|
1710
|
+
delete this.nameFirst;
|
|
1706
1711
|
}
|
|
1707
|
-
if (this
|
|
1708
|
-
|
|
1709
|
-
delete this.$data.nameMiddle;
|
|
1712
|
+
if (this.nameMiddle === '' || !tilmeld.config.userFields.includes('name')) {
|
|
1713
|
+
delete this.nameMiddle;
|
|
1710
1714
|
}
|
|
1711
|
-
if (this
|
|
1712
|
-
|
|
1713
|
-
delete this.$data.nameLast;
|
|
1715
|
+
if (this.nameLast === '' || !tilmeld.config.userFields.includes('name')) {
|
|
1716
|
+
delete this.nameLast;
|
|
1714
1717
|
}
|
|
1715
|
-
if (this
|
|
1716
|
-
delete this
|
|
1718
|
+
if (this.name === '' || !tilmeld.config.userFields.includes('name')) {
|
|
1719
|
+
delete this.name;
|
|
1717
1720
|
}
|
|
1718
|
-
if (this
|
|
1719
|
-
|
|
1720
|
-
delete this.$data.email;
|
|
1721
|
+
if (this.email === '' || !tilmeld.config.userFields.includes('email')) {
|
|
1722
|
+
delete this.email;
|
|
1721
1723
|
}
|
|
1722
|
-
if (this
|
|
1723
|
-
delete this
|
|
1724
|
+
if (this.avatar === '') {
|
|
1725
|
+
delete this.avatar;
|
|
1724
1726
|
}
|
|
1725
|
-
if (this
|
|
1726
|
-
|
|
1727
|
-
delete this.$data.phone;
|
|
1727
|
+
if (this.phone === '' || !tilmeld.config.userFields.includes('phone')) {
|
|
1728
|
+
delete this.phone;
|
|
1728
1729
|
}
|
|
1729
|
-
if (this
|
|
1730
|
-
|
|
1731
|
-
delete this.$data.secret;
|
|
1730
|
+
if (this.secret === '' || !tilmeld.config.userFields.includes('email')) {
|
|
1731
|
+
delete this.secret;
|
|
1732
1732
|
}
|
|
1733
|
-
if (this
|
|
1733
|
+
if (this.emailChangeDate === 0 ||
|
|
1734
1734
|
!tilmeld.config.userFields.includes('email')) {
|
|
1735
|
-
delete this
|
|
1735
|
+
delete this.emailChangeDate;
|
|
1736
1736
|
}
|
|
1737
|
-
if (this
|
|
1737
|
+
if (this.newEmailSecret === '' ||
|
|
1738
1738
|
!tilmeld.config.userFields.includes('email')) {
|
|
1739
|
-
delete this
|
|
1739
|
+
delete this.newEmailSecret;
|
|
1740
1740
|
}
|
|
1741
|
-
if (this
|
|
1741
|
+
if (this.newEmailAddress === '' ||
|
|
1742
1742
|
!tilmeld.config.userFields.includes('email')) {
|
|
1743
|
-
delete this
|
|
1743
|
+
delete this.newEmailAddress;
|
|
1744
1744
|
}
|
|
1745
|
-
if (this
|
|
1745
|
+
if (this.cancelEmailSecret === '' ||
|
|
1746
1746
|
!tilmeld.config.userFields.includes('email')) {
|
|
1747
|
-
delete this
|
|
1747
|
+
delete this.cancelEmailSecret;
|
|
1748
1748
|
}
|
|
1749
|
-
if (this
|
|
1749
|
+
if (this.cancelEmailAddress === '' ||
|
|
1750
1750
|
!tilmeld.config.userFields.includes('email')) {
|
|
1751
|
-
delete this
|
|
1751
|
+
delete this.cancelEmailAddress;
|
|
1752
1752
|
}
|
|
1753
|
-
if (this
|
|
1753
|
+
if (this.recoverSecret === '' ||
|
|
1754
1754
|
!tilmeld.config.userFields.includes('email')) {
|
|
1755
|
-
delete this
|
|
1755
|
+
delete this.recoverSecret;
|
|
1756
1756
|
}
|
|
1757
|
-
if (this
|
|
1757
|
+
if (this.recoverSecretDate === 0 ||
|
|
1758
1758
|
!tilmeld.config.userFields.includes('email')) {
|
|
1759
|
-
delete this
|
|
1759
|
+
delete this.recoverSecretDate;
|
|
1760
1760
|
}
|
|
1761
|
-
if (this
|
|
1762
|
-
delete this
|
|
1761
|
+
if (this.password === '') {
|
|
1762
|
+
delete this.password;
|
|
1763
1763
|
}
|
|
1764
|
-
if (this
|
|
1765
|
-
delete this
|
|
1764
|
+
if (this.passwordTemp === '') {
|
|
1765
|
+
delete this.passwordTemp;
|
|
1766
1766
|
}
|
|
1767
|
-
if (this
|
|
1768
|
-
delete this
|
|
1767
|
+
if (this.inheritAbilities === false) {
|
|
1768
|
+
delete this.inheritAbilities;
|
|
1769
1769
|
}
|
|
1770
1770
|
// Verification.
|
|
1771
1771
|
const unCheck = await this.$checkUsername();
|
|
@@ -1789,67 +1789,67 @@ let User = (() => {
|
|
|
1789
1789
|
if (this.guid == null) {
|
|
1790
1790
|
// If this is the first user, they'll be made an admin, so they don't
|
|
1791
1791
|
// need to verify.
|
|
1792
|
-
if (!this
|
|
1793
|
-
this
|
|
1792
|
+
if (!this.abilities?.includes('system/admin')) {
|
|
1793
|
+
this.secret = nanoid();
|
|
1794
1794
|
sendVerification = true;
|
|
1795
1795
|
}
|
|
1796
1796
|
}
|
|
1797
1797
|
else if (this.$originalEmail != null &&
|
|
1798
|
-
this.$originalEmail !== this
|
|
1798
|
+
this.$originalEmail !== this.email) {
|
|
1799
1799
|
// The user already has an old email address.
|
|
1800
1800
|
if (tilmeld.config.emailRateLimit !== '' &&
|
|
1801
|
-
this
|
|
1802
|
-
this
|
|
1801
|
+
this.emailChangeDate != null &&
|
|
1802
|
+
this.emailChangeDate >=
|
|
1803
1803
|
(strtotime('-' + tilmeld.config.emailRateLimit) || 0) * 1000) {
|
|
1804
1804
|
throw new EmailChangeRateLimitExceededError('You already changed your email address recently. Please wait until ' +
|
|
1805
|
-
new Date((strtotime('+' + tilmeld.config.emailRateLimit, Math.floor(this
|
|
1805
|
+
new Date((strtotime('+' + tilmeld.config.emailRateLimit, Math.floor(this.emailChangeDate / 1000)) || 0) * 1000).toString() +
|
|
1806
1806
|
' to change your email address again.');
|
|
1807
1807
|
}
|
|
1808
1808
|
else {
|
|
1809
|
-
if (this
|
|
1809
|
+
if (this.newEmailSecret == null &&
|
|
1810
1810
|
tilmeld.config.emailRateLimit !== '' &&
|
|
1811
1811
|
// Make sure the user has at least the rate limit time to cancel
|
|
1812
1812
|
// an email change.
|
|
1813
|
-
(this
|
|
1814
|
-
this
|
|
1813
|
+
(this.emailChangeDate == null ||
|
|
1814
|
+
this.emailChangeDate <
|
|
1815
1815
|
(strtotime('-' + tilmeld.config.emailRateLimit) || 0) * 1000)) {
|
|
1816
1816
|
// Save the old email in case the cancel change link is clicked.
|
|
1817
|
-
this
|
|
1818
|
-
this
|
|
1819
|
-
this
|
|
1817
|
+
this.cancelEmailAddress = this.$originalEmail;
|
|
1818
|
+
this.cancelEmailSecret = nanoid();
|
|
1819
|
+
this.emailChangeDate = Date.now();
|
|
1820
1820
|
}
|
|
1821
1821
|
// Save the new address and reset to the old one (until they verify
|
|
1822
1822
|
// it).
|
|
1823
|
-
this
|
|
1824
|
-
this
|
|
1823
|
+
this.newEmailAddress = this.email;
|
|
1824
|
+
this.email = this.$originalEmail;
|
|
1825
1825
|
if (tilmeld.config.emailUsernames) {
|
|
1826
|
-
this
|
|
1826
|
+
this.username = this.email;
|
|
1827
1827
|
}
|
|
1828
|
-
this
|
|
1828
|
+
this.newEmailSecret = nanoid();
|
|
1829
1829
|
sendVerification = true;
|
|
1830
1830
|
}
|
|
1831
1831
|
}
|
|
1832
1832
|
}
|
|
1833
1833
|
else if (this.guid != null &&
|
|
1834
1834
|
this.$originalEmail != null &&
|
|
1835
|
-
this.$originalEmail !== this
|
|
1835
|
+
this.$originalEmail !== this.email &&
|
|
1836
1836
|
// Make sure the user has at least the rate limit time to cancel an
|
|
1837
1837
|
// email change.
|
|
1838
|
-
(this
|
|
1839
|
-
this
|
|
1838
|
+
(this.emailChangeDate == null ||
|
|
1839
|
+
this.emailChangeDate <
|
|
1840
1840
|
(strtotime('-' + tilmeld.config.emailRateLimit) || 0) * 1000)) {
|
|
1841
1841
|
// The user doesn't need to verify their new email address, but should
|
|
1842
1842
|
// be able to cancel the email change from their old address.
|
|
1843
|
-
this
|
|
1844
|
-
this
|
|
1843
|
+
this.cancelEmailAddress = this.$originalEmail;
|
|
1844
|
+
this.cancelEmailSecret = nanoid();
|
|
1845
1845
|
sendVerification = true;
|
|
1846
1846
|
}
|
|
1847
1847
|
}
|
|
1848
|
-
if (this
|
|
1849
|
-
this.$password(this
|
|
1848
|
+
if (this.passwordTemp != null) {
|
|
1849
|
+
this.$password(this.passwordTemp);
|
|
1850
1850
|
}
|
|
1851
|
-
delete this
|
|
1852
|
-
if (this
|
|
1851
|
+
delete this.passwordTemp;
|
|
1852
|
+
if (this.password == null) {
|
|
1853
1853
|
throw new BadDataError('A password is required.');
|
|
1854
1854
|
}
|
|
1855
1855
|
let preGuid = this.guid;
|
|
@@ -1859,7 +1859,7 @@ let User = (() => {
|
|
|
1859
1859
|
this.$setNymph(nymph);
|
|
1860
1860
|
const tilmeld = enforceTilmeld(this);
|
|
1861
1861
|
const Group = this.$nymph.getEntityClass(GroupClass);
|
|
1862
|
-
let group = this
|
|
1862
|
+
let group = this.group;
|
|
1863
1863
|
if (group != null) {
|
|
1864
1864
|
// Make sure to get fresh group data.
|
|
1865
1865
|
if (group.$asleep()) {
|
|
@@ -1885,53 +1885,53 @@ let User = (() => {
|
|
|
1885
1885
|
// Add the default primary.
|
|
1886
1886
|
const group = await this.$nymph.getEntity({ class: Group }, { type: '&', equal: ['defaultPrimary', true] });
|
|
1887
1887
|
if (group != null) {
|
|
1888
|
-
this
|
|
1888
|
+
this.group = group;
|
|
1889
1889
|
}
|
|
1890
1890
|
}
|
|
1891
1891
|
}
|
|
1892
1892
|
if (group != null && group.user != null && this.$is(group.user)) {
|
|
1893
1893
|
// Update the user's generated primary group.
|
|
1894
1894
|
let changed = false;
|
|
1895
|
-
if (group.groupname !== this
|
|
1896
|
-
group.groupname = this
|
|
1895
|
+
if (group.groupname !== this.username) {
|
|
1896
|
+
group.groupname = this.username;
|
|
1897
1897
|
changed = true;
|
|
1898
1898
|
}
|
|
1899
|
-
if (group.avatar !== this
|
|
1900
|
-
group.avatar = this
|
|
1899
|
+
if (group.avatar !== this.avatar) {
|
|
1900
|
+
group.avatar = this.avatar;
|
|
1901
1901
|
changed = true;
|
|
1902
1902
|
}
|
|
1903
1903
|
if (tilmeld.config.userFields.includes('email') &&
|
|
1904
|
-
group.email !== this
|
|
1905
|
-
group.email = this
|
|
1904
|
+
group.email !== this.email) {
|
|
1905
|
+
group.email = this.email;
|
|
1906
1906
|
changed = true;
|
|
1907
1907
|
}
|
|
1908
1908
|
if (tilmeld.config.userFields.includes('name') &&
|
|
1909
|
-
group.name !== this
|
|
1910
|
-
group.name = this
|
|
1909
|
+
group.name !== this.name) {
|
|
1910
|
+
group.name = this.name;
|
|
1911
1911
|
changed = true;
|
|
1912
1912
|
}
|
|
1913
1913
|
if (tilmeld.config.userFields.includes('phone') &&
|
|
1914
|
-
group.phone !== this
|
|
1915
|
-
group.phone = this
|
|
1914
|
+
group.phone !== this.phone) {
|
|
1915
|
+
group.phone = this.phone;
|
|
1916
1916
|
changed = true;
|
|
1917
1917
|
}
|
|
1918
1918
|
if (changed || group.guid == null) {
|
|
1919
1919
|
await group.$saveSkipAC();
|
|
1920
1920
|
}
|
|
1921
|
-
this
|
|
1921
|
+
this.group = group;
|
|
1922
1922
|
}
|
|
1923
|
-
if (this
|
|
1923
|
+
if (this.groups == null || this.groups.length === 0) {
|
|
1924
1924
|
// Add secondary groups.
|
|
1925
1925
|
if (tilmeld.config.userFields.includes('email') &&
|
|
1926
1926
|
tilmeld.config.verifyEmail &&
|
|
1927
1927
|
tilmeld.config.unverifiedAccess &&
|
|
1928
|
-
this
|
|
1928
|
+
this.secret != null) {
|
|
1929
1929
|
// Add the default secondaries for unverified users.
|
|
1930
|
-
this
|
|
1930
|
+
this.groups = await this.$nymph.getEntities({ class: Group }, { type: '&', equal: ['unverifiedSecondary', true] });
|
|
1931
1931
|
}
|
|
1932
1932
|
else {
|
|
1933
1933
|
// Add the default secondaries.
|
|
1934
|
-
this
|
|
1934
|
+
this.groups = await this.$nymph.getEntities({ class: Group }, { type: '&', equal: ['defaultSecondary', true] });
|
|
1935
1935
|
}
|
|
1936
1936
|
}
|
|
1937
1937
|
try {
|
|
@@ -1943,7 +1943,7 @@ let User = (() => {
|
|
|
1943
1943
|
for (let callback of this.constructor
|
|
1944
1944
|
.beforeSaveCallbacks) {
|
|
1945
1945
|
if (callback) {
|
|
1946
|
-
await callback(this
|
|
1946
|
+
await callback(this);
|
|
1947
1947
|
}
|
|
1948
1948
|
}
|
|
1949
1949
|
await super.$save();
|
|
@@ -1958,14 +1958,14 @@ let User = (() => {
|
|
|
1958
1958
|
for (let callback of this.constructor
|
|
1959
1959
|
.afterSaveCallbacks) {
|
|
1960
1960
|
if (callback) {
|
|
1961
|
-
await callback(this
|
|
1961
|
+
await callback(this);
|
|
1962
1962
|
}
|
|
1963
1963
|
}
|
|
1964
1964
|
}, async (nymph, committed) => {
|
|
1965
1965
|
this.$setNymph(nymph);
|
|
1966
1966
|
if (committed) {
|
|
1967
|
-
this.$originalEmail = this
|
|
1968
|
-
this.$originalUsername = this
|
|
1967
|
+
this.$originalEmail = this.email;
|
|
1968
|
+
this.$originalUsername = this.username;
|
|
1969
1969
|
try {
|
|
1970
1970
|
const tilmeld = enforceTilmeld(nymph);
|
|
1971
1971
|
if (tilmeld.User.current(true).$is(this)) {
|
|
@@ -2005,21 +2005,21 @@ let User = (() => {
|
|
|
2005
2005
|
}
|
|
2006
2006
|
if (!this.$skipAcWhenDeleting &&
|
|
2007
2007
|
!tilmeld.gatekeeper('system/admin') &&
|
|
2008
|
-
this
|
|
2008
|
+
this.abilities?.includes('system/admin')) {
|
|
2009
2009
|
throw new AccessControlError("You don't have the authority to delete system admins.");
|
|
2010
2010
|
}
|
|
2011
|
-
if (this
|
|
2011
|
+
if (this.username !== this.$originalUsername) {
|
|
2012
2012
|
throw new AccessControlError("You can't delete a user while switching their username.");
|
|
2013
2013
|
}
|
|
2014
2014
|
if (!(await this.$refresh())) {
|
|
2015
2015
|
throw new BadDataError('User could not be refreshed.');
|
|
2016
2016
|
}
|
|
2017
|
-
if (this
|
|
2018
|
-
if (this
|
|
2019
|
-
await this
|
|
2017
|
+
if (this.group != null) {
|
|
2018
|
+
if (this.group.$asleep()) {
|
|
2019
|
+
await this.group.$wake();
|
|
2020
2020
|
}
|
|
2021
2021
|
else {
|
|
2022
|
-
if (!(await this
|
|
2022
|
+
if (!(await this.group.$refresh())) {
|
|
2023
2023
|
throw new BadDataError('Group could not be refreshed.');
|
|
2024
2024
|
}
|
|
2025
2025
|
}
|
|
@@ -2027,23 +2027,23 @@ let User = (() => {
|
|
|
2027
2027
|
for (let callback of this.constructor
|
|
2028
2028
|
.beforeDeleteCallbacks) {
|
|
2029
2029
|
if (callback) {
|
|
2030
|
-
await callback(this
|
|
2030
|
+
await callback(this);
|
|
2031
2031
|
}
|
|
2032
2032
|
}
|
|
2033
2033
|
if (tilmeld.User.current(true).$is(this)) {
|
|
2034
2034
|
await this.$logout();
|
|
2035
2035
|
}
|
|
2036
|
-
if (this
|
|
2036
|
+
if (this.group != null && this.$is(this.group.user)) {
|
|
2037
2037
|
// Read the skip ac value here, because super.$delete changes it.
|
|
2038
2038
|
const $skipAcWhenDeleting = this.$skipAcWhenDeleting;
|
|
2039
2039
|
// Delete the user.
|
|
2040
2040
|
await super.$delete();
|
|
2041
2041
|
// Delete the generated group.
|
|
2042
2042
|
if ($skipAcWhenDeleting) {
|
|
2043
|
-
await this
|
|
2043
|
+
await this.group.$deleteSkipAC();
|
|
2044
2044
|
}
|
|
2045
2045
|
else {
|
|
2046
|
-
await this
|
|
2046
|
+
await this.group.$delete();
|
|
2047
2047
|
}
|
|
2048
2048
|
}
|
|
2049
2049
|
else {
|
|
@@ -2052,7 +2052,7 @@ let User = (() => {
|
|
|
2052
2052
|
for (let callback of this.constructor
|
|
2053
2053
|
.afterDeleteCallbacks) {
|
|
2054
2054
|
if (callback) {
|
|
2055
|
-
await callback(this
|
|
2055
|
+
await callback(this);
|
|
2056
2056
|
}
|
|
2057
2057
|
}
|
|
2058
2058
|
}
|
|
@@ -2094,14 +2094,5 @@ let User = (() => {
|
|
|
2094
2094
|
}
|
|
2095
2095
|
};
|
|
2096
2096
|
})();
|
|
2097
|
-
|
|
2098
|
-
* A user data model.
|
|
2099
|
-
*
|
|
2100
|
-
* Written by Hunter Perrin for SciActive.
|
|
2101
|
-
*
|
|
2102
|
-
* @author Hunter Perrin <hperrin@port87.com>
|
|
2103
|
-
* @copyright SciActive Inc
|
|
2104
|
-
* @see http://nymph.io/
|
|
2105
|
-
*/
|
|
2106
|
-
export default User;
|
|
2097
|
+
export { User };
|
|
2107
2098
|
//# sourceMappingURL=User.js.map
|