@nymphjs/tilmeld 1.0.0-beta.3 → 1.0.0-beta.31

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/dist/User.js CHANGED
@@ -3,12 +3,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ const guid_1 = require("@nymphjs/guid");
6
7
  const strtotime_1 = __importDefault(require("locutus/php/datetime/strtotime"));
7
8
  const enc_base64_1 = __importDefault(require("crypto-js/enc-base64"));
8
9
  const sha256_1 = __importDefault(require("crypto-js/sha256"));
9
10
  const md5_1 = __importDefault(require("crypto-js/md5"));
10
11
  const lodash_1 = require("lodash");
11
- const guid_1 = require("@nymphjs/guid");
12
+ const otpauth_1 = require("otpauth");
13
+ const qrcode_1 = require("qrcode");
14
+ const enforceTilmeld_1 = require("./enforceTilmeld");
12
15
  const AbleObject_1 = __importDefault(require("./AbleObject"));
13
16
  const errors_1 = require("./errors");
14
17
  class User extends AbleObject_1.default {
@@ -49,13 +52,15 @@ class User extends AbleObject_1.default {
49
52
  }
50
53
  }
51
54
  static current(returnObjectIfNotExist) {
52
- if (this.tilmeld.currentUser == null) {
55
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
56
+ if (tilmeld.currentUser == null) {
53
57
  return returnObjectIfNotExist ? this.factorySync() : null;
54
58
  }
55
- return this.tilmeld.currentUser;
59
+ return tilmeld.currentUser;
56
60
  }
57
61
  static async sendRecovery(data) {
58
- if (!this.tilmeld.config.pwRecovery) {
62
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
63
+ if (!tilmeld.config.pwRecovery) {
59
64
  return {
60
65
  result: false,
61
66
  message: 'Account recovery is not allowed.',
@@ -63,9 +68,8 @@ class User extends AbleObject_1.default {
63
68
  }
64
69
  let user;
65
70
  const options = {};
66
- if (!this.tilmeld.config.emailUsernames &&
67
- data.recoveryType === 'username') {
68
- const getUser = await this.nymph.getEntity({ class: this.tilmeld.User, skipAc: true }, {
71
+ if (!tilmeld.config.emailUsernames && data.recoveryType === 'username') {
72
+ const getUser = await this.nymph.getEntity({ class: tilmeld.User, skipAc: true }, {
69
73
  type: '&',
70
74
  ilike: ['email', data.account.replace(/([\\%_])/g, (s) => `\\${s}`)],
71
75
  });
@@ -86,7 +90,7 @@ class User extends AbleObject_1.default {
86
90
  options.locals = {};
87
91
  }
88
92
  else if (data.recoveryType === 'password') {
89
- const getUser = await this.tilmeld.User.factoryUsername(data.account);
93
+ const getUser = await tilmeld.User.factoryUsername(data.account);
90
94
  if (getUser.guid == null) {
91
95
  return {
92
96
  result: false,
@@ -108,13 +112,13 @@ class User extends AbleObject_1.default {
108
112
  };
109
113
  options.locals = {
110
114
  recoverCode: getUser.recoverSecret,
111
- timeLimit: this.tilmeld.config.pwRecoveryTimeLimit,
115
+ timeLimit: tilmeld.config.pwRecoveryTimeLimit,
112
116
  };
113
117
  }
114
118
  else {
115
119
  return { result: false, message: 'Invalid recovery type.' };
116
120
  }
117
- if (await this.tilmeld.config.sendEmail(this.tilmeld, options, user)) {
121
+ if (await tilmeld.config.sendEmail(tilmeld, options, user)) {
118
122
  return {
119
123
  result: true,
120
124
  message: "We've sent an email to your registered address. Please " +
@@ -126,17 +130,18 @@ class User extends AbleObject_1.default {
126
130
  }
127
131
  }
128
132
  static async recover(data) {
129
- if (!this.tilmeld.config.pwRecovery) {
133
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
134
+ if (!tilmeld.config.pwRecovery) {
130
135
  return {
131
136
  result: false,
132
137
  message: 'Account recovery is not allowed.',
133
138
  };
134
139
  }
135
- const user = await this.tilmeld.User.factoryUsername(data.username);
140
+ const user = await tilmeld.User.factoryUsername(data.username);
136
141
  if (user.guid == null ||
137
142
  user.recoverSecret == null ||
138
143
  data.secret !== user.recoverSecret ||
139
- (0, strtotime_1.default)('+' + this.tilmeld.config.pwRecoveryTimeLimit, Math.floor((user.recoverSecretDate ?? 0) / 1000)) *
144
+ (0, strtotime_1.default)('+' + tilmeld.config.pwRecoveryTimeLimit, Math.floor((user.recoverSecretDate ?? 0) / 1000)) *
140
145
  1000 <
141
146
  Date.now()) {
142
147
  return {
@@ -161,22 +166,24 @@ class User extends AbleObject_1.default {
161
166
  }
162
167
  }
163
168
  static getClientConfig() {
169
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
164
170
  return {
165
- regFields: this.tilmeld.config.regFields,
166
- userFields: this.tilmeld.config.userFields,
167
- emailUsernames: this.tilmeld.config.emailUsernames,
168
- allowRegistration: this.tilmeld.config.allowRegistration,
169
- allowUsernameChange: this.tilmeld.config.allowUsernameChange,
170
- pwRecovery: this.tilmeld.config.pwRecovery,
171
- verifyEmail: this.tilmeld.config.verifyEmail,
172
- unverifiedAccess: this.tilmeld.config.unverifiedAccess,
171
+ regFields: tilmeld.config.regFields,
172
+ userFields: tilmeld.config.userFields,
173
+ emailUsernames: tilmeld.config.emailUsernames,
174
+ allowRegistration: tilmeld.config.allowRegistration,
175
+ allowUsernameChange: tilmeld.config.allowUsernameChange,
176
+ pwRecovery: tilmeld.config.pwRecovery,
177
+ verifyEmail: tilmeld.config.verifyEmail,
178
+ unverifiedAccess: tilmeld.config.unverifiedAccess,
173
179
  };
174
180
  }
175
181
  static async loginUser(data) {
182
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
176
183
  if (!('username' in data) || !data.username.length) {
177
184
  return { result: false, message: 'Incorrect login/password.' };
178
185
  }
179
- const user = await this.tilmeld.User.factoryUsername(data.username);
186
+ const user = await tilmeld.User.factoryUsername(data.username);
180
187
  const result = await user.$login(data);
181
188
  if (result.result) {
182
189
  user.$updateDataProtection();
@@ -185,7 +192,7 @@ class User extends AbleObject_1.default {
185
192
  return result;
186
193
  }
187
194
  async $login(data) {
188
- const tilmeld = this.$nymph.tilmeld;
195
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
189
196
  if (this.guid == null) {
190
197
  return { result: false, message: 'Incorrect login/password.' };
191
198
  }
@@ -198,6 +205,20 @@ class User extends AbleObject_1.default {
198
205
  if (!this.$checkPassword(data.password)) {
199
206
  return { result: false, message: 'Incorrect login/password.' };
200
207
  }
208
+ if (this.$data.totpSecret != null) {
209
+ if (data.code == null ||
210
+ typeof data.code !== 'string' ||
211
+ data.code.length !== 6) {
212
+ return {
213
+ result: false,
214
+ needTOTP: true,
215
+ message: 'You need to provide a 2FA code.',
216
+ };
217
+ }
218
+ if (!this.$checkTOTPCode(data.code)) {
219
+ return { result: false, message: 'Incorrect 2FA code.' };
220
+ }
221
+ }
201
222
  try {
202
223
  for (let callback of this.constructor
203
224
  .beforeLoginCallbacks) {
@@ -223,8 +244,63 @@ class User extends AbleObject_1.default {
223
244
  }
224
245
  return { result: true, message: 'You are logged in.' };
225
246
  }
247
+ async $switchUser(data) {
248
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
249
+ if (this.guid == null) {
250
+ return { result: false, message: 'Incorrect login/password.' };
251
+ }
252
+ let user = tilmeld.User.current();
253
+ if (!user || user.guid == null) {
254
+ return { result: false, message: "You're not logged in." };
255
+ }
256
+ const isCurrentUser = user != null && user.$is(this);
257
+ if (isCurrentUser) {
258
+ return { result: false, message: "You can't switch to yourself." };
259
+ }
260
+ const toAbilities = this.$data.abilities || [];
261
+ const fromAbilities = user.abilities || [];
262
+ if (fromAbilities.indexOf('tilmeld/switch') === -1 &&
263
+ fromAbilities.indexOf('system/admin') === -1) {
264
+ return {
265
+ result: false,
266
+ message: "You don't have permission to switch users.",
267
+ };
268
+ }
269
+ if (toAbilities.indexOf('system/admin') !== -1 ||
270
+ toAbilities.indexOf('tilmeld/admin') !== -1) {
271
+ return { result: false, message: "You can't switch to an admin." };
272
+ }
273
+ try {
274
+ for (let callback of this.constructor
275
+ .beforeLoginCallbacks) {
276
+ if (callback) {
277
+ await callback(this, {
278
+ username: this.$data.username || '',
279
+ password: '',
280
+ ...(data || {}),
281
+ });
282
+ }
283
+ }
284
+ }
285
+ catch (e) {
286
+ return {
287
+ result: false,
288
+ message: e.message,
289
+ };
290
+ }
291
+ if (!tilmeld.loginSwitch(this, true)) {
292
+ return { result: false, message: 'Incorrect login/password.' };
293
+ }
294
+ for (let callback of this.constructor
295
+ .afterLoginCallbacks) {
296
+ if (callback) {
297
+ await callback(this);
298
+ }
299
+ }
300
+ return { result: true, message: 'You are logged in.' };
301
+ }
226
302
  async $logout() {
227
- const tilmeld = this.$nymph.tilmeld;
303
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
228
304
  try {
229
305
  for (let callback of this.constructor
230
306
  .beforeLogoutCallbacks) {
@@ -284,25 +360,49 @@ class User extends AbleObject_1.default {
284
360
  return this.$descendantGroups;
285
361
  }
286
362
  $jsonAcceptData(input, allowConflict = false) {
287
- const tilmeld = this.$nymph.tilmeld;
363
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
288
364
  this.$referenceWake();
289
- if (input.data.abilities?.indexOf('system/admin') !== -1 &&
290
- this.$data.abilities?.indexOf('system/admin') === -1 &&
291
- tilmeld.gatekeeper('tilmeld/admin') &&
365
+ if ('abilities' in input.data &&
366
+ input.data.abilities?.includes('system/admin') !==
367
+ this.$data.abilities?.includes('system/admin') &&
368
+ !tilmeld.gatekeeper('system/admin')) {
369
+ throw new errors_1.BadDataError("You don't have the authority to grant or revoke system/admin.");
370
+ }
371
+ if ('abilities' in input.data &&
372
+ input.data.abilities?.includes('tilmeld/admin') !==
373
+ this.$data.abilities?.includes('tilmeld/admin') &&
292
374
  !tilmeld.gatekeeper('system/admin')) {
293
- throw new errors_1.BadDataError("You don't have the authority to make this user a system admin.");
375
+ throw new errors_1.BadDataError("You don't have the authority to grant or revoke tilmeld/admin.");
376
+ }
377
+ if ('abilities' in input.data &&
378
+ input.data.abilities?.includes('tilmeld/switch') !==
379
+ this.$data.abilities?.includes('tilmeld/switch') &&
380
+ !tilmeld.gatekeeper('system/admin')) {
381
+ throw new errors_1.BadDataError("You don't have the authority to grant or revoke tilmeld/switch.");
294
382
  }
295
383
  this.$originalEmail = this.$data.email;
296
384
  super.$jsonAcceptData(input, allowConflict);
297
385
  }
298
386
  $jsonAcceptPatch(patch, allowConflict = false) {
299
- const tilmeld = this.$nymph.tilmeld;
387
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
300
388
  this.$referenceWake();
301
- if (patch.set.abilities?.indexOf('system/admin') !== -1 &&
302
- this.$data.abilities?.indexOf('system/admin') === -1 &&
303
- tilmeld.gatekeeper('tilmeld/admin') &&
389
+ if ('abilities' in patch.set &&
390
+ patch.set.abilities?.includes('system/admin') !==
391
+ this.$data.abilities?.includes('system/admin') &&
392
+ !tilmeld.gatekeeper('system/admin')) {
393
+ throw new errors_1.BadDataError("You don't have the authority to grant or revoke system/admin.");
394
+ }
395
+ if ('abilities' in patch.set &&
396
+ patch.set.abilities?.includes('tilmeld/admin') !==
397
+ this.$data.abilities?.includes('tilmeld/admin') &&
304
398
  !tilmeld.gatekeeper('system/admin')) {
305
- throw new errors_1.BadDataError("You don't have the authority to make this user a system admin.");
399
+ throw new errors_1.BadDataError("You don't have the authority to grant or revoke tilmeld/admin.");
400
+ }
401
+ if ('abilities' in patch.set &&
402
+ patch.set.abilities?.includes('tilmeld/switch') !==
403
+ this.$data.abilities?.includes('tilmeld/switch') &&
404
+ !tilmeld.gatekeeper('system/admin')) {
405
+ throw new errors_1.BadDataError("You don't have the authority to grant or revoke tilmeld/switch.");
306
406
  }
307
407
  this.$originalEmail = this.$data.email;
308
408
  super.$jsonAcceptPatch(patch, allowConflict);
@@ -312,7 +412,7 @@ class User extends AbleObject_1.default {
312
412
  this.$updateDataProtection();
313
413
  }
314
414
  $updateDataProtection(givenUser) {
315
- const tilmeld = this.$nymph.tilmeld;
415
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
316
416
  let user = givenUser ?? tilmeld.User.current();
317
417
  this.$clientEnabledMethods = [...User.DEFAULT_CLIENT_ENABLED_METHODS];
318
418
  this.$privateData = [...User.DEFAULT_PRIVATE_DATA];
@@ -328,13 +428,18 @@ class User extends AbleObject_1.default {
328
428
  if (isCurrentUser) {
329
429
  this.$clientEnabledMethods.push('$gatekeeper');
330
430
  this.$clientEnabledMethods.push('$changePassword');
431
+ this.$clientEnabledMethods.push('$revokeCurrentTokens');
331
432
  this.$clientEnabledMethods.push('$logout');
332
433
  this.$clientEnabledMethods.push('$sendEmailVerification');
434
+ this.$clientEnabledMethods.push('$hasTOTPSecret');
435
+ this.$clientEnabledMethods.push('$getNewTOTPSecret');
436
+ this.$clientEnabledMethods.push('$saveTOTPSecret');
437
+ this.$clientEnabledMethods.push('$removeTOTPSecret');
333
438
  }
334
439
  if (user != null &&
335
440
  (abilities.indexOf('tilmeld/admin') !== -1 ||
336
441
  abilities.indexOf('system/admin') !== -1)) {
337
- this.$privateData = ['password', 'salt'];
442
+ this.$privateData = ['password', 'salt', 'totpSecret'];
338
443
  this.$allowlistData = undefined;
339
444
  }
340
445
  else if (isCurrentUser || isNewUser) {
@@ -355,20 +460,29 @@ class User extends AbleObject_1.default {
355
460
  this.$allowlistData.push('phone');
356
461
  }
357
462
  this.$privateData = [
463
+ 'enabled',
358
464
  'secret',
465
+ 'emailChangeDate',
359
466
  'newEmailSecret',
360
- 'cancelEmailAddress',
361
467
  'cancelEmailSecret',
362
- 'emailChangeDate',
468
+ 'cancelEmailAddress',
363
469
  'recoverSecret',
364
470
  'recoverSecretDate',
365
- 'password',
366
471
  'salt',
472
+ 'password',
473
+ 'passwordTemp',
474
+ 'totpSecret',
367
475
  ];
368
476
  }
477
+ if (user != null &&
478
+ !isCurrentUser &&
479
+ (abilities.indexOf('tilmeld/switch') !== -1 ||
480
+ abilities.indexOf('system/admin') !== -1)) {
481
+ this.$clientEnabledMethods.push('$switchUser');
482
+ }
369
483
  }
370
484
  $gatekeeper(ability) {
371
- const tilmeld = this.$nymph.tilmeld;
485
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
372
486
  if (ability == null) {
373
487
  return tilmeld.User.current()?.$is(this) ?? false;
374
488
  }
@@ -389,7 +503,7 @@ class User extends AbleObject_1.default {
389
503
  this.$gatekeeperCache = Object.fromEntries(abilities.map((ability) => [ability, true]));
390
504
  }
391
505
  return ((ability in this.$gatekeeperCache && !!this.$gatekeeperCache[ability]) ||
392
- !!this.$gatekeeperCache['system/admin']);
506
+ !!this.$data.abilities?.includes('system/admin'));
393
507
  }
394
508
  $clearCache() {
395
509
  const ret = super.$clearCache();
@@ -398,7 +512,7 @@ class User extends AbleObject_1.default {
398
512
  return ret;
399
513
  }
400
514
  async $sendEmailVerification() {
401
- const tilmeld = this.$nymph.tilmeld;
515
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
402
516
  if (this.guid == null) {
403
517
  return false;
404
518
  }
@@ -472,7 +586,7 @@ class User extends AbleObject_1.default {
472
586
  return true;
473
587
  }
474
588
  $checkPassword(password) {
475
- const tilmeld = this.$nymph.tilmeld;
589
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
476
590
  switch (tilmeld.config.pwMethod) {
477
591
  case 'plain':
478
592
  return this.$data.password === password;
@@ -531,7 +645,14 @@ class User extends AbleObject_1.default {
531
645
  return { result: false, message: 'Incorrect password.' };
532
646
  }
533
647
  this.$data.passwordTemp = data.newPassword;
648
+ if (data.revokeCurrentTokens) {
649
+ this.$data.revokeTokenDate = Date.now();
650
+ }
534
651
  if (await this.$save()) {
652
+ if (data.revokeCurrentTokens) {
653
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
654
+ tilmeld.login(this, true);
655
+ }
535
656
  return { result: true, message: 'Your password has been changed.' };
536
657
  }
537
658
  else {
@@ -539,7 +660,7 @@ class User extends AbleObject_1.default {
539
660
  }
540
661
  }
541
662
  $password(password) {
542
- const tilmeld = this.$nymph.tilmeld;
663
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
543
664
  switch (tilmeld.config.pwMethod) {
544
665
  case 'plain':
545
666
  delete this.$data.salt;
@@ -557,8 +678,137 @@ class User extends AbleObject_1.default {
557
678
  }
558
679
  return this.$data.password;
559
680
  }
681
+ async $revokeCurrentTokens(data) {
682
+ if (!this.$checkPassword(data.password ?? '')) {
683
+ return { result: false, message: 'Incorrect password.' };
684
+ }
685
+ this.$data.revokeTokenDate = Date.now();
686
+ if (await this.$save()) {
687
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
688
+ tilmeld.login(this, true);
689
+ return {
690
+ result: true,
691
+ message: 'You have logged out of all other sessions.',
692
+ };
693
+ }
694
+ else {
695
+ return { result: false, message: "Couldn't save revocation date." };
696
+ }
697
+ }
698
+ async $hasTOTPSecret() {
699
+ return this.$data.totpSecret != null;
700
+ }
701
+ $checkTOTPCode(code) {
702
+ if (this.$data.totpSecret == null) {
703
+ return false;
704
+ }
705
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
706
+ const totp = new otpauth_1.TOTP({
707
+ issuer: tilmeld.config.appName,
708
+ label: this.$data.username,
709
+ algorithm: 'SHA1',
710
+ digits: 6,
711
+ period: 30,
712
+ secret: this.$data.totpSecret,
713
+ });
714
+ return totp.validate({ token: code, window: 2 }) != null;
715
+ }
716
+ async $getNewTOTPSecret() {
717
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
718
+ if (this.$data.totpSecret != null) {
719
+ throw new errors_1.BadDataError('You already have a 2FA secret.');
720
+ }
721
+ const secret = new otpauth_1.Secret();
722
+ const totp = new otpauth_1.TOTP({
723
+ issuer: tilmeld.config.appName,
724
+ label: this.$data.username,
725
+ algorithm: 'SHA1',
726
+ digits: 6,
727
+ period: 30,
728
+ secret,
729
+ });
730
+ const uri = totp.toString();
731
+ const qrcode = await (0, qrcode_1.toDataURL)(uri);
732
+ return {
733
+ uri,
734
+ qrcode,
735
+ secret: secret.base32,
736
+ };
737
+ }
738
+ async $saveTOTPSecret(data) {
739
+ if (!this.$checkPassword(data.password ?? '')) {
740
+ return { result: false, message: 'Incorrect password.' };
741
+ }
742
+ if (this.$data.totpSecret != null) {
743
+ return { result: false, message: 'You already have a 2FA secret.' };
744
+ }
745
+ if (data.secret == null ||
746
+ typeof data.secret !== 'string' ||
747
+ data.secret === '') {
748
+ return { result: false, message: '2FA secret is invalid.' };
749
+ }
750
+ if (data.code == null ||
751
+ typeof data.code !== 'string' ||
752
+ data.code.length !== 6) {
753
+ return { result: false, message: '2FA code is invalid.' };
754
+ }
755
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
756
+ const totp = new otpauth_1.TOTP({
757
+ issuer: tilmeld.config.appName,
758
+ label: this.$data.username,
759
+ algorithm: 'SHA1',
760
+ digits: 6,
761
+ period: 30,
762
+ secret: data.secret,
763
+ });
764
+ if (totp.validate({ token: data.code, window: 2 }) == null) {
765
+ return { result: false, message: '2FA code is incorrect.' };
766
+ }
767
+ this.$data.totpSecret = data.secret;
768
+ if (await this.$save()) {
769
+ return { result: true, message: 'Your two factor secret is now set.' };
770
+ }
771
+ else {
772
+ return { result: false, message: "Couldn't save two factor secret." };
773
+ }
774
+ }
775
+ async $removeTOTPSecret(data) {
776
+ if (!this.$checkPassword(data.password ?? '')) {
777
+ return { result: false, message: 'Incorrect password.' };
778
+ }
779
+ if (this.$data.totpSecret == null) {
780
+ return { result: false, message: "You don't have a 2FA secret." };
781
+ }
782
+ if (data.code == null ||
783
+ typeof data.code !== 'string' ||
784
+ data.code.length !== 6) {
785
+ return { result: false, message: '2FA code is invalid.' };
786
+ }
787
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
788
+ const totp = new otpauth_1.TOTP({
789
+ issuer: tilmeld.config.appName,
790
+ label: this.$data.username,
791
+ algorithm: 'SHA1',
792
+ digits: 6,
793
+ period: 30,
794
+ secret: this.$data.totpSecret,
795
+ });
796
+ if (totp.validate({ token: data.code, window: 2 }) == null) {
797
+ return { result: false, message: '2FA code is incorrect.' };
798
+ }
799
+ delete this.$data.totpSecret;
800
+ if (await this.$save()) {
801
+ return {
802
+ result: true,
803
+ message: 'Your two factor secret has been removed.',
804
+ };
805
+ }
806
+ else {
807
+ return { result: false, message: "Couldn't remove two factor secret." };
808
+ }
809
+ }
560
810
  async $checkUsername() {
561
- const tilmeld = this.$nymph.tilmeld;
811
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
562
812
  if (!tilmeld.config.emailUsernames) {
563
813
  if (this.$data.username == null || !this.$data.username.length) {
564
814
  return { result: false, message: 'Please specify a username.' };
@@ -598,6 +848,17 @@ class User extends AbleObject_1.default {
598
848
  if (test != null) {
599
849
  return { result: false, message: 'That username is taken.' };
600
850
  }
851
+ for (let callback of this.constructor
852
+ .checkUsernameCallbacks) {
853
+ if (callback) {
854
+ const result = await callback(this, {
855
+ username: this.$data.username,
856
+ });
857
+ if (!result.result) {
858
+ return { message: 'Username is not available.', ...result };
859
+ }
860
+ }
861
+ }
601
862
  return {
602
863
  result: true,
603
864
  message: this.guid != null ? 'Username is valid.' : 'Username is available!',
@@ -621,7 +882,7 @@ class User extends AbleObject_1.default {
621
882
  }
622
883
  }
623
884
  async $checkEmail() {
624
- const tilmeld = this.$nymph.tilmeld;
885
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
625
886
  if (this.$data.email == null || !this.$data.email.length) {
626
887
  if (tilmeld.config.verifyEmail) {
627
888
  return { result: false, message: 'Please specify an email.' };
@@ -656,7 +917,7 @@ class User extends AbleObject_1.default {
656
917
  };
657
918
  }
658
919
  async $checkPhone() {
659
- const tilmeld = this.$nymph.tilmeld;
920
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
660
921
  if (this.$data.phone == null || !this.$data.phone.length) {
661
922
  return { result: false, message: 'Please specify a phone number.' };
662
923
  }
@@ -684,7 +945,7 @@ class User extends AbleObject_1.default {
684
945
  };
685
946
  }
686
947
  async $register(data) {
687
- let tilmeld = this.$nymph.tilmeld;
948
+ let tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
688
949
  if (!tilmeld.config.allowRegistration) {
689
950
  return {
690
951
  result: false,
@@ -742,7 +1003,7 @@ class User extends AbleObject_1.default {
742
1003
  const nymph = this.$nymph;
743
1004
  const tnymph = await this.$nymph.startTransaction(transaction);
744
1005
  this.$nymph = tnymph;
745
- tilmeld = this.$nymph.tilmeld;
1006
+ tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
746
1007
  try {
747
1008
  let generatedPrimaryGroup = null;
748
1009
  if (tilmeld.config.generatePrimary) {
@@ -909,13 +1170,13 @@ class User extends AbleObject_1.default {
909
1170
  }
910
1171
  }
911
1172
  async $save() {
912
- let tilmeld = this.$nymph.tilmeld;
1173
+ let tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
913
1174
  if (this.$data.username == null || !this.$data.username.trim().length) {
914
1175
  return false;
915
1176
  }
916
1177
  if (tilmeld.gatekeeper('tilmeld/admin') &&
917
1178
  !tilmeld.gatekeeper('system/admin') &&
918
- this.$gatekeeper('system/admin')) {
1179
+ this.$data.abilities?.includes('system/admin')) {
919
1180
  throw new errors_1.BadDataError("You don't have the authority to modify system admins.");
920
1181
  }
921
1182
  let sendVerification = false;
@@ -1062,7 +1323,7 @@ class User extends AbleObject_1.default {
1062
1323
  const nymph = this.$nymph;
1063
1324
  const tnymph = await this.$nymph.startTransaction(transaction);
1064
1325
  this.$nymph = tnymph;
1065
- tilmeld = this.$nymph.tilmeld;
1326
+ tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
1066
1327
  if (this.$data.group != null &&
1067
1328
  this.$data.group.user != null &&
1068
1329
  this.$is(this.$data.group.user)) {
@@ -1106,8 +1367,8 @@ class User extends AbleObject_1.default {
1106
1367
  this.$descendantGroups = undefined;
1107
1368
  this.$gatekeeperCache = undefined;
1108
1369
  await tnymph.commit(transaction);
1109
- if (nymph.tilmeld.User.current(true).$is(this)) {
1110
- nymph.tilmeld.fillSession(this);
1370
+ if ((0, enforceTilmeld_1.enforceTilmeld)(nymph).User.current(true).$is(this)) {
1371
+ (0, enforceTilmeld_1.enforceTilmeld)(nymph).fillSession(this);
1111
1372
  }
1112
1373
  }
1113
1374
  else {
@@ -1128,12 +1389,12 @@ class User extends AbleObject_1.default {
1128
1389
  return false;
1129
1390
  }
1130
1391
  async $delete() {
1131
- const tilmeld = this.$nymph.tilmeld;
1392
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
1132
1393
  if (!tilmeld.gatekeeper('tilmeld/admin')) {
1133
1394
  throw new errors_1.BadDataError("You don't have the authority to delete users.");
1134
1395
  }
1135
1396
  if (!tilmeld.gatekeeper('system/admin') &&
1136
- this.$gatekeeper('system/admin')) {
1397
+ this.$data.abilities?.includes('system/admin')) {
1137
1398
  throw new errors_1.BadDataError("You don't have the authority to delete system admins.");
1138
1399
  }
1139
1400
  if (tilmeld.User.current(true).$is(this)) {
@@ -1161,9 +1422,9 @@ class User extends AbleObject_1.default {
1161
1422
  return true;
1162
1423
  }
1163
1424
  }
1164
- exports.default = User;
1165
1425
  User.ETYPE = 'tilmeld_user';
1166
1426
  User.class = 'User';
1427
+ User.checkUsernameCallbacks = [];
1167
1428
  User.beforeRegisterCallbacks = [];
1168
1429
  User.afterRegisterCallbacks = [];
1169
1430
  User.beforeLoginCallbacks = [];
@@ -1178,23 +1439,26 @@ User.DEFAULT_CLIENT_ENABLED_METHODS = [
1178
1439
  '$register',
1179
1440
  ];
1180
1441
  User.DEFAULT_PRIVATE_DATA = [
1442
+ 'abilities',
1181
1443
  'email',
1182
- 'originalEmail',
1183
1444
  'phone',
1184
1445
  'group',
1185
1446
  'groups',
1186
- 'abilities',
1187
1447
  'inheritAbilities',
1188
- 'recoverSecret',
1189
- 'recoverSecretDate',
1190
- 'password',
1191
- 'salt',
1448
+ 'enabled',
1192
1449
  'secret',
1193
- 'newEmailAddress',
1450
+ 'emailChangeDate',
1194
1451
  'newEmailSecret',
1195
- 'cancelEmailAddress',
1452
+ 'newEmailAddress',
1196
1453
  'cancelEmailSecret',
1197
- 'emailChangeDate',
1454
+ 'cancelEmailAddress',
1455
+ 'recoverSecret',
1456
+ 'recoverSecretDate',
1457
+ 'salt',
1458
+ 'password',
1459
+ 'passwordTemp',
1460
+ 'revokeTokenDate',
1461
+ 'totpSecret',
1198
1462
  ];
1199
1463
  User.DEFAULT_ALLOWLIST_DATA = [];
1200
1464
  User.clientEnabledStaticMethods = [
@@ -1205,4 +1469,5 @@ User.clientEnabledStaticMethods = [
1205
1469
  'getClientConfig',
1206
1470
  ];
1207
1471
  User.searchRestrictedData = [...User.DEFAULT_PRIVATE_DATA];
1472
+ exports.default = User;
1208
1473
  //# sourceMappingURL=User.js.map