@nymphjs/tilmeld 1.0.0-beta.121 → 1.0.0-beta.123

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +6 -3
  3. package/dist/Group.d.ts +9 -9
  4. package/dist/Group.js +790 -793
  5. package/dist/Group.js.map +1 -1
  6. package/dist/Group.testArtifacts.js +3 -3
  7. package/dist/Group.testArtifacts.js.map +1 -1
  8. package/dist/Tilmeld.js +6 -15
  9. package/dist/Tilmeld.js.map +1 -1
  10. package/dist/User.d.ts +8 -9
  11. package/dist/User.js +1838 -1957
  12. package/dist/User.js.map +1 -1
  13. package/dist/User.testArtifacts.js +7 -7
  14. package/dist/User.testArtifacts.js.map +1 -1
  15. package/dist/decorators/index.d.ts +2 -0
  16. package/dist/decorators/index.js +3 -0
  17. package/dist/decorators/index.js.map +1 -0
  18. package/dist/decorators/requireAbility.d.ts +10 -0
  19. package/dist/decorators/requireAbility.js +21 -0
  20. package/dist/decorators/requireAbility.js.map +1 -0
  21. package/dist/decorators/requireAuthentication.d.ts +8 -0
  22. package/dist/decorators/requireAuthentication.js +16 -0
  23. package/dist/decorators/requireAuthentication.js.map +1 -0
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.js +1 -0
  26. package/dist/index.js.map +1 -1
  27. package/dist/testArtifacts.d.ts +5 -1
  28. package/dist/testArtifacts.js +211 -67
  29. package/dist/testArtifacts.js.map +1 -1
  30. package/package.json +13 -7
  31. package/src/Group.testArtifacts.ts +3 -3
  32. package/src/Group.ts +108 -158
  33. package/src/Tilmeld.ts +6 -35
  34. package/src/User.testArtifacts.ts +7 -7
  35. package/src/User.ts +364 -515
  36. package/src/decorators/index.ts +2 -0
  37. package/src/decorators/requireAbility.ts +26 -0
  38. package/src/decorators/requireAuthentication.ts +19 -0
  39. package/src/index.ts +1 -0
  40. package/src/testArtifacts.ts +165 -46
