@nymphjs/tilmeld 1.0.0-beta.85 → 1.0.0-beta.87

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
@@ -1,12 +1,13 @@
1
1
  import crypto from 'node:crypto';
2
2
  import { humanSecret, nanoid } from '@nymphjs/guid';
3
3
  import strtotime from 'locutus/php/datetime/strtotime.js';
4
- import { difference } from 'lodash-es';
4
+ import { difference, xor } from 'lodash-es';
5
5
  import { TOTP, Secret } from 'otpauth';
6
6
  import { toDataURL } from 'qrcode';
7
+ import { splitn } from '@sciactive/splitn';
7
8
  import { enforceTilmeld } from './enforceTilmeld.js';
8
9
  import AbleObject from './AbleObject.js';
9
- import { BadDataError, BadEmailError, BadUsernameError, EmailChangeRateLimitExceededError, } from './errors/index.js';
10
+ import { AccessControlError, BadDataError, BadEmailError, BadUsernameError, EmailChangeRateLimitExceededError, } from './errors/index.js';
10
11
  /**
11
12
  * A user data model.
12
13
  *
@@ -73,6 +74,7 @@ export default class User extends AbleObject {
73
74
  'sendRecovery',
74
75
  'recover',
75
76
  'getClientConfig',
77
+ 'getDomainUsers',
76
78
  ];
77
79
  $privateData = [...User.DEFAULT_PRIVATE_DATA];
78
80
  static searchRestrictedData = [...User.DEFAULT_PRIVATE_DATA];
@@ -105,6 +107,10 @@ export default class User extends AbleObject {
105
107
  * verification, don't set this.
106
108
  */
107
109
  $originalEmail;
