@nymphjs/tilmeld 1.0.0-beta.61 → 1.0.0-beta.63

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
@@ -24,6 +24,94 @@ const errors_1 = require("./errors");
24
24
  * @see http://nymph.io/
25
25
  */
26
26
  class User extends AbleObject_1.default {
27
+ /**
28
+ * The instance of Tilmeld to use for queries.
29
+ */
30
+ static ETYPE = 'tilmeld_user';
31
+ static class = 'User';
32
+ static checkUsernameCallbacks = [];
33
+ static beforeRegisterCallbacks = [];
34
+ static afterRegisterCallbacks = [];
35
+ static beforeLoginCallbacks = [];
36
+ static afterLoginCallbacks = [];
37
+ static beforeLogoutCallbacks = [];
38
+ static afterLogoutCallbacks = [];
39
+ static DEFAULT_CLIENT_ENABLED_METHODS = [
40
+ '$checkUsername',
41
+ '$checkEmail',
42
+ '$checkPhone',
43
+ '$getAvatar',
44
+ '$register',
45
+ ];
46
+ static DEFAULT_PRIVATE_DATA = [
47
+ // The things that are commented are there to show what is _not_ private.
48
+ 'abilities',
49
+ // 'username',
50
+ // 'nameFirst',
51
+ // 'nameMiddle',
52
+ // 'nameLast',
53
+ // 'name',
54
+ 'email',
55
+ // 'avatar',
56
+ 'phone',
57
+ 'group',
58
+ 'groups',
59
+ 'inheritAbilities',
60
+ 'enabled',
61
+ 'secret',
62
+ 'emailChangeDate',
63
+ 'newEmailSecret',
64
+ 'newEmailAddress',
65
+ 'cancelEmailSecret',
66
+ 'cancelEmailAddress',
67
+ 'recoverSecret',
68
+ 'recoverSecretDate',
69
+ 'salt',
70
+ 'password',
71
+ 'passwordTemp',
72
+ 'revokeTokenDate',
73
+ 'totpSecret',
74
+ ];
75
+ static DEFAULT_ALLOWLIST_DATA = [];
76
+ $clientEnabledMethods = [...User.DEFAULT_CLIENT_ENABLED_METHODS];
77
+ static clientEnabledStaticMethods = [
78
+ 'current',
79
+ 'loginUser',
80
+ 'sendRecovery',
81
+ 'recover',
82
+ 'getClientConfig',
83
+ ];
84
+ $privateData = [...User.DEFAULT_PRIVATE_DATA];
85
+ static searchRestrictedData = [...User.DEFAULT_PRIVATE_DATA];
86
+ $allowlistData = [...User.DEFAULT_ALLOWLIST_DATA];
87
+ $allowlistTags = [];
88
+ /**
89
+ * Gatekeeper ability cache.
90
+ *
91
+ * Gatekeeper will cache the user's abilities that it calculates, so it can
92
+ * check faster if that user has been checked before.
93
+ */
94
+ $gatekeeperCache;
95
+ /**
96
+ * This should only be used by the backend.
97
+ */
98
+ $skipAcWhenSaving = false;
99
+ /**
100
+ * This should only be used by the backend.
101
+ */
102
+ $skipAcWhenDeleting = false;
103
+ /**
104
+ * The user's group descendants.
105
+ */
106
+ $descendantGroups;
107
+ /**
108
+ * Used to save the current email address to send verification if it changes
109
+ * from the frontend.
110
+ *
111
+ * If you are changing a user's email address and want to bypass email
112
+ * verification, don't set this.
113
+ */
114
+ $originalEmail;
27
115
  static async factoryUsername(username) {
28
116
  const entity = new this();
29
117
  if (username != null) {
@@ -39,23 +127,6 @@ class User extends AbleObject_1.default {
39
127
  }
40
128
  return entity;
41
129
  }
42
- constructor() {
43
- super();
44
- this.$clientEnabledMethods = [...User.DEFAULT_CLIENT_ENABLED_METHODS];
45
- this.$privateData = [...User.DEFAULT_PRIVATE_DATA];
46
- this.$allowlistData = [...User.DEFAULT_ALLOWLIST_DATA];
47
- this.$allowlistTags = [];
48
- /**
49
- * This is explicitly used only during the registration proccess.
50
- */
51
- this.$skipAcWhenSaving = false;
52
- // Defaults.
53
- this.$data.enabled = true;
54
- this.$data.abilities = [];
55
- this.$data.groups = [];
56
- this.$data.inheritAbilities = true;
57
- this.$updateDataProtection();
58
- }
59
130
  static current(returnObjectIfNotExist) {
60
131
  const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
61
132
  if (tilmeld.currentUser == null) {
@@ -216,6 +287,40 @@ class User extends AbleObject_1.default {
216
287
  }
217
288
  return result;
218
289
  }
290
+ constructor() {
291
+ super();
292
+ // Defaults.
293
+ this.$data.enabled = true;
294
+ this.$data.abilities = [];
295
+ this.$data.groups = [];
296
+ this.$data.inheritAbilities = true;
297
+ this.$updateDataProtection();
298
+ }
299
+ $setNymph(nymph) {
300
+ this.$nymph = nymph;
301
+ if (!this.$asleep()) {
302
+ if (this.$data.group && this.$data.group.$nymph !== nymph) {
303
+ this.$data.group.$setNymph(nymph);
304
+ }
305
+ for (const group of this.$data.groups ?? []) {
306
+ if (group && group.$nymph !== nymph) {
307
+ group.$setNymph(nymph);
308
+ }
309
+ }
310
+ }
311
+ }
312
+ async $getUniques() {
313
+ const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
314
+ const uniques = [`u:${this.$data.username}`];
315
+ if (!tilmeld.config.emailUsernames &&
316
+ tilmeld.config.userFields.includes('email')) {
317
+ uniques.push(`e:${this.$data.email}`);
318
+ if (this.$data.newEmailAddress != null) {
319
+ uniques.push(`e:${this.$data.newEmailAddress}`);
320
+ }
321
+ }
322
+ return uniques;
323
+ }
219
324
  async $login(data) {
220
325
  const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
221
326
  if (this.guid == null) {
@@ -1227,7 +1332,7 @@ class User extends AbleObject_1.default {
1227
1332
  const transaction = 'tilmeld-register-' + (0, guid_1.nanoid)();
1228
1333
  const nymph = this.$nymph;
1229
1334
  const tnymph = await this.$nymph.startTransaction(transaction);
1230
- this.$nymph = tnymph;
1335
+ this.$setNymph(tnymph);
1231
1336
  tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
1232
1337
  let message = '';
1233
1338
  let loggedin = false;
@@ -1242,71 +1347,13 @@ class User extends AbleObject_1.default {
1242
1347
  }
1243
1348
  catch (e) {
1244
1349
  await tnymph.rollback(transaction);
1245
- this.$nymph = nymph;
1350
+ this.$setNymph(nymph);
1246
1351
  return {
1247
1352
  result: false,
1248
1353
  loggedin: false,
1249
1354
  message: e.message,
1250
1355
  };
1251
1356
  }
1252
- // Add primary group.
1253
- let generatedPrimaryGroup = null;
1254
- if (tilmeld.config.generatePrimary) {
1255
- // Generate a new primary group for the user.
1256
- generatedPrimaryGroup = await tilmeld.Group.factory();
1257
- generatedPrimaryGroup.groupname = this.$data.username;
1258
- generatedPrimaryGroup.avatar = this.$data.avatar;
1259
- if (tilmeld.config.userFields.includes('name')) {
1260
- generatedPrimaryGroup.name = this.$data.name;
1261
- }
1262
- if (tilmeld.config.userFields.includes('email')) {
1263
- generatedPrimaryGroup.email = this.$data.email;
1264
- }
1265
- const parent = await tnymph.getEntity({ class: tilmeld.Group }, {
1266
- type: '&',
1267
- equal: ['defaultPrimary', true],
1268
- });
1269
- if (parent != null) {
1270
- generatedPrimaryGroup.parent = parent;
1271
- }
1272
- if (!(await generatedPrimaryGroup.$saveSkipAC())) {
1273
- await tnymph.rollback(transaction);
1274
- this.$nymph = nymph;
1275
- return {
1276
- result: false,
1277
- loggedin: false,
1278
- message: 'Error creating primary group for user.',
1279
- };
1280
- }
1281
- this.$data.group = generatedPrimaryGroup;
1282
- }
1283
- else {
1284
- // Add the default primary.
1285
- const group = await tnymph.getEntity({ class: tilmeld.Group }, {
1286
- type: '&',
1287
- equal: ['defaultPrimary', true],
1288
- });
1289
- if (group != null) {
1290
- this.$data.group = group;
1291
- }
1292
- }
1293
- // Add secondary groups.
1294
- if (tilmeld.config.userFields.includes('email') &&
1295
- tilmeld.config.verifyEmail &&
1296
- tilmeld.config.unverifiedAccess) {
1297
- // Add the default secondaries for unverified users.
1298
- this.$data.groups = await tnymph.getEntities({ class: tilmeld.Group }, {
1299
- type: '&',
1300
- equal: ['unverifiedSecondary', true],
1301
- });
1302
- }
1303
- else {
1304
- // Add the default secondaries.
1305
- this.$data.groups = await tnymph.getEntities({ class: tilmeld.Group }, {
1306
- type: '&',
1307
- equal: ['defaultSecondary', true],
1308
- });
1309
- }
1310
1357
  if (tilmeld.config.userFields.includes('email') &&
1311
1358
  tilmeld.config.verifyEmail) {
1312
1359
  // The user will be enabled after verifying their e-mail address.
@@ -1317,7 +1364,7 @@ class User extends AbleObject_1.default {
1317
1364
  else {
1318
1365
  this.$data.enabled = true;
1319
1366
  }
1320
- // If create_admin is true and there are no other users, grant
1367
+ // If createAdmin is true and there are no other users, grant
1321
1368
  // "system/admin".
1322
1369
  let madeAdmin = false;
1323
1370
  if (tilmeld.config.createAdmin) {
@@ -1354,19 +1401,6 @@ class User extends AbleObject_1.default {
1354
1401
  },
1355
1402
  }, this);
1356
1403
  }
1357
- // Save the primary group.
1358
- if (generatedPrimaryGroup != null) {
1359
- generatedPrimaryGroup.user = this;
1360
- if (!(await generatedPrimaryGroup.$saveSkipAC())) {
1361
- await tnymph.rollback(transaction);
1362
- this.$nymph = nymph;
1363
- return {
1364
- result: false,
1365
- loggedin: false,
1366
- message: 'Your primary group could not be assigned. Please try again later.',
1367
- };
1368
- }
1369
- }
1370
1404
  // Finish up.
1371
1405
  if (tilmeld.config.verifyEmail &&
1372
1406
  !tilmeld.config.unverifiedAccess &&
@@ -1409,7 +1443,7 @@ class User extends AbleObject_1.default {
1409
1443
  else {
1410
1444
  this.$nymph.config.debugError('tilmeld', `Error registering new user "${this.$data.username}".`);
1411
1445
  await tnymph.rollback(transaction);
1412
- this.$nymph = nymph;
1446
+ this.$setNymph(nymph);
1413
1447
  return {
1414
1448
  result: false,
1415
1449
  loggedin: false,
@@ -1420,12 +1454,12 @@ class User extends AbleObject_1.default {
1420
1454
  catch (e) {
1421
1455
  this.$nymph.config.debugError('tilmeld', `Error registering new user "${this.$data.username}": ${e}`);
1422
1456
  await tnymph.rollback(transaction);
1423
- this.$nymph = nymph;
1457
+ this.$setNymph(nymph);
1424
1458
  throw e;
1425
1459
  }
1426
1460
  try {
1427
1461
  await tnymph.commit(transaction);
1428
- this.$nymph = nymph;
1462
+ this.$setNymph(nymph);
1429
1463
  await this.$nymph.tilmeld?.fillSession(this);
1430
1464
  }
1431
1465
  catch (e) {
@@ -1442,7 +1476,8 @@ class User extends AbleObject_1.default {
1442
1476
  if (this.$data.username == null || !this.$data.username.trim().length) {
1443
1477
  return false;
1444
1478
  }
1445
- if (tilmeld.gatekeeper('tilmeld/admin') &&
1479
+ if (!this.$skipAcWhenSaving &&
1480
+ tilmeld.gatekeeper('tilmeld/admin') &&
1446
1481
  !tilmeld.gatekeeper('system/admin') &&
1447
1482
  this.$data.abilities?.includes('system/admin')) {
1448
1483
  throw new errors_1.BadDataError("You don't have the authority to modify system admins.");
@@ -1615,53 +1650,133 @@ class User extends AbleObject_1.default {
1615
1650
  if (this.$data.password == null) {
1616
1651
  throw new errors_1.BadDataError('A password is required.');
1617
1652
  }
1618
- try {
1619
- tilmeld.config.validatorUser(tilmeld, this);
1620
- }
1621
- catch (e) {
1622
- throw new errors_1.BadDataError(e?.message);
1623
- }
1624
1653
  // Start transaction.
1625
1654
  const transaction = 'tilmeld-save-' + (0, guid_1.nanoid)();
1626
1655
  const nymph = this.$nymph;
1627
1656
  const tnymph = await this.$nymph.startTransaction(transaction);
1628
- this.$nymph = tnymph;
1657
+ this.$setNymph(tnymph);
1629
1658
  tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
1630
- const group = this.$data.group;
1631
- await group?.$wake();
1632
- if (group != null && group.user != null && this.$is(group.user)) {
1633
- try {
1659
+ // If the primary group is generated now, the user assigned to it won't have
1660
+ // a guid yet, so it needs to be assigned after the user is saved.
1661
+ let saveGeneratedPrimaryGroupAgain = false;
1662
+ try {
1663
+ let group = this.$data.group;
1664
+ await group?.$wake();
1665
+ if (group == null && this.guid == null) {
1666
+ if (tilmeld.config.generatePrimary) {
1667
+ // Generate a new primary group for the user.
1668
+ group = await tilmeld.Group.factory();
1669
+ // This user doesn't have a guid yet, so that's why we set
1670
+ // saveGeneratedPrimaryGroupAgain.
1671
+ group.user = this;
1672
+ saveGeneratedPrimaryGroupAgain = true;
1673
+ const parent = await tnymph.getEntity({ class: tilmeld.Group }, {
1674
+ type: '&',
1675
+ equal: ['defaultPrimary', true],
1676
+ });
1677
+ if (parent != null) {
1678
+ group.parent = parent;
1679
+ }
1680
+ }
1681
+ else {
1682
+ // Add the default primary.
1683
+ const group = await tnymph.getEntity({ class: tilmeld.Group }, {
1684
+ type: '&',
1685
+ equal: ['defaultPrimary', true],
1686
+ });
1687
+ if (group != null) {
1688
+ this.$data.group = group;
1689
+ }
1690
+ }
1691
+ }
1692
+ if (group != null && group.user != null && this.$is(group.user)) {
1634
1693
  // Update the user's generated primary group.
1635
- group.groupname = this.$data.username;
1636
- group.avatar = this.$data.avatar;
1637
- if (tilmeld.config.userFields.includes('email')) {
1694
+ let changed = false;
1695
+ if (group.groupname !== this.$data.username) {
1696
+ group.groupname = this.$data.username;
1697
+ changed = true;
1698
+ }
1699
+ if (group.avatar !== this.$data.avatar) {
1700
+ group.avatar = this.$data.avatar;
1701
+ changed = true;
1702
+ }
1703
+ if (tilmeld.config.userFields.includes('email') &&
1704
+ group.email !== this.$data.email) {
1638
1705
  group.email = this.$data.email;
1706
+ changed = true;
1639
1707
  }
1640
- if (tilmeld.config.userFields.includes('name')) {
1708
+ if (tilmeld.config.userFields.includes('name') &&
1709
+ group.name !== this.$data.name) {
1641
1710
  group.name = this.$data.name;
1711
+ changed = true;
1642
1712
  }
1643
- if (tilmeld.config.userFields.includes('phone')) {
1713
+ if (tilmeld.config.userFields.includes('phone') &&
1714
+ group.phone !== this.$data.phone) {
1644
1715
  group.phone = this.$data.phone;
1716
+ changed = true;
1717
+ }
1718
+ if (changed) {
1719
+ if (!(await group.$saveSkipAC())) {
1720
+ throw Error('Error updating primary group for user.');
1721
+ }
1645
1722
  }
1646
- await group.$saveSkipAC();
1647
1723
  this.$data.group = group;
1648
1724
  }
1725
+ if (this.$data.groups == null || this.$data.groups.length === 0) {
1726
+ // Add secondary groups.
1727
+ if (tilmeld.config.userFields.includes('email') &&
1728
+ tilmeld.config.verifyEmail &&
1729
+ tilmeld.config.unverifiedAccess &&
1730
+ this.$data.secret != null) {
1731
+ // Add the default secondaries for unverified users.
1732
+ this.$data.groups = await tnymph.getEntities({ class: tilmeld.Group }, {
1733
+ type: '&',
1734
+ equal: ['unverifiedSecondary', true],
1735
+ });
1736
+ }
1737
+ else {
1738
+ // Add the default secondaries.
1739
+ this.$data.groups = await tnymph.getEntities({ class: tilmeld.Group }, {
1740
+ type: '&',
1741
+ equal: ['defaultSecondary', true],
1742
+ });
1743
+ }
1744
+ }
1745
+ try {
1746
+ tilmeld.config.validatorUser(tilmeld, this);
1747
+ }
1649
1748
  catch (e) {
1650
- await tnymph.rollback(transaction);
1651
- this.$nymph = nymph;
1652
- throw e;
1749
+ throw new errors_1.BadDataError(e?.message);
1653
1750
  }
1654
1751
  }
1752
+ catch (e) {
1753
+ await tnymph.rollback(transaction);
1754
+ this.$setNymph(nymph);
1755
+ throw e;
1756
+ }
1655
1757
  let ret;
1656
1758
  let preGuid = this.guid;
1657
1759
  let preCdate = this.cdate;
1658
1760
  let preMdate = this.mdate;
1659
1761
  try {
1660
1762
  ret = await super.$save();
1763
+ if (ret && saveGeneratedPrimaryGroupAgain) {
1764
+ const group = this.$data.group;
1765
+ if (group == null) {
1766
+ throw Error('Generated primary group not found.');
1767
+ }
1768
+ await group.$wake();
1769
+ // Now this user has a GUID, so saving it on the group will succeed.
1770
+ group.user = this;
1771
+ if (!(await group.$saveSkipAC())) {
1772
+ throw Error("Generated primary group couldn't be saved.");
1773
+ }
1774
+ this.$data.group = group;
1775
+ }
1661
1776
  }
1662
1777
  catch (e) {
1663
1778
  await tnymph.rollback(transaction);
1664
- this.$nymph = nymph;
1779
+ this.$setNymph(nymph);
1665
1780
  throw e;
1666
1781
  }
1667
1782
  if (ret) {
@@ -1672,7 +1787,7 @@ class User extends AbleObject_1.default {
1672
1787
  this.guid = preGuid;
1673
1788
  this.cdate = preCdate;
1674
1789
  this.mdate = preMdate;
1675
- this.$nymph = nymph;
1790
+ this.$setNymph(nymph);
1676
1791
  throw new Error("Couldn't send verification email.");
1677
1792
  }
1678
1793
  }
@@ -1688,7 +1803,7 @@ class User extends AbleObject_1.default {
1688
1803
  else {
1689
1804
  await tnymph.rollback(transaction);
1690
1805
  }
1691
- this.$nymph = nymph;
1806
+ this.$setNymph(nymph);
1692
1807
  return ret;
1693
1808
  }
1694
1809
  /**
@@ -1707,10 +1822,11 @@ class User extends AbleObject_1.default {
1707
1822
  }
1708
1823
  async $delete() {
1709
1824
  const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
1710
- if (!tilmeld.gatekeeper('tilmeld/admin')) {
1825
+ if (!this.$skipAcWhenDeleting && !tilmeld.gatekeeper('tilmeld/admin')) {
1711
1826
  throw new errors_1.BadDataError("You don't have the authority to delete users.");
1712
1827
  }
1713
- if (!tilmeld.gatekeeper('system/admin') &&
1828
+ if (!this.$skipAcWhenDeleting &&
1829
+ !tilmeld.gatekeeper('system/admin') &&
1714
1830
  this.$data.abilities?.includes('system/admin')) {
1715
1831
  throw new errors_1.BadDataError("You don't have the authority to delete system admins.");
1716
1832
  }
@@ -1719,6 +1835,20 @@ class User extends AbleObject_1.default {
1719
1835
  }
1720
1836
  return await super.$delete();
1721
1837
  }
1838
+ /*
1839
+ * This should *never* be accessible on the client.
1840
+ */
1841
+ async $deleteSkipAC() {
1842
+ this.$skipAcWhenDeleting = true;
1843
+ return await this.$delete();
1844
+ }
1845
+ $tilmeldDeleteSkipAC() {
1846
+ if (this.$skipAcWhenDeleting) {
1847
+ this.$skipAcWhenDeleting = false;
1848
+ return true;
1849
+ }
1850
+ return false;
1851
+ }
1722
1852
  static on(event, callback) {
1723
1853
  const prop = (event + 'Callbacks');
1724
1854
  if (!(prop in this)) {
@@ -1742,62 +1872,5 @@ class User extends AbleObject_1.default {
1742
1872
  return true;
1743
1873
  }
1744
1874
  }
1745
- /**
1746
- * The instance of Tilmeld to use for queries.
1747
- */
1748
- User.ETYPE = 'tilmeld_user';
1749
- User.class = 'User';
1750
- User.checkUsernameCallbacks = [];
1751
- User.beforeRegisterCallbacks = [];
1752
- User.afterRegisterCallbacks = [];
1753
- User.beforeLoginCallbacks = [];
1754
- User.afterLoginCallbacks = [];
1755
- User.beforeLogoutCallbacks = [];
1756
- User.afterLogoutCallbacks = [];
1757
- User.DEFAULT_CLIENT_ENABLED_METHODS = [
1758
- '$checkUsername',
1759
- '$checkEmail',
1760
- '$checkPhone',
1761
- '$getAvatar',
1762
- '$register',
1763
- ];
1764
- User.DEFAULT_PRIVATE_DATA = [
1765
- // The things that are commented are there to show what is _not_ private.
1766
- 'abilities',
1767
- // 'username',
1768
- // 'nameFirst',
1769
- // 'nameMiddle',
1770
- // 'nameLast',
1771
- // 'name',
1772
- 'email',
1773
- // 'avatar',
1774
- 'phone',
1775
- 'group',
1776
- 'groups',
1777
- 'inheritAbilities',
1778
- 'enabled',
1779
- 'secret',
1780
- 'emailChangeDate',
1781
- 'newEmailSecret',
1782
- 'newEmailAddress',
1783
- 'cancelEmailSecret',
1784
- 'cancelEmailAddress',
1785
- 'recoverSecret',
1786
- 'recoverSecretDate',
1787
- 'salt',
1788
- 'password',
1789
- 'passwordTemp',
1790
- 'revokeTokenDate',
1791
- 'totpSecret',
1792
- ];
1793
- User.DEFAULT_ALLOWLIST_DATA = [];
1794
- User.clientEnabledStaticMethods = [
1795
- 'current',
1796
- 'loginUser',
1797
- 'sendRecovery',
1798
- 'recover',
1799
- 'getClientConfig',
1800
- ];
1801
- User.searchRestrictedData = [...User.DEFAULT_PRIVATE_DATA];
1802
1875
  exports.default = User;
1803
1876
  //# sourceMappingURL=User.js.map