package/src/Group.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import crypto from 'node:crypto';
2
- import type {
3
- Nymph,
4
- EntityData,
5
- EntityJson,
6
- EntityPatch,
7
- Options,
8
- Selector,
9
- SerializedEntityData,
2
+ import {
3
+ type EntityData,
4
+ type EntityJson,
5
+ type EntityPatch,
6
+ type Options,
7
+ type Selector,
8
+ type SerializedEntityData,
9
+ transaction,
10
+ transactional,
10
11
  } from '@nymphjs/nymph';
11
12
  import { nanoid } from '@nymphjs/guid';
12
13
  import { difference, xor } from 'lodash-es';
@@ -310,18 +311,6 @@ export default class Group extends AbleObject<GroupData> {
310
311
  this.$updateDataProtection();
311
312
  }
312
313
 
313
- $setNymph(nymph: Nymph) {
314
- this.$nymph = nymph;
315
- if (!this.$asleep()) {
316
- if (this.$data.user && this.$data.user.$nymph !== nymph) {
317
- this.$data.user.$setNymph(nymph);
318
- }
319
- if (this.$data.parent && this.$data.parent.$nymph !== nymph) {
320
- this.$data.parent.$setNymph(nymph);
321
- }
322
- }
323
- }
324
-
325
314
  async $getUniques(): Promise<string[]> {
326
315
  const tilmeld = enforceTilmeld(this);
327
316
  const uniques = [`u:${this.$data.groupname}`];
@@ -337,7 +326,7 @@ export default class Group extends AbleObject<GroupData> {
337
326
  return uniques;
338
327
  }
339
328
 
340
- public $getAvatar() {
329
+ public $getAvatar(): string {
341
330
  if (this.$data.avatar != null && this.$data.avatar !== '') {
342
331
  return this.$data.avatar;
343
332
  }
@@ -504,7 +493,7 @@ export default class Group extends AbleObject<GroupData> {
504
493
  }
505
494
 
506
495
  /**
507
- * Check whether the group is a descendant of a group.
496
+ * Check whether the group is a descendant of the given group.
508
497
  *
509
498
  * @param group The group, or the group's GUID.
510
499
  * @returns True or false.
@@ -542,7 +531,7 @@ export default class Group extends AbleObject<GroupData> {
542
531
  *
543
532
  * @returns An array of groups.
544
533
  */
545
- public async $getChildren() {
534
+ public async $getChildren(): Promise<(Group & GroupData)[]> {
546
535
  const tilmeld = enforceTilmeld(this);
547
536
  return await this.$nymph.getEntities(
548
537
  { class: tilmeld.Group },
@@ -558,10 +547,12 @@ export default class Group extends AbleObject<GroupData> {
558
547
  * Gets an array of the group's descendant groups.
559
548
  *
560
549
  * @param andSelf Include this group in the returned array.
550
+ * @param andDisabled Include disabled groups.
561
551
  * @returns An array of groups.
562
552
  */
563
553
  public async $getDescendants(
564
554
  andSelf = false,
555
+ andDisabled = false,
565
556
  ): Promise<(Group & GroupData)[]> {
566
557
  const tilmeld = enforceTilmeld(this);
567
558
  let groups: (Group & GroupData)[] = [];
@@ -569,7 +560,7 @@ export default class Group extends AbleObject<GroupData> {
569
560
  { class: tilmeld.Group },
570
561
  {
571
562
  type: '&',
572
- equal: ['enabled', true],
563
+ ...(andDisabled ? {} : { equal: ['enabled', true] }),
573
564
  ref: ['parent', this],
574
565
  },
575
566
  );
@@ -593,7 +584,7 @@ export default class Group extends AbleObject<GroupData> {
593
584
  *
594
585
  * @returns The level of the group.
595
586
  */
596
- public async $getLevel() {
587
+ public async $getLevel(): Promise<number> {
597
588
  const tilmeld = enforceTilmeld(this);
598
589
  let group = await tilmeld.Group.factory(this.guid ?? undefined);
599
590
  let parent = group.parent;
@@ -609,7 +600,7 @@ export default class Group extends AbleObject<GroupData> {
609
600
  }
610
601
 
611
602
  /**
612
- * Gets an array of users in the group.
603
+ * Gets an array of enabled users in the group.
613
604
  *
614
605
  * @param descendants Include users in all descendant groups too.
615
606
  * @param limit The limit for the query.
@@ -632,10 +623,7 @@ export default class Group extends AbleObject<GroupData> {
632
623
  limit,
633
624
  offset,
634
625
  },
635
- {
636
- type: '&',
637
- equal: ['enabled', true],
638
- },
626
+ { type: '&', equal: ['enabled', true] },
639
627
  {
640
628
  type: '|',
641
629
  ref: [
@@ -845,7 +833,7 @@ export default class Group extends AbleObject<GroupData> {
845
833
  public async $save() {
846
834
  let tilmeld = enforceTilmeld(this);
847
835
  if (this.$data.groupname == null || !this.$data.groupname.trim().length) {
848
- return false;
836
+ throw new BadUsernameError('Groupname must not be empty.');
849
837
  }
850
838
 
851
839
  // Lowercase the domain part.
@@ -1007,11 +995,13 @@ export default class Group extends AbleObject<GroupData> {
1007
995
  );
1008
996
  if (currentPrimary != null && !this.$is(currentPrimary)) {
1009
997
  currentPrimary.defaultPrimary = false;
1010
- if (
1011
- this.$skipAcWhenSaving
1012
- ? !(await currentPrimary.$saveSkipAC())
1013
- : !(await currentPrimary.$save())
1014
- ) {
998
+ try {
999
+ if (this.$skipAcWhenSaving) {
1000
+ await currentPrimary.$saveSkipAC();
1001
+ } else {
1002
+ await currentPrimary.$save();
1003
+ }
1004
+ } catch (e: any) {
1015
1005
  throw new CouldNotChangeDefaultPrimaryGroupError(
1016
1006
  'Could not change new user primary group from ' +
1017
1007
  `${currentPrimary.groupname}.`,
@@ -1020,45 +1010,43 @@ export default class Group extends AbleObject<GroupData> {
1020
1010
  }
1021
1011
  }
1022
1012
 
1023
- const transaction = 'tilmeld-save-group-' + (this.guid || nanoid());
1024
- const nymph = this.$nymph;
1025
- const tnymph = await nymph.startTransaction(transaction);
1026
- this.$setNymph(tnymph);
1027
- tilmeld = enforceTilmeld(this);
1028
-
1029
1013
  let preGuid = this.guid;
1030
1014
  let preCdate = this.cdate;
1031
1015
  let preMdate = this.mdate;
1016
+ return await transaction(
1017
+ this.$nymph,
1018
+ 'tilmeld-save-group-' + (this.guid || nanoid()),
1019
+ async (nymph) => {
1020
+ this.$setNymph(nymph);
1032
1021
 
1033
- try {
1034
- for (let callback of (this.constructor as typeof Group)
1035
- .beforeSaveCallbacks) {
1036
- if (callback) {
1037
- await callback(this);
1022
+ for (let callback of (this.constructor as typeof Group)
1023
+ .beforeSaveCallbacks) {
1024
+ if (callback) {
1025
+ await callback(this);
1026
+ }
1038
1027
  }
1039
- }
1040
1028
 
1041
- let ret = await super.$save();
1042
- if (ret) {
1043
- this.$originalGroupname = this.$data.groupname;
1044
- }
1045
- for (let callback of (this.constructor as typeof Group)
1046
- .afterSaveCallbacks) {
1047
- if (callback) {
1048
- await callback(this);
1029
+ await super.$save();
1030
+
1031
+ for (let callback of (this.constructor as typeof Group)
1032
+ .afterSaveCallbacks) {
1033
+ if (callback) {
1034
+ await callback(this);
1035
+ }
1049
1036
  }
1050
- }
1051
- ret = await tnymph.commit(transaction);
1052
- this.$setNymph(nymph);
1053
- return ret;
1054
- } catch (e: any) {
1055
- await tnymph.rollback(transaction);
1056
- this.guid = preGuid;
1057
- this.cdate = preCdate;
1058
- this.mdate = preMdate;
1059
- this.$setNymph(nymph);
1060
- throw e;
1061
- }
1037
+ },
1038
+ async (nymph, committed) => {
1039
+ this.$setNymph(nymph);
1040
+
1041
+ if (committed) {
1042
+ this.$originalGroupname = this.$data.groupname;
1043
+ } else {
1044
+ this.guid = preGuid;
1045
+ this.cdate = preCdate;
1046
+ this.mdate = preMdate;
1047
+ }
1048
+ },
1049
+ );
1062
1050
  }
1063
1051
 
1064
1052
  /*
@@ -1066,7 +1054,7 @@ export default class Group extends AbleObject<GroupData> {
1066
1054
  */
1067
1055
  public async $saveSkipAC() {
1068
1056
  this.$skipAcWhenSaving = true;
1069
- return await this.$save();
1057
+ await this.$save();
1070
1058
  }
1071
1059
 
1072
1060
  public $tilmeldSaveSkipAC() {
@@ -1077,8 +1065,11 @@ export default class Group extends AbleObject<GroupData> {
1077
1065
  return false;
1078
1066
  }
1079
1067
 
1068
+ @transactional
1080
1069
  public async $delete() {
1081
1070
  let tilmeld = enforceTilmeld(this);
1071
+ const User = this.$nymph.getEntityClass(UserClass);
1072
+
1082
1073
  if (!this.$skipAcWhenDeleting && !tilmeld.gatekeeper('tilmeld/admin')) {
1083
1074
  throw new AccessControlError(
1084
1075
  "You don't have the authority to delete groups.",
@@ -1090,105 +1081,64 @@ export default class Group extends AbleObject<GroupData> {
1090
1081
  );
1091
1082
  }
1092
1083
 
1093
- const transaction = 'tilmeld-delete-group-' + this.guid;
1094
- const nymph = this.$nymph;
1095
- const tnymph = await nymph.startTransaction(transaction);
1096
- this.$setNymph(tnymph);
1097
- tilmeld = enforceTilmeld(this);
1098
- const User = tnymph.getEntityClass(UserClass);
1099
-
1100
- try {
1101
- for (let callback of (this.constructor as typeof Group)
1102
- .beforeDeleteCallbacks) {
1103
- if (callback) {
1104
- await callback(this);
1105
- }
1084
+ for (let callback of (this.constructor as typeof Group)
1085
+ .beforeDeleteCallbacks) {
1086
+ if (callback) {
1087
+ await callback(this);
1106
1088
  }
1089
+ }
1107
1090
 
1108
- // Delete descendants.
1109
- const descendants = await this.$getDescendants();
1110
- if (descendants.length) {
1111
- for (let curGroup of descendants) {
1112
- if (
1113
- this.$skipAcWhenDeleting
1114
- ? !(await curGroup.$deleteSkipAC())
1115
- : !(await curGroup.$delete())
1116
- ) {
1117
- await tnymph.rollback(transaction);
1118
- this.$setNymph(nymph);
1119
- return false;
1120
- }
1091
+ // Delete children.
1092
+ const children = await this.$nymph.getEntities(
1093
+ { class: tilmeld.Group, skipAc: true },
1094
+ { type: '&', ref: ['parent', this] },
1095
+ );
1096
+ if (children.length) {
1097
+ for (let curGroup of children) {
1098
+ if (this.$skipAcWhenDeleting) {
1099
+ await curGroup.$deleteSkipAC();
1100
+ } else {
1101
+ await curGroup.$delete();
1121
1102
  }
1122
1103
  }
1104
+ }
1123
1105
 
1124
- // Remove users from this primary group.
1125
- const primaryUsers = await tnymph.getEntities(
1126
- {
1127
- class: User,
1128
- skipAc: true,
1129
- },
1130
- {
1131
- type: '&',
1132
- ref: ['group', this],
1133
- },
1134
- );
1135
- for (let user of primaryUsers) {
1136
- delete user.group;
1137
- if (
1138
- this.$skipAcWhenDeleting
1139
- ? !(await user.$saveSkipAC())
1140
- : !(await user.$save())
1141
- ) {
1142
- await tnymph.rollback(transaction);
1143
- this.$setNymph(nymph);
1144
- return false;
1145
- }
1106
+ // Remove users from this primary group.
1107
+ const primaryUsers = await this.$nymph.getEntities(
1108
+ { class: User, skipAc: true },
1109
+ { type: '&', ref: ['group', this] },
1110
+ );
1111
+ for (let user of primaryUsers) {
1112
+ delete user.group;
1113
+ if (this.$skipAcWhenDeleting) {
1114
+ await user.$saveSkipAC();
1115
+ } else {
1116
+ await user.$save();
1146
1117
  }
1118
+ }
1147
1119
 
1148
- // Remove users from this secondary group.
1149
- const secondaryUsers = await tnymph.getEntities(
1150
- {
1151
- class: User,
1152
- skipAc: true,
1153
- },
1154
- {
1155
- type: '&',
1156
- ref: ['groups', this],
1157
- },
1158
- );
1159
- for (let user of secondaryUsers) {
1160
- user.$delGroup(this);
1161
- if (
1162
- this.$skipAcWhenDeleting
1163
- ? !(await user.$saveSkipAC())
1164
- : !(await user.$save())
1165
- ) {
1166
- await tnymph.rollback(transaction);
1167
- this.$setNymph(nymph);
1168
- return false;
1169
- }
1120
+ // Remove users from this secondary group.
1121
+ const secondaryUsers = await this.$nymph.getEntities(
1122
+ { class: User, skipAc: true },
1123
+ { type: '&', ref: ['groups', this] },
1124
+ );
1125
+ for (let user of secondaryUsers) {
1126
+ user.$delGroup(this);
1127
+ if (this.$skipAcWhenDeleting) {
1128
+ await user.$saveSkipAC();
1129
+ } else {
1130
+ await user.$save();
1170
1131
  }
1132
+ }
1171
1133
 
1172
- // Delete the group.
1173
- let success = await super.$delete();
1174
- if (success) {
1175
- for (let callback of (this.constructor as typeof Group)
1176
- .afterDeleteCallbacks) {
1177
- if (callback) {
1178
- await callback(this);
1179
- }
1180
- }
1134
+ // Delete the group.
1135
+ await super.$delete();
1181
1136
 
1182
- success = await tnymph.commit(transaction);
1183
- } else {
1184
- await tnymph.rollback(transaction);
1137
+ for (let callback of (this.constructor as typeof Group)
1138
+ .afterDeleteCallbacks) {
1139
+ if (callback) {
1140
+ await callback(this);
1185
1141
  }
1186
- this.$setNymph(nymph);
1187
- return success;
1188
- } catch (e: any) {
1189
- await tnymph.rollback(transaction);
1190
- this.$setNymph(nymph);
1191
- throw e;
1192
1142
  }
1193
1143
  }
1194
1144
 
@@ -1197,7 +1147,7 @@ export default class Group extends AbleObject<GroupData> {
1197
1147
  */
1198
1148
  async $deleteSkipAC() {
1199
1149
  this.$skipAcWhenDeleting = true;
1200
- return await this.$delete();
1150
+ await this.$delete();
1201
1151
  }
1202
1152
 
1203
1153
  $tilmeldDeleteSkipAC() {
package/src/Tilmeld.ts CHANGED
@@ -230,8 +230,10 @@ export default class Tilmeld implements TilmeldInterface {
230
230
  options.class?.factorySync() instanceof User) || // The use is searching for a user and searching is disabled.
231
231
  (!tilmeld.config.enableGroupSearch &&
232
232
  options.class?.factorySync() instanceof Group)) && // Or the same, but for a group.
233
- (selectors[0] == null || // No selector specified.
233
+ (selectors.length > 1 || // There's more than one selector.
234
+ selectors[0] == null || // Or no selector specified.
234
235
  selectors[0].type !== '&' || // Or the type is not and.
236
+ Object.keys(selectors[0]).length > 2 || // Or there's more than one clause.
235
237
  (!('guid' in selectors[0]) && !('equal' in selectors[0])) || // Or they're searching for something other than guid or equal.
236
238
  ('guid' in selectors[0] &&
237
239
  (!selectors[0].guid?.length ||
@@ -684,20 +686,12 @@ export default class Tilmeld implements TilmeldInterface {
684
686
  subSelectors.push({
685
687
  type: '&',
686
688
  gte: [['acGroup', TilmeldAccessLevels.READ_ACCESS]],
687
- selector: [
688
- {
689
- type: '|',
690
- ref: groupRefs,
691
- },
692
- ],
689
+ selector: [{ type: '|', ref: groupRefs }],
693
690
  });
694
691
  }
695
692
  // All the acRead, acWrite, and acFull contains.
696
693
  if (acContains.length) {
697
- subSelectors.push({
698
- type: '|',
699
- contain: acContains,
700
- });
694
+ subSelectors.push({ type: '|', contain: acContains });
701
695
  }
702
696
  const selector: FormattedSelector = { type: '|' };
703
697
  if (
@@ -1044,10 +1038,7 @@ export default class Tilmeld implements TilmeldInterface {
1044
1038
 
1045
1039
  const user = await this.nymph.getEntity(
1046
1040
  { class: this.User },
1047
- {
1048
- type: '&',
1049
- guid: guid,
1050
- },
1041
+ { type: '&', guid },
1051
1042
  );
1052
1043
  if (!user || !user.guid) {
1053
1044
  return null;
@@ -1332,24 +1323,4 @@ export default class Tilmeld implements TilmeldInterface {
1332
1323
  await this.authenticate(false, true);
1333
1324
  }
1334
1325
  }
1335
-
1336
- // /**
1337
- // * Sort an array of groups hierarchically.
1338
- // *
1339
- // * An additional property of the groups can be used to sort them under their
1340
- // * parents.
1341
- // *
1342
- // * @param array The array of groups.
1343
- // * @param property The name of the property to sort groups by. Undefined for no additional sorting.
1344
- // * @param caseSensitive Sort case sensitively.
1345
- // * @param reverse Reverse the sort order.
1346
- // */
1347
- // public static groupSort(
1348
- // array: any[],
1349
- // property?: string,
1350
- // caseSensitive = false,
1351
- // reverse = false
1352
- // ) {
1353
- // hsort(array, property, 'parent', caseSensitive, reverse);
1354
- // }
1355
1326
  }
@@ -95,7 +95,7 @@ export function UserTest(
95
95
  expect(e).toBeInstanceOf(EntityUniqueConstraintError);
96
96
  }
97
97
 
98
- expect(await newUserA.$deleteSkipAC()).toEqual(true);
98
+ await newUserA.$deleteSkipAC();
99
99
 
100
100
  tilmeld.config.generatePrimary = true;
101
101
  });
@@ -139,7 +139,7 @@ export function UserTest(
139
139
  expect(e).toBeInstanceOf(EntityUniqueConstraintError);
140
140
  }
141
141
 
142
- expect(await newUserA.$deleteSkipAC()).toEqual(true);
142
+ await newUserA.$deleteSkipAC();
143
143
 
144
144
  tilmeld.config.generatePrimary = true;
145
145
  });
@@ -155,7 +155,7 @@ export function UserTest(
155
155
  domainAdmin.$password('password');
156
156
  domainAdmin.$grant('tilmeld/domain/example.com/admin');
157
157
 
158
- expect(await domainAdmin.$saveSkipAC()).toEqual(true);
158
+ await domainAdmin.$saveSkipAC();
159
159
 
160
160
  // Log in the domain admin.
161
161
  await tilmeld.fillSession(domainAdmin);
@@ -169,13 +169,13 @@ export function UserTest(
169
169
  domainUser.name = 'New User';
170
170
  domainUser.$password('password');
171
171
 
172
- expect(await domainUser.$save()).toEqual(true);
172
+ await domainUser.$save();
173
173
 
174
174
  tilmeld.clearSession();
175
175
 
176
176
  for (const user of [domainAdmin, domainUser]) {
177
177
  if (user.guid != null) {
178
- expect(await user.$deleteSkipAC()).toEqual(true);
178
+ await user.$deleteSkipAC();
179
179
  }
180
180
  }
181
181
  });
@@ -191,7 +191,7 @@ export function UserTest(
191
191
  domainAdmin.$password('password');
192
192
  domainAdmin.$grant('tilmeld/domain/example.com/admin');
193
193
 
194
- expect(await domainAdmin.$saveSkipAC()).toEqual(true);
194
+ await domainAdmin.$saveSkipAC();
195
195
 
196
196
  // Log in the domain admin.
197
197
  await tilmeld.fillSession(domainAdmin);
@@ -218,7 +218,7 @@ export function UserTest(
218
218
 
219
219
  for (const user of [domainAdmin, domainUser]) {
220
220
  if (user.guid != null) {
221
- expect(await user.$deleteSkipAC()).toEqual(true);
221
+ await user.$deleteSkipAC();
222
222
  }
223
223
  }
224
224
  });