@nymphjs/tilmeld 1.0.0-beta.2 → 1.0.0-beta.20
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 +100 -0
- package/README.md +13 -0
- package/dist/Group.js +49 -23
- package/dist/Group.js.map +1 -1
- package/dist/Tilmeld.d.ts +5 -2
- package/dist/Tilmeld.js +151 -60
- package/dist/Tilmeld.js.map +1 -1
- package/dist/User.d.ts +26 -3
- package/dist/User.js +169 -43
- package/dist/User.js.map +1 -1
- package/dist/conf/d.d.ts +3 -1
- package/dist/conf/defaults.js +10 -4
- package/dist/conf/defaults.js.map +1 -1
- package/dist/enforceTilmeld.d.ts +3 -0
- package/dist/enforceTilmeld.js +22 -0
- package/dist/enforceTilmeld.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +18 -18
- package/src/Group.ts +70 -23
- package/src/Tilmeld.ts +225 -76
- package/src/User.ts +269 -47
- package/src/conf/d.ts +15 -6
- package/src/conf/defaults.ts +12 -4
- package/src/enforceTilmeld.ts +20 -0
- package/src/index.ts +2 -0
package/src/User.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { difference } from 'lodash';
|
|
|
14
14
|
import { humanSecret, nanoid } from '@nymphjs/guid';
|
|
15
15
|
|
|
16
16
|
import type Tilmeld from './Tilmeld';
|
|
17
|
+
import { enforceTilmeld } from './enforceTilmeld';
|
|
17
18
|
import AbleObject from './AbleObject';
|
|
18
19
|
import Group, { GroupData } from './Group';
|
|
19
20
|
import {
|
|
@@ -24,12 +25,22 @@ import {
|
|
|
24
25
|
} from './errors';
|
|
25
26
|
|
|
26
27
|
export type EventType =
|
|
28
|
+
| 'checkUsername'
|
|
27
29
|
| 'beforeRegister'
|
|
28
30
|
| 'afterRegister'
|
|
29
31
|
| 'beforeLogin'
|
|
30
32
|
| 'afterLogin'
|
|
31
33
|
| 'beforeLogout'
|
|
32
34
|
| 'afterLogout';
|
|
35
|
+
/**
|
|
36
|
+
* This is run when the user has entered an otherwise valid username into the
|
|
37
|
+
* signup form. It should return a result, which when false will stop the
|
|
38
|
+
* process and return the included message, disallowing the username.
|
|
39
|
+
*/
|
|
40
|
+
export type TilmeldCheckUsernameCallback = (
|
|
41
|
+
user: User & UserData,
|
|
42
|
+
data: { username: string }
|
|
43
|
+
) => Promise<{ result: boolean; message?: string }>;
|
|
33
44
|
/**
|
|
34
45
|
* Theses are run before the user data checks, so the only checks before are
|
|
35
46
|
* whether registration is allowed and whether the user is already registered.
|
|
@@ -135,7 +146,8 @@ export type UserData = {
|
|
|
135
146
|
*/
|
|
136
147
|
newEmailSecret?: string;
|
|
137
148
|
/**
|
|
138
|
-
*
|
|
149
|
+
* If the user has changed their email address, this is the new one, awaiting
|
|
150
|
+
* verification.
|
|
139
151
|
*/
|
|
140
152
|
newEmailAddress?: string;
|
|
141
153
|
/**
|
|
@@ -167,6 +179,11 @@ export type UserData = {
|
|
|
167
179
|
* database.
|
|
168
180
|
*/
|
|
169
181
|
passwordTemp?: string;
|
|
182
|
+
/**
|
|
183
|
+
* If set, this timestamp is the cutoff point for JWT issue dates. Any token
|
|
184
|
+
* issued before this date will not authenticate the user.
|
|
185
|
+
*/
|
|
186
|
+
revokeTokenDate?: number;
|
|
170
187
|
};
|
|
171
188
|
|
|
172
189
|
/**
|
|
@@ -185,6 +202,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
185
202
|
static tilmeld: Tilmeld;
|
|
186
203
|
static ETYPE = 'tilmeld_user';
|
|
187
204
|
static class = 'User';
|
|
205
|
+
private static checkUsernameCallbacks: TilmeldCheckUsernameCallback[] = [];
|
|
188
206
|
private static beforeRegisterCallbacks: TilmeldBeforeRegisterCallback[] = [];
|
|
189
207
|
private static afterRegisterCallbacks: TilmeldAfterRegisterCallback[] = [];
|
|
190
208
|
private static beforeLoginCallbacks: TilmeldBeforeLoginCallback[] = [];
|
|
@@ -200,23 +218,31 @@ export default class User extends AbleObject<UserData> {
|
|
|
200
218
|
'$register',
|
|
201
219
|
];
|
|
202
220
|
private static DEFAULT_PRIVATE_DATA = [
|
|
221
|
+
// The things that are commented are there to show what is _not_ private.
|
|
222
|
+
'abilities',
|
|
223
|
+
// 'username',
|
|
224
|
+
// 'nameFirst',
|
|
225
|
+
// 'nameMiddle',
|
|
226
|
+
// 'nameLast',
|
|
227
|
+
// 'name',
|
|
203
228
|
'email',
|
|
204
|
-
'
|
|
229
|
+
// 'avatar',
|
|
205
230
|
'phone',
|
|
206
231
|
'group',
|
|
207
232
|
'groups',
|
|
208
|
-
'abilities',
|
|
209
233
|
'inheritAbilities',
|
|
210
|
-
'
|
|
211
|
-
'recoverSecretDate',
|
|
212
|
-
'password',
|
|
213
|
-
'salt',
|
|
234
|
+
'enabled',
|
|
214
235
|
'secret',
|
|
215
|
-
'
|
|
236
|
+
'emailChangeDate',
|
|
216
237
|
'newEmailSecret',
|
|
217
|
-
'
|
|
238
|
+
'newEmailAddress',
|
|
218
239
|
'cancelEmailSecret',
|
|
219
|
-
'
|
|
240
|
+
'cancelEmailAddress',
|
|
241
|
+
'recoverSecret',
|
|
242
|
+
'recoverSecretDate',
|
|
243
|
+
'salt',
|
|
244
|
+
'password',
|
|
245
|
+
'passwordTemp',
|
|
220
246
|
];
|
|
221
247
|
private static DEFAULT_ALLOWLIST_DATA: string[] = [];
|
|
222
248
|
|
|
@@ -501,7 +527,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
501
527
|
password: string;
|
|
502
528
|
additionalData?: { [k: string]: any };
|
|
503
529
|
}) {
|
|
504
|
-
const tilmeld = this
|
|
530
|
+
const tilmeld = enforceTilmeld(this);
|
|
505
531
|
if (this.guid == null) {
|
|
506
532
|
return { result: false, message: 'Incorrect login/password.' };
|
|
507
533
|
}
|
|
@@ -545,12 +571,84 @@ export default class User extends AbleObject<UserData> {
|
|
|
545
571
|
return { result: true, message: 'You are logged in.' };
|
|
546
572
|
}
|
|
547
573
|
|
|
574
|
+
public async $switchUser(data?: { additionalData?: { [k: string]: any } }) {
|
|
575
|
+
const tilmeld = enforceTilmeld(this);
|
|
576
|
+
if (this.guid == null) {
|
|
577
|
+
return { result: false, message: 'Incorrect login/password.' };
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
let user = tilmeld.User.current();
|
|
581
|
+
|
|
582
|
+
if (!user || user.guid == null) {
|
|
583
|
+
return { result: false, message: "You're not logged in." };
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const isCurrentUser = user != null && user.$is(this);
|
|
587
|
+
|
|
588
|
+
if (isCurrentUser) {
|
|
589
|
+
return { result: false, message: "You can't switch to yourself." };
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const toAbilities = this.$data.abilities || [];
|
|
593
|
+
const fromAbilities = user.abilities || [];
|
|
594
|
+
|
|
595
|
+
if (
|
|
596
|
+
fromAbilities.indexOf('tilmeld/switch') === -1 &&
|
|
597
|
+
fromAbilities.indexOf('system/admin') === -1
|
|
598
|
+
) {
|
|
599
|
+
return {
|
|
600
|
+
result: false,
|
|
601
|
+
message: "You don't have permission to switch users.",
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (
|
|
606
|
+
toAbilities.indexOf('system/admin') !== -1 ||
|
|
607
|
+
toAbilities.indexOf('tilmeld/admin') !== -1
|
|
608
|
+
) {
|
|
609
|
+
return { result: false, message: "You can't switch to an admin." };
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
try {
|
|
613
|
+
for (let callback of (this.constructor as typeof User)
|
|
614
|
+
.beforeLoginCallbacks) {
|
|
615
|
+
if (callback) {
|
|
616
|
+
await callback(this, {
|
|
617
|
+
username: this.$data.username || '',
|
|
618
|
+
password: '',
|
|
619
|
+
...(data || {}),
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
} catch (e: any) {
|
|
624
|
+
return {
|
|
625
|
+
result: false,
|
|
626
|
+
message: e.message,
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Authentication was successful, attempt to login.
|
|
631
|
+
if (!tilmeld.loginSwitch(this, true)) {
|
|
632
|
+
return { result: false, message: 'Incorrect login/password.' };
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
for (let callback of (this.constructor as typeof User)
|
|
636
|
+
.afterLoginCallbacks) {
|
|
637
|
+
if (callback) {
|
|
638
|
+
await callback(this);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Login was successful.
|
|
643
|
+
return { result: true, message: 'You are logged in.' };
|
|
644
|
+
}
|
|
645
|
+
|
|
548
646
|
/**
|
|
549
647
|
* Log a user out of the system.
|
|
550
648
|
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
551
649
|
*/
|
|
552
650
|
public async $logout() {
|
|
553
|
-
const tilmeld = this
|
|
651
|
+
const tilmeld = enforceTilmeld(this);
|
|
554
652
|
|
|
555
653
|
try {
|
|
556
654
|
for (let callback of (this.constructor as typeof User)
|
|
@@ -629,17 +727,39 @@ export default class User extends AbleObject<UserData> {
|
|
|
629
727
|
}
|
|
630
728
|
|
|
631
729
|
public $jsonAcceptData(input: EntityJson, allowConflict = false) {
|
|
632
|
-
const tilmeld = this
|
|
730
|
+
const tilmeld = enforceTilmeld(this);
|
|
633
731
|
this.$referenceWake();
|
|
634
732
|
|
|
635
733
|
if (
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
734
|
+
'abilities' in input.data &&
|
|
735
|
+
input.data.abilities?.includes('system/admin') !==
|
|
736
|
+
this.$data.abilities?.includes('system/admin') &&
|
|
737
|
+
!tilmeld.gatekeeper('system/admin')
|
|
738
|
+
) {
|
|
739
|
+
throw new BadDataError(
|
|
740
|
+
"You don't have the authority to grant or revoke system/admin."
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
if (
|
|
745
|
+
'abilities' in input.data &&
|
|
746
|
+
input.data.abilities?.includes('tilmeld/admin') !==
|
|
747
|
+
this.$data.abilities?.includes('tilmeld/admin') &&
|
|
748
|
+
!tilmeld.gatekeeper('system/admin')
|
|
749
|
+
) {
|
|
750
|
+
throw new BadDataError(
|
|
751
|
+
"You don't have the authority to grant or revoke tilmeld/admin."
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
if (
|
|
756
|
+
'abilities' in input.data &&
|
|
757
|
+
input.data.abilities?.includes('tilmeld/switch') !==
|
|
758
|
+
this.$data.abilities?.includes('tilmeld/switch') &&
|
|
639
759
|
!tilmeld.gatekeeper('system/admin')
|
|
640
760
|
) {
|
|
641
761
|
throw new BadDataError(
|
|
642
|
-
"You don't have the authority to
|
|
762
|
+
"You don't have the authority to grant or revoke tilmeld/switch."
|
|
643
763
|
);
|
|
644
764
|
}
|
|
645
765
|
|
|
@@ -648,17 +768,39 @@ export default class User extends AbleObject<UserData> {
|
|
|
648
768
|
}
|
|
649
769
|
|
|
650
770
|
public $jsonAcceptPatch(patch: EntityPatch, allowConflict = false) {
|
|
651
|
-
const tilmeld = this
|
|
771
|
+
const tilmeld = enforceTilmeld(this);
|
|
652
772
|
this.$referenceWake();
|
|
653
773
|
|
|
654
774
|
if (
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
775
|
+
'abilities' in patch.set &&
|
|
776
|
+
patch.set.abilities?.includes('system/admin') !==
|
|
777
|
+
this.$data.abilities?.includes('system/admin') &&
|
|
658
778
|
!tilmeld.gatekeeper('system/admin')
|
|
659
779
|
) {
|
|
660
780
|
throw new BadDataError(
|
|
661
|
-
"You don't have the authority to
|
|
781
|
+
"You don't have the authority to grant or revoke system/admin."
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (
|
|
786
|
+
'abilities' in patch.set &&
|
|
787
|
+
patch.set.abilities?.includes('tilmeld/admin') !==
|
|
788
|
+
this.$data.abilities?.includes('tilmeld/admin') &&
|
|
789
|
+
!tilmeld.gatekeeper('system/admin')
|
|
790
|
+
) {
|
|
791
|
+
throw new BadDataError(
|
|
792
|
+
"You don't have the authority to grant or revoke tilmeld/admin."
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
if (
|
|
797
|
+
'abilities' in patch.set &&
|
|
798
|
+
patch.set.abilities?.includes('tilmeld/switch') !==
|
|
799
|
+
this.$data.abilities?.includes('tilmeld/switch') &&
|
|
800
|
+
!tilmeld.gatekeeper('system/admin')
|
|
801
|
+
) {
|
|
802
|
+
throw new BadDataError(
|
|
803
|
+
"You don't have the authority to grant or revoke tilmeld/switch."
|
|
662
804
|
);
|
|
663
805
|
}
|
|
664
806
|
|
|
@@ -677,7 +819,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
677
819
|
* @param givenUser User to update protection for. If undefined, will use the currently logged in user.
|
|
678
820
|
*/
|
|
679
821
|
public $updateDataProtection(givenUser?: User & UserData) {
|
|
680
|
-
const tilmeld = this
|
|
822
|
+
const tilmeld = enforceTilmeld(this);
|
|
681
823
|
let user = givenUser ?? tilmeld.User.current();
|
|
682
824
|
|
|
683
825
|
this.$clientEnabledMethods = [...User.DEFAULT_CLIENT_ENABLED_METHODS];
|
|
@@ -701,6 +843,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
701
843
|
// Users can check to see what abilities they have.
|
|
702
844
|
this.$clientEnabledMethods.push('$gatekeeper');
|
|
703
845
|
this.$clientEnabledMethods.push('$changePassword');
|
|
846
|
+
this.$clientEnabledMethods.push('$revokeCurrentTokens');
|
|
704
847
|
this.$clientEnabledMethods.push('$logout');
|
|
705
848
|
this.$clientEnabledMethods.push('$sendEmailVerification');
|
|
706
849
|
}
|
|
@@ -732,17 +875,43 @@ export default class User extends AbleObject<UserData> {
|
|
|
732
875
|
this.$allowlistData.push('phone');
|
|
733
876
|
}
|
|
734
877
|
this.$privateData = [
|
|
878
|
+
// The things that are commented are there to show what is _not_ private.
|
|
879
|
+
// 'abilities',
|
|
880
|
+
// 'username',
|
|
881
|
+
// 'nameFirst',
|
|
882
|
+
// 'nameMiddle',
|
|
883
|
+
// 'nameLast',
|
|
884
|
+
// 'name',
|
|
885
|
+
// 'email',
|
|
886
|
+
// 'avatar',
|
|
887
|
+
// 'phone',
|
|
888
|
+
// 'group',
|
|
889
|
+
// 'groups',
|
|
890
|
+
// 'inheritAbilities',
|
|
891
|
+
'enabled',
|
|
735
892
|
'secret',
|
|
893
|
+
'emailChangeDate',
|
|
736
894
|
'newEmailSecret',
|
|
737
|
-
'
|
|
895
|
+
// 'newEmailAddress',
|
|
738
896
|
'cancelEmailSecret',
|
|
739
|
-
'
|
|
897
|
+
'cancelEmailAddress',
|
|
740
898
|
'recoverSecret',
|
|
741
899
|
'recoverSecretDate',
|
|
742
|
-
'password',
|
|
743
900
|
'salt',
|
|
901
|
+
'password',
|
|
902
|
+
'passwordTemp',
|
|
744
903
|
];
|
|
745
904
|
}
|
|
905
|
+
|
|
906
|
+
if (
|
|
907
|
+
user != null &&
|
|
908
|
+
!isCurrentUser &&
|
|
909
|
+
(abilities.indexOf('tilmeld/switch') !== -1 ||
|
|
910
|
+
abilities.indexOf('system/admin') !== -1)
|
|
911
|
+
) {
|
|
912
|
+
// Users with this ability can switch to other users.
|
|
913
|
+
this.$clientEnabledMethods.push('$switchUser');
|
|
914
|
+
}
|
|
746
915
|
}
|
|
747
916
|
|
|
748
917
|
/**
|
|
@@ -760,7 +929,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
760
929
|
* @returns True or false.
|
|
761
930
|
*/
|
|
762
931
|
public $gatekeeper(ability?: string): boolean {
|
|
763
|
-
const tilmeld = this
|
|
932
|
+
const tilmeld = enforceTilmeld(this);
|
|
764
933
|
if (ability == null) {
|
|
765
934
|
return tilmeld.User.current()?.$is(this) ?? false;
|
|
766
935
|
}
|
|
@@ -787,7 +956,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
787
956
|
}
|
|
788
957
|
return (
|
|
789
958
|
(ability in this.$gatekeeperCache && !!this.$gatekeeperCache[ability]) ||
|
|
790
|
-
!!this.$
|
|
959
|
+
!!this.$data.abilities?.includes('system/admin')
|
|
791
960
|
);
|
|
792
961
|
}
|
|
793
962
|
|
|
@@ -804,7 +973,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
804
973
|
* @returns True on success, false on failure.
|
|
805
974
|
*/
|
|
806
975
|
public async $sendEmailVerification() {
|
|
807
|
-
const tilmeld = this
|
|
976
|
+
const tilmeld = enforceTilmeld(this);
|
|
808
977
|
if (this.guid == null) {
|
|
809
978
|
return false;
|
|
810
979
|
}
|
|
@@ -917,7 +1086,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
917
1086
|
* @returns True if the passwords match, otherwise false.
|
|
918
1087
|
*/
|
|
919
1088
|
public $checkPassword(password: string) {
|
|
920
|
-
const tilmeld = this
|
|
1089
|
+
const tilmeld = enforceTilmeld(this);
|
|
921
1090
|
switch (tilmeld.config.pwMethod) {
|
|
922
1091
|
case 'plain':
|
|
923
1092
|
return this.$data.password === password;
|
|
@@ -1006,6 +1175,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1006
1175
|
public async $changePassword(data: {
|
|
1007
1176
|
newPassword: string;
|
|
1008
1177
|
currentPassword: string;
|
|
1178
|
+
revokeCurrentTokens?: boolean;
|
|
1009
1179
|
}): Promise<{ result: boolean; message: string }> {
|
|
1010
1180
|
if (!('newPassword' in data) || !data.newPassword.length) {
|
|
1011
1181
|
return { result: false, message: 'Please specify a password.' };
|
|
@@ -1014,7 +1184,14 @@ export default class User extends AbleObject<UserData> {
|
|
|
1014
1184
|
return { result: false, message: 'Incorrect password.' };
|
|
1015
1185
|
}
|
|
1016
1186
|
this.$data.passwordTemp = data.newPassword;
|
|
1187
|
+
if (data.revokeCurrentTokens) {
|
|
1188
|
+
this.$data.revokeTokenDate = Date.now();
|
|
1189
|
+
}
|
|
1017
1190
|
if (await this.$save()) {
|
|
1191
|
+
if (data.revokeCurrentTokens) {
|
|
1192
|
+
const tilmeld = enforceTilmeld(this);
|
|
1193
|
+
tilmeld.login(this, true);
|
|
1194
|
+
}
|
|
1018
1195
|
return { result: true, message: 'Your password has been changed.' };
|
|
1019
1196
|
} else {
|
|
1020
1197
|
return { result: false, message: "Couldn't save new password." };
|
|
@@ -1028,7 +1205,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1028
1205
|
* @returns The resulting password or hash which is stored in the entity.
|
|
1029
1206
|
*/
|
|
1030
1207
|
public $password(password: string) {
|
|
1031
|
-
const tilmeld = this
|
|
1208
|
+
const tilmeld = enforceTilmeld(this);
|
|
1032
1209
|
switch (tilmeld.config.pwMethod) {
|
|
1033
1210
|
case 'plain':
|
|
1034
1211
|
delete this.$data.salt;
|
|
@@ -1049,13 +1226,38 @@ export default class User extends AbleObject<UserData> {
|
|
|
1049
1226
|
return this.$data.password;
|
|
1050
1227
|
}
|
|
1051
1228
|
|
|
1229
|
+
/**
|
|
1230
|
+
* A frontend accessible method to revoke all currently issued tokens.
|
|
1231
|
+
*
|
|
1232
|
+
* @param data The input data from the client.
|
|
1233
|
+
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
1234
|
+
*/
|
|
1235
|
+
public async $revokeCurrentTokens(data: {
|
|
1236
|
+
password: string;
|
|
1237
|
+
}): Promise<{ result: boolean; message: string }> {
|
|
1238
|
+
if (!this.$checkPassword(data.password ?? '')) {
|
|
1239
|
+
return { result: false, message: 'Incorrect password.' };
|
|
1240
|
+
}
|
|
1241
|
+
this.$data.revokeTokenDate = Date.now();
|
|
1242
|
+
if (await this.$save()) {
|
|
1243
|
+
const tilmeld = enforceTilmeld(this);
|
|
1244
|
+
tilmeld.login(this, true);
|
|
1245
|
+
return {
|
|
1246
|
+
result: true,
|
|
1247
|
+
message: 'You have logged out of all other sessions.',
|
|
1248
|
+
};
|
|
1249
|
+
} else {
|
|
1250
|
+
return { result: false, message: "Couldn't save revocation date." };
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1052
1254
|
/**
|
|
1053
1255
|
* Check that a username is valid.
|
|
1054
1256
|
*
|
|
1055
1257
|
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
1056
1258
|
*/
|
|
1057
1259
|
public async $checkUsername(): Promise<{ result: boolean; message: string }> {
|
|
1058
|
-
const tilmeld = this
|
|
1260
|
+
const tilmeld = enforceTilmeld(this);
|
|
1059
1261
|
if (!tilmeld.config.emailUsernames) {
|
|
1060
1262
|
if (this.$data.username == null || !this.$data.username.length) {
|
|
1061
1263
|
return { result: false, message: 'Please specify a username.' };
|
|
@@ -1109,6 +1311,18 @@ export default class User extends AbleObject<UserData> {
|
|
|
1109
1311
|
return { result: false, message: 'That username is taken.' };
|
|
1110
1312
|
}
|
|
1111
1313
|
|
|
1314
|
+
for (let callback of (this.constructor as typeof User)
|
|
1315
|
+
.checkUsernameCallbacks) {
|
|
1316
|
+
if (callback) {
|
|
1317
|
+
const result = await callback(this, {
|
|
1318
|
+
username: this.$data.username,
|
|
1319
|
+
});
|
|
1320
|
+
if (!result.result) {
|
|
1321
|
+
return { message: 'Username is not available.', ...result };
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1112
1326
|
return {
|
|
1113
1327
|
result: true,
|
|
1114
1328
|
message:
|
|
@@ -1142,7 +1356,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1142
1356
|
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
1143
1357
|
*/
|
|
1144
1358
|
public async $checkEmail(): Promise<{ result: boolean; message: string }> {
|
|
1145
|
-
const tilmeld = this
|
|
1359
|
+
const tilmeld = enforceTilmeld(this);
|
|
1146
1360
|
if (this.$data.email == null || !this.$data.email.length) {
|
|
1147
1361
|
if (tilmeld.config.verifyEmail) {
|
|
1148
1362
|
return { result: false, message: 'Please specify an email.' };
|
|
@@ -1187,7 +1401,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1187
1401
|
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
1188
1402
|
*/
|
|
1189
1403
|
public async $checkPhone(): Promise<{ result: boolean; message: string }> {
|
|
1190
|
-
const tilmeld = this
|
|
1404
|
+
const tilmeld = enforceTilmeld(this);
|
|
1191
1405
|
if (this.$data.phone == null || !this.$data.phone.length) {
|
|
1192
1406
|
return { result: false, message: 'Please specify a phone number.' };
|
|
1193
1407
|
}
|
|
@@ -1226,7 +1440,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1226
1440
|
password: string;
|
|
1227
1441
|
additionalData?: { [k: string]: any };
|
|
1228
1442
|
}): Promise<{ result: boolean; loggedin: boolean; message: string }> {
|
|
1229
|
-
let tilmeld = this
|
|
1443
|
+
let tilmeld = enforceTilmeld(this);
|
|
1230
1444
|
if (!tilmeld.config.allowRegistration) {
|
|
1231
1445
|
return {
|
|
1232
1446
|
result: false,
|
|
@@ -1288,7 +1502,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1288
1502
|
const nymph = this.$nymph;
|
|
1289
1503
|
const tnymph = await this.$nymph.startTransaction(transaction);
|
|
1290
1504
|
this.$nymph = tnymph;
|
|
1291
|
-
tilmeld = this
|
|
1505
|
+
tilmeld = enforceTilmeld(this);
|
|
1292
1506
|
|
|
1293
1507
|
try {
|
|
1294
1508
|
// Add primary group.
|
|
@@ -1493,7 +1707,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1493
1707
|
}
|
|
1494
1708
|
|
|
1495
1709
|
public async $save() {
|
|
1496
|
-
let tilmeld = this
|
|
1710
|
+
let tilmeld = enforceTilmeld(this);
|
|
1497
1711
|
if (this.$data.username == null || !this.$data.username.trim().length) {
|
|
1498
1712
|
return false;
|
|
1499
1713
|
}
|
|
@@ -1501,7 +1715,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1501
1715
|
if (
|
|
1502
1716
|
tilmeld.gatekeeper('tilmeld/admin') &&
|
|
1503
1717
|
!tilmeld.gatekeeper('system/admin') &&
|
|
1504
|
-
this.$
|
|
1718
|
+
this.$data.abilities?.includes('system/admin')
|
|
1505
1719
|
) {
|
|
1506
1720
|
throw new BadDataError(
|
|
1507
1721
|
"You don't have the authority to modify system admins."
|
|
@@ -1691,7 +1905,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1691
1905
|
const nymph = this.$nymph;
|
|
1692
1906
|
const tnymph = await this.$nymph.startTransaction(transaction);
|
|
1693
1907
|
this.$nymph = tnymph;
|
|
1694
|
-
tilmeld = this
|
|
1908
|
+
tilmeld = enforceTilmeld(this);
|
|
1695
1909
|
|
|
1696
1910
|
if (
|
|
1697
1911
|
this.$data.group != null &&
|
|
@@ -1742,9 +1956,9 @@ export default class User extends AbleObject<UserData> {
|
|
|
1742
1956
|
this.$gatekeeperCache = undefined;
|
|
1743
1957
|
await tnymph.commit(transaction);
|
|
1744
1958
|
|
|
1745
|
-
if ((nymph
|
|
1959
|
+
if (enforceTilmeld(nymph).User.current(true).$is(this)) {
|
|
1746
1960
|
// Update the user in the session cache.
|
|
1747
|
-
(nymph
|
|
1961
|
+
enforceTilmeld(nymph).fillSession(this);
|
|
1748
1962
|
}
|
|
1749
1963
|
} else {
|
|
1750
1964
|
await tnymph.rollback(transaction);
|
|
@@ -1770,13 +1984,13 @@ export default class User extends AbleObject<UserData> {
|
|
|
1770
1984
|
}
|
|
1771
1985
|
|
|
1772
1986
|
public async $delete() {
|
|
1773
|
-
const tilmeld = this
|
|
1987
|
+
const tilmeld = enforceTilmeld(this);
|
|
1774
1988
|
if (!tilmeld.gatekeeper('tilmeld/admin')) {
|
|
1775
1989
|
throw new BadDataError("You don't have the authority to delete users.");
|
|
1776
1990
|
}
|
|
1777
1991
|
if (
|
|
1778
1992
|
!tilmeld.gatekeeper('system/admin') &&
|
|
1779
|
-
this.$
|
|
1993
|
+
this.$data.abilities?.includes('system/admin')
|
|
1780
1994
|
) {
|
|
1781
1995
|
throw new BadDataError(
|
|
1782
1996
|
"You don't have the authority to delete system admins."
|
|
@@ -1790,7 +2004,9 @@ export default class User extends AbleObject<UserData> {
|
|
|
1790
2004
|
|
|
1791
2005
|
public static on<T extends EventType>(
|
|
1792
2006
|
event: T,
|
|
1793
|
-
callback: T extends '
|
|
2007
|
+
callback: T extends 'checkUsername'
|
|
2008
|
+
? TilmeldCheckUsernameCallback
|
|
2009
|
+
: T extends 'beforeRegister'
|
|
1794
2010
|
? TilmeldBeforeRegisterCallback
|
|
1795
2011
|
: T extends 'afterRegister'
|
|
1796
2012
|
? TilmeldAfterRegisterCallback
|
|
@@ -1804,7 +2020,9 @@ export default class User extends AbleObject<UserData> {
|
|
|
1804
2020
|
? TilmeldAfterLogoutCallback
|
|
1805
2021
|
: never
|
|
1806
2022
|
) {
|
|
1807
|
-
const prop = (event + 'Callbacks') as T extends '
|
|
2023
|
+
const prop = (event + 'Callbacks') as T extends 'checkUsername'
|
|
2024
|
+
? 'checkUsernameCallbacks'
|
|
2025
|
+
: T extends 'beforeRegister'
|
|
1808
2026
|
? 'beforeRegisterCallbacks'
|
|
1809
2027
|
: T extends 'afterRegister'
|
|
1810
2028
|
? 'afterRegisterCallbacks'
|
|
@@ -1827,7 +2045,9 @@ export default class User extends AbleObject<UserData> {
|
|
|
1827
2045
|
|
|
1828
2046
|
public static off<T extends EventType>(
|
|
1829
2047
|
event: T,
|
|
1830
|
-
callback: T extends '
|
|
2048
|
+
callback: T extends 'checkUsername'
|
|
2049
|
+
? TilmeldCheckUsernameCallback
|
|
2050
|
+
: T extends 'beforeRegister'
|
|
1831
2051
|
? TilmeldBeforeRegisterCallback
|
|
1832
2052
|
: T extends 'afterRegister'
|
|
1833
2053
|
? TilmeldAfterRegisterCallback
|
|
@@ -1841,7 +2061,9 @@ export default class User extends AbleObject<UserData> {
|
|
|
1841
2061
|
? TilmeldAfterLogoutCallback
|
|
1842
2062
|
: never
|
|
1843
2063
|
) {
|
|
1844
|
-
const prop = (event + 'Callbacks') as T extends '
|
|
2064
|
+
const prop = (event + 'Callbacks') as T extends 'checkUsername'
|
|
2065
|
+
? 'checkUsernameCallbacks'
|
|
2066
|
+
: T extends 'beforeRegister'
|
|
1845
2067
|
? 'beforeRegisterCallbacks'
|
|
1846
2068
|
: T extends 'afterRegister'
|
|
1847
2069
|
? 'afterRegisterCallbacks'
|
package/src/conf/d.ts
CHANGED
|
@@ -217,6 +217,11 @@ export interface Config {
|
|
|
217
217
|
* How long from current time, in seconds, the JWT token expires.
|
|
218
218
|
*/
|
|
219
219
|
jwtExpire: number;
|
|
220
|
+
/**
|
|
221
|
+
* How long from current time, in seconds, the JWT token for a user switch
|
|
222
|
+
* expires.
|
|
223
|
+
*/
|
|
224
|
+
jwtSwitchExpire: number;
|
|
220
225
|
/**
|
|
221
226
|
* How long, in seconds, before the JWT token expires to give the user a new
|
|
222
227
|
* one.
|
|
@@ -225,21 +230,25 @@ export interface Config {
|
|
|
225
230
|
/**
|
|
226
231
|
* Function to build the JWT for user sessions.
|
|
227
232
|
*/
|
|
228
|
-
jwtBuilder: (
|
|
233
|
+
jwtBuilder: (
|
|
234
|
+
config: Config,
|
|
235
|
+
user: User & UserData,
|
|
236
|
+
switchToken?: boolean
|
|
237
|
+
) => string;
|
|
229
238
|
/**
|
|
230
|
-
* Function to verify that a JWT was signed with the secret key,
|
|
231
|
-
* data, validate the XSRF token, and extract the GUID.
|
|
239
|
+
* Function to verify that a JWT was signed with the secret key, validate its
|
|
240
|
+
* data, validate the XSRF token, and extract the GUID and dates.
|
|
232
241
|
*
|
|
233
242
|
* If no XSRF token is supplied, ignore it.
|
|
234
243
|
*
|
|
235
|
-
* Return false if the JWT is not valid, or an
|
|
236
|
-
*
|
|
244
|
+
* Return false if the JWT is not valid, or an object with GUID, issued date,
|
|
245
|
+
* and expire date otherwise.
|
|
237
246
|
*/
|
|
238
247
|
jwtExtract: (
|
|
239
248
|
config: Config,
|
|
240
249
|
token: string,
|
|
241
250
|
xsrfToken?: string
|
|
242
|
-
) => { guid: string; expire: Date } | null;
|
|
251
|
+
) => { guid: string; issued: Date; expire: Date } | null;
|
|
243
252
|
/**
|
|
244
253
|
* The absolute path to the email template directory. Used by the default
|
|
245
254
|
* email sender.
|