@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/src/User.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
import {
|
|
3
|
-
type EntityData,
|
|
4
3
|
type EntityJson,
|
|
5
4
|
type EntityPatch,
|
|
6
5
|
type Selector,
|
|
@@ -17,9 +16,8 @@ import { toDataURL } from 'qrcode';
|
|
|
17
16
|
import { splitn } from '@sciactive/splitn';
|
|
18
17
|
|
|
19
18
|
import { enforceTilmeld } from './enforceTilmeld.js';
|
|
20
|
-
import AbleObject from './AbleObject.js';
|
|
21
|
-
import GroupClass from './Group.js';
|
|
22
|
-
import type { GroupData } from './Group.js';
|
|
19
|
+
import { AbleObject } from './AbleObject.js';
|
|
20
|
+
import { Group as GroupClass } from './Group.js';
|
|
23
21
|
import {
|
|
24
22
|
AccessControlError,
|
|
25
23
|
BadDataError,
|
|
@@ -46,7 +44,7 @@ export type EventType =
|
|
|
46
44
|
* process and return the included message, disallowing the username.
|
|
47
45
|
*/
|
|
48
46
|
export type TilmeldCheckUsernameCallback = (
|
|
49
|
-
user: User
|
|
47
|
+
user: User,
|
|
50
48
|
data: { username: string },
|
|
51
49
|
) => Promise<{ result: boolean; message?: string }>;
|
|
52
50
|
/**
|
|
@@ -54,7 +52,7 @@ export type TilmeldCheckUsernameCallback = (
|
|
|
54
52
|
* whether registration is allowed and whether the user is already registered.
|
|
55
53
|
*/
|
|
56
54
|
export type TilmeldBeforeRegisterCallback = (
|
|
57
|
-
user: User
|
|
55
|
+
user: User,
|
|
58
56
|
data: { password: string; additionalData?: { [k: string]: any } },
|
|
59
57
|
) => Promise<void>;
|
|
60
58
|
/**
|
|
@@ -62,7 +60,7 @@ export type TilmeldBeforeRegisterCallback = (
|
|
|
62
60
|
* additional functions on the transaction, which is available in `user.$nymph`.
|
|
63
61
|
*/
|
|
64
62
|
export type TilmeldAfterRegisterCallback = (
|
|
65
|
-
user: User
|
|
63
|
+
user: User,
|
|
66
64
|
result: { loggedin: boolean; message: string },
|
|
67
65
|
) => Promise<void>;
|
|
68
66
|
/**
|
|
@@ -70,9 +68,7 @@ export type TilmeldAfterRegisterCallback = (
|
|
|
70
68
|
*
|
|
71
69
|
* The transaction is available in `user.$nymph`.
|
|
72
70
|
*/
|
|
73
|
-
export type TilmeldBeforeSaveCallback = (
|
|
74
|
-
user: User & UserData,
|
|
75
|
-
) => Promise<void>;
|
|
71
|
+
export type TilmeldBeforeSaveCallback = (user: User) => Promise<void>;
|
|
76
72
|
/**
|
|
77
73
|
* This is run before the transaction is committed, and you can perform
|
|
78
74
|
* additional functions on the transaction, which is available in `user.$nymph`.
|
|
@@ -80,45 +76,35 @@ export type TilmeldBeforeSaveCallback = (
|
|
|
80
76
|
* This is run after a new email verification email has been sent, so throwing
|
|
81
77
|
* an error will cause confusion for the user if they've changed their email.
|
|
82
78
|
*/
|
|
83
|
-
export type TilmeldAfterSaveCallback = (user: User
|
|
79
|
+
export type TilmeldAfterSaveCallback = (user: User) => Promise<void>;
|
|
84
80
|
/**
|
|
85
81
|
* This is run after the authentication checks, but before the login action.
|
|
86
82
|
*/
|
|
87
83
|
export type TilmeldBeforeLoginCallback = (
|
|
88
|
-
user: User
|
|
84
|
+
user: User,
|
|
89
85
|
data: {
|
|
90
86
|
username: string;
|
|
91
87
|
password: string;
|
|
92
88
|
additionalData?: { [k: string]: any };
|
|
93
89
|
},
|
|
94
90
|
) => Promise<void>;
|
|
95
|
-
export type TilmeldAfterLoginCallback = (
|
|
96
|
-
|
|
97
|
-
) => Promise<void>;
|
|
98
|
-
export type TilmeldBeforeLogoutCallback = (
|
|
99
|
-
user: User & UserData,
|
|
100
|
-
) => Promise<void>;
|
|
101
|
-
export type TilmeldAfterLogoutCallback = (
|
|
102
|
-
user: User & UserData,
|
|
103
|
-
) => Promise<void>;
|
|
91
|
+
export type TilmeldAfterLoginCallback = (user: User) => Promise<void>;
|
|
92
|
+
export type TilmeldBeforeLogoutCallback = (user: User) => Promise<void>;
|
|
93
|
+
export type TilmeldAfterLogoutCallback = (user: User) => Promise<void>;
|
|
104
94
|
/**
|
|
105
95
|
* This is run before the user is logged out (if the current user is being
|
|
106
96
|
* deleted).
|
|
107
97
|
*
|
|
108
98
|
* The transaction is available in `user.$nymph`.
|
|
109
99
|
*/
|
|
110
|
-
export type TilmeldBeforeDeleteCallback = (
|
|
111
|
-
user: User & UserData,
|
|
112
|
-
) => Promise<void>;
|
|
100
|
+
export type TilmeldBeforeDeleteCallback = (user: User) => Promise<void>;
|
|
113
101
|
/**
|
|
114
102
|
* This is run before the transaction is committed, and you can perform
|
|
115
103
|
* additional functions on the transaction, which is available in `user.$nymph`.
|
|
116
104
|
*/
|
|
117
|
-
export type TilmeldAfterDeleteCallback = (
|
|
118
|
-
user: User & UserData,
|
|
119
|
-
) => Promise<void>;
|
|
105
|
+
export type TilmeldAfterDeleteCallback = (user: User) => Promise<void>;
|
|
120
106
|
|
|
121
|
-
export
|
|
107
|
+
export interface UserData {
|
|
122
108
|
/**
|
|
123
109
|
* The abilities granted to the user.
|
|
124
110
|
*/
|
|
@@ -159,11 +145,11 @@ export type UserData = {
|
|
|
159
145
|
/**
|
|
160
146
|
* The user's primary group.
|
|
161
147
|
*/
|
|
162
|
-
group?: GroupClass
|
|
148
|
+
group?: GroupClass;
|
|
163
149
|
/**
|
|
164
150
|
* The user's secondary groups.
|
|
165
151
|
*/
|
|
166
|
-
groups?:
|
|
152
|
+
groups?: GroupClass[];
|
|
167
153
|
/**
|
|
168
154
|
* Whether the user should inherit the abilities of his groups.
|
|
169
155
|
*/
|
|
@@ -228,7 +214,9 @@ export type UserData = {
|
|
|
228
214
|
* Two factor auth secret.
|
|
229
215
|
*/
|
|
230
216
|
totpSecret?: string;
|
|
231
|
-
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export interface User extends UserData {}
|
|
232
220
|
|
|
233
221
|
/**
|
|
234
222
|
* A user data model.
|
|
@@ -239,7 +227,7 @@ export type UserData = {
|
|
|
239
227
|
* @copyright SciActive Inc
|
|
240
228
|
* @see http://nymph.io/
|
|
241
229
|
*/
|
|
242
|
-
export
|
|
230
|
+
export class User extends AbleObject<UserData> {
|
|
243
231
|
static ETYPE = 'tilmeld_user';
|
|
244
232
|
static class = 'User';
|
|
245
233
|
private static checkUsernameCallbacks: TilmeldCheckUsernameCallback[] = [];
|
|
@@ -324,7 +312,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
324
312
|
/**
|
|
325
313
|
* The user's group descendants.
|
|
326
314
|
*/
|
|
327
|
-
private $descendantGroups?:
|
|
315
|
+
private $descendantGroups?: GroupClass[];
|
|
328
316
|
/**
|
|
329
317
|
* Used to save the current email address to send verification if it changes
|
|
330
318
|
* from the frontend.
|
|
@@ -366,7 +354,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
366
354
|
return super.getFTSText(name, value);
|
|
367
355
|
}
|
|
368
356
|
|
|
369
|
-
static async factoryUsername(username?: string): Promise<User
|
|
357
|
+
static async factoryUsername(username?: string): Promise<User> {
|
|
370
358
|
const entity = new this();
|
|
371
359
|
if (username != null) {
|
|
372
360
|
const entity = await this.nymph.getEntity(
|
|
@@ -383,13 +371,9 @@ export default class User extends AbleObject<UserData> {
|
|
|
383
371
|
return entity;
|
|
384
372
|
}
|
|
385
373
|
|
|
386
|
-
public static current(returnObjectIfNotExist: true): User
|
|
387
|
-
public static current(
|
|
388
|
-
|
|
389
|
-
): (User & UserData) | null;
|
|
390
|
-
public static current(
|
|
391
|
-
returnObjectIfNotExist?: boolean,
|
|
392
|
-
): (User & UserData) | null {
|
|
374
|
+
public static current(returnObjectIfNotExist: true): User;
|
|
375
|
+
public static current(returnObjectIfNotExist?: false): User | null;
|
|
376
|
+
public static current(returnObjectIfNotExist?: boolean): User | null {
|
|
393
377
|
const tilmeld = enforceTilmeld(this);
|
|
394
378
|
if (tilmeld.currentUser == null) {
|
|
395
379
|
return returnObjectIfNotExist ? this.factorySync() : null;
|
|
@@ -412,7 +396,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
412
396
|
return { result: false, message: 'Account recovery is not allowed.' };
|
|
413
397
|
}
|
|
414
398
|
|
|
415
|
-
let user: User
|
|
399
|
+
let user: User;
|
|
416
400
|
const options: EmailOptions = {};
|
|
417
401
|
|
|
418
402
|
if (!tilmeld.config.emailUsernames && data.recoveryType === 'username') {
|
|
@@ -575,7 +559,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
575
559
|
return { result: false, message: 'Incorrect login/password.' };
|
|
576
560
|
}
|
|
577
561
|
const user = await tilmeld.User.factoryUsername(data.username);
|
|
578
|
-
const result: { result: boolean; message: string; user?: User
|
|
562
|
+
const result: { result: boolean; message: string; user?: User } =
|
|
579
563
|
await user.$login(data);
|
|
580
564
|
if (result.result) {
|
|
581
565
|
user.$updateDataProtection();
|
|
@@ -636,24 +620,24 @@ export default class User extends AbleObject<UserData> {
|
|
|
636
620
|
super();
|
|
637
621
|
|
|
638
622
|
// Defaults.
|
|
639
|
-
this
|
|
640
|
-
this
|
|
641
|
-
this
|
|
642
|
-
this
|
|
623
|
+
this.enabled = true;
|
|
624
|
+
this.abilities = [];
|
|
625
|
+
this.groups = [];
|
|
626
|
+
this.inheritAbilities = true;
|
|
643
627
|
this.$updateDataProtection();
|
|
644
628
|
}
|
|
645
629
|
|
|
646
630
|
async $getUniques(): Promise<string[]> {
|
|
647
631
|
const tilmeld = enforceTilmeld(this);
|
|
648
|
-
const uniques = [`u:${this
|
|
632
|
+
const uniques = [`u:${this.username}`];
|
|
649
633
|
if (
|
|
650
634
|
!tilmeld.config.emailUsernames &&
|
|
651
635
|
tilmeld.config.userFields.includes('email')
|
|
652
636
|
) {
|
|
653
|
-
uniques.push(`e:${this
|
|
637
|
+
uniques.push(`e:${this.email}`);
|
|
654
638
|
|
|
655
|
-
if (this
|
|
656
|
-
uniques.push(`e:${this
|
|
639
|
+
if (this.newEmailAddress != null) {
|
|
640
|
+
uniques.push(`e:${this.newEmailAddress}`);
|
|
657
641
|
}
|
|
658
642
|
}
|
|
659
643
|
return uniques;
|
|
@@ -674,14 +658,14 @@ export default class User extends AbleObject<UserData> {
|
|
|
674
658
|
await tilmeld.config.failedLoginAttempt(this, data);
|
|
675
659
|
return { result: false, message: 'Incorrect login/password.' };
|
|
676
660
|
}
|
|
677
|
-
if (!this
|
|
661
|
+
if (!this.enabled) {
|
|
678
662
|
return { result: false, message: 'Your account is disabled.' };
|
|
679
663
|
}
|
|
680
664
|
if (await this.$gatekeeper()) {
|
|
681
665
|
return { result: true, message: 'You are already logged in.' };
|
|
682
666
|
}
|
|
683
667
|
|
|
684
|
-
if (this
|
|
668
|
+
if (this.totpSecret != null) {
|
|
685
669
|
if (
|
|
686
670
|
data.code == null ||
|
|
687
671
|
typeof data.code !== 'string' ||
|
|
@@ -713,7 +697,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
713
697
|
|
|
714
698
|
this.$nymph.config.debugLog(
|
|
715
699
|
'tilmeld',
|
|
716
|
-
`User login for "${this
|
|
700
|
+
`User login for "${this.username}".`,
|
|
717
701
|
);
|
|
718
702
|
|
|
719
703
|
// Authentication was successful, attempt to login.
|
|
@@ -724,7 +708,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
724
708
|
for (let callback of (this.constructor as typeof User)
|
|
725
709
|
.afterLoginCallbacks) {
|
|
726
710
|
if (callback) {
|
|
727
|
-
await callback(this
|
|
711
|
+
await callback(this);
|
|
728
712
|
}
|
|
729
713
|
}
|
|
730
714
|
|
|
@@ -752,7 +736,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
752
736
|
return { result: false, message: "You can't switch to yourself." };
|
|
753
737
|
}
|
|
754
738
|
|
|
755
|
-
const toAbilities = this
|
|
739
|
+
const toAbilities = this.abilities || [];
|
|
756
740
|
const fromAbilities = user.abilities || [];
|
|
757
741
|
|
|
758
742
|
if (
|
|
@@ -777,7 +761,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
777
761
|
.beforeLoginCallbacks) {
|
|
778
762
|
if (callback) {
|
|
779
763
|
await callback(this, {
|
|
780
|
-
username: this
|
|
764
|
+
username: this.username || '',
|
|
781
765
|
password: '',
|
|
782
766
|
...(data || {}),
|
|
783
767
|
});
|
|
@@ -789,7 +773,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
789
773
|
|
|
790
774
|
this.$nymph.config.debugLog(
|
|
791
775
|
'tilmeld',
|
|
792
|
-
`User login switch for "${user.username}" to "${this
|
|
776
|
+
`User login switch for "${user.username}" to "${this.username}".`,
|
|
793
777
|
);
|
|
794
778
|
|
|
795
779
|
// Authentication was successful, attempt to login.
|
|
@@ -800,7 +784,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
800
784
|
for (let callback of (this.constructor as typeof User)
|
|
801
785
|
.afterLoginCallbacks) {
|
|
802
786
|
if (callback) {
|
|
803
|
-
await callback(this
|
|
787
|
+
await callback(this);
|
|
804
788
|
}
|
|
805
789
|
}
|
|
806
790
|
|
|
@@ -819,7 +803,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
819
803
|
for (let callback of (this.constructor as typeof User)
|
|
820
804
|
.beforeLogoutCallbacks) {
|
|
821
805
|
if (callback) {
|
|
822
|
-
await callback(this
|
|
806
|
+
await callback(this);
|
|
823
807
|
}
|
|
824
808
|
}
|
|
825
809
|
} catch (e: any) {
|
|
@@ -828,7 +812,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
828
812
|
|
|
829
813
|
this.$nymph.config.debugLog(
|
|
830
814
|
'tilmeld',
|
|
831
|
-
`User logout for "${this
|
|
815
|
+
`User logout for "${this.username}".`,
|
|
832
816
|
);
|
|
833
817
|
|
|
834
818
|
await tilmeld.logout();
|
|
@@ -836,7 +820,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
836
820
|
for (let callback of (this.constructor as typeof User)
|
|
837
821
|
.afterLogoutCallbacks) {
|
|
838
822
|
if (callback) {
|
|
839
|
-
await callback(this
|
|
823
|
+
await callback(this);
|
|
840
824
|
}
|
|
841
825
|
}
|
|
842
826
|
|
|
@@ -844,17 +828,17 @@ export default class User extends AbleObject<UserData> {
|
|
|
844
828
|
}
|
|
845
829
|
|
|
846
830
|
public $getAvatar(): string {
|
|
847
|
-
if (this
|
|
848
|
-
return this
|
|
831
|
+
if (this.avatar != null && this.avatar !== '') {
|
|
832
|
+
return this.avatar;
|
|
849
833
|
}
|
|
850
|
-
if (this
|
|
834
|
+
if (this.email == null || this.email === '') {
|
|
851
835
|
return 'https://secure.gravatar.com/avatar/?d=mm&s=40';
|
|
852
836
|
}
|
|
853
837
|
return (
|
|
854
838
|
'https://secure.gravatar.com/avatar/' +
|
|
855
839
|
crypto
|
|
856
840
|
.createHash('sha256')
|
|
857
|
-
.update(this
|
|
841
|
+
.update(this.email.trim().toLowerCase())
|
|
858
842
|
.digest('hex')
|
|
859
843
|
.toLowerCase() +
|
|
860
844
|
'?d=identicon&s=40'
|
|
@@ -862,15 +846,15 @@ export default class User extends AbleObject<UserData> {
|
|
|
862
846
|
}
|
|
863
847
|
|
|
864
848
|
public $getGid() {
|
|
865
|
-
if (this
|
|
866
|
-
return this
|
|
849
|
+
if (this.group) {
|
|
850
|
+
return this.group.guid;
|
|
867
851
|
}
|
|
868
852
|
return null;
|
|
869
853
|
}
|
|
870
854
|
|
|
871
855
|
public $getGids() {
|
|
872
|
-
if (this
|
|
873
|
-
return this
|
|
856
|
+
if (this.groups) {
|
|
857
|
+
return this.groups
|
|
874
858
|
.map((group) => group.guid)
|
|
875
859
|
.filter((guid) => guid != null) as string[];
|
|
876
860
|
}
|
|
@@ -880,15 +864,15 @@ export default class User extends AbleObject<UserData> {
|
|
|
880
864
|
/**
|
|
881
865
|
* Get the user's group descendants.
|
|
882
866
|
*/
|
|
883
|
-
public async $getDescendantGroups(): Promise<
|
|
867
|
+
public async $getDescendantGroups(): Promise<GroupClass[]> {
|
|
884
868
|
if (this.$descendantGroups == null) {
|
|
885
869
|
this.$descendantGroups = [];
|
|
886
|
-
if (this
|
|
887
|
-
await this
|
|
888
|
-
this.$descendantGroups = await this
|
|
870
|
+
if (this.group != null) {
|
|
871
|
+
await this.group.$wake();
|
|
872
|
+
this.$descendantGroups = await this.group.$getDescendants();
|
|
889
873
|
}
|
|
890
|
-
await Promise.all(this
|
|
891
|
-
for (let curGroup of this
|
|
874
|
+
await Promise.all(this.groups?.map((e) => e.$wake()) || []);
|
|
875
|
+
for (let curGroup of this.groups ?? []) {
|
|
892
876
|
this.$descendantGroups = this.$descendantGroups?.concat(
|
|
893
877
|
await curGroup.$getDescendants(),
|
|
894
878
|
);
|
|
@@ -920,9 +904,8 @@ export default class User extends AbleObject<UserData> {
|
|
|
920
904
|
// While loading, accessing abilities on the current user will infinitely
|
|
921
905
|
// loop, but if this is the current user, we can use its abilities.
|
|
922
906
|
const abilities =
|
|
923
|
-
(isCurrentUser || user == null
|
|
924
|
-
|
|
925
|
-
: user?.abilities) ?? [];
|
|
907
|
+
(isCurrentUser || user == null ? this.abilities : user?.abilities) ??
|
|
908
|
+
[];
|
|
926
909
|
const isNewUser = input.guid == null;
|
|
927
910
|
const isDomainUser =
|
|
928
911
|
!isCurrentUser &&
|
|
@@ -964,7 +947,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
964
947
|
|
|
965
948
|
if (
|
|
966
949
|
incoming.includes('system/admin') !==
|
|
967
|
-
this
|
|
950
|
+
this.abilities?.includes('system/admin') &&
|
|
968
951
|
(!tilmeld.currentUser ||
|
|
969
952
|
!tilmeld.currentUser.abilities?.includes('system/admin'))
|
|
970
953
|
) {
|
|
@@ -975,7 +958,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
975
958
|
|
|
976
959
|
if (
|
|
977
960
|
incoming.includes('tilmeld/admin') !==
|
|
978
|
-
this
|
|
961
|
+
this.abilities?.includes('tilmeld/admin') &&
|
|
979
962
|
(!tilmeld.currentUser ||
|
|
980
963
|
!tilmeld.currentUser.abilities?.includes('system/admin'))
|
|
981
964
|
) {
|
|
@@ -986,7 +969,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
986
969
|
|
|
987
970
|
if (
|
|
988
971
|
incoming.includes('tilmeld/switch') !==
|
|
989
|
-
this
|
|
972
|
+
this.abilities?.includes('tilmeld/switch') &&
|
|
990
973
|
(!tilmeld.currentUser ||
|
|
991
974
|
!tilmeld.currentUser.abilities?.includes('system/admin'))
|
|
992
975
|
) {
|
|
@@ -998,7 +981,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
998
981
|
if (
|
|
999
982
|
xor(
|
|
1000
983
|
incoming.filter((ability: string) => ability.startsWith('tilmeld/')),
|
|
1001
|
-
this
|
|
984
|
+
this.abilities?.filter((ability: string) =>
|
|
1002
985
|
ability.startsWith('tilmeld/'),
|
|
1003
986
|
) ?? [],
|
|
1004
987
|
).length &&
|
|
@@ -1013,21 +996,21 @@ export default class User extends AbleObject<UserData> {
|
|
|
1013
996
|
}
|
|
1014
997
|
|
|
1015
998
|
public $putData(
|
|
1016
|
-
data:
|
|
999
|
+
data: UserData,
|
|
1017
1000
|
sdata?: SerializedEntityData,
|
|
1018
1001
|
source?: 'server',
|
|
1019
1002
|
) {
|
|
1020
1003
|
super.$putData(data, sdata, source);
|
|
1021
1004
|
// TODO: Fix these being added by the drivers.
|
|
1022
1005
|
// @ts-ignore
|
|
1023
|
-
delete this
|
|
1006
|
+
delete this.acRead;
|
|
1024
1007
|
// @ts-ignore
|
|
1025
|
-
delete this
|
|
1008
|
+
delete this.acWrite;
|
|
1026
1009
|
// @ts-ignore
|
|
1027
|
-
delete this
|
|
1010
|
+
delete this.acFull;
|
|
1028
1011
|
if (source === 'server') {
|
|
1029
|
-
this.$originalEmail = this
|
|
1030
|
-
this.$originalUsername = this
|
|
1012
|
+
this.$originalEmail = this.email;
|
|
1013
|
+
this.$originalUsername = this.username;
|
|
1031
1014
|
}
|
|
1032
1015
|
this.$updateDataProtection();
|
|
1033
1016
|
}
|
|
@@ -1037,12 +1020,12 @@ export default class User extends AbleObject<UserData> {
|
|
|
1037
1020
|
*
|
|
1038
1021
|
* @param givenUser User to update protection for. If undefined, will use the currently logged in user.
|
|
1039
1022
|
*/
|
|
1040
|
-
public $updateDataProtection(givenUser?: User
|
|
1023
|
+
public $updateDataProtection(givenUser?: User) {
|
|
1041
1024
|
const tilmeld = enforceTilmeld(this);
|
|
1042
1025
|
let user = givenUser ?? tilmeld.User.current();
|
|
1043
1026
|
let [_username, domain] = tilmeld.config.domainSupport
|
|
1044
|
-
? splitn(this.$originalUsername ?? this
|
|
1045
|
-
: [this.$originalUsername ?? this
|
|
1027
|
+
? splitn(this.$originalUsername ?? this.username ?? '', '@', 2)
|
|
1028
|
+
: [this.$originalUsername ?? this.username, null];
|
|
1046
1029
|
// Lowercase the domain part.
|
|
1047
1030
|
if (domain) {
|
|
1048
1031
|
domain = domain.toLowerCase();
|
|
@@ -1060,9 +1043,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1060
1043
|
// While loading, accessing abilities on the current user will infinitely
|
|
1061
1044
|
// loop, but if this is the current user, we can use its abilities.
|
|
1062
1045
|
const abilities =
|
|
1063
|
-
(isCurrentUser || user == null
|
|
1064
|
-
? this.$data.abilities
|
|
1065
|
-
: user?.abilities) ?? [];
|
|
1046
|
+
(isCurrentUser || user == null ? this.abilities : user?.abilities) ?? [];
|
|
1066
1047
|
const isNewUser = this.guid == null;
|
|
1067
1048
|
const isDomainUser =
|
|
1068
1049
|
tilmeld.config.domainSupport &&
|
|
@@ -1214,10 +1195,10 @@ export default class User extends AbleObject<UserData> {
|
|
|
1214
1195
|
}
|
|
1215
1196
|
|
|
1216
1197
|
public $updateGroupDataProtection() {
|
|
1217
|
-
if (this
|
|
1218
|
-
this
|
|
1198
|
+
if (this.group != null && !this.group.$asleep()) {
|
|
1199
|
+
this.group.$updateDataProtection();
|
|
1219
1200
|
}
|
|
1220
|
-
for (let group of this
|
|
1201
|
+
for (let group of this.groups ?? []) {
|
|
1221
1202
|
if (!group.$asleep()) {
|
|
1222
1203
|
group.$updateDataProtection();
|
|
1223
1204
|
}
|
|
@@ -1250,7 +1231,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1250
1231
|
}
|
|
1251
1232
|
return (
|
|
1252
1233
|
(ability in this.$gatekeeperCache && !!this.$gatekeeperCache[ability]) ||
|
|
1253
|
-
!!this
|
|
1234
|
+
!!this.abilities?.includes('system/admin')
|
|
1254
1235
|
);
|
|
1255
1236
|
}
|
|
1256
1237
|
|
|
@@ -1259,16 +1240,16 @@ export default class User extends AbleObject<UserData> {
|
|
|
1259
1240
|
*/
|
|
1260
1241
|
public async $getGatekeeperCache() {
|
|
1261
1242
|
await this.$wake();
|
|
1262
|
-
let abilities = this
|
|
1263
|
-
if (this
|
|
1264
|
-
await Promise.all(this
|
|
1265
|
-
for (let curGroup of this
|
|
1243
|
+
let abilities = this.abilities ?? [];
|
|
1244
|
+
if (this.inheritAbilities) {
|
|
1245
|
+
await Promise.all(this.groups?.map((e) => e.$wake()) || []);
|
|
1246
|
+
for (let curGroup of this.groups ?? []) {
|
|
1266
1247
|
if (curGroup.enabled) {
|
|
1267
1248
|
abilities = abilities.concat(curGroup.abilities ?? []);
|
|
1268
1249
|
}
|
|
1269
1250
|
}
|
|
1270
|
-
await this
|
|
1271
|
-
const group = this
|
|
1251
|
+
await this.group?.$wake();
|
|
1252
|
+
const group = this.group;
|
|
1272
1253
|
if (group != null && group.cdate != null && group.enabled) {
|
|
1273
1254
|
abilities = abilities.concat(group.abilities ?? []);
|
|
1274
1255
|
}
|
|
@@ -1301,10 +1282,10 @@ export default class User extends AbleObject<UserData> {
|
|
|
1301
1282
|
tilmeld.config.setupPath
|
|
1302
1283
|
}/verify`;
|
|
1303
1284
|
|
|
1304
|
-
if (this
|
|
1285
|
+
if (this.secret != null) {
|
|
1305
1286
|
const link = `${verifyUrl}?action=verify&id=${encodeURIComponent(
|
|
1306
1287
|
this.guid,
|
|
1307
|
-
)}&secret=${encodeURIComponent(this
|
|
1288
|
+
)}&secret=${encodeURIComponent(this.secret)}`;
|
|
1308
1289
|
success =
|
|
1309
1290
|
success &&
|
|
1310
1291
|
(await tilmeld.config.sendEmail(
|
|
@@ -1313,8 +1294,8 @@ export default class User extends AbleObject<UserData> {
|
|
|
1313
1294
|
template: 'VerifyEmail',
|
|
1314
1295
|
message: {
|
|
1315
1296
|
to: {
|
|
1316
|
-
name: this
|
|
1317
|
-
address: this
|
|
1297
|
+
name: this.name ?? '',
|
|
1298
|
+
address: this.email ?? '',
|
|
1318
1299
|
},
|
|
1319
1300
|
},
|
|
1320
1301
|
locals: { verifyLink: link },
|
|
@@ -1323,10 +1304,10 @@ export default class User extends AbleObject<UserData> {
|
|
|
1323
1304
|
));
|
|
1324
1305
|
}
|
|
1325
1306
|
|
|
1326
|
-
if (this
|
|
1307
|
+
if (this.newEmailSecret != null) {
|
|
1327
1308
|
const link = `${verifyUrl}?action=verifychange&id=${encodeURIComponent(
|
|
1328
1309
|
this.guid,
|
|
1329
|
-
)}&secret=${encodeURIComponent(this
|
|
1310
|
+
)}&secret=${encodeURIComponent(this.newEmailSecret)}`;
|
|
1330
1311
|
success =
|
|
1331
1312
|
success &&
|
|
1332
1313
|
(await tilmeld.config.sendEmail(
|
|
@@ -1335,24 +1316,24 @@ export default class User extends AbleObject<UserData> {
|
|
|
1335
1316
|
template: 'VerifyEmailChange',
|
|
1336
1317
|
message: {
|
|
1337
1318
|
to: {
|
|
1338
|
-
name: this
|
|
1339
|
-
address: this
|
|
1319
|
+
name: this.name ?? '',
|
|
1320
|
+
address: this.newEmailAddress ?? '',
|
|
1340
1321
|
},
|
|
1341
1322
|
},
|
|
1342
1323
|
locals: {
|
|
1343
1324
|
verifyLink: link,
|
|
1344
|
-
oldEmail: this
|
|
1345
|
-
newEmail: this
|
|
1325
|
+
oldEmail: this.cancelEmailAddress,
|
|
1326
|
+
newEmail: this.newEmailAddress,
|
|
1346
1327
|
},
|
|
1347
1328
|
},
|
|
1348
1329
|
this,
|
|
1349
1330
|
));
|
|
1350
1331
|
}
|
|
1351
1332
|
|
|
1352
|
-
if (this
|
|
1333
|
+
if (this.cancelEmailSecret != null) {
|
|
1353
1334
|
const link = `${verifyUrl}?action=cancelchange&id=${encodeURIComponent(
|
|
1354
1335
|
this.guid,
|
|
1355
|
-
)}&secret=${encodeURIComponent(this
|
|
1336
|
+
)}&secret=${encodeURIComponent(this.cancelEmailSecret)}`;
|
|
1356
1337
|
success =
|
|
1357
1338
|
success &&
|
|
1358
1339
|
(await tilmeld.config.sendEmail(
|
|
@@ -1361,14 +1342,14 @@ export default class User extends AbleObject<UserData> {
|
|
|
1361
1342
|
template: 'CancelEmailChange',
|
|
1362
1343
|
message: {
|
|
1363
1344
|
to: {
|
|
1364
|
-
name: this
|
|
1365
|
-
address: this
|
|
1345
|
+
name: this.name ?? '',
|
|
1346
|
+
address: this.cancelEmailAddress ?? '',
|
|
1366
1347
|
},
|
|
1367
1348
|
},
|
|
1368
1349
|
locals: {
|
|
1369
1350
|
cancelLink: link,
|
|
1370
|
-
oldEmail: this
|
|
1371
|
-
newEmail: this
|
|
1351
|
+
oldEmail: this.cancelEmailAddress,
|
|
1352
|
+
newEmail: this.newEmailAddress ?? this.email,
|
|
1372
1353
|
},
|
|
1373
1354
|
},
|
|
1374
1355
|
this,
|
|
@@ -1384,15 +1365,15 @@ export default class User extends AbleObject<UserData> {
|
|
|
1384
1365
|
* @param group The group.
|
|
1385
1366
|
* @returns True if the user is already in the group. The resulting array of groups if the user was not.
|
|
1386
1367
|
*/
|
|
1387
|
-
public async $addGroup(group: GroupClass
|
|
1388
|
-
await Promise.all(this
|
|
1389
|
-
let groups = this
|
|
1368
|
+
public async $addGroup(group: GroupClass) {
|
|
1369
|
+
await Promise.all(this.groups?.map((e) => e.$wake()) || []);
|
|
1370
|
+
let groups = this.groups;
|
|
1390
1371
|
if (groups == null) {
|
|
1391
1372
|
groups = [];
|
|
1392
1373
|
}
|
|
1393
1374
|
if (!group.$inArray(groups)) {
|
|
1394
1375
|
groups.push(group);
|
|
1395
|
-
this
|
|
1376
|
+
this.groups = groups;
|
|
1396
1377
|
return groups;
|
|
1397
1378
|
}
|
|
1398
1379
|
return true;
|
|
@@ -1408,19 +1389,19 @@ export default class User extends AbleObject<UserData> {
|
|
|
1408
1389
|
const tilmeld = enforceTilmeld(this);
|
|
1409
1390
|
switch (tilmeld.config.pwMethod) {
|
|
1410
1391
|
case 'plain':
|
|
1411
|
-
return this
|
|
1392
|
+
return this.password === password;
|
|
1412
1393
|
case 'digest':
|
|
1413
1394
|
return (
|
|
1414
|
-
this
|
|
1395
|
+
this.password ===
|
|
1415
1396
|
crypto.createHash('sha256').update(password).digest('base64')
|
|
1416
1397
|
);
|
|
1417
1398
|
case 'salt':
|
|
1418
1399
|
default:
|
|
1419
1400
|
return (
|
|
1420
|
-
this
|
|
1401
|
+
this.password ===
|
|
1421
1402
|
crypto
|
|
1422
1403
|
.createHash('sha256')
|
|
1423
|
-
.update(password + this
|
|
1404
|
+
.update(password + this.salt)
|
|
1424
1405
|
.digest('base64')
|
|
1425
1406
|
);
|
|
1426
1407
|
}
|
|
@@ -1432,18 +1413,18 @@ export default class User extends AbleObject<UserData> {
|
|
|
1432
1413
|
* @param group The group.
|
|
1433
1414
|
* @returns True if the user wasn't in the group. The resulting array of groups if the user was.
|
|
1434
1415
|
*/
|
|
1435
|
-
public async $delGroup(group: GroupClass
|
|
1436
|
-
await Promise.all(this
|
|
1437
|
-
let groups = this
|
|
1438
|
-
if (this
|
|
1439
|
-
const newGroups:
|
|
1416
|
+
public async $delGroup(group: GroupClass) {
|
|
1417
|
+
await Promise.all(this.groups?.map((e) => e.$wake()) || []);
|
|
1418
|
+
let groups = this.groups || [];
|
|
1419
|
+
if (this.groups != null && group.$inArray(groups)) {
|
|
1420
|
+
const newGroups: GroupClass[] = [];
|
|
1440
1421
|
for (let curGroup of groups) {
|
|
1441
1422
|
if (!group.$is(curGroup)) {
|
|
1442
1423
|
newGroups.push(curGroup);
|
|
1443
1424
|
}
|
|
1444
1425
|
}
|
|
1445
|
-
this
|
|
1446
|
-
return this
|
|
1426
|
+
this.groups = newGroups;
|
|
1427
|
+
return this.groups;
|
|
1447
1428
|
}
|
|
1448
1429
|
return true;
|
|
1449
1430
|
}
|
|
@@ -1454,21 +1435,19 @@ export default class User extends AbleObject<UserData> {
|
|
|
1454
1435
|
* @param group The group, or the group's GUID.
|
|
1455
1436
|
* @returns True or false.
|
|
1456
1437
|
*/
|
|
1457
|
-
public async $inGroup(group:
|
|
1438
|
+
public async $inGroup(group: GroupClass | string) {
|
|
1458
1439
|
if (typeof group === 'string') {
|
|
1459
|
-
if (this
|
|
1440
|
+
if (this.group?.guid === group) {
|
|
1460
1441
|
return true;
|
|
1461
1442
|
}
|
|
1462
|
-
for (let curGroup of this
|
|
1443
|
+
for (let curGroup of this.groups ?? []) {
|
|
1463
1444
|
if (curGroup.guid === group) {
|
|
1464
1445
|
return true;
|
|
1465
1446
|
}
|
|
1466
1447
|
}
|
|
1467
1448
|
return false;
|
|
1468
1449
|
}
|
|
1469
|
-
return (
|
|
1470
|
-
group.$is(this.$data.group) || group.$inArray(this.$data.groups ?? [])
|
|
1471
|
-
);
|
|
1450
|
+
return group.$is(this.group) || group.$inArray(this.groups ?? []);
|
|
1472
1451
|
}
|
|
1473
1452
|
|
|
1474
1453
|
/**
|
|
@@ -1477,18 +1456,18 @@ export default class User extends AbleObject<UserData> {
|
|
|
1477
1456
|
* @param group The group, or the group's GUID.
|
|
1478
1457
|
* @returns True or false.
|
|
1479
1458
|
*/
|
|
1480
|
-
public async $isDescendant(group:
|
|
1459
|
+
public async $isDescendant(group: GroupClass | string) {
|
|
1481
1460
|
// Check to see if the user is in a descendant group of the given group.
|
|
1482
|
-
await this
|
|
1461
|
+
await this.group?.$wake();
|
|
1483
1462
|
if (
|
|
1484
|
-
this
|
|
1485
|
-
this
|
|
1486
|
-
(await this
|
|
1463
|
+
this.group != null &&
|
|
1464
|
+
this.group?.cdate != null &&
|
|
1465
|
+
(await this.group?.$isDescendant(group))
|
|
1487
1466
|
) {
|
|
1488
1467
|
return true;
|
|
1489
1468
|
}
|
|
1490
|
-
await Promise.all(this
|
|
1491
|
-
for (let curGroup of this
|
|
1469
|
+
await Promise.all(this.groups?.map((e) => e.$wake()) || []);
|
|
1470
|
+
for (let curGroup of this.groups ?? []) {
|
|
1492
1471
|
if (await curGroup.$isDescendant(group)) {
|
|
1493
1472
|
return true;
|
|
1494
1473
|
}
|
|
@@ -1509,7 +1488,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1509
1488
|
}): Promise<{ result: boolean; message: string }> {
|
|
1510
1489
|
this.$nymph.config.debugLog(
|
|
1511
1490
|
'tilmeld',
|
|
1512
|
-
`Password change request for "${this
|
|
1491
|
+
`Password change request for "${this.username}".`,
|
|
1513
1492
|
);
|
|
1514
1493
|
if (!('newPassword' in data) || !data.newPassword.length) {
|
|
1515
1494
|
return { result: false, message: 'Please specify a password.' };
|
|
@@ -1517,13 +1496,13 @@ export default class User extends AbleObject<UserData> {
|
|
|
1517
1496
|
if (!this.$checkPassword(data.currentPassword ?? '')) {
|
|
1518
1497
|
this.$nymph.config.debugLog(
|
|
1519
1498
|
'tilmeld',
|
|
1520
|
-
`Incorrect password for change request for "${this
|
|
1499
|
+
`Incorrect password for change request for "${this.username}".`,
|
|
1521
1500
|
);
|
|
1522
1501
|
return { result: false, message: 'Incorrect password.' };
|
|
1523
1502
|
}
|
|
1524
|
-
this
|
|
1503
|
+
this.passwordTemp = data.newPassword;
|
|
1525
1504
|
if (data.revokeCurrentTokens) {
|
|
1526
|
-
this
|
|
1505
|
+
this.revokeTokenDate = Date.now();
|
|
1527
1506
|
}
|
|
1528
1507
|
try {
|
|
1529
1508
|
await this.$save();
|
|
@@ -1547,26 +1526,26 @@ export default class User extends AbleObject<UserData> {
|
|
|
1547
1526
|
const tilmeld = enforceTilmeld(this);
|
|
1548
1527
|
switch (tilmeld.config.pwMethod) {
|
|
1549
1528
|
case 'plain':
|
|
1550
|
-
delete this
|
|
1551
|
-
this
|
|
1529
|
+
delete this.salt;
|
|
1530
|
+
this.password = password;
|
|
1552
1531
|
break;
|
|
1553
1532
|
case 'digest':
|
|
1554
|
-
delete this
|
|
1555
|
-
this
|
|
1533
|
+
delete this.salt;
|
|
1534
|
+
this.password = crypto
|
|
1556
1535
|
.createHash('sha256')
|
|
1557
1536
|
.update(password)
|
|
1558
1537
|
.digest('base64');
|
|
1559
1538
|
break;
|
|
1560
1539
|
case 'salt':
|
|
1561
1540
|
default:
|
|
1562
|
-
this
|
|
1563
|
-
this
|
|
1541
|
+
this.salt = nanoid();
|
|
1542
|
+
this.password = crypto
|
|
1564
1543
|
.createHash('sha256')
|
|
1565
|
-
.update(password + this
|
|
1544
|
+
.update(password + this.salt)
|
|
1566
1545
|
.digest('base64');
|
|
1567
1546
|
break;
|
|
1568
1547
|
}
|
|
1569
|
-
return this
|
|
1548
|
+
return this.password;
|
|
1570
1549
|
}
|
|
1571
1550
|
|
|
1572
1551
|
/**
|
|
@@ -1582,7 +1561,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1582
1561
|
return { result: false, message: 'Incorrect password.' };
|
|
1583
1562
|
}
|
|
1584
1563
|
|
|
1585
|
-
this
|
|
1564
|
+
this.revokeTokenDate = Date.now();
|
|
1586
1565
|
|
|
1587
1566
|
try {
|
|
1588
1567
|
await this.$save();
|
|
@@ -1598,7 +1577,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1598
1577
|
}
|
|
1599
1578
|
|
|
1600
1579
|
public $hasTOTPSecret(): boolean {
|
|
1601
|
-
return this
|
|
1580
|
+
return this.totpSecret != null;
|
|
1602
1581
|
}
|
|
1603
1582
|
|
|
1604
1583
|
/**
|
|
@@ -1608,7 +1587,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1608
1587
|
* @returns True if the code is valid, otherwise false.
|
|
1609
1588
|
*/
|
|
1610
1589
|
public $checkTOTPCode(code: string) {
|
|
1611
|
-
if (this
|
|
1590
|
+
if (this.totpSecret == null) {
|
|
1612
1591
|
return false;
|
|
1613
1592
|
}
|
|
1614
1593
|
|
|
@@ -1616,11 +1595,11 @@ export default class User extends AbleObject<UserData> {
|
|
|
1616
1595
|
|
|
1617
1596
|
const totp = new TOTP({
|
|
1618
1597
|
issuer: tilmeld.config.appName,
|
|
1619
|
-
label: this
|
|
1598
|
+
label: this.username,
|
|
1620
1599
|
algorithm: 'SHA1',
|
|
1621
1600
|
digits: 6,
|
|
1622
1601
|
period: 30,
|
|
1623
|
-
secret: this
|
|
1602
|
+
secret: this.totpSecret,
|
|
1624
1603
|
});
|
|
1625
1604
|
|
|
1626
1605
|
return totp.validate({ token: code, window: 2 }) != null;
|
|
@@ -1638,7 +1617,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1638
1617
|
}> {
|
|
1639
1618
|
const tilmeld = enforceTilmeld(this);
|
|
1640
1619
|
|
|
1641
|
-
if (this
|
|
1620
|
+
if (this.totpSecret != null) {
|
|
1642
1621
|
throw new BadDataError('You already have a 2FA secret.');
|
|
1643
1622
|
}
|
|
1644
1623
|
|
|
@@ -1646,7 +1625,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1646
1625
|
|
|
1647
1626
|
const totp = new TOTP({
|
|
1648
1627
|
issuer: tilmeld.config.appName,
|
|
1649
|
-
label: this
|
|
1628
|
+
label: this.username,
|
|
1650
1629
|
algorithm: 'SHA1',
|
|
1651
1630
|
digits: 6,
|
|
1652
1631
|
period: 30,
|
|
@@ -1675,7 +1654,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1675
1654
|
return { result: false, message: 'Incorrect password.' };
|
|
1676
1655
|
}
|
|
1677
1656
|
|
|
1678
|
-
if (this
|
|
1657
|
+
if (this.totpSecret != null) {
|
|
1679
1658
|
return { result: false, message: 'You already have a 2FA secret.' };
|
|
1680
1659
|
}
|
|
1681
1660
|
|
|
@@ -1699,7 +1678,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1699
1678
|
|
|
1700
1679
|
const totp = new TOTP({
|
|
1701
1680
|
issuer: tilmeld.config.appName,
|
|
1702
|
-
label: this
|
|
1681
|
+
label: this.username,
|
|
1703
1682
|
algorithm: 'SHA1',
|
|
1704
1683
|
digits: 6,
|
|
1705
1684
|
period: 30,
|
|
@@ -1710,7 +1689,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1710
1689
|
return { result: false, message: '2FA code is incorrect.' };
|
|
1711
1690
|
}
|
|
1712
1691
|
|
|
1713
|
-
this
|
|
1692
|
+
this.totpSecret = data.secret;
|
|
1714
1693
|
|
|
1715
1694
|
try {
|
|
1716
1695
|
await this.$save();
|
|
@@ -1746,17 +1725,17 @@ export default class User extends AbleObject<UserData> {
|
|
|
1746
1725
|
return { result: false, message: '2FA code is invalid.' };
|
|
1747
1726
|
}
|
|
1748
1727
|
|
|
1749
|
-
if (this
|
|
1728
|
+
if (this.totpSecret == null) {
|
|
1750
1729
|
return { result: false, message: "You don't have a 2FA secret." };
|
|
1751
1730
|
}
|
|
1752
1731
|
|
|
1753
1732
|
const totp = new TOTP({
|
|
1754
1733
|
issuer: tilmeld.config.appName,
|
|
1755
|
-
label: this
|
|
1734
|
+
label: this.username,
|
|
1756
1735
|
algorithm: 'SHA1',
|
|
1757
1736
|
digits: 6,
|
|
1758
1737
|
period: 30,
|
|
1759
|
-
secret: this
|
|
1738
|
+
secret: this.totpSecret,
|
|
1760
1739
|
});
|
|
1761
1740
|
|
|
1762
1741
|
if (totp.validate({ token: data.code, window: 2 }) == null) {
|
|
@@ -1769,7 +1748,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1769
1748
|
};
|
|
1770
1749
|
}
|
|
1771
1750
|
|
|
1772
|
-
delete this
|
|
1751
|
+
delete this.totpSecret;
|
|
1773
1752
|
|
|
1774
1753
|
try {
|
|
1775
1754
|
await this.$save();
|
|
@@ -1789,23 +1768,23 @@ export default class User extends AbleObject<UserData> {
|
|
|
1789
1768
|
|
|
1790
1769
|
this.$nymph.config.debugInfo(
|
|
1791
1770
|
'tilmeld',
|
|
1792
|
-
`Username check "${this
|
|
1771
|
+
`Username check "${this.username}".`,
|
|
1793
1772
|
);
|
|
1794
1773
|
|
|
1795
1774
|
if (!tilmeld.config.emailUsernames) {
|
|
1796
1775
|
let [username, domain] = tilmeld.config.domainSupport
|
|
1797
|
-
? splitn(this
|
|
1798
|
-
: [this
|
|
1776
|
+
? splitn(this.username ?? '', '@', 2)
|
|
1777
|
+
: [this.username, null];
|
|
1799
1778
|
|
|
1800
1779
|
// Lowercase the domain part.
|
|
1801
1780
|
if (domain) {
|
|
1802
1781
|
domain = domain.toLowerCase();
|
|
1803
|
-
this
|
|
1782
|
+
this.username = `${username}@${domain}`;
|
|
1804
1783
|
}
|
|
1805
1784
|
|
|
1806
1785
|
if (
|
|
1807
|
-
this
|
|
1808
|
-
!this
|
|
1786
|
+
this.username == null ||
|
|
1787
|
+
!this.username.length ||
|
|
1809
1788
|
username == null ||
|
|
1810
1789
|
!username.length
|
|
1811
1790
|
) {
|
|
@@ -1863,7 +1842,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1863
1842
|
type: '&',
|
|
1864
1843
|
ilike: [
|
|
1865
1844
|
'username',
|
|
1866
|
-
this
|
|
1845
|
+
this.username.replace(/([\\%_])/g, (s) => `\\${s}`),
|
|
1867
1846
|
],
|
|
1868
1847
|
};
|
|
1869
1848
|
if (this.guid != null) {
|
|
@@ -1881,7 +1860,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1881
1860
|
.checkUsernameCallbacks) {
|
|
1882
1861
|
if (callback) {
|
|
1883
1862
|
const result = await callback(this, {
|
|
1884
|
-
username: this
|
|
1863
|
+
username: this.username,
|
|
1885
1864
|
});
|
|
1886
1865
|
if (!result.result) {
|
|
1887
1866
|
return { message: 'Username is not available.', ...result };
|
|
@@ -1895,11 +1874,11 @@ export default class User extends AbleObject<UserData> {
|
|
|
1895
1874
|
this.guid != null ? 'Username is valid.' : 'Username is available!',
|
|
1896
1875
|
};
|
|
1897
1876
|
} else {
|
|
1898
|
-
this
|
|
1899
|
-
if (this
|
|
1877
|
+
this.email = this.username;
|
|
1878
|
+
if (this.username == null || !this.username.length) {
|
|
1900
1879
|
return { result: false, message: 'Please specify an email.' };
|
|
1901
1880
|
}
|
|
1902
|
-
if (this
|
|
1881
|
+
if (this.username.length < tilmeld.config.minUsernameLength) {
|
|
1903
1882
|
return {
|
|
1904
1883
|
result: false,
|
|
1905
1884
|
message:
|
|
@@ -1908,7 +1887,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
1908
1887
|
' characters.',
|
|
1909
1888
|
};
|
|
1910
1889
|
}
|
|
1911
|
-
if (this
|
|
1890
|
+
if (this.username.length > tilmeld.config.maxUsernameLength) {
|
|
1912
1891
|
return {
|
|
1913
1892
|
result: false,
|
|
1914
1893
|
message:
|
|
@@ -1930,24 +1909,21 @@ export default class User extends AbleObject<UserData> {
|
|
|
1930
1909
|
public async $checkEmail(): Promise<{ result: boolean; message: string }> {
|
|
1931
1910
|
const tilmeld = enforceTilmeld(this);
|
|
1932
1911
|
|
|
1933
|
-
this.$nymph.config.debugInfo(
|
|
1934
|
-
'tilmeld',
|
|
1935
|
-
`Email check "${this.$data.email}".`,
|
|
1936
|
-
);
|
|
1912
|
+
this.$nymph.config.debugInfo('tilmeld', `Email check "${this.email}".`);
|
|
1937
1913
|
|
|
1938
|
-
if (this
|
|
1914
|
+
if (this.email == null || !this.email.length) {
|
|
1939
1915
|
if (tilmeld.config.verifyEmail) {
|
|
1940
1916
|
return { result: false, message: 'Please specify an email.' };
|
|
1941
1917
|
} else {
|
|
1942
1918
|
return { result: true, message: '' };
|
|
1943
1919
|
}
|
|
1944
1920
|
}
|
|
1945
|
-
if (!tilmeld.config.validEmailRegex.test(this
|
|
1921
|
+
if (!tilmeld.config.validEmailRegex.test(this.email)) {
|
|
1946
1922
|
return { result: false, message: tilmeld.config.validEmailRegexNotice };
|
|
1947
1923
|
}
|
|
1948
1924
|
const selector: Selector = {
|
|
1949
1925
|
type: '&',
|
|
1950
|
-
ilike: ['email', this
|
|
1926
|
+
ilike: ['email', this.email.replace(/([\\%_])/g, (s) => `\\${s}`)],
|
|
1951
1927
|
};
|
|
1952
1928
|
if (this.guid != null) {
|
|
1953
1929
|
selector['!guid'] = this.guid;
|
|
@@ -1978,16 +1954,13 @@ export default class User extends AbleObject<UserData> {
|
|
|
1978
1954
|
public async $checkPhone(): Promise<{ result: boolean; message: string }> {
|
|
1979
1955
|
const tilmeld = enforceTilmeld(this);
|
|
1980
1956
|
|
|
1981
|
-
this.$nymph.config.debugInfo(
|
|
1982
|
-
'tilmeld',
|
|
1983
|
-
`Phone check "${this.$data.phone}".`,
|
|
1984
|
-
);
|
|
1957
|
+
this.$nymph.config.debugInfo('tilmeld', `Phone check "${this.phone}".`);
|
|
1985
1958
|
|
|
1986
|
-
if (this
|
|
1959
|
+
if (this.phone == null || !this.phone.length) {
|
|
1987
1960
|
return { result: false, message: 'Please specify a phone number.' };
|
|
1988
1961
|
}
|
|
1989
1962
|
|
|
1990
|
-
const stripToDigits = this
|
|
1963
|
+
const stripToDigits = this.phone.replace(/\D/g, () => '');
|
|
1991
1964
|
if (!/\d{10}/.test(stripToDigits)) {
|
|
1992
1965
|
return {
|
|
1993
1966
|
result: false,
|
|
@@ -2051,16 +2024,16 @@ export default class User extends AbleObject<UserData> {
|
|
|
2051
2024
|
|
|
2052
2025
|
this.$password(data.password);
|
|
2053
2026
|
if (tilmeld.config.regFields.includes('name')) {
|
|
2054
|
-
this
|
|
2055
|
-
this
|
|
2056
|
-
(this
|
|
2057
|
-
(this
|
|
2058
|
-
if (this
|
|
2059
|
-
this
|
|
2027
|
+
this.name =
|
|
2028
|
+
this.nameFirst +
|
|
2029
|
+
(this.nameMiddle == null ? '' : ' ' + this.nameMiddle) +
|
|
2030
|
+
(this.nameLast == null ? '' : ' ' + this.nameLast);
|
|
2031
|
+
if (this.name === '') {
|
|
2032
|
+
this.name = this.username;
|
|
2060
2033
|
}
|
|
2061
2034
|
}
|
|
2062
2035
|
if (tilmeld.config.emailUsernames) {
|
|
2063
|
-
this
|
|
2036
|
+
this.email = this.username;
|
|
2064
2037
|
}
|
|
2065
2038
|
|
|
2066
2039
|
try {
|
|
@@ -2087,10 +2060,10 @@ export default class User extends AbleObject<UserData> {
|
|
|
2087
2060
|
) {
|
|
2088
2061
|
// The user will be enabled after verifying their e-mail address.
|
|
2089
2062
|
if (!tilmeld.config.unverifiedAccess) {
|
|
2090
|
-
this
|
|
2063
|
+
this.enabled = false;
|
|
2091
2064
|
}
|
|
2092
2065
|
} else {
|
|
2093
|
-
this
|
|
2066
|
+
this.enabled = true;
|
|
2094
2067
|
}
|
|
2095
2068
|
|
|
2096
2069
|
// If createAdmin is true and there are no other users, grant
|
|
@@ -2109,14 +2082,14 @@ export default class User extends AbleObject<UserData> {
|
|
|
2109
2082
|
}
|
|
2110
2083
|
if (!otherUsers.length) {
|
|
2111
2084
|
this.$grant('system/admin');
|
|
2112
|
-
this
|
|
2085
|
+
this.enabled = true;
|
|
2113
2086
|
madeAdmin = true;
|
|
2114
2087
|
}
|
|
2115
2088
|
}
|
|
2116
2089
|
|
|
2117
2090
|
this.$nymph.config.debugLog(
|
|
2118
2091
|
'tilmeld',
|
|
2119
|
-
`Registering new user "${this
|
|
2092
|
+
`Registering new user "${this.username}".`,
|
|
2120
2093
|
);
|
|
2121
2094
|
|
|
2122
2095
|
await this.$saveSkipAC();
|
|
@@ -2129,7 +2102,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
2129
2102
|
tilmeld.config.userFields.includes('email')
|
|
2130
2103
|
) {
|
|
2131
2104
|
message +=
|
|
2132
|
-
`Almost there. An email has been sent to ${this
|
|
2105
|
+
`Almost there. An email has been sent to ${this.email} ` +
|
|
2133
2106
|
'with a verification link for you to finish registration.';
|
|
2134
2107
|
} else if (
|
|
2135
2108
|
tilmeld.config.verifyEmail &&
|
|
@@ -2142,7 +2115,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
2142
2115
|
}
|
|
2143
2116
|
message +=
|
|
2144
2117
|
"You're now logged in! An email has been sent to " +
|
|
2145
|
-
`${this
|
|
2118
|
+
`${this.email} with a verification link for you to finish ` +
|
|
2146
2119
|
'registration.';
|
|
2147
2120
|
loggedin = true;
|
|
2148
2121
|
} else {
|
|
@@ -2177,12 +2150,12 @@ export default class User extends AbleObject<UserData> {
|
|
|
2177
2150
|
template: 'UserRegistered',
|
|
2178
2151
|
message: { to: tilmeld.config.userRegisteredRecipient },
|
|
2179
2152
|
locals: {
|
|
2180
|
-
userUsername: this
|
|
2181
|
-
userName: this
|
|
2182
|
-
userFirstName: this
|
|
2183
|
-
userLastName: this
|
|
2184
|
-
userEmail: this
|
|
2185
|
-
userPhone: this
|
|
2153
|
+
userUsername: this.username,
|
|
2154
|
+
userName: this.name,
|
|
2155
|
+
userFirstName: this.nameFirst,
|
|
2156
|
+
userLastName: this.nameLast,
|
|
2157
|
+
userEmail: this.email,
|
|
2158
|
+
userPhone: this.phone,
|
|
2186
2159
|
},
|
|
2187
2160
|
},
|
|
2188
2161
|
this,
|
|
@@ -2193,7 +2166,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
2193
2166
|
} catch (e: any) {
|
|
2194
2167
|
this.$nymph.config.debugError(
|
|
2195
2168
|
'tilmeld',
|
|
2196
|
-
`Couldn't send new user registered email for "${this
|
|
2169
|
+
`Couldn't send new user registered email for "${this.username}": ${e.message}`,
|
|
2197
2170
|
);
|
|
2198
2171
|
}
|
|
2199
2172
|
}
|
|
@@ -2218,7 +2191,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
2218
2191
|
|
|
2219
2192
|
public async $save() {
|
|
2220
2193
|
let tilmeld = enforceTilmeld(this);
|
|
2221
|
-
if (this
|
|
2194
|
+
if (this.username == null || !this.username.trim().length) {
|
|
2222
2195
|
throw new BadUsernameError('Username must not be empty.');
|
|
2223
2196
|
}
|
|
2224
2197
|
|
|
@@ -2226,7 +2199,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
2226
2199
|
!this.$skipAcWhenSaving &&
|
|
2227
2200
|
tilmeld.gatekeeper('tilmeld/admin') &&
|
|
2228
2201
|
!tilmeld.gatekeeper('system/admin') &&
|
|
2229
|
-
this
|
|
2202
|
+
this.abilities?.includes('system/admin')
|
|
2230
2203
|
) {
|
|
2231
2204
|
throw new AccessControlError(
|
|
2232
2205
|
"You don't have the authority to modify system admins.",
|
|
@@ -2235,14 +2208,14 @@ export default class User extends AbleObject<UserData> {
|
|
|
2235
2208
|
|
|
2236
2209
|
// Lowercase the domain part.
|
|
2237
2210
|
if (tilmeld.config.domainSupport) {
|
|
2238
|
-
const [username, domain] = splitn(this
|
|
2211
|
+
const [username, domain] = splitn(this.username ?? '', '@', 2);
|
|
2239
2212
|
if (domain) {
|
|
2240
|
-
this
|
|
2213
|
+
this.username = `${username}@${domain.toLowerCase()}`;
|
|
2241
2214
|
}
|
|
2242
2215
|
}
|
|
2243
2216
|
|
|
2244
2217
|
if (tilmeld.config.domainSupport) {
|
|
2245
|
-
const [_username, domain] = splitn(this
|
|
2218
|
+
const [_username, domain] = splitn(this.username ?? '', '@', 2);
|
|
2246
2219
|
const [_originalUsername, originalDomain] = splitn(
|
|
2247
2220
|
this.$originalUsername ?? '',
|
|
2248
2221
|
'@',
|
|
@@ -2289,113 +2262,95 @@ export default class User extends AbleObject<UserData> {
|
|
|
2289
2262
|
let sendVerification = false;
|
|
2290
2263
|
|
|
2291
2264
|
// Formatting.
|
|
2292
|
-
this
|
|
2265
|
+
this.username = this.username.trim();
|
|
2293
2266
|
if (tilmeld.config.emailUsernames) {
|
|
2294
|
-
this
|
|
2295
|
-
}
|
|
2296
|
-
this
|
|
2297
|
-
this
|
|
2298
|
-
this
|
|
2299
|
-
this
|
|
2300
|
-
this
|
|
2301
|
-
this
|
|
2302
|
-
(this
|
|
2303
|
-
(this
|
|
2304
|
-
this
|
|
2267
|
+
this.email = this.username;
|
|
2268
|
+
}
|
|
2269
|
+
this.nameFirst = (this.nameFirst ?? '').trim();
|
|
2270
|
+
this.nameMiddle = (this.nameMiddle ?? '').trim();
|
|
2271
|
+
this.nameLast = (this.nameLast ?? '').trim();
|
|
2272
|
+
this.phone = (this.phone ?? '').trim();
|
|
2273
|
+
this.name =
|
|
2274
|
+
this.nameFirst +
|
|
2275
|
+
(this.nameMiddle ? ' ' + this.nameMiddle : '') +
|
|
2276
|
+
(this.nameLast ? ' ' + this.nameLast : '');
|
|
2277
|
+
this.enabled = !!this.enabled;
|
|
2305
2278
|
|
|
2306
2279
|
// Clear empty values.
|
|
2307
|
-
if (
|
|
2308
|
-
this
|
|
2309
|
-
!tilmeld.config.userFields.includes('name')
|
|
2310
|
-
) {
|
|
2311
|
-
delete this.$data.nameFirst;
|
|
2280
|
+
if (this.nameFirst === '' || !tilmeld.config.userFields.includes('name')) {
|
|
2281
|
+
delete this.nameFirst;
|
|
2312
2282
|
}
|
|
2313
|
-
if (
|
|
2314
|
-
this
|
|
2315
|
-
!tilmeld.config.userFields.includes('name')
|
|
2316
|
-
) {
|
|
2317
|
-
delete this.$data.nameMiddle;
|
|
2283
|
+
if (this.nameMiddle === '' || !tilmeld.config.userFields.includes('name')) {
|
|
2284
|
+
delete this.nameMiddle;
|
|
2318
2285
|
}
|
|
2319
|
-
if (
|
|
2320
|
-
this
|
|
2321
|
-
!tilmeld.config.userFields.includes('name')
|
|
2322
|
-
) {
|
|
2323
|
-
delete this.$data.nameLast;
|
|
2286
|
+
if (this.nameLast === '' || !tilmeld.config.userFields.includes('name')) {
|
|
2287
|
+
delete this.nameLast;
|
|
2324
2288
|
}
|
|
2325
|
-
if (this
|
|
2326
|
-
delete this
|
|
2289
|
+
if (this.name === '' || !tilmeld.config.userFields.includes('name')) {
|
|
2290
|
+
delete this.name;
|
|
2327
2291
|
}
|
|
2328
|
-
if (
|
|
2329
|
-
this
|
|
2330
|
-
!tilmeld.config.userFields.includes('email')
|
|
2331
|
-
) {
|
|
2332
|
-
delete this.$data.email;
|
|
2292
|
+
if (this.email === '' || !tilmeld.config.userFields.includes('email')) {
|
|
2293
|
+
delete this.email;
|
|
2333
2294
|
}
|
|
2334
|
-
if (this
|
|
2335
|
-
delete this
|
|
2295
|
+
if (this.avatar === '') {
|
|
2296
|
+
delete this.avatar;
|
|
2336
2297
|
}
|
|
2337
|
-
if (
|
|
2338
|
-
this
|
|
2339
|
-
!tilmeld.config.userFields.includes('phone')
|
|
2340
|
-
) {
|
|
2341
|
-
delete this.$data.phone;
|
|
2298
|
+
if (this.phone === '' || !tilmeld.config.userFields.includes('phone')) {
|
|
2299
|
+
delete this.phone;
|
|
2342
2300
|
}
|
|
2343
|
-
if (
|
|
2344
|
-
this
|
|
2345
|
-
!tilmeld.config.userFields.includes('email')
|
|
2346
|
-
) {
|
|
2347
|
-
delete this.$data.secret;
|
|
2301
|
+
if (this.secret === '' || !tilmeld.config.userFields.includes('email')) {
|
|
2302
|
+
delete this.secret;
|
|
2348
2303
|
}
|
|
2349
2304
|
if (
|
|
2350
|
-
this
|
|
2305
|
+
this.emailChangeDate === 0 ||
|
|
2351
2306
|
!tilmeld.config.userFields.includes('email')
|
|
2352
2307
|
) {
|
|
2353
|
-
delete this
|
|
2308
|
+
delete this.emailChangeDate;
|
|
2354
2309
|
}
|
|
2355
2310
|
if (
|
|
2356
|
-
this
|
|
2311
|
+
this.newEmailSecret === '' ||
|
|
2357
2312
|
!tilmeld.config.userFields.includes('email')
|
|
2358
2313
|
) {
|
|
2359
|
-
delete this
|
|
2314
|
+
delete this.newEmailSecret;
|
|
2360
2315
|
}
|
|
2361
2316
|
if (
|
|
2362
|
-
this
|
|
2317
|
+
this.newEmailAddress === '' ||
|
|
2363
2318
|
!tilmeld.config.userFields.includes('email')
|
|
2364
2319
|
) {
|
|
2365
|
-
delete this
|
|
2320
|
+
delete this.newEmailAddress;
|
|
2366
2321
|
}
|
|
2367
2322
|
if (
|
|
2368
|
-
this
|
|
2323
|
+
this.cancelEmailSecret === '' ||
|
|
2369
2324
|
!tilmeld.config.userFields.includes('email')
|
|
2370
2325
|
) {
|
|
2371
|
-
delete this
|
|
2326
|
+
delete this.cancelEmailSecret;
|
|
2372
2327
|
}
|
|
2373
2328
|
if (
|
|
2374
|
-
this
|
|
2329
|
+
this.cancelEmailAddress === '' ||
|
|
2375
2330
|
!tilmeld.config.userFields.includes('email')
|
|
2376
2331
|
) {
|
|
2377
|
-
delete this
|
|
2332
|
+
delete this.cancelEmailAddress;
|
|
2378
2333
|
}
|
|
2379
2334
|
if (
|
|
2380
|
-
this
|
|
2335
|
+
this.recoverSecret === '' ||
|
|
2381
2336
|
!tilmeld.config.userFields.includes('email')
|
|
2382
2337
|
) {
|
|
2383
|
-
delete this
|
|
2338
|
+
delete this.recoverSecret;
|
|
2384
2339
|
}
|
|
2385
2340
|
if (
|
|
2386
|
-
this
|
|
2341
|
+
this.recoverSecretDate === 0 ||
|
|
2387
2342
|
!tilmeld.config.userFields.includes('email')
|
|
2388
2343
|
) {
|
|
2389
|
-
delete this
|
|
2344
|
+
delete this.recoverSecretDate;
|
|
2390
2345
|
}
|
|
2391
|
-
if (this
|
|
2392
|
-
delete this
|
|
2346
|
+
if (this.password === '') {
|
|
2347
|
+
delete this.password;
|
|
2393
2348
|
}
|
|
2394
|
-
if (this
|
|
2395
|
-
delete this
|
|
2349
|
+
if (this.passwordTemp === '') {
|
|
2350
|
+
delete this.passwordTemp;
|
|
2396
2351
|
}
|
|
2397
|
-
if (this
|
|
2398
|
-
delete this
|
|
2352
|
+
if (this.inheritAbilities === false) {
|
|
2353
|
+
delete this.inheritAbilities;
|
|
2399
2354
|
}
|
|
2400
2355
|
|
|
2401
2356
|
// Verification.
|
|
@@ -2425,19 +2380,19 @@ export default class User extends AbleObject<UserData> {
|
|
|
2425
2380
|
if (this.guid == null) {
|
|
2426
2381
|
// If this is the first user, they'll be made an admin, so they don't
|
|
2427
2382
|
// need to verify.
|
|
2428
|
-
if (!this
|
|
2429
|
-
this
|
|
2383
|
+
if (!this.abilities?.includes('system/admin')) {
|
|
2384
|
+
this.secret = nanoid();
|
|
2430
2385
|
sendVerification = true;
|
|
2431
2386
|
}
|
|
2432
2387
|
} else if (
|
|
2433
2388
|
this.$originalEmail != null &&
|
|
2434
|
-
this.$originalEmail !== this
|
|
2389
|
+
this.$originalEmail !== this.email
|
|
2435
2390
|
) {
|
|
2436
2391
|
// The user already has an old email address.
|
|
2437
2392
|
if (
|
|
2438
2393
|
tilmeld.config.emailRateLimit !== '' &&
|
|
2439
|
-
this
|
|
2440
|
-
this
|
|
2394
|
+
this.emailChangeDate != null &&
|
|
2395
|
+
this.emailChangeDate >=
|
|
2441
2396
|
(strtotime('-' + tilmeld.config.emailRateLimit) || 0) * 1000
|
|
2442
2397
|
) {
|
|
2443
2398
|
throw new EmailChangeRateLimitExceededError(
|
|
@@ -2445,61 +2400,61 @@ export default class User extends AbleObject<UserData> {
|
|
|
2445
2400
|
new Date(
|
|
2446
2401
|
(strtotime(
|
|
2447
2402
|
'+' + tilmeld.config.emailRateLimit,
|
|
2448
|
-
Math.floor(this
|
|
2403
|
+
Math.floor(this.emailChangeDate / 1000),
|
|
2449
2404
|
) || 0) * 1000,
|
|
2450
2405
|
).toString() +
|
|
2451
2406
|
' to change your email address again.',
|
|
2452
2407
|
);
|
|
2453
2408
|
} else {
|
|
2454
2409
|
if (
|
|
2455
|
-
this
|
|
2410
|
+
this.newEmailSecret == null &&
|
|
2456
2411
|
tilmeld.config.emailRateLimit !== '' &&
|
|
2457
2412
|
// Make sure the user has at least the rate limit time to cancel
|
|
2458
2413
|
// an email change.
|
|
2459
|
-
(this
|
|
2460
|
-
this
|
|
2414
|
+
(this.emailChangeDate == null ||
|
|
2415
|
+
this.emailChangeDate <
|
|
2461
2416
|
(strtotime('-' + tilmeld.config.emailRateLimit) || 0) * 1000)
|
|
2462
2417
|
) {
|
|
2463
2418
|
// Save the old email in case the cancel change link is clicked.
|
|
2464
|
-
this
|
|
2465
|
-
this
|
|
2466
|
-
this
|
|
2419
|
+
this.cancelEmailAddress = this.$originalEmail;
|
|
2420
|
+
this.cancelEmailSecret = nanoid();
|
|
2421
|
+
this.emailChangeDate = Date.now();
|
|
2467
2422
|
}
|
|
2468
2423
|
// Save the new address and reset to the old one (until they verify
|
|
2469
2424
|
// it).
|
|
2470
|
-
this
|
|
2471
|
-
this
|
|
2425
|
+
this.newEmailAddress = this.email;
|
|
2426
|
+
this.email = this.$originalEmail;
|
|
2472
2427
|
if (tilmeld.config.emailUsernames) {
|
|
2473
|
-
this
|
|
2428
|
+
this.username = this.email;
|
|
2474
2429
|
}
|
|
2475
|
-
this
|
|
2430
|
+
this.newEmailSecret = nanoid();
|
|
2476
2431
|
sendVerification = true;
|
|
2477
2432
|
}
|
|
2478
2433
|
}
|
|
2479
2434
|
} else if (
|
|
2480
2435
|
this.guid != null &&
|
|
2481
2436
|
this.$originalEmail != null &&
|
|
2482
|
-
this.$originalEmail !== this
|
|
2437
|
+
this.$originalEmail !== this.email &&
|
|
2483
2438
|
// Make sure the user has at least the rate limit time to cancel an
|
|
2484
2439
|
// email change.
|
|
2485
|
-
(this
|
|
2486
|
-
this
|
|
2440
|
+
(this.emailChangeDate == null ||
|
|
2441
|
+
this.emailChangeDate <
|
|
2487
2442
|
(strtotime('-' + tilmeld.config.emailRateLimit) || 0) * 1000)
|
|
2488
2443
|
) {
|
|
2489
2444
|
// The user doesn't need to verify their new email address, but should
|
|
2490
2445
|
// be able to cancel the email change from their old address.
|
|
2491
|
-
this
|
|
2492
|
-
this
|
|
2446
|
+
this.cancelEmailAddress = this.$originalEmail;
|
|
2447
|
+
this.cancelEmailSecret = nanoid();
|
|
2493
2448
|
sendVerification = true;
|
|
2494
2449
|
}
|
|
2495
2450
|
}
|
|
2496
2451
|
|
|
2497
|
-
if (this
|
|
2498
|
-
this.$password(this
|
|
2452
|
+
if (this.passwordTemp != null) {
|
|
2453
|
+
this.$password(this.passwordTemp);
|
|
2499
2454
|
}
|
|
2500
|
-
delete this
|
|
2455
|
+
delete this.passwordTemp;
|
|
2501
2456
|
|
|
2502
|
-
if (this
|
|
2457
|
+
if (this.password == null) {
|
|
2503
2458
|
throw new BadDataError('A password is required.');
|
|
2504
2459
|
}
|
|
2505
2460
|
|
|
@@ -2514,7 +2469,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
2514
2469
|
const tilmeld = enforceTilmeld(this);
|
|
2515
2470
|
const Group = this.$nymph.getEntityClass(GroupClass);
|
|
2516
2471
|
|
|
2517
|
-
let group = this
|
|
2472
|
+
let group = this.group;
|
|
2518
2473
|
if (group != null) {
|
|
2519
2474
|
// Make sure to get fresh group data.
|
|
2520
2475
|
if (group.$asleep()) {
|
|
@@ -2544,64 +2499,64 @@ export default class User extends AbleObject<UserData> {
|
|
|
2544
2499
|
{ type: '&', equal: ['defaultPrimary', true] },
|
|
2545
2500
|
);
|
|
2546
2501
|
if (group != null) {
|
|
2547
|
-
this
|
|
2502
|
+
this.group = group;
|
|
2548
2503
|
}
|
|
2549
2504
|
}
|
|
2550
2505
|
}
|
|
2551
2506
|
if (group != null && group.user != null && this.$is(group.user)) {
|
|
2552
2507
|
// Update the user's generated primary group.
|
|
2553
2508
|
let changed = false;
|
|
2554
|
-
if (group.groupname !== this
|
|
2555
|
-
group.groupname = this
|
|
2509
|
+
if (group.groupname !== this.username) {
|
|
2510
|
+
group.groupname = this.username;
|
|
2556
2511
|
changed = true;
|
|
2557
2512
|
}
|
|
2558
|
-
if (group.avatar !== this
|
|
2559
|
-
group.avatar = this
|
|
2513
|
+
if (group.avatar !== this.avatar) {
|
|
2514
|
+
group.avatar = this.avatar;
|
|
2560
2515
|
changed = true;
|
|
2561
2516
|
}
|
|
2562
2517
|
if (
|
|
2563
2518
|
tilmeld.config.userFields.includes('email') &&
|
|
2564
|
-
group.email !== this
|
|
2519
|
+
group.email !== this.email
|
|
2565
2520
|
) {
|
|
2566
|
-
group.email = this
|
|
2521
|
+
group.email = this.email;
|
|
2567
2522
|
changed = true;
|
|
2568
2523
|
}
|
|
2569
2524
|
if (
|
|
2570
2525
|
tilmeld.config.userFields.includes('name') &&
|
|
2571
|
-
group.name !== this
|
|
2526
|
+
group.name !== this.name
|
|
2572
2527
|
) {
|
|
2573
|
-
group.name = this
|
|
2528
|
+
group.name = this.name;
|
|
2574
2529
|
changed = true;
|
|
2575
2530
|
}
|
|
2576
2531
|
if (
|
|
2577
2532
|
tilmeld.config.userFields.includes('phone') &&
|
|
2578
|
-
group.phone !== this
|
|
2533
|
+
group.phone !== this.phone
|
|
2579
2534
|
) {
|
|
2580
|
-
group.phone = this
|
|
2535
|
+
group.phone = this.phone;
|
|
2581
2536
|
changed = true;
|
|
2582
2537
|
}
|
|
2583
2538
|
if (changed || group.guid == null) {
|
|
2584
2539
|
await group.$saveSkipAC();
|
|
2585
2540
|
}
|
|
2586
|
-
this
|
|
2541
|
+
this.group = group;
|
|
2587
2542
|
}
|
|
2588
2543
|
|
|
2589
|
-
if (this
|
|
2544
|
+
if (this.groups == null || this.groups.length === 0) {
|
|
2590
2545
|
// Add secondary groups.
|
|
2591
2546
|
if (
|
|
2592
2547
|
tilmeld.config.userFields.includes('email') &&
|
|
2593
2548
|
tilmeld.config.verifyEmail &&
|
|
2594
2549
|
tilmeld.config.unverifiedAccess &&
|
|
2595
|
-
this
|
|
2550
|
+
this.secret != null
|
|
2596
2551
|
) {
|
|
2597
2552
|
// Add the default secondaries for unverified users.
|
|
2598
|
-
this
|
|
2553
|
+
this.groups = await this.$nymph.getEntities(
|
|
2599
2554
|
{ class: Group },
|
|
2600
2555
|
{ type: '&', equal: ['unverifiedSecondary', true] },
|
|
2601
2556
|
);
|
|
2602
2557
|
} else {
|
|
2603
2558
|
// Add the default secondaries.
|
|
2604
|
-
this
|
|
2559
|
+
this.groups = await this.$nymph.getEntities(
|
|
2605
2560
|
{ class: Group },
|
|
2606
2561
|
{ type: '&', equal: ['defaultSecondary', true] },
|
|
2607
2562
|
);
|
|
@@ -2617,7 +2572,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
2617
2572
|
for (let callback of (this.constructor as typeof User)
|
|
2618
2573
|
.beforeSaveCallbacks) {
|
|
2619
2574
|
if (callback) {
|
|
2620
|
-
await callback(this
|
|
2575
|
+
await callback(this);
|
|
2621
2576
|
}
|
|
2622
2577
|
}
|
|
2623
2578
|
|
|
@@ -2636,7 +2591,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
2636
2591
|
for (let callback of (this.constructor as typeof User)
|
|
2637
2592
|
.afterSaveCallbacks) {
|
|
2638
2593
|
if (callback) {
|
|
2639
|
-
await callback(this
|
|
2594
|
+
await callback(this);
|
|
2640
2595
|
}
|
|
2641
2596
|
}
|
|
2642
2597
|
},
|
|
@@ -2644,8 +2599,8 @@ export default class User extends AbleObject<UserData> {
|
|
|
2644
2599
|
this.$setNymph(nymph);
|
|
2645
2600
|
|
|
2646
2601
|
if (committed) {
|
|
2647
|
-
this.$originalEmail = this
|
|
2648
|
-
this.$originalUsername = this
|
|
2602
|
+
this.$originalEmail = this.email;
|
|
2603
|
+
this.$originalUsername = this.username;
|
|
2649
2604
|
|
|
2650
2605
|
try {
|
|
2651
2606
|
const tilmeld = enforceTilmeld(nymph);
|
|
@@ -2693,13 +2648,13 @@ export default class User extends AbleObject<UserData> {
|
|
|
2693
2648
|
if (
|
|
2694
2649
|
!this.$skipAcWhenDeleting &&
|
|
2695
2650
|
!tilmeld.gatekeeper('system/admin') &&
|
|
2696
|
-
this
|
|
2651
|
+
this.abilities?.includes('system/admin')
|
|
2697
2652
|
) {
|
|
2698
2653
|
throw new AccessControlError(
|
|
2699
2654
|
"You don't have the authority to delete system admins.",
|
|
2700
2655
|
);
|
|
2701
2656
|
}
|
|
2702
|
-
if (this
|
|
2657
|
+
if (this.username !== this.$originalUsername) {
|
|
2703
2658
|
throw new AccessControlError(
|
|
2704
2659
|
"You can't delete a user while switching their username.",
|
|
2705
2660
|
);
|
|
@@ -2709,11 +2664,11 @@ export default class User extends AbleObject<UserData> {
|
|
|
2709
2664
|
throw new BadDataError('User could not be refreshed.');
|
|
2710
2665
|
}
|
|
2711
2666
|
|
|
2712
|
-
if (this
|
|
2713
|
-
if (this
|
|
2714
|
-
await this
|
|
2667
|
+
if (this.group != null) {
|
|
2668
|
+
if (this.group.$asleep()) {
|
|
2669
|
+
await this.group.$wake();
|
|
2715
2670
|
} else {
|
|
2716
|
-
if (!(await this
|
|
2671
|
+
if (!(await this.group.$refresh())) {
|
|
2717
2672
|
throw new BadDataError('Group could not be refreshed.');
|
|
2718
2673
|
}
|
|
2719
2674
|
}
|
|
@@ -2722,7 +2677,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
2722
2677
|
for (let callback of (this.constructor as typeof User)
|
|
2723
2678
|
.beforeDeleteCallbacks) {
|
|
2724
2679
|
if (callback) {
|
|
2725
|
-
await callback(this
|
|
2680
|
+
await callback(this);
|
|
2726
2681
|
}
|
|
2727
2682
|
}
|
|
2728
2683
|
|
|
@@ -2730,7 +2685,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
2730
2685
|
await this.$logout();
|
|
2731
2686
|
}
|
|
2732
2687
|
|
|
2733
|
-
if (this
|
|
2688
|
+
if (this.group != null && this.$is(this.group.user)) {
|
|
2734
2689
|
// Read the skip ac value here, because super.$delete changes it.
|
|
2735
2690
|
const $skipAcWhenDeleting = this.$skipAcWhenDeleting;
|
|
2736
2691
|
|
|
@@ -2738,9 +2693,9 @@ export default class User extends AbleObject<UserData> {
|
|
|
2738
2693
|
await super.$delete();
|
|
2739
2694
|
// Delete the generated group.
|
|
2740
2695
|
if ($skipAcWhenDeleting) {
|
|
2741
|
-
await this
|
|
2696
|
+
await this.group.$deleteSkipAC();
|
|
2742
2697
|
} else {
|
|
2743
|
-
await this
|
|
2698
|
+
await this.group.$delete();
|
|
2744
2699
|
}
|
|
2745
2700
|
} else {
|
|
2746
2701
|
await super.$delete();
|
|
@@ -2749,7 +2704,7 @@ export default class User extends AbleObject<UserData> {
|
|
|
2749
2704
|
for (let callback of (this.constructor as typeof User)
|
|
2750
2705
|
.afterDeleteCallbacks) {
|
|
2751
2706
|
if (callback) {
|
|
2752
|
-
await callback(this
|
|
2707
|
+
await callback(this);
|
|
2753
2708
|
}
|
|
2754
2709
|
}
|
|
2755
2710
|
}
|