@nymphjs/tilmeld 1.0.0-beta.8 → 1.0.0-beta.80
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/CHANGELOG.md +364 -0
- package/README.md +14 -1
- package/dist/AbleObject.d.ts +15 -0
- package/dist/AbleObject.js +16 -1
- package/dist/AbleObject.js.map +1 -1
- package/dist/Group.d.ts +130 -10
- package/dist/Group.js +311 -129
- package/dist/Group.js.map +1 -1
- package/dist/Group.test.js +77 -2
- package/dist/Group.test.js.map +1 -1
- package/dist/Tilmeld.d.ts +237 -9
- package/dist/Tilmeld.js +569 -201
- package/dist/Tilmeld.js.map +1 -1
- package/dist/User.d.ts +344 -16
- package/dist/User.js +979 -313
- package/dist/User.js.map +1 -1
- package/dist/User.test.js +63 -2
- package/dist/User.test.js.map +1 -1
- package/dist/conf/d.d.ts +250 -3
- package/dist/conf/defaults.js +146 -98
- package/dist/conf/defaults.js.map +1 -1
- package/dist/enforceTilmeld.d.ts +3 -0
- package/dist/enforceTilmeld.js +22 -0
- package/dist/enforceTilmeld.js.map +1 -0
- package/dist/errors/AccessControlError.d.ts +2 -1
- package/dist/errors/AccessControlError.js +3 -2
- package/dist/errors/AccessControlError.js.map +1 -1
- package/dist/errors/BadDataError.d.ts +2 -1
- package/dist/errors/BadDataError.js +3 -2
- package/dist/errors/BadDataError.js.map +1 -1
- package/dist/errors/BadEmailError.d.ts +2 -1
- package/dist/errors/BadEmailError.js +3 -2
- package/dist/errors/BadEmailError.js.map +1 -1
- package/dist/errors/BadUsernameError.d.ts +2 -1
- package/dist/errors/BadUsernameError.js +3 -2
- package/dist/errors/BadUsernameError.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +30 -26
- package/src/AbleObject.ts +2 -2
- package/src/Group.test.ts +102 -4
- package/src/Group.ts +274 -158
- package/src/Tilmeld.ts +383 -249
- package/src/User.test.ts +80 -4
- package/src/User.ts +1061 -382
- package/src/conf/d.ts +26 -13
- package/src/conf/defaults.ts +151 -104
- package/src/enforceTilmeld.ts +20 -0
- package/src/errors/AccessControlError.ts +4 -2
- package/src/errors/BadDataError.ts +4 -2
- package/src/errors/BadEmailError.ts +4 -2
- package/src/errors/BadUsernameError.ts +4 -2
- package/src/index.ts +2 -0
- package/src/tilmeldJoiProps.ts +9 -9
- package/tsconfig.json +3 -3
- package/typedoc.json +4 -0
package/dist/Group.js
CHANGED
|
@@ -5,12 +5,52 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const md5_1 = __importDefault(require("crypto-js/md5"));
|
|
7
7
|
const lodash_1 = require("lodash");
|
|
8
|
+
const enforceTilmeld_1 = require("./enforceTilmeld");
|
|
8
9
|
const AbleObject_1 = __importDefault(require("./AbleObject"));
|
|
9
10
|
const errors_1 = require("./errors");
|
|
11
|
+
/**
|
|
12
|
+
* A user group data model.
|
|
13
|
+
*
|
|
14
|
+
* Written by Hunter Perrin for SciActive.
|
|
15
|
+
*
|
|
16
|
+
* @author Hunter Perrin <hperrin@gmail.com>
|
|
17
|
+
* @copyright SciActive Inc
|
|
18
|
+
* @see http://nymph.io/
|
|
19
|
+
*/
|
|
10
20
|
class Group extends AbleObject_1.default {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
/**
|
|
22
|
+
* The instance of Tilmeld to use for queries.
|
|
23
|
+
*/
|
|
24
|
+
static ETYPE = 'tilmeld_group';
|
|
25
|
+
static class = 'Group';
|
|
26
|
+
static DEFAULT_PRIVATE_DATA = ['email', 'phone', 'abilities', 'user'];
|
|
27
|
+
static DEFAULT_ALLOWLIST_DATA = [];
|
|
28
|
+
$tags = [];
|
|
29
|
+
$clientEnabledMethods = [
|
|
30
|
+
'$checkGroupname',
|
|
31
|
+
'$checkEmail',
|
|
32
|
+
'$getAvatar',
|
|
33
|
+
'$getChildren',
|
|
34
|
+
'$getDescendants',
|
|
35
|
+
'$getLevel',
|
|
36
|
+
'$isDescendant',
|
|
37
|
+
];
|
|
38
|
+
static clientEnabledStaticMethods = [
|
|
39
|
+
'getPrimaryGroups',
|
|
40
|
+
'getSecondaryGroups',
|
|
41
|
+
];
|
|
42
|
+
$privateData = [...Group.DEFAULT_PRIVATE_DATA];
|
|
43
|
+
static searchRestrictedData = [...Group.DEFAULT_PRIVATE_DATA];
|
|
44
|
+
$allowlistData = [...Group.DEFAULT_ALLOWLIST_DATA];
|
|
45
|
+
$allowlistTags = [];
|
|
46
|
+
/**
|
|
47
|
+
* This should only be used by the backend.
|
|
48
|
+
*/
|
|
49
|
+
$skipAcWhenSaving = false;
|
|
50
|
+
/**
|
|
51
|
+
* This should only be used by the backend.
|
|
52
|
+
*/
|
|
53
|
+
$skipAcWhenDeleting = false;
|
|
14
54
|
static async factoryGroupname(groupname) {
|
|
15
55
|
const entity = new this();
|
|
16
56
|
if (groupname != null) {
|
|
@@ -26,63 +66,89 @@ class Group extends AbleObject_1.default {
|
|
|
26
66
|
}
|
|
27
67
|
return entity;
|
|
28
68
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
'$checkGroupname',
|
|
37
|
-
'$checkEmail',
|
|
38
|
-
'$getAvatar',
|
|
39
|
-
'$getChildren',
|
|
40
|
-
'$getDescendants',
|
|
41
|
-
'$getLevel',
|
|
42
|
-
'$isDescendant',
|
|
43
|
-
];
|
|
44
|
-
this.$privateData = [...Group.DEFAULT_PRIVATE_DATA];
|
|
45
|
-
this.$allowlistData = [...Group.DEFAULT_ALLOWLIST_DATA];
|
|
46
|
-
this.$allowlistTags = [];
|
|
47
|
-
this.$skipAcWhenSaving = false;
|
|
48
|
-
if (this.guid == null) {
|
|
49
|
-
this.$data.enabled = true;
|
|
50
|
-
this.$data.abilities = [];
|
|
51
|
-
this.$updateDataProtection();
|
|
52
|
-
}
|
|
53
|
-
}
|
|
69
|
+
/**
|
|
70
|
+
* Get all the groups that can be assigned as primary groups.
|
|
71
|
+
*
|
|
72
|
+
* @param options The options for an optional search query.
|
|
73
|
+
* @param selectors The selectors for an optional search query.
|
|
74
|
+
* @returns An array of the assignable primary groups.
|
|
75
|
+
*/
|
|
54
76
|
static async getPrimaryGroups(options, selectors) {
|
|
55
|
-
|
|
77
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
78
|
+
if (!tilmeld.gatekeeper('tilmeld/admin')) {
|
|
56
79
|
throw new Error("You don't have permission to do that.");
|
|
57
80
|
}
|
|
58
|
-
return await this.getAssignableGroups(
|
|
81
|
+
return await this.getAssignableGroups(tilmeld.config.highestPrimary, { ...options, class: tilmeld.Group, return: 'entity' }, [...(selectors ?? [])]);
|
|
59
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Get all the groups that can be assigned as secondary groups.
|
|
85
|
+
*
|
|
86
|
+
* @param options The options for an optional search query.
|
|
87
|
+
* @param selectors The selectors for an optional search query.
|
|
88
|
+
* @returns An array of the assignable secondary groups.
|
|
89
|
+
*/
|
|
60
90
|
static async getSecondaryGroups(options, selectors) {
|
|
61
|
-
|
|
91
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
92
|
+
if (!tilmeld.gatekeeper('tilmeld/admin')) {
|
|
62
93
|
throw new Error("You don't have permission to do that.");
|
|
63
94
|
}
|
|
64
|
-
return await this.getAssignableGroups(
|
|
95
|
+
return await this.getAssignableGroups(tilmeld.config.highestSecondary, { ...options, class: tilmeld.Group, return: 'entity' }, [...(selectors ?? [])]);
|
|
65
96
|
}
|
|
66
97
|
static async getAssignableGroups(highestParent, options, selectors) {
|
|
98
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
67
99
|
let assignableGroups = [];
|
|
68
100
|
if (highestParent === false) {
|
|
69
101
|
return assignableGroups;
|
|
70
102
|
}
|
|
71
|
-
assignableGroups = await this.nymph.getEntities({ ...options, class:
|
|
103
|
+
assignableGroups = await this.nymph.getEntities({ ...options, class: tilmeld.Group }, ...selectors);
|
|
72
104
|
if (highestParent !== true) {
|
|
73
|
-
assignableGroups = assignableGroups.
|
|
105
|
+
assignableGroups = (await Promise.all(assignableGroups.map(async (group) => {
|
|
74
106
|
let curGroup = group;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
107
|
+
let parent = curGroup.parent;
|
|
108
|
+
await parent?.$wake();
|
|
109
|
+
while (parent != null && parent?.cdate != null) {
|
|
110
|
+
if (parent?.guid === highestParent) {
|
|
111
|
+
return group;
|
|
78
112
|
}
|
|
79
|
-
curGroup =
|
|
113
|
+
curGroup = parent;
|
|
114
|
+
parent = curGroup.parent;
|
|
115
|
+
await parent?.$wake();
|
|
80
116
|
}
|
|
81
|
-
return
|
|
82
|
-
});
|
|
117
|
+
return null;
|
|
118
|
+
}))).filter((group) => group != null);
|
|
83
119
|
}
|
|
84
120
|
return assignableGroups;
|
|
85
121
|
}
|
|
122
|
+
constructor() {
|
|
123
|
+
super();
|
|
124
|
+
// Defaults.
|
|
125
|
+
this.$data.enabled = true;
|
|
126
|
+
this.$data.abilities = [];
|
|
127
|
+
this.$updateDataProtection();
|
|
128
|
+
}
|
|
129
|
+
$setNymph(nymph) {
|
|
130
|
+
this.$nymph = nymph;
|
|
131
|
+
if (!this.$asleep()) {
|
|
132
|
+
if (this.$data.user && this.$data.user.$nymph !== nymph) {
|
|
133
|
+
this.$data.user.$setNymph(nymph);
|
|
134
|
+
}
|
|
135
|
+
if (this.$data.parent && this.$data.parent.$nymph !== nymph) {
|
|
136
|
+
this.$data.parent.$setNymph(nymph);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async $getUniques() {
|
|
141
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
142
|
+
const uniques = [`u:${this.$data.groupname}`];
|
|
143
|
+
if (!tilmeld.config.emailUsernames &&
|
|
144
|
+
tilmeld.config.userFields.includes('email')) {
|
|
145
|
+
uniques.push(`e:${this.$data.email}`);
|
|
146
|
+
}
|
|
147
|
+
if (this.$data.defaultPrimary) {
|
|
148
|
+
uniques.push(`o:default-primary`);
|
|
149
|
+
}
|
|
150
|
+
return uniques;
|
|
151
|
+
}
|
|
86
152
|
$getAvatar() {
|
|
87
153
|
if (this.$data.avatar != null && this.$data.avatar !== '') {
|
|
88
154
|
return this.$data.avatar;
|
|
@@ -95,24 +161,54 @@ class Group extends AbleObject_1.default {
|
|
|
95
161
|
'?d=identicon&s=40');
|
|
96
162
|
}
|
|
97
163
|
$jsonAcceptData(input, allowConflict = false) {
|
|
98
|
-
const tilmeld = this
|
|
99
|
-
this.$
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
!tilmeld.
|
|
104
|
-
|
|
164
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
165
|
+
this.$check();
|
|
166
|
+
if ('abilities' in input.data &&
|
|
167
|
+
input.data.abilities?.includes('system/admin') !==
|
|
168
|
+
this.$data.abilities?.includes('system/admin') &&
|
|
169
|
+
(!tilmeld.currentUser ||
|
|
170
|
+
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
171
|
+
throw new errors_1.BadDataError("You don't have the authority to grant or revoke system/admin.");
|
|
172
|
+
}
|
|
173
|
+
if ('abilities' in input.data &&
|
|
174
|
+
input.data.abilities?.includes('tilmeld/admin') !==
|
|
175
|
+
this.$data.abilities?.includes('tilmeld/admin') &&
|
|
176
|
+
(!tilmeld.currentUser ||
|
|
177
|
+
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
178
|
+
throw new errors_1.BadDataError("You don't have the authority to grant or revoke tilmeld/admin.");
|
|
179
|
+
}
|
|
180
|
+
if ('abilities' in input.data &&
|
|
181
|
+
input.data.abilities?.includes('tilmeld/switch') !==
|
|
182
|
+
this.$data.abilities?.includes('tilmeld/switch') &&
|
|
183
|
+
(!tilmeld.currentUser ||
|
|
184
|
+
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
185
|
+
throw new errors_1.BadDataError("You don't have the authority to grant or revoke tilmeld/switch.");
|
|
105
186
|
}
|
|
106
187
|
super.$jsonAcceptData(input, allowConflict);
|
|
107
188
|
}
|
|
108
189
|
$jsonAcceptPatch(patch, allowConflict = false) {
|
|
109
|
-
const tilmeld = this
|
|
110
|
-
this.$
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
!tilmeld.
|
|
115
|
-
|
|
190
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
191
|
+
this.$check();
|
|
192
|
+
if ('abilities' in patch.set &&
|
|
193
|
+
patch.set.abilities?.includes('system/admin') !==
|
|
194
|
+
this.$data.abilities?.includes('system/admin') &&
|
|
195
|
+
(!tilmeld.currentUser ||
|
|
196
|
+
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
197
|
+
throw new errors_1.BadDataError("You don't have the authority to grant or revoke system/admin.");
|
|
198
|
+
}
|
|
199
|
+
if ('abilities' in patch.set &&
|
|
200
|
+
patch.set.abilities?.includes('tilmeld/admin') !==
|
|
201
|
+
this.$data.abilities?.includes('tilmeld/admin') &&
|
|
202
|
+
(!tilmeld.currentUser ||
|
|
203
|
+
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
204
|
+
throw new errors_1.BadDataError("You don't have the authority to grant or revoke tilmeld/admin.");
|
|
205
|
+
}
|
|
206
|
+
if ('abilities' in patch.set &&
|
|
207
|
+
patch.set.abilities?.includes('tilmeld/switch') !==
|
|
208
|
+
this.$data.abilities?.includes('tilmeld/switch') &&
|
|
209
|
+
(!tilmeld.currentUser ||
|
|
210
|
+
!tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
211
|
+
throw new errors_1.BadDataError("You don't have the authority to grant or revoke tilmeld/switch.");
|
|
116
212
|
}
|
|
117
213
|
super.$jsonAcceptPatch(patch, allowConflict);
|
|
118
214
|
}
|
|
@@ -120,8 +216,13 @@ class Group extends AbleObject_1.default {
|
|
|
120
216
|
super.$putData(data, sdata);
|
|
121
217
|
this.$updateDataProtection();
|
|
122
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Update the data protection arrays for a user.
|
|
221
|
+
*
|
|
222
|
+
* @param givenUser User to update protection for. If undefined, will use the currently logged in user.
|
|
223
|
+
*/
|
|
123
224
|
$updateDataProtection(givenUser) {
|
|
124
|
-
const tilmeld = this
|
|
225
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
125
226
|
let user = givenUser ?? tilmeld.User.current();
|
|
126
227
|
this.$privateData = [...Group.DEFAULT_PRIVATE_DATA];
|
|
127
228
|
this.$allowlistData = [...Group.DEFAULT_ALLOWLIST_DATA];
|
|
@@ -129,22 +230,30 @@ class Group extends AbleObject_1.default {
|
|
|
129
230
|
this.$privateData.push('groupname');
|
|
130
231
|
}
|
|
131
232
|
if (user != null &&
|
|
132
|
-
(
|
|
133
|
-
|
|
233
|
+
(user.abilities?.includes('tilmeld/admin') ||
|
|
234
|
+
user.abilities?.includes('system/admin'))) {
|
|
235
|
+
// Users who can edit groups can see their data.
|
|
134
236
|
this.$privateData = [];
|
|
135
237
|
this.$allowlistData = undefined;
|
|
136
238
|
}
|
|
137
239
|
else if (this.$data.user != null &&
|
|
138
240
|
user != null &&
|
|
139
|
-
this.$data.user
|
|
241
|
+
this.$data.user.guid === user.guid) {
|
|
242
|
+
// Users can see their group's data.
|
|
140
243
|
this.$privateData = [];
|
|
141
244
|
}
|
|
142
245
|
}
|
|
143
|
-
|
|
144
|
-
|
|
246
|
+
/**
|
|
247
|
+
* Check whether the group is a descendant of a group.
|
|
248
|
+
*
|
|
249
|
+
* @param group The group, or the group's GUID.
|
|
250
|
+
* @returns True or false.
|
|
251
|
+
*/
|
|
252
|
+
async $isDescendant(givenGroup) {
|
|
253
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
145
254
|
let group;
|
|
146
255
|
if (typeof givenGroup === 'string') {
|
|
147
|
-
group = tilmeld.Group.
|
|
256
|
+
group = await tilmeld.Group.factory(givenGroup);
|
|
148
257
|
}
|
|
149
258
|
else {
|
|
150
259
|
group = givenGroup;
|
|
@@ -152,27 +261,41 @@ class Group extends AbleObject_1.default {
|
|
|
152
261
|
if (group.guid == null) {
|
|
153
262
|
return false;
|
|
154
263
|
}
|
|
155
|
-
if
|
|
264
|
+
// Check to see if the group is a descendant of the given group.
|
|
265
|
+
const parent = this.$data.parent;
|
|
266
|
+
await parent?.$wake();
|
|
267
|
+
if (parent == null) {
|
|
156
268
|
return false;
|
|
157
269
|
}
|
|
158
|
-
if (
|
|
270
|
+
if (parent.$is(group)) {
|
|
159
271
|
return true;
|
|
160
272
|
}
|
|
161
|
-
if (
|
|
273
|
+
if (await parent.$isDescendant(group)) {
|
|
162
274
|
return true;
|
|
163
275
|
}
|
|
164
276
|
return false;
|
|
165
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* Gets an array of the group's child groups.
|
|
280
|
+
*
|
|
281
|
+
* @returns An array of groups.
|
|
282
|
+
*/
|
|
166
283
|
async $getChildren() {
|
|
167
|
-
const tilmeld = this
|
|
284
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
168
285
|
return await this.$nymph.getEntities({ class: tilmeld.Group }, {
|
|
169
286
|
type: '&',
|
|
170
287
|
equal: ['enabled', true],
|
|
171
288
|
ref: ['parent', this],
|
|
172
289
|
});
|
|
173
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Gets an array of the group's descendant groups.
|
|
293
|
+
*
|
|
294
|
+
* @param andSelf Include this group in the returned array.
|
|
295
|
+
* @returns An array of groups.
|
|
296
|
+
*/
|
|
174
297
|
async $getDescendants(andSelf = false) {
|
|
175
|
-
const tilmeld = this
|
|
298
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
176
299
|
let groups = [];
|
|
177
300
|
const entities = await this.$nymph.getEntities({ class: tilmeld.Group }, {
|
|
178
301
|
type: '&',
|
|
@@ -187,39 +310,41 @@ class Group extends AbleObject_1.default {
|
|
|
187
310
|
}
|
|
188
311
|
return groups;
|
|
189
312
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if (andSelf) {
|
|
207
|
-
groups.push(this);
|
|
208
|
-
}
|
|
209
|
-
return groups;
|
|
210
|
-
}
|
|
211
|
-
$getLevel() {
|
|
212
|
-
const tilmeld = this.$nymph.tilmeld;
|
|
213
|
-
let group = tilmeld.Group.factorySync(this.guid ?? undefined);
|
|
313
|
+
/**
|
|
314
|
+
* Get the number of parents the group has.
|
|
315
|
+
*
|
|
316
|
+
* If the group is a top level group, this will return 0. If it is a child of
|
|
317
|
+
* a top level group, this will return 1. If it is a grandchild of a top level
|
|
318
|
+
* group, this will return 2, and so on.
|
|
319
|
+
*
|
|
320
|
+
* Levels will max out at 1024 to avoid recursive loops.
|
|
321
|
+
*
|
|
322
|
+
* @returns The level of the group.
|
|
323
|
+
*/
|
|
324
|
+
async $getLevel() {
|
|
325
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
326
|
+
let group = await tilmeld.Group.factory(this.guid ?? undefined);
|
|
327
|
+
let parent = group.parent;
|
|
328
|
+
await parent?.$wake();
|
|
214
329
|
let level = 0;
|
|
215
|
-
while (
|
|
330
|
+
while (parent != null && parent.cdate != null && level < 1024) {
|
|
216
331
|
level++;
|
|
217
|
-
group =
|
|
332
|
+
group = parent;
|
|
333
|
+
parent = group.parent;
|
|
334
|
+
await parent?.$wake();
|
|
218
335
|
}
|
|
219
336
|
return level;
|
|
220
337
|
}
|
|
338
|
+
/**
|
|
339
|
+
* Gets an array of users in the group.
|
|
340
|
+
*
|
|
341
|
+
* @param descendants Include users in all descendant groups too.
|
|
342
|
+
* @param limit The limit for the query.
|
|
343
|
+
* @param offset The offset for the query.
|
|
344
|
+
* @returns An array of users.
|
|
345
|
+
*/
|
|
221
346
|
async $getUsers(descendants = false, limit, offset) {
|
|
222
|
-
const tilmeld = this
|
|
347
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
223
348
|
let groups = [];
|
|
224
349
|
if (descendants) {
|
|
225
350
|
groups = await this.$getDescendants();
|
|
@@ -247,14 +372,26 @@ class Group extends AbleObject_1.default {
|
|
|
247
372
|
],
|
|
248
373
|
});
|
|
249
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* Check that a groupname is valid.
|
|
377
|
+
*
|
|
378
|
+
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
379
|
+
*/
|
|
250
380
|
async $checkGroupname() {
|
|
251
|
-
const tilmeld = this
|
|
381
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
252
382
|
if (!tilmeld.config.emailUsernames) {
|
|
253
383
|
if (this.$data.groupname == null || !this.$data.groupname.length) {
|
|
254
384
|
return { result: false, message: 'Please specify a groupname.' };
|
|
255
385
|
}
|
|
256
|
-
if (tilmeld.config.
|
|
257
|
-
|
|
386
|
+
if (this.$data.groupname.length < tilmeld.config.minUsernameLength) {
|
|
387
|
+
return {
|
|
388
|
+
result: false,
|
|
389
|
+
message: 'Groupnames must be at least ' +
|
|
390
|
+
tilmeld.config.minUsernameLength +
|
|
391
|
+
' characters.',
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
if (this.$data.groupname.length > tilmeld.config.maxUsernameLength) {
|
|
258
395
|
return {
|
|
259
396
|
result: false,
|
|
260
397
|
message: 'Groupnames must not exceed ' +
|
|
@@ -298,8 +435,15 @@ class Group extends AbleObject_1.default {
|
|
|
298
435
|
if (this.$data.groupname == null || !this.$data.groupname.length) {
|
|
299
436
|
return { result: false, message: 'Please specify an email.' };
|
|
300
437
|
}
|
|
301
|
-
if (tilmeld.config.
|
|
302
|
-
|
|
438
|
+
if (this.$data.groupname.length < tilmeld.config.minUsernameLength) {
|
|
439
|
+
return {
|
|
440
|
+
result: false,
|
|
441
|
+
message: 'Emails must be at least ' +
|
|
442
|
+
tilmeld.config.minUsernameLength +
|
|
443
|
+
' characters.',
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
if (this.$data.groupname.length > tilmeld.config.maxUsernameLength) {
|
|
303
447
|
return {
|
|
304
448
|
result: false,
|
|
305
449
|
message: 'Emails must not exceed ' +
|
|
@@ -310,8 +454,13 @@ class Group extends AbleObject_1.default {
|
|
|
310
454
|
return await this.$checkEmail();
|
|
311
455
|
}
|
|
312
456
|
}
|
|
457
|
+
/**
|
|
458
|
+
* Check that an email is unique.
|
|
459
|
+
*
|
|
460
|
+
* @returns An object with a boolean 'result' entry and a 'message' entry.
|
|
461
|
+
*/
|
|
313
462
|
async $checkEmail() {
|
|
314
|
-
const tilmeld = this
|
|
463
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
315
464
|
if (this.$data.email === '') {
|
|
316
465
|
return { result: true, message: '' };
|
|
317
466
|
}
|
|
@@ -344,10 +493,11 @@ class Group extends AbleObject_1.default {
|
|
|
344
493
|
};
|
|
345
494
|
}
|
|
346
495
|
async $save() {
|
|
347
|
-
const tilmeld = this
|
|
496
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
348
497
|
if (this.$data.groupname == null || !this.$data.groupname.trim().length) {
|
|
349
498
|
return false;
|
|
350
499
|
}
|
|
500
|
+
// Formatting.
|
|
351
501
|
this.$data.groupname = this.$data.groupname.trim();
|
|
352
502
|
if (tilmeld.config.emailUsernames) {
|
|
353
503
|
this.$data.email = this.$data.groupname;
|
|
@@ -356,16 +506,19 @@ class Group extends AbleObject_1.default {
|
|
|
356
506
|
this.$data.name = (this.$data.name ?? '').trim();
|
|
357
507
|
this.$data.phone = (this.$data.phone ?? '').trim();
|
|
358
508
|
this.$data.enabled = !!this.$data.enabled;
|
|
359
|
-
|
|
509
|
+
// Clear empty values.
|
|
510
|
+
if (this.$data.name === '' || !tilmeld.config.userFields.includes('name')) {
|
|
360
511
|
delete this.$data.name;
|
|
361
512
|
}
|
|
362
|
-
if (this.$data.email === ''
|
|
513
|
+
if (this.$data.email === '' ||
|
|
514
|
+
!tilmeld.config.userFields.includes('email')) {
|
|
363
515
|
delete this.$data.email;
|
|
364
516
|
}
|
|
365
517
|
if (this.$data.avatar === '') {
|
|
366
518
|
delete this.$data.avatar;
|
|
367
519
|
}
|
|
368
|
-
if (this.$data.phone === ''
|
|
520
|
+
if (this.$data.phone === '' ||
|
|
521
|
+
!tilmeld.config.userFields.includes('phone')) {
|
|
369
522
|
delete this.$data.phone;
|
|
370
523
|
}
|
|
371
524
|
if (this.$data.parent == null) {
|
|
@@ -383,39 +536,50 @@ class Group extends AbleObject_1.default {
|
|
|
383
536
|
if (!this.$data.unverifiedSecondary) {
|
|
384
537
|
delete this.$data.unverifiedSecondary;
|
|
385
538
|
}
|
|
539
|
+
// Groups should never have 'system/admin', 'tilmeld/admin', or
|
|
540
|
+
// 'tilmeld/switch' abilities.
|
|
386
541
|
this.$data.abilities = (0, lodash_1.difference)(this.$data.abilities ?? [], [
|
|
387
542
|
'system/admin',
|
|
388
543
|
'tilmeld/admin',
|
|
544
|
+
'tilmeld/switch',
|
|
389
545
|
null,
|
|
390
546
|
undefined,
|
|
391
547
|
]);
|
|
548
|
+
// Verification.
|
|
392
549
|
const unCheck = await this.$checkGroupname();
|
|
393
550
|
if (!unCheck.result) {
|
|
394
551
|
throw new errors_1.BadUsernameError(unCheck.message);
|
|
395
552
|
}
|
|
396
|
-
if (!tilmeld.config.emailUsernames
|
|
553
|
+
if (!tilmeld.config.emailUsernames &&
|
|
554
|
+
tilmeld.config.userFields.includes('email')) {
|
|
397
555
|
const emCheck = await this.$checkEmail();
|
|
398
556
|
if (!emCheck.result) {
|
|
399
557
|
throw new errors_1.BadEmailError(emCheck.message);
|
|
400
558
|
}
|
|
401
559
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
560
|
+
// Validate group parent. Make sure it's not a descendant of this group.
|
|
561
|
+
const parent = this.$data.parent;
|
|
562
|
+
await parent?.$wake();
|
|
563
|
+
if (parent &&
|
|
564
|
+
(parent.cdate == null ||
|
|
565
|
+
this.$is(parent) ||
|
|
566
|
+
(await parent.$isDescendant(this)))) {
|
|
406
567
|
throw new errors_1.BadDataError("Group parent can't be itself or descendant of itself.");
|
|
407
568
|
}
|
|
408
569
|
try {
|
|
409
|
-
tilmeld.config.validatorGroup(this);
|
|
570
|
+
tilmeld.config.validatorGroup(tilmeld, this);
|
|
410
571
|
}
|
|
411
572
|
catch (e) {
|
|
412
573
|
throw new errors_1.BadDataError(e?.message);
|
|
413
574
|
}
|
|
575
|
+
// Only one default primary group is allowed.
|
|
414
576
|
if (this.$data.defaultPrimary) {
|
|
415
577
|
const currentPrimary = await this.$nymph.getEntity({ class: tilmeld.Group }, { type: '&', truthy: 'defaultPrimary' });
|
|
416
578
|
if (currentPrimary != null && !this.$is(currentPrimary)) {
|
|
417
579
|
currentPrimary.defaultPrimary = false;
|
|
418
|
-
if (
|
|
580
|
+
if (this.$skipAcWhenSaving
|
|
581
|
+
? !(await currentPrimary.$saveSkipAC())
|
|
582
|
+
: !(await currentPrimary.$save())) {
|
|
419
583
|
throw new errors_1.CouldNotChangeDefaultPrimaryGroupError('Could not change new user primary group from ' +
|
|
420
584
|
`${currentPrimary.groupname}.`);
|
|
421
585
|
}
|
|
@@ -423,6 +587,9 @@ class Group extends AbleObject_1.default {
|
|
|
423
587
|
}
|
|
424
588
|
return await super.$save();
|
|
425
589
|
}
|
|
590
|
+
/*
|
|
591
|
+
* This should *never* be accessible on the client.
|
|
592
|
+
*/
|
|
426
593
|
async $saveSkipAC() {
|
|
427
594
|
this.$skipAcWhenSaving = true;
|
|
428
595
|
return await this.$save();
|
|
@@ -435,25 +602,29 @@ class Group extends AbleObject_1.default {
|
|
|
435
602
|
return false;
|
|
436
603
|
}
|
|
437
604
|
async $delete() {
|
|
438
|
-
let tilmeld = this
|
|
439
|
-
if (!tilmeld.gatekeeper('tilmeld/admin')) {
|
|
605
|
+
let tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
606
|
+
if (!this.$skipAcWhenDeleting && !tilmeld.gatekeeper('tilmeld/admin')) {
|
|
440
607
|
throw new errors_1.BadDataError("You don't have the authority to delete groups.");
|
|
441
608
|
}
|
|
442
609
|
const transaction = 'tilmeld-delete-' + this.guid;
|
|
443
610
|
const nymph = this.$nymph;
|
|
444
611
|
const tnymph = await nymph.startTransaction(transaction);
|
|
445
|
-
this.$
|
|
446
|
-
tilmeld = this
|
|
612
|
+
this.$setNymph(tnymph);
|
|
613
|
+
tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
614
|
+
// Delete descendants.
|
|
447
615
|
const descendants = await this.$getDescendants();
|
|
448
616
|
if (descendants.length) {
|
|
449
617
|
for (let curGroup of descendants) {
|
|
450
|
-
if (
|
|
618
|
+
if (this.$skipAcWhenDeleting
|
|
619
|
+
? !(await curGroup.$deleteSkipAC())
|
|
620
|
+
: !(await curGroup.$delete())) {
|
|
451
621
|
await tnymph.rollback(transaction);
|
|
452
|
-
this.$nymph
|
|
622
|
+
this.$setNymph(nymph);
|
|
453
623
|
return false;
|
|
454
624
|
}
|
|
455
625
|
}
|
|
456
626
|
}
|
|
627
|
+
// Remove users from this primary group.
|
|
457
628
|
const primaryUsers = await tnymph.getEntities({
|
|
458
629
|
class: tilmeld.User,
|
|
459
630
|
skipAc: true,
|
|
@@ -463,12 +634,15 @@ class Group extends AbleObject_1.default {
|
|
|
463
634
|
});
|
|
464
635
|
for (let user of primaryUsers) {
|
|
465
636
|
delete user.group;
|
|
466
|
-
if (
|
|
637
|
+
if (this.$skipAcWhenDeleting
|
|
638
|
+
? !(await user.$saveSkipAC())
|
|
639
|
+
: !(await user.$save())) {
|
|
467
640
|
await tnymph.rollback(transaction);
|
|
468
|
-
this.$nymph
|
|
641
|
+
this.$setNymph(nymph);
|
|
469
642
|
return false;
|
|
470
643
|
}
|
|
471
644
|
}
|
|
645
|
+
// Remove users from this secondary group.
|
|
472
646
|
const secondaryUsers = await tnymph.getEntities({
|
|
473
647
|
class: tilmeld.User,
|
|
474
648
|
skipAc: true,
|
|
@@ -478,11 +652,14 @@ class Group extends AbleObject_1.default {
|
|
|
478
652
|
});
|
|
479
653
|
for (let user of secondaryUsers) {
|
|
480
654
|
user.$delGroup(this);
|
|
481
|
-
if (
|
|
655
|
+
if (this.$skipAcWhenDeleting
|
|
656
|
+
? !(await user.$saveSkipAC())
|
|
657
|
+
: !(await user.$save())) {
|
|
482
658
|
await tnymph.rollback(transaction);
|
|
483
659
|
return false;
|
|
484
660
|
}
|
|
485
661
|
}
|
|
662
|
+
// Delete the group.
|
|
486
663
|
const success = await super.$delete();
|
|
487
664
|
if (success) {
|
|
488
665
|
await tnymph.commit(transaction);
|
|
@@ -490,18 +667,23 @@ class Group extends AbleObject_1.default {
|
|
|
490
667
|
else {
|
|
491
668
|
await tnymph.rollback(transaction);
|
|
492
669
|
}
|
|
493
|
-
this.$nymph
|
|
670
|
+
this.$setNymph(nymph);
|
|
494
671
|
return success;
|
|
495
672
|
}
|
|
673
|
+
/*
|
|
674
|
+
* This should *never* be accessible on the client.
|
|
675
|
+
*/
|
|
676
|
+
async $deleteSkipAC() {
|
|
677
|
+
this.$skipAcWhenDeleting = true;
|
|
678
|
+
return await this.$delete();
|
|
679
|
+
}
|
|
680
|
+
$tilmeldDeleteSkipAC() {
|
|
681
|
+
if (this.$skipAcWhenDeleting) {
|
|
682
|
+
this.$skipAcWhenDeleting = false;
|
|
683
|
+
return true;
|
|
684
|
+
}
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
496
687
|
}
|
|
497
688
|
exports.default = Group;
|
|
498
|
-
Group.ETYPE = 'tilmeld_group';
|
|
499
|
-
Group.class = 'Group';
|
|
500
|
-
Group.DEFAULT_PRIVATE_DATA = ['email', 'phone', 'abilities', 'user'];
|
|
501
|
-
Group.DEFAULT_ALLOWLIST_DATA = [];
|
|
502
|
-
Group.clientEnabledStaticMethods = [
|
|
503
|
-
'getPrimaryGroups',
|
|
504
|
-
'getSecondaryGroups',
|
|
505
|
-
];
|
|
506
|
-
Group.searchRestrictedData = [...Group.DEFAULT_PRIVATE_DATA];
|
|
507
689
|
//# sourceMappingURL=Group.js.map
|