@nymphjs/tilmeld 1.0.0-beta.4 → 1.0.0-beta.41

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/src/Group.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  EntityData,
3
- EntityInterface,
4
3
  EntityJson,
5
4
  EntityPatch,
6
5
  Options,
@@ -10,7 +9,7 @@ import {
10
9
  import md5 from 'crypto-js/md5';
11
10
  import { difference } from 'lodash';
12
11
 
13
- import type Tilmeld from './Tilmeld';
12
+ import { enforceTilmeld } from './enforceTilmeld';
14
13
  import AbleObject from './AbleObject';
15
14
  import {
16
15
  BadDataError,
@@ -85,7 +84,6 @@ export default class Group extends AbleObject<GroupData> {
85
84
  /**
86
85
  * The instance of Tilmeld to use for queries.
87
86
  */
88
- static tilmeld: Tilmeld;
89
87
  static ETYPE = 'tilmeld_group';
90
88
  static class = 'Group';
91
89
 
@@ -141,19 +139,17 @@ export default class Group extends AbleObject<GroupData> {
141
139
  return entity;
142
140
  }
143
141
 
144
- static factorySync(guid?: string): Group & GroupData {
145
- return super.factorySync(guid) as Group & GroupData;
142
+ static factorySync(): Group & GroupData {
143
+ return super.factorySync() as Group & GroupData;
146
144
  }
147
145
 
148
- constructor(guid?: string) {
149
- super(guid);
146
+ constructor() {
147
+ super();
150
148
 
151
- if (this.guid == null) {
152
- // Defaults.
153
- this.$data.enabled = true;
154
- this.$data.abilities = [];
155
- this.$updateDataProtection();
156
- }
149
+ // Defaults.
150
+ this.$data.enabled = true;
151
+ this.$data.abilities = [];
152
+ this.$updateDataProtection();
157
153
  }
158
154
 
159
155
  /**
@@ -167,13 +163,14 @@ export default class Group extends AbleObject<GroupData> {
167
163
  options?: Options,
168
164
  selectors?: Selector[]
169
165
  ) {
170
- if (!this.tilmeld.gatekeeper('tilmeld/admin')) {
166
+ const tilmeld = enforceTilmeld(this);
167
+ if (!(await tilmeld.gatekeeper('tilmeld/admin'))) {
171
168
  throw new Error("You don't have permission to do that.");
172
169
  }
173
170
 
174
171
  return await this.getAssignableGroups(
175
- this.tilmeld.config.highestPrimary,
176
- { ...options, class: this.tilmeld.Group, return: 'entity' },
172
+ tilmeld.config.highestPrimary,
173
+ { ...options, class: tilmeld.Group, return: 'entity' },
177
174
  [...(selectors ?? [])]
178
175
  );
179
176
  }
@@ -189,13 +186,14 @@ export default class Group extends AbleObject<GroupData> {
189
186
  options?: Options,
190
187
  selectors?: Selector[]
191
188
  ) {
192
- if (!this.tilmeld.gatekeeper('tilmeld/admin')) {
189
+ const tilmeld = enforceTilmeld(this);
190
+ if (!(await tilmeld.gatekeeper('tilmeld/admin'))) {
193
191
  throw new Error("You don't have permission to do that.");
194
192
  }
195
193
 
196
194
  return await this.getAssignableGroups(
197
- this.tilmeld.config.highestSecondary,
198
- { ...options, class: this.tilmeld.Group, return: 'entity' },
195
+ tilmeld.config.highestSecondary,
196
+ { ...options, class: tilmeld.Group, return: 'entity' },
199
197
  [...(selectors ?? [])]
200
198
  );
201
199
  }
@@ -205,6 +203,7 @@ export default class Group extends AbleObject<GroupData> {
205
203
  options: Options<typeof Group>,
206
204
  selectors: Selector[]
207
205
  ) {
206
+ const tilmeld = enforceTilmeld(this);
208
207
  let assignableGroups: (Group & GroupData)[] = [];
209
208
 
210
209
  if (highestParent === false) {
@@ -212,20 +211,28 @@ export default class Group extends AbleObject<GroupData> {
212
211
  }
213
212
 
214
213
  assignableGroups = await this.nymph.getEntities(
215
- { ...options, class: this.tilmeld.Group },
214
+ { ...options, class: tilmeld.Group },
216
215
  ...selectors
217
216
  );
218
217
  if (highestParent !== true) {
219
- assignableGroups = assignableGroups.filter((group) => {
220
- let curGroup = group;
221
- while (curGroup.parent != null && curGroup.parent.cdate != null) {
222
- if (curGroup.parent.guid === highestParent) {
223
- return true;
224
- }
225
- curGroup = curGroup.parent;
226
- }
227
- return false;
228
- });
218
+ assignableGroups = (
219
+ await Promise.all(
220
+ assignableGroups.map(async (group) => {
221
+ let curGroup = group;
222
+ let parent = curGroup.parent;
223
+ await parent?.$wake();
224
+ while (parent != null && parent?.cdate != null) {
225
+ if (parent?.guid === highestParent) {
226
+ return group;
227
+ }
228
+ curGroup = parent;
229
+ parent = curGroup.parent;
230
+ await parent?.$wake();
231
+ }
232
+ return null;
233
+ })
234
+ )
235
+ ).filter((group) => group != null) as (Group & GroupData)[];
229
236
  }
230
237
  return assignableGroups;
231
238
  }
@@ -245,17 +252,42 @@ export default class Group extends AbleObject<GroupData> {
245
252
  }
246
253
 
247
254
  public $jsonAcceptData(input: EntityJson, allowConflict = false) {
248
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
249
- this.$referenceWake();
255
+ const tilmeld = enforceTilmeld(this);
256
+ this.$check();
257
+
258
+ if (
259
+ 'abilities' in input.data &&
260
+ input.data.abilities?.includes('system/admin') !==
261
+ this.$data.abilities?.includes('system/admin') &&
262
+ (!tilmeld.currentUser ||
263
+ !tilmeld.currentUser.abilities?.includes('system/admin'))
264
+ ) {
265
+ throw new BadDataError(
266
+ "You don't have the authority to grant or revoke system/admin."
267
+ );
268
+ }
269
+
270
+ if (
271
+ 'abilities' in input.data &&
272
+ input.data.abilities?.includes('tilmeld/admin') !==
273
+ this.$data.abilities?.includes('tilmeld/admin') &&
274
+ (!tilmeld.currentUser ||
275
+ !tilmeld.currentUser.abilities?.includes('system/admin'))
276
+ ) {
277
+ throw new BadDataError(
278
+ "You don't have the authority to grant or revoke tilmeld/admin."
279
+ );
280
+ }
250
281
 
251
282
  if (
252
- input.data.abilities?.indexOf('system/admin') !== -1 &&
253
- this.$data.abilities?.indexOf('system/admin') === -1 &&
254
- tilmeld.gatekeeper('tilmeld/admin') &&
255
- !tilmeld.gatekeeper('system/admin')
283
+ 'abilities' in input.data &&
284
+ input.data.abilities?.includes('tilmeld/switch') !==
285
+ this.$data.abilities?.includes('tilmeld/switch') &&
286
+ (!tilmeld.currentUser ||
287
+ !tilmeld.currentUser.abilities?.includes('system/admin'))
256
288
  ) {
257
289
  throw new BadDataError(
258
- "You don't have the authority to make this group a system admin."
290
+ "You don't have the authority to grant or revoke tilmeld/switch."
259
291
  );
260
292
  }
261
293
 
@@ -263,17 +295,42 @@ export default class Group extends AbleObject<GroupData> {
263
295
  }
264
296
 
265
297
  public $jsonAcceptPatch(patch: EntityPatch, allowConflict = false) {
266
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
267
- this.$referenceWake();
298
+ const tilmeld = enforceTilmeld(this);
299
+ this.$check();
300
+
301
+ if (
302
+ 'abilities' in patch.set &&
303
+ patch.set.abilities?.includes('system/admin') !==
304
+ this.$data.abilities?.includes('system/admin') &&
305
+ (!tilmeld.currentUser ||
306
+ !tilmeld.currentUser.abilities?.includes('system/admin'))
307
+ ) {
308
+ throw new BadDataError(
309
+ "You don't have the authority to grant or revoke system/admin."
310
+ );
311
+ }
312
+
313
+ if (
314
+ 'abilities' in patch.set &&
315
+ patch.set.abilities?.includes('tilmeld/admin') !==
316
+ this.$data.abilities?.includes('tilmeld/admin') &&
317
+ (!tilmeld.currentUser ||
318
+ !tilmeld.currentUser.abilities?.includes('system/admin'))
319
+ ) {
320
+ throw new BadDataError(
321
+ "You don't have the authority to grant or revoke tilmeld/admin."
322
+ );
323
+ }
268
324
 
269
325
  if (
270
- patch.set.abilities?.indexOf('system/admin') !== -1 &&
271
- this.$data.abilities?.indexOf('system/admin') === -1 &&
272
- tilmeld.gatekeeper('tilmeld/admin') &&
273
- !tilmeld.gatekeeper('system/admin')
326
+ 'abilities' in patch.set &&
327
+ patch.set.abilities?.includes('tilmeld/switch') !==
328
+ this.$data.abilities?.includes('tilmeld/switch') &&
329
+ (!tilmeld.currentUser ||
330
+ !tilmeld.currentUser.abilities?.includes('system/admin'))
274
331
  ) {
275
332
  throw new BadDataError(
276
- "You don't have the authority to make this group a system admin."
333
+ "You don't have the authority to grant or revoke tilmeld/switch."
277
334
  );
278
335
  }
279
336
 
@@ -291,7 +348,7 @@ export default class Group extends AbleObject<GroupData> {
291
348
  * @param givenUser User to update protection for. If undefined, will use the currently logged in user.
292
349
  */
293
350
  public $updateDataProtection(givenUser?: User & UserData) {
294
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
351
+ const tilmeld = enforceTilmeld(this);
295
352
  let user = givenUser ?? tilmeld.User.current();
296
353
 
297
354
  this.$privateData = [...Group.DEFAULT_PRIVATE_DATA];
@@ -312,7 +369,7 @@ export default class Group extends AbleObject<GroupData> {
312
369
  } else if (
313
370
  this.$data.user != null &&
314
371
  user != null &&
315
- this.$data.user.$is(user)
372
+ this.$data.user.guid === user.guid
316
373
  ) {
317
374
  // Users can see their group's data.
318
375
  this.$privateData = [];
@@ -325,11 +382,13 @@ export default class Group extends AbleObject<GroupData> {
325
382
  * @param group The group, or the group's GUID.
326
383
  * @returns True or false.
327
384
  */
328
- public $isDescendant(givenGroup: (Group & GroupData) | string): boolean {
329
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
385
+ public async $isDescendant(
386
+ givenGroup: (Group & GroupData) | string
387
+ ): Promise<boolean> {
388
+ const tilmeld = enforceTilmeld(this);
330
389
  let group: Group & GroupData;
331
390
  if (typeof givenGroup === 'string') {
332
- group = tilmeld.Group.factorySync(givenGroup);
391
+ group = await tilmeld.Group.factory(givenGroup);
333
392
  } else {
334
393
  group = givenGroup;
335
394
  }
@@ -337,13 +396,15 @@ export default class Group extends AbleObject<GroupData> {
337
396
  return false;
338
397
  }
339
398
  // Check to see if the group is a descendant of the given group.
340
- if (this.$data.parent == null) {
399
+ const parent = this.$data.parent;
400
+ await parent?.$wake();
401
+ if (parent == null) {
341
402
  return false;
342
403
  }
343
- if (this.$data.parent.$is(group)) {
404
+ if (parent.$is(group)) {
344
405
  return true;
345
406
  }
346
- if (this.$data.parent.$isDescendant(group)) {
407
+ if (await parent.$isDescendant(group)) {
347
408
  return true;
348
409
  }
349
410
  return false;
@@ -355,7 +416,7 @@ export default class Group extends AbleObject<GroupData> {
355
416
  * @returns An array of groups.
356
417
  */
357
418
  public async $getChildren() {
358
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
419
+ const tilmeld = enforceTilmeld(this);
359
420
  return await this.$nymph.getEntities(
360
421
  { class: tilmeld.Group },
361
422
  {
@@ -375,7 +436,7 @@ export default class Group extends AbleObject<GroupData> {
375
436
  public async $getDescendants(
376
437
  andSelf = false
377
438
  ): Promise<(Group & GroupData)[]> {
378
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
439
+ const tilmeld = enforceTilmeld(this);
379
440
  let groups: (Group & GroupData)[] = [];
380
441
  const entities = await this.$nymph.getEntities(
381
442
  { class: tilmeld.Group },
@@ -394,37 +455,6 @@ export default class Group extends AbleObject<GroupData> {
394
455
  return groups;
395
456
  }
396
457
 
397
- /**
398
- * Gets an array of the group's descendant groups.
399
- *
400
- * @param andSelf Include this group in the returned array.
401
- * @returns An array of groups.
402
- */
403
- public $getDescendantsSync(andSelf = false): (Group & GroupData)[] {
404
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
405
- let groups: (Group & GroupData)[] = [];
406
- let entity: EntityInterface | null;
407
- let offset = 0;
408
- do {
409
- entity = this.$nymph.driver.getEntitySync(
410
- { class: tilmeld.Group, offset },
411
- {
412
- type: '&',
413
- equal: ['enabled', true],
414
- ref: ['parent', this],
415
- }
416
- );
417
- if (entity != null) {
418
- groups = entity.$getDescendantsSync(true).concat(groups);
419
- }
420
- offset++;
421
- } while (entity != null);
422
- if (andSelf) {
423
- groups.push(this);
424
- }
425
- return groups;
426
- }
427
-
428
458
  /**
429
459
  * Get the number of parents the group has.
430
460
  *
@@ -436,13 +466,17 @@ export default class Group extends AbleObject<GroupData> {
436
466
  *
437
467
  * @returns The level of the group.
438
468
  */
439
- public $getLevel() {
440
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
441
- let group = tilmeld.Group.factorySync(this.guid ?? undefined);
469
+ public async $getLevel() {
470
+ const tilmeld = enforceTilmeld(this);
471
+ let group = await tilmeld.Group.factory(this.guid ?? undefined);
472
+ let parent = group.parent;
473
+ await parent?.$wake();
442
474
  let level = 0;
443
- while (group.parent != null && group.parent.cdate != null && level < 1024) {
475
+ while (parent != null && parent.cdate != null && level < 1024) {
444
476
  level++;
445
- group = group.parent;
477
+ group = parent;
478
+ parent = group.parent;
479
+ await parent?.$wake();
446
480
  }
447
481
  return level;
448
482
  }
@@ -460,7 +494,7 @@ export default class Group extends AbleObject<GroupData> {
460
494
  limit?: number,
461
495
  offset?: number
462
496
  ): Promise<(User & UserData)[]> {
463
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
497
+ const tilmeld = enforceTilmeld(this);
464
498
  let groups: (Group & GroupData)[] = [];
465
499
  if (descendants) {
466
500
  groups = await this.$getDescendants();
@@ -502,7 +536,7 @@ export default class Group extends AbleObject<GroupData> {
502
536
  result: boolean;
503
537
  message: string;
504
538
  }> {
505
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
539
+ const tilmeld = enforceTilmeld(this);
506
540
  if (!tilmeld.config.emailUsernames) {
507
541
  if (this.$data.groupname == null || !this.$data.groupname.length) {
508
542
  return { result: false, message: 'Please specify a groupname.' };
@@ -588,7 +622,7 @@ export default class Group extends AbleObject<GroupData> {
588
622
  * @returns An object with a boolean 'result' entry and a 'message' entry.
589
623
  */
590
624
  public async $checkEmail(): Promise<{ result: boolean; message: string }> {
591
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
625
+ const tilmeld = enforceTilmeld(this);
592
626
  if (this.$data.email === '') {
593
627
  return { result: true, message: '' };
594
628
  }
@@ -627,7 +661,7 @@ export default class Group extends AbleObject<GroupData> {
627
661
  }
628
662
 
629
663
  public async $save() {
630
- const tilmeld = this.$nymph.tilmeld as Tilmeld;
664
+ const tilmeld = enforceTilmeld(this);
631
665
  if (this.$data.groupname == null || !this.$data.groupname.trim().length) {
632
666
  return false;
633
667
  }
@@ -671,10 +705,12 @@ export default class Group extends AbleObject<GroupData> {
671
705
  delete this.$data.unverifiedSecondary;
672
706
  }
673
707
 
674
- // Groups should never have 'system/admin' or 'tilmeld/admin' abilities.
708
+ // Groups should never have 'system/admin', 'tilmeld/admin', or
709
+ // 'tilmeld/switch' abilities.
675
710
  this.$data.abilities = difference(this.$data.abilities ?? [], [
676
711
  'system/admin',
677
712
  'tilmeld/admin',
713
+ 'tilmeld/switch',
678
714
  null,
679
715
  undefined,
680
716
  ]) as string[];
@@ -692,11 +728,13 @@ export default class Group extends AbleObject<GroupData> {
692
728
  }
693
729
 
694
730
  // Validate group parent. Make sure it's not a descendant of this group.
731
+ const parent = this.$data.parent;
732
+ await parent?.$wake();
695
733
  if (
696
- this.$data.parent &&
697
- (this.$data.parent.cdate == null ||
698
- this.$is(this.$data.parent) ||
699
- this.$data.parent.$isDescendant(this))
734
+ parent &&
735
+ (parent.cdate == null ||
736
+ this.$is(parent) ||
737
+ (await parent.$isDescendant(this)))
700
738
  ) {
701
739
  throw new BadDataError(
702
740
  "Group parent can't be itself or descendant of itself."
@@ -746,8 +784,8 @@ export default class Group extends AbleObject<GroupData> {
746
784
  }
747
785
 
748
786
  public async $delete() {
749
- let tilmeld = this.$nymph.tilmeld as Tilmeld;
750
- if (!tilmeld.gatekeeper('tilmeld/admin')) {
787
+ let tilmeld = enforceTilmeld(this);
788
+ if (!(await tilmeld.gatekeeper('tilmeld/admin'))) {
751
789
  throw new BadDataError("You don't have the authority to delete groups.");
752
790
  }
753
791
 
@@ -755,7 +793,7 @@ export default class Group extends AbleObject<GroupData> {
755
793
  const nymph = this.$nymph;
756
794
  const tnymph = await nymph.startTransaction(transaction);
757
795
  this.$nymph = tnymph;
758
- tilmeld = this.$nymph.tilmeld as Tilmeld;
796
+ tilmeld = enforceTilmeld(this);
759
797
 
760
798
  // Delete descendants.
761
799
  const descendants = await this.$getDescendants();