110
+ /**
111
+ * Used to save the current username for domain admin permissions.
112
+ */
113
+ $originalUsername;
108
114
  static async factoryUsername(username) {
109
115
  const entity = new this();
110
116
  if (username != null) {
@@ -260,6 +266,7 @@ export default class User extends AbleObject {
260
266
  regFields: tilmeld.config.regFields,
261
267
  userFields: tilmeld.config.userFields,
262
268
  emailUsernames: tilmeld.config.emailUsernames,
269
+ domainSupport: tilmeld.config.domainSupport,
263
270
  allowRegistration: tilmeld.config.allowRegistration,
264
271
  allowUsernameChange: tilmeld.config.allowUsernameChange,
265
272
  pwRecovery: tilmeld.config.pwRecovery,
@@ -280,6 +287,51 @@ export default class User extends AbleObject {
280
287
  }
281
288
  return result;
282
289
  }
290
+ /**
291
+ * Get the users in a domain. This is only accessible to the domain's admins.
292
+ */
293
+ static async getDomainUsers(domain, options) {
294
+ const tilmeld = enforceTilmeld(this);
295
+ if (!tilmeld.config.domainSupport) {
296
+ throw new AccessControlError('Domain support is not enabled.');
297
+ }
298
+ if (!domain) {
299
+ throw new BadDataError('Domain is required.');
300
+ }
301
+ domain = domain.toLowerCase();
302
+ if (!tilmeld.config.validDomainRegex.test(domain)) {
303
+ throw new BadDataError(tilmeld.config.validDomainRegexNotice);
304
+ }
305
+ if (!tilmeld.gatekeeper(`tilmeld/domain/${domain}/admin`)) {
306
+ throw new AccessControlError('You do not have the authority to manage this domain.');
307
+ }
308
+ return await this.nymph.getEntities({
309
+ class: this.nymph.getEntityClass(User),
310
+ ...(options?.limit != null
311
+ ? {
312
+ limit: options.limit,
313
+ }
314
+ : {}),
315
+ ...(options?.offset != null
316
+ ? {
317
+ offset: options.offset,
318
+ }
319
+ : {}),
320
+ ...(options?.sort != null
321
+ ? {
322
+ sort: options.sort,
323
+ }
324
+ : {}),
325
+ ...(options?.reverse != null
326
+ ? {
327
+ reverse: options.reverse,
328
+ }
329
+ : {}),
330
+ }, {
331
+ type: '&',
332
+ like: ['username', `%@${domain.replace(/([\\%_])/g, (s) => `\\${s}`)}`],
333
+ });
334
+ }
283
335
  constructor() {
284
336
  super();
285
337
  // Defaults.
@@ -507,59 +559,84 @@ export default class User extends AbleObject {
507
559
  $jsonAcceptData(input, allowConflict = false) {
508
560
  const tilmeld = enforceTilmeld(this);
509
561
  this.$check();
510
- if ('abilities' in input.data &&
511
- input.data.abilities?.includes('system/admin') !==
512
- this.$data.abilities?.includes('system/admin') &&
513
- (!tilmeld.currentUser ||
514
- !tilmeld.currentUser.abilities?.includes('system/admin'))) {
515
- throw new BadDataError("You don't have the authority to grant or revoke system/admin.");
516
- }
517
- if ('abilities' in input.data &&
518
- input.data.abilities?.includes('tilmeld/admin') !==
519
- this.$data.abilities?.includes('tilmeld/admin') &&
520
- (!tilmeld.currentUser ||
521
- !tilmeld.currentUser.abilities?.includes('system/admin'))) {
522
- throw new BadDataError("You don't have the authority to grant or revoke tilmeld/admin.");
562
+ if ('abilities' in input.data) {
563
+ this.$checkIncomingAbilities(input.data.abilities);
564
+ }
565
+ // If the user is a new domain user, the data protection needs to be updated
566
+ // before the data is accepted, or else some things won't be accepted
567
+ // correctly (enabled, passwordTemp).
568
+ let reloadDataProtection = false;
569
+ if (tilmeld.config.domainSupport) {
570
+ let [_username, domain] = splitn(input.data.username ?? '', '@', 2);
571
+ // Lowercase the domain part.
572
+ if (domain) {
573
+ domain = domain.toLowerCase();
574
+ }
575
+ const user = tilmeld.User.current();
576
+ const isCurrentUser = user != null && user.$is(this);
577
+ // While loading, accessing abilities on the current user will infinitely
578
+ // loop, but if this is the current user, we can use its abilities.
579
+ const abilities = (isCurrentUser || user == null
580
+ ? this.$data.abilities
581
+ : user?.abilities) ?? [];
582
+ const isNewUser = input.guid == null;
583
+ const isDomainUser = !isCurrentUser &&
584
+ user != null &&
585
+ domain &&
586
+ abilities.includes(`tilmeld/domain/${domain}/admin`);
587
+ reloadDataProtection = !!(isNewUser && isDomainUser);
523
588
  }
524
- if ('abilities' in input.data &&
525
- input.data.abilities?.includes('tilmeld/switch') !==
526
- this.$data.abilities?.includes('tilmeld/switch') &&
527
- (!tilmeld.currentUser ||
528
- !tilmeld.currentUser.abilities?.includes('system/admin'))) {
529
- throw new BadDataError("You don't have the authority to grant or revoke tilmeld/switch.");
530
- }
531
- this.$originalEmail = this.$data.email;
532
589
  super.$jsonAcceptData(input, allowConflict);
590
+ if (reloadDataProtection) {
591
+ // Load it again after data protection is run.
592
+ this.$updateDataProtection();
593
+ super.$jsonAcceptData(input, allowConflict);
594
+ }
533
595
  }
534
596
  $jsonAcceptPatch(patch, allowConflict = false) {
535
- const tilmeld = enforceTilmeld(this);
536
597
  this.$check();
537
- if ('abilities' in patch.set &&
538
- patch.set.abilities?.includes('system/admin') !==
539
- this.$data.abilities?.includes('system/admin') &&
598
+ if ('abilities' in patch.set) {
599
+ this.$checkIncomingAbilities(patch.set.abilities);
600
+ }
601
+ super.$jsonAcceptPatch(patch, allowConflict);
602
+ }
603
+ $checkIncomingAbilities(incoming) {
604
+ const tilmeld = enforceTilmeld(this);
605
+ if (!Array.isArray(incoming) ||
606
+ incoming.find((ability) => typeof ability !== 'string')) {
607
+ throw new BadDataError('Abilities must be an array of strings.');
608
+ }
609
+ if (incoming.includes('system/admin') !==
610
+ this.$data.abilities?.includes('system/admin') &&
540
611
  (!tilmeld.currentUser ||
541
612
  !tilmeld.currentUser.abilities?.includes('system/admin'))) {
542
613
  throw new BadDataError("You don't have the authority to grant or revoke system/admin.");
543
614
  }
544
- if ('abilities' in patch.set &&
545
- patch.set.abilities?.includes('tilmeld/admin') !==
546
- this.$data.abilities?.includes('tilmeld/admin') &&
615
+ if (incoming.includes('tilmeld/admin') !==
616
+ this.$data.abilities?.includes('tilmeld/admin') &&
547
617
  (!tilmeld.currentUser ||
548
618
  !tilmeld.currentUser.abilities?.includes('system/admin'))) {
549
619
  throw new BadDataError("You don't have the authority to grant or revoke tilmeld/admin.");
550
620
  }
551
- if ('abilities' in patch.set &&
552
- patch.set.abilities?.includes('tilmeld/switch') !==
553
- this.$data.abilities?.includes('tilmeld/switch') &&
621
+ if (incoming.includes('tilmeld/switch') !==
622
+ this.$data.abilities?.includes('tilmeld/switch') &&
554
623
  (!tilmeld.currentUser ||
555
624
  !tilmeld.currentUser.abilities?.includes('system/admin'))) {
556
625
  throw new BadDataError("You don't have the authority to grant or revoke tilmeld/switch.");
557
626
  }
558
- this.$originalEmail = this.$data.email;
559
- super.$jsonAcceptPatch(patch, allowConflict);
627
+ if (xor(incoming.filter((ability) => ability.startsWith('tilmeld/')), this.$data.abilities?.filter((ability) => ability.startsWith('tilmeld/')) ?? []).length &&
628
+ (!tilmeld.currentUser ||
629
+ (!tilmeld.currentUser.abilities?.includes('system/admin') &&
630
+ !tilmeld.currentUser.abilities?.includes('tilmeld/admin')))) {
631
+ throw new BadDataError("You don't have the authority to grant or revoke Tilmeld abilities.");
632
+ }
560
633
  }
561
- $putData(data, sdata) {
562
- super.$putData(data, sdata);
634
+ $putData(data, sdata, source) {
635
+ super.$putData(data, sdata, source);
636
+ if (source === 'server') {
637
+ this.$originalEmail = this.$data.email;
638
+ this.$originalUsername = this.$data.username;
639
+ }
563
640
  this.$updateDataProtection();
564
641
  }
565
642
  /**
@@ -570,6 +647,13 @@ export default class User extends AbleObject {
570
647
  $updateDataProtection(givenUser) {
571
648
  const tilmeld = enforceTilmeld(this);
572
649
  let user = givenUser ?? tilmeld.User.current();
650
+ let [_username, domain] = tilmeld.config.domainSupport
651
+ ? splitn(this.$data.username ?? '', '@', 2)
652
+ : [this.$data.username, null];
653
+ // Lowercase the domain part.
654
+ if (domain) {
655
+ domain = domain.toLowerCase();
656
+ }
573
657
  this.$clientEnabledMethods = [...User.DEFAULT_CLIENT_ENABLED_METHODS];
574
658
  this.$privateData = [...User.DEFAULT_PRIVATE_DATA];
575
659
  this.$allowlistData = [...User.DEFAULT_ALLOWLIST_DATA];
@@ -583,6 +667,11 @@ export default class User extends AbleObject {
583
667
  ? this.$data.abilities
584
668
  : user?.abilities) ?? [];
585
669
  const isNewUser = this.guid == null;
670
+ const isDomainUser = tilmeld.config.domainSupport &&
671
+ !isCurrentUser &&
672
+ user != null &&
673
+ domain &&
674
+ abilities.includes(`tilmeld/domain/${domain}/admin`);
586
675
  if (isCurrentUser) {
587
676
  // Users can check to see what abilities they have.
588
677
  this.$clientEnabledMethods.push('$gatekeeper');
@@ -595,6 +684,16 @@ export default class User extends AbleObject {
595
684
  this.$clientEnabledMethods.push('$saveTOTPSecret');
596
685
  this.$clientEnabledMethods.push('$removeTOTPSecret');
597
686
  }
687
+ if (isDomainUser) {
688
+ // Domain admins can check to see what abilities their users have.
689
+ this.$clientEnabledMethods.push('$gatekeeper');
690
+ this.$clientEnabledMethods.push('$changePassword');
691
+ this.$clientEnabledMethods.push('$revokeCurrentTokens');
692
+ this.$clientEnabledMethods.push('$hasTOTPSecret');
693
+ this.$clientEnabledMethods.push('$getNewTOTPSecret');
694
+ this.$clientEnabledMethods.push('$saveTOTPSecret');
695
+ this.$clientEnabledMethods.push('$removeTOTPSecret');
696
+ }
598
697
  if (user != null &&
599
698
  (abilities.includes('tilmeld/admin') ||
600
699
  abilities.includes('system/admin'))) {
@@ -604,6 +703,56 @@ export default class User extends AbleObject {
604
703
  this.$clientEnabledMethods.push('$removeTOTPSecret');
605
704
  this.$allowlistData = undefined;
606
705
  }
706
+ else if (isDomainUser) {
707
+ // Domain admins can see their users' data, and edit some of it.
708
+ if (tilmeld.config.allowUsernameChange || isNewUser) {
709
+ this.$allowlistData.push('username');
710
+ }
711
+ this.$allowlistData.push('avatar');
712
+ if (tilmeld.config.userFields.includes('name')) {
713
+ this.$allowlistData.push('nameFirst');
714
+ this.$allowlistData.push('nameMiddle');
715
+ this.$allowlistData.push('nameLast');
716
+ this.$allowlistData.push('name');
717
+ }
718
+ if (tilmeld.config.userFields.includes('email')) {
719
+ this.$allowlistData.push('email');
720
+ }
721
+ if (tilmeld.config.userFields.includes('phone')) {
722
+ this.$allowlistData.push('phone');
723
+ }
724
+ this.$allowlistData.push('enabled');
725
+ this.$allowlistData.push('passwordTemp');
726
+ this.$privateData = [
727
+ // The things commented are there to show what is _not_ private.
728
+ // 'abilities',
729
+ // 'username',
730
+ // 'nameFirst',
731
+ // 'nameMiddle',
732
+ // 'nameLast',
733
+ // 'name',
734
+ // 'email',
735
+ // 'avatar',
736
+ // 'phone',
737
+ // 'group',
738
+ // 'groups',
739
+ // 'inheritAbilities',
740
+ // 'enabled',
741
+ 'secret',
742
+ 'emailChangeDate',
743
+ 'newEmailSecret',
744
+ // 'newEmailAddress',
745
+ 'cancelEmailSecret',
746
+ 'cancelEmailAddress',
747
+ 'recoverSecret',
748
+ 'recoverSecretDate',
749
+ 'salt',
750
+ 'password',
751
+ // 'passwordTemp',
752
+ // 'revokeTokenDate',
753
+ 'totpSecret',
754
+ ];
755
+ }
607
756
  else if (isCurrentUser || isNewUser) {
608
757
  // Users can see their own data, and edit some of it.
609
758
  if (tilmeld.config.allowUsernameChange || isNewUser) {
@@ -623,7 +772,7 @@ export default class User extends AbleObject {
623
772
  this.$allowlistData.push('phone');
624
773
  }
625
774
  this.$privateData = [
626
- // The things that are commented are there to show what is _not_ private.
775
+ // The things commented are there to show what is _not_ private.
627
776
  // 'abilities',
628
777
  // 'username',
629
778
  // 'nameFirst',
@@ -1135,10 +1284,21 @@ export default class User extends AbleObject {
1135
1284
  const tilmeld = enforceTilmeld(this);
1136
1285
  this.$nymph.config.debugInfo('tilmeld', `Username check "${this.$data.username}".`);
1137
1286
  if (!tilmeld.config.emailUsernames) {
1138
- if (this.$data.username == null || !this.$data.username.length) {
1287
+ let [username, domain] = tilmeld.config.domainSupport
1288
+ ? splitn(this.$data.username ?? '', '@', 2)
1289
+ : [this.$data.username, null];
1290
+ // Lowercase the domain part.
1291
+ if (domain) {
1292
+ domain = domain.toLowerCase();
1293
+ this.$data.username = `${username}@${domain}`;
1294
+ }
1295
+ if (this.$data.username == null ||
1296
+ !this.$data.username.length ||
1297
+ username == null ||
1298
+ !username.length) {
1139
1299
  return { result: false, message: 'Please specify a username.' };
1140
1300
  }
1141
- if (this.$data.username.length < tilmeld.config.minUsernameLength) {
1301
+ if (username.length < tilmeld.config.minUsernameLength) {
1142
1302
  return {
1143
1303
  result: false,
1144
1304
  message: 'Usernames must be at least ' +
@@ -1146,7 +1306,7 @@ export default class User extends AbleObject {
1146
1306
  ' characters.',
1147
1307
  };
1148
1308
  }
1149
- if (this.$data.username.length > tilmeld.config.maxUsernameLength) {
1309
+ if (username.length > tilmeld.config.maxUsernameLength) {
1150
1310
  return {
1151
1311
  result: false,
1152
1312
  message: 'Usernames must not exceed ' +
@@ -1154,18 +1314,34 @@ export default class User extends AbleObject {
1154
1314
  ' characters.',
1155
1315
  };
1156
1316
  }
1157
- if (difference(this.$data.username.split(''), tilmeld.config.validChars.split('')).length) {
1317
+ if (difference(username.split(''), tilmeld.config.validChars.split(''))
1318
+ .length) {
1158
1319
  return {
1159
1320
  result: false,
1160
1321
  message: tilmeld.config.validCharsNotice,
1161
1322
  };
1162
1323
  }
1163
- if (!tilmeld.config.validRegex.test(this.$data.username)) {
1324
+ if (!tilmeld.config.validRegex.test(username)) {
1164
1325
  return {
1165
1326
  result: false,
1166
1327
  message: tilmeld.config.validRegexNotice,
1167
1328
  };
1168
1329
  }
1330
+ if (domain != null) {
1331
+ if (!tilmeld.config.validDomainRegex.test(domain)) {
1332
+ return {
1333
+ result: false,
1334
+ message: tilmeld.config.validDomainRegexNotice,
1335
+ };
1336
+ }
1337
+ if (!this.$is(tilmeld.currentUser) &&
1338
+ !tilmeld.gatekeeper(`tilmeld/domain/${domain}/admin`)) {
1339
+ return {
1340
+ result: false,
1341
+ message: "You don't have the authority to manage users on this domain.",
1342
+ };
1343
+ }
1344
+ }
1169
1345
  const selector = {
1170
1346
  type: '&',
1171
1347
  ilike: [
@@ -1487,7 +1663,38 @@ export default class User extends AbleObject {
1487
1663
  tilmeld.gatekeeper('tilmeld/admin') &&
1488
1664
  !tilmeld.gatekeeper('system/admin') &&
1489
1665
  this.$data.abilities?.includes('system/admin')) {
1490
- throw new BadDataError("You don't have the authority to modify system admins.");
1666
+ throw new AccessControlError("You don't have the authority to modify system admins.");
1667
+ }
1668
+ // Lowercase the domain part.
1669
+ if (tilmeld.config.domainSupport) {
1670
+ const [username, domain] = splitn(this.$data.username ?? '', '@', 2);
1671
+ if (domain) {
1672
+ this.$data.username = `${username}@${domain.toLowerCase()}`;
1673
+ }
1674
+ }
1675
+ if (tilmeld.config.domainSupport) {
1676
+ const [_username, domain] = splitn(this.$data.username ?? '', '@', 2);
1677
+ const [_originalUsername, originalDomain] = splitn(this.$originalUsername ?? '', '@', 2);
1678
+ if (this.guid != null &&
1679
+ ((originalDomain == null && domain != null) ||
1680
+ (domain == null && originalDomain != null))) {
1681
+ throw new AccessControlError("Users can't switch to or from having a domain once created.");
1682
+ }
1683
+ if (!this.$skipAcWhenSaving &&
1684
+ !tilmeld.gatekeeper('tilmeld/admin') &&
1685
+ !tilmeld.gatekeeper('system/admin')) {
1686
+ if (domain != null &&
1687
+ !this.$is(tilmeld.currentUser) &&
1688
+ !tilmeld.gatekeeper(`tilmeld/domain/${domain}/admin`)) {
1689
+ throw new AccessControlError("You don't have the authority to modify users on this domain.");
1690
+ }
1691
+ if (originalDomain != null &&
1692
+ originalDomain !== domain &&
1693
+ (!tilmeld.gatekeeper(`tilmeld/domain/${domain}/admin`) ||
1694
+ !tilmeld.gatekeeper(`tilmeld/domain/${originalDomain}/admin`))) {
1695
+ throw new AccessControlError("You don't have the authority to modify users on this domain.");
1696
+ }
1697
+ }
1491
1698
  }
1492
1699
  let sendVerification = false;
1493
1700
  // Formatting.
@@ -1761,7 +1968,7 @@ export default class User extends AbleObject {
1761
1968
  this.$setNymph(nymph);
1762
1969
  throw e;
1763
1970
  }
1764
- let ret;
1971
+ let ret = false;
1765
1972
  let preGuid = this.guid;
1766
1973
  let preCdate = this.cdate;
1767
1974
  let preMdate = this.mdate;
@@ -1783,6 +1990,9 @@ export default class User extends AbleObject {
1783
1990
  }
1784
1991
  catch (e) {
1785
1992
  await tnymph.rollback(transaction);
1993
+ this.guid = preGuid;
1994
+ this.cdate = preCdate;
1995
+ this.mdate = preMdate;
1786
1996
  this.$setNymph(nymph);
1787
1997
  throw e;
1788
1998
  }
@@ -1800,17 +2010,21 @@ export default class User extends AbleObject {
1800
2010
  }
1801
2011
  this.$descendantGroups = undefined;
1802
2012
  this.$gatekeeperCache = undefined;
1803
- await tnymph.commit(transaction);
2013
+ ret = await tnymph.commit(transaction);
2014
+ }
2015
+ else {
2016
+ await tnymph.rollback(transaction);
2017
+ }
2018
+ this.$setNymph(nymph);
2019
+ if (ret) {
2020
+ this.$originalEmail = this.$data.email;
2021
+ this.$originalUsername = this.$data.username;
1804
2022
  const tilmeld = enforceTilmeld(nymph);
1805
2023
  if (tilmeld.User.current(true).$is(this)) {
1806
2024
  // Update the user in the session cache.
1807
2025
  await tilmeld.fillSession(this);
1808
2026
  }
1809
2027
  }
1810
- else {
1811
- await tnymph.rollback(transaction);
1812
- }
1813
- this.$setNymph(nymph);
1814
2028
  return ret;
1815
2029
  }
1816
2030
  /**
@@ -1830,17 +2044,49 @@ export default class User extends AbleObject {
1830
2044
  async $delete() {
1831
2045
  const tilmeld = enforceTilmeld(this);
1832
2046
  if (!this.$skipAcWhenDeleting && !tilmeld.gatekeeper('tilmeld/admin')) {
1833
- throw new BadDataError("You don't have the authority to delete users.");
2047
+ throw new AccessControlError("You don't have the authority to delete users.");
1834
2048
  }
1835
2049
  if (!this.$skipAcWhenDeleting &&
1836
2050
  !tilmeld.gatekeeper('system/admin') &&
1837
2051
  this.$data.abilities?.includes('system/admin')) {
1838
- throw new BadDataError("You don't have the authority to delete system admins.");
2052
+ throw new AccessControlError("You don't have the authority to delete system admins.");
2053
+ }
2054
+ if (this.$data.username !== this.$originalUsername) {
2055
+ throw new AccessControlError("You can't delete a user while switching their username.");
1839
2056
  }
1840
2057
  if (tilmeld.User.current(true).$is(this)) {
1841
2058
  await this.$logout();
1842
2059
  }
1843
- return await super.$delete();
2060
+ if (this.$data.group != null && this.$is(this.$data.group.user)) {
2061
+ const transaction = 'tilmeld-delete-' + this.guid;
2062
+ const nymph = this.$nymph;
2063
+ const tnymph = await nymph.startTransaction(transaction);
2064
+ this.$setNymph(tnymph);
2065
+ // Read the skip ac value here, because super.$delete changes it.
2066
+ const $skipAcWhenDeleting = this.$skipAcWhenDeleting;
2067
+ // Delete the user.
2068
+ let success = await super.$delete();
2069
+ if (!success) {
2070
+ await tnymph.rollback(transaction);
2071
+ this.$setNymph(nymph);
2072
+ return success;
2073
+ }
2074
+ // Delete the generated group.
2075
+ success = $skipAcWhenDeleting
2076
+ ? await this.$data.group.$deleteSkipAC()
2077
+ : await this.$data.group.$delete();
2078
+ if (success) {
2079
+ success = await tnymph.commit(transaction);
2080
+ }
2081
+ else {
2082
+ await tnymph.rollback(transaction);
2083
+ }
2084
+ this.$setNymph(nymph);
2085
+ return success;
2086
+ }
2087
+ else {
2088
+ return await super.$delete();
2089
+ }
1844
2090
  }
1845
2091
  /*
1846
2092
  * This should *never* be accessible on the client.