@nymphjs/tilmeld 1.0.0-beta.5 → 1.0.0-beta.50
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 +238 -0
- package/README.md +14 -1
- package/dist/AbleObject.js.map +1 -1
- package/dist/Group.d.ts +4 -7
- package/dist/Group.js +124 -86
- package/dist/Group.js.map +1 -1
- package/dist/Tilmeld.d.ts +12 -9
- package/dist/Tilmeld.js +240 -179
- package/dist/Tilmeld.js.map +1 -1
- package/dist/User.d.ts +69 -13
- package/dist/User.js +492 -169
- package/dist/User.js.map +1 -1
- package/dist/conf/d.d.ts +4 -1
- package/dist/conf/defaults.js +22 -8
- 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/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +29 -26
- package/src/AbleObject.ts +1 -1
- package/src/Group.test.ts +1 -1
- package/src/Group.ts +180 -130
- package/src/Tilmeld.ts +355 -244
- package/src/User.test.ts +1 -1
- package/src/User.ts +717 -230
- package/src/conf/d.ts +24 -11
- package/src/conf/defaults.ts +30 -14
- package/src/enforceTilmeld.ts +20 -0
- package/src/index.ts +2 -0
- package/src/tilmeldJoiProps.ts +9 -9
- package/typedoc.json +4 -0
package/dist/Tilmeld.js
CHANGED
|
@@ -10,6 +10,7 @@ const conf_1 = require("./conf");
|
|
|
10
10
|
const errors_1 = require("./errors");
|
|
11
11
|
const Group_1 = __importDefault(require("./Group"));
|
|
12
12
|
const User_1 = __importDefault(require("./User"));
|
|
13
|
+
const enforceTilmeld_1 = require("./enforceTilmeld");
|
|
13
14
|
class Tilmeld {
|
|
14
15
|
constructor(config) {
|
|
15
16
|
this.nymph = {};
|
|
@@ -18,7 +19,8 @@ class Tilmeld {
|
|
|
18
19
|
this.Group = Group_1.default;
|
|
19
20
|
this.request = null;
|
|
20
21
|
this.response = null;
|
|
21
|
-
this.
|
|
22
|
+
this.alreadyLoggedOutSwitch = false;
|
|
23
|
+
this.gatekeeperCache = null;
|
|
22
24
|
this.config = { ...conf_1.ConfigDefaults, ...config };
|
|
23
25
|
}
|
|
24
26
|
clone() {
|
|
@@ -26,7 +28,7 @@ class Tilmeld {
|
|
|
26
28
|
tilmeld.request = this.request;
|
|
27
29
|
tilmeld.response = this.response;
|
|
28
30
|
tilmeld.currentUser = this.currentUser;
|
|
29
|
-
tilmeld.
|
|
31
|
+
tilmeld.gatekeeperCache = this.gatekeeperCache;
|
|
30
32
|
return tilmeld;
|
|
31
33
|
}
|
|
32
34
|
init(nymph) {
|
|
@@ -34,41 +36,54 @@ class Tilmeld {
|
|
|
34
36
|
if (this.nymph.driver == null) {
|
|
35
37
|
throw new Error("Tilmeld can't be configured before Nymph.");
|
|
36
38
|
}
|
|
37
|
-
|
|
39
|
+
if (nymph.parent) {
|
|
40
|
+
this.User = this.nymph.getEntityClass(User_1.default);
|
|
41
|
+
this.Group = this.nymph.getEntityClass(Group_1.default);
|
|
38
42
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
this.
|
|
44
|
-
|
|
43
|
+
else {
|
|
44
|
+
this.User = this.nymph.addEntityClass(User_1.default);
|
|
45
|
+
this.Group = this.nymph.addEntityClass(Group_1.default);
|
|
46
|
+
}
|
|
47
|
+
if (this.currentUser) {
|
|
48
|
+
const currentUserGUID = this.currentUser.guid;
|
|
49
|
+
const currentUserCDate = this.currentUser.cdate;
|
|
50
|
+
const currentUserMDate = this.currentUser.mdate;
|
|
51
|
+
const currentUserTags = this.currentUser.tags;
|
|
52
|
+
const currentUserData = this.currentUser.$getData();
|
|
53
|
+
const currentUserSData = this.currentUser.$getSData();
|
|
54
|
+
const user = this.User.factorySync();
|
|
55
|
+
user.guid = currentUserGUID;
|
|
56
|
+
user.cdate = currentUserCDate;
|
|
57
|
+
user.mdate = currentUserMDate;
|
|
58
|
+
user.tags = currentUserTags;
|
|
59
|
+
user.$putData(currentUserData, currentUserSData);
|
|
60
|
+
this.currentUser = user;
|
|
61
|
+
this.currentUser.$updateDataProtection();
|
|
62
|
+
this.currentUser.$updateGroupDataProtection();
|
|
45
63
|
}
|
|
46
|
-
NymphGroup.nymph = this.nymph;
|
|
47
|
-
NymphGroup.tilmeld = this;
|
|
48
|
-
NymphUser.skipOnClone = true;
|
|
49
|
-
this.Group = NymphGroup;
|
|
50
|
-
this.nymph.addEntityClass(NymphGroup);
|
|
51
64
|
this.initAccessControl();
|
|
52
|
-
if (this.request != null && !this.skipAuthenticate) {
|
|
53
|
-
this.authenticate();
|
|
54
|
-
}
|
|
55
|
-
this.skipAuthenticate = false;
|
|
56
65
|
}
|
|
57
66
|
gatekeeper(ability) {
|
|
58
|
-
if (this.currentUser == null) {
|
|
67
|
+
if (this.currentUser == null || this.gatekeeperCache == null) {
|
|
59
68
|
return false;
|
|
60
69
|
}
|
|
61
|
-
|
|
70
|
+
if (ability == null) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
return ((ability in this.gatekeeperCache && !!this.gatekeeperCache[ability]) ||
|
|
74
|
+
!!this.currentUser.abilities?.includes('system/admin'));
|
|
62
75
|
}
|
|
63
76
|
initAccessControl() {
|
|
64
77
|
const handleQuery = function (nymph, options, selectors) {
|
|
65
|
-
const tilmeld = nymph
|
|
78
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(nymph);
|
|
66
79
|
if (!options ||
|
|
67
80
|
!('skipAc' in options) ||
|
|
68
81
|
!options.skipAc ||
|
|
69
82
|
options.source === 'client') {
|
|
70
83
|
if (options?.source === 'client') {
|
|
71
|
-
if (!tilmeld.
|
|
84
|
+
if ((!tilmeld.currentUser ||
|
|
85
|
+
(!tilmeld.currentUser.abilities?.includes('tilmeld/admin') &&
|
|
86
|
+
!tilmeld.currentUser.abilities?.includes('system/admin'))) &&
|
|
72
87
|
((!tilmeld.config.enableUserSearch &&
|
|
73
88
|
options.class?.factorySync() instanceof User_1.default) ||
|
|
74
89
|
(!tilmeld.config.enableGroupSearch &&
|
|
@@ -90,7 +105,7 @@ class Tilmeld {
|
|
|
90
105
|
};
|
|
91
106
|
handleQuery.skipOnClone = true;
|
|
92
107
|
const checkPermissionsDelete = async function (nymph, entity) {
|
|
93
|
-
const tilmeld = nymph
|
|
108
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(nymph);
|
|
94
109
|
if (typeof entity.$tilmeldDeleteSkipAC === 'function' &&
|
|
95
110
|
entity.$tilmeldDeleteSkipAC()) {
|
|
96
111
|
return;
|
|
@@ -101,14 +116,14 @@ class Tilmeld {
|
|
|
101
116
|
};
|
|
102
117
|
checkPermissionsDelete.skipOnClone = true;
|
|
103
118
|
const checkPermissionsDeleteByID = async function (nymph, guid, className) {
|
|
104
|
-
const entity = nymph.
|
|
119
|
+
const entity = await nymph.getEntity({ class: nymph.getEntityClass(className ?? 'Entity') }, { type: '&', guid: guid });
|
|
105
120
|
if (entity != null) {
|
|
106
121
|
checkPermissionsDelete(nymph, entity);
|
|
107
122
|
}
|
|
108
123
|
};
|
|
109
124
|
checkPermissionsDeleteByID.skipOnClone = true;
|
|
110
125
|
const checkPermissionsSaveAndFilterAcChanges = async function (nymph, entity) {
|
|
111
|
-
const tilmeld = nymph
|
|
126
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(nymph);
|
|
112
127
|
if (!entity) {
|
|
113
128
|
return;
|
|
114
129
|
}
|
|
@@ -118,28 +133,7 @@ class Tilmeld {
|
|
|
118
133
|
}
|
|
119
134
|
if (entity.guid != null) {
|
|
120
135
|
const originalAc = entity.$getOriginalAcValues();
|
|
121
|
-
const newAc =
|
|
122
|
-
user: entity.user ?? undefined,
|
|
123
|
-
group: entity.group ?? undefined,
|
|
124
|
-
acUser: entity.acUser ?? undefined,
|
|
125
|
-
acGroup: entity.acGroup ?? undefined,
|
|
126
|
-
acOther: entity.acOther ?? undefined,
|
|
127
|
-
acRead: entity.acRead ?? undefined,
|
|
128
|
-
acWrite: entity.acWrite ?? undefined,
|
|
129
|
-
acFull: entity.acFull ?? undefined,
|
|
130
|
-
};
|
|
131
|
-
const setAcProperties = (acValues) => {
|
|
132
|
-
for (let name in acValues) {
|
|
133
|
-
const value = acValues[name];
|
|
134
|
-
if (value == null) {
|
|
135
|
-
delete entity[name];
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
entity[name] = value;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
setAcProperties(originalAc);
|
|
136
|
+
const newAc = entity.$getCurrentAcValues();
|
|
143
137
|
function areAcPropertiesChanged(a, b) {
|
|
144
138
|
for (const name of ['user', 'group']) {
|
|
145
139
|
const aVal = a[name];
|
|
@@ -150,7 +144,7 @@ class Tilmeld {
|
|
|
150
144
|
else if (aVal == null || bVal == null) {
|
|
151
145
|
return true;
|
|
152
146
|
}
|
|
153
|
-
if (
|
|
147
|
+
if (aVal !== bVal) {
|
|
154
148
|
return true;
|
|
155
149
|
}
|
|
156
150
|
}
|
|
@@ -179,31 +173,29 @@ class Tilmeld {
|
|
|
179
173
|
if (aVal.length !== bVal.length) {
|
|
180
174
|
return true;
|
|
181
175
|
}
|
|
182
|
-
if ((0, lodash_1.xor)(aVal
|
|
176
|
+
if ((0, lodash_1.xor)(aVal, bVal).length) {
|
|
183
177
|
return true;
|
|
184
178
|
}
|
|
185
179
|
}
|
|
186
180
|
return false;
|
|
187
181
|
}
|
|
188
182
|
if (areAcPropertiesChanged(originalAc, newAc)) {
|
|
189
|
-
if (!tilmeld.checkPermissions(entity, nymph_1.TilmeldAccessLevels.FULL_ACCESS)) {
|
|
183
|
+
if (!tilmeld.checkPermissions(entity, nymph_1.TilmeldAccessLevels.FULL_ACCESS, undefined, originalAc)) {
|
|
190
184
|
throw new errors_1.AccessControlError('No permission to change access control properties.');
|
|
191
185
|
}
|
|
192
|
-
if ((
|
|
186
|
+
if ((originalAc.user && originalAc.user !== newAc.user) ||
|
|
193
187
|
(!originalAc.user &&
|
|
194
188
|
newAc.user &&
|
|
195
|
-
|
|
196
|
-
!tilmeld.gatekeeper('tilmeld/admin')) {
|
|
189
|
+
newAc.user !== tilmeld.currentUser)) {
|
|
197
190
|
throw new errors_1.AccessControlError('No permission to assign to another user.');
|
|
198
191
|
}
|
|
199
192
|
if (newAc.group &&
|
|
200
|
-
|
|
201
|
-
!(newAc.group
|
|
202
|
-
|
|
203
|
-
|
|
193
|
+
newAc.group !== originalAc.group &&
|
|
194
|
+
!(newAc.group =
|
|
195
|
+
tilmeld.currentUser?.$getGid() ||
|
|
196
|
+
(tilmeld.currentUser?.$getGids() ?? []).includes(newAc.group))) {
|
|
204
197
|
throw new errors_1.AccessControlError('No permission to assign to another group.');
|
|
205
198
|
}
|
|
206
|
-
setAcProperties(newAc);
|
|
207
199
|
}
|
|
208
200
|
}
|
|
209
201
|
if (!tilmeld.checkPermissions(entity, nymph_1.TilmeldAccessLevels.WRITE_ACCESS)) {
|
|
@@ -212,7 +204,7 @@ class Tilmeld {
|
|
|
212
204
|
};
|
|
213
205
|
checkPermissionsSaveAndFilterAcChanges.skipOnClone = true;
|
|
214
206
|
const addAccess = async function (nymph, entity) {
|
|
215
|
-
const tilmeld = nymph
|
|
207
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(nymph);
|
|
216
208
|
const user = tilmeld.currentUser;
|
|
217
209
|
if (user != null &&
|
|
218
210
|
entity.guid == null &&
|
|
@@ -221,10 +213,11 @@ class Tilmeld {
|
|
|
221
213
|
if (entity.user == null) {
|
|
222
214
|
entity.user = user;
|
|
223
215
|
}
|
|
224
|
-
if (entity.group == null
|
|
225
|
-
user.group
|
|
226
|
-
|
|
227
|
-
|
|
216
|
+
if (entity.group == null) {
|
|
217
|
+
const group = user.group;
|
|
218
|
+
if (group != null && group.guid != null) {
|
|
219
|
+
entity.group = group;
|
|
220
|
+
}
|
|
228
221
|
}
|
|
229
222
|
if (entity.acUser == null) {
|
|
230
223
|
entity.acUser = nymph_1.TilmeldAccessLevels.FULL_ACCESS;
|
|
@@ -306,10 +299,6 @@ class Tilmeld {
|
|
|
306
299
|
}
|
|
307
300
|
addAccessControlSelectors(options, selectors) {
|
|
308
301
|
const user = this.currentUser;
|
|
309
|
-
if (user != null &&
|
|
310
|
-
(user.abilities?.indexOf('system/admin') ?? -1) !== -1) {
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
302
|
if (options == null) {
|
|
314
303
|
throw new Error('No options in argument.');
|
|
315
304
|
}
|
|
@@ -344,27 +333,19 @@ class Tilmeld {
|
|
|
344
333
|
];
|
|
345
334
|
const groupRefs = [];
|
|
346
335
|
const acRefs = [];
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
acRefs.push(['
|
|
351
|
-
acRefs.push(['
|
|
352
|
-
|
|
353
|
-
for (let curSecondaryGroup of user.groups ?? []) {
|
|
354
|
-
if (curSecondaryGroup != null && curSecondaryGroup.guid != null) {
|
|
355
|
-
groupRefs.push(['group', curSecondaryGroup.guid]);
|
|
356
|
-
acRefs.push(['acRead', curSecondaryGroup.guid]);
|
|
357
|
-
acRefs.push(['acWrite', curSecondaryGroup.guid]);
|
|
358
|
-
acRefs.push(['acFull', curSecondaryGroup.guid]);
|
|
359
|
-
}
|
|
336
|
+
const gid = user.$getGid();
|
|
337
|
+
if (gid != null) {
|
|
338
|
+
groupRefs.push(['group', gid]);
|
|
339
|
+
acRefs.push(['acRead', gid]);
|
|
340
|
+
acRefs.push(['acWrite', gid]);
|
|
341
|
+
acRefs.push(['acFull', gid]);
|
|
360
342
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
343
|
+
const gids = user.$getGids();
|
|
344
|
+
for (let curGid of gids ?? []) {
|
|
345
|
+
groupRefs.push(['group', curGid]);
|
|
346
|
+
acRefs.push(['acRead', curGid]);
|
|
347
|
+
acRefs.push(['acWrite', curGid]);
|
|
348
|
+
acRefs.push(['acFull', curGid]);
|
|
368
349
|
}
|
|
369
350
|
if (groupRefs.length) {
|
|
370
351
|
subSelectors.push({
|
|
@@ -397,9 +378,9 @@ class Tilmeld {
|
|
|
397
378
|
selectors.push(selector);
|
|
398
379
|
}
|
|
399
380
|
}
|
|
400
|
-
checkPermissions(entity, type = nymph_1.TilmeldAccessLevels.READ_ACCESS, user) {
|
|
401
|
-
if (
|
|
402
|
-
|
|
381
|
+
checkPermissions(entity, type = nymph_1.TilmeldAccessLevels.READ_ACCESS, user, acProperties) {
|
|
382
|
+
if (!acProperties) {
|
|
383
|
+
acProperties = entity.$getCurrentAcValues();
|
|
403
384
|
}
|
|
404
385
|
let userOrNull = null;
|
|
405
386
|
let userOrEmpty = this.User.factorySync();
|
|
@@ -411,75 +392,64 @@ class Tilmeld {
|
|
|
411
392
|
userOrNull = user;
|
|
412
393
|
userOrEmpty = user;
|
|
413
394
|
}
|
|
414
|
-
if (userOrEmpty.$gatekeeper('system/admin')) {
|
|
415
|
-
return true;
|
|
416
|
-
}
|
|
417
395
|
if ((entity instanceof User_1.default || entity instanceof Group_1.default) &&
|
|
418
396
|
(type === nymph_1.TilmeldAccessLevels.READ_ACCESS ||
|
|
419
|
-
userOrEmpty
|
|
420
|
-
|
|
421
|
-
}
|
|
422
|
-
if (entity.user == null && entity.group == null) {
|
|
397
|
+
userOrEmpty.abilities?.includes('tilmeld/admin') ||
|
|
398
|
+
userOrEmpty.abilities?.includes('system/admin'))) {
|
|
423
399
|
return true;
|
|
424
400
|
}
|
|
425
|
-
const acUser =
|
|
426
|
-
const acGroup =
|
|
427
|
-
const acOther =
|
|
428
|
-
if (userOrNull
|
|
401
|
+
const acUser = acProperties.acUser ?? nymph_1.TilmeldAccessLevels.FULL_ACCESS;
|
|
402
|
+
const acGroup = acProperties.acGroup ?? nymph_1.TilmeldAccessLevels.READ_ACCESS;
|
|
403
|
+
const acOther = acProperties.acOther ?? nymph_1.TilmeldAccessLevels.NO_ACCESS;
|
|
404
|
+
if (userOrNull == null) {
|
|
429
405
|
return acOther >= type;
|
|
430
406
|
}
|
|
431
407
|
if (userOrEmpty.$is(entity)) {
|
|
432
408
|
return true;
|
|
433
409
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
410
|
+
const gid = userOrEmpty.$getGid();
|
|
411
|
+
if (gid != null &&
|
|
412
|
+
entity.guid === gid &&
|
|
437
413
|
type === nymph_1.TilmeldAccessLevels.READ_ACCESS) {
|
|
438
414
|
return true;
|
|
439
415
|
}
|
|
440
|
-
let
|
|
441
|
-
|
|
442
|
-
allGroups = allGroups.concat(userOrEmpty.$getDescendantGroupsSync());
|
|
416
|
+
let allGids = gid == null ? [] : [gid];
|
|
417
|
+
allGids = allGids.concat(userOrEmpty.$getGids() ?? []);
|
|
443
418
|
const checks = [
|
|
444
419
|
{
|
|
445
420
|
type: nymph_1.TilmeldAccessLevels.FULL_ACCESS,
|
|
446
|
-
array: [...(
|
|
421
|
+
array: [...(acProperties.acFull ?? [])],
|
|
447
422
|
},
|
|
448
423
|
{
|
|
449
424
|
type: nymph_1.TilmeldAccessLevels.WRITE_ACCESS,
|
|
450
|
-
array: [...(
|
|
425
|
+
array: [...(acProperties.acWrite ?? [])],
|
|
451
426
|
},
|
|
452
427
|
{
|
|
453
428
|
type: nymph_1.TilmeldAccessLevels.READ_ACCESS,
|
|
454
|
-
array: [...(
|
|
429
|
+
array: [...(acProperties.acRead ?? [])],
|
|
455
430
|
},
|
|
456
431
|
];
|
|
457
432
|
for (let curCheck of checks) {
|
|
458
433
|
if (type <= curCheck.type) {
|
|
459
|
-
if (
|
|
434
|
+
if (curCheck.array.includes(userOrEmpty.guid)) {
|
|
460
435
|
return true;
|
|
461
436
|
}
|
|
462
|
-
for (let
|
|
463
|
-
if (
|
|
464
|
-
curGroup.$inArray(curCheck.array)) {
|
|
437
|
+
for (let curGid of allGids) {
|
|
438
|
+
if (curCheck.array.includes(curGid)) {
|
|
465
439
|
return true;
|
|
466
440
|
}
|
|
467
441
|
}
|
|
468
442
|
}
|
|
469
443
|
}
|
|
470
|
-
if (
|
|
471
|
-
typeof entity.user.$is === 'function' &&
|
|
472
|
-
entity.user.$is(userOrNull)) {
|
|
444
|
+
if (acProperties.user != null && acProperties.user === userOrNull.guid) {
|
|
473
445
|
return acUser >= type;
|
|
474
446
|
}
|
|
475
|
-
if (
|
|
476
|
-
typeof entity.group.$inArray === 'function' &&
|
|
477
|
-
entity.group.$inArray(allGroups)) {
|
|
447
|
+
if (acProperties.group != null && allGids.includes(acProperties.group)) {
|
|
478
448
|
return acGroup >= type;
|
|
479
449
|
}
|
|
480
450
|
return acOther >= type;
|
|
481
451
|
}
|
|
482
|
-
checkClientUIDPermissions(name, type = nymph_1.TilmeldAccessLevels.READ_ACCESS, user) {
|
|
452
|
+
async checkClientUIDPermissions(name, type = nymph_1.TilmeldAccessLevels.READ_ACCESS, user) {
|
|
483
453
|
let userOrEmpty = this.User.factorySync();
|
|
484
454
|
if (user == null) {
|
|
485
455
|
userOrEmpty = this.User.current(true);
|
|
@@ -487,52 +457,42 @@ class Tilmeld {
|
|
|
487
457
|
else if (user !== false) {
|
|
488
458
|
userOrEmpty = user;
|
|
489
459
|
}
|
|
490
|
-
if (userOrEmpty
|
|
460
|
+
if (userOrEmpty.abilities?.includes('system/admin')) {
|
|
491
461
|
return true;
|
|
492
462
|
}
|
|
493
463
|
if (type === nymph_1.TilmeldAccessLevels.FULL_ACCESS) {
|
|
494
464
|
return (this.config.clientSetabledUIDs.indexOf(name) !== -1 ||
|
|
495
|
-
userOrEmpty.$gatekeeper(`uid/set/${name}`));
|
|
465
|
+
(await userOrEmpty.$gatekeeper(`uid/set/${name}`)));
|
|
496
466
|
}
|
|
497
467
|
else if (type === nymph_1.TilmeldAccessLevels.WRITE_ACCESS) {
|
|
498
468
|
return (this.config.clientEnabledUIDs.indexOf(name) !== -1 ||
|
|
499
469
|
this.config.clientSetabledUIDs.indexOf(name) !== -1 ||
|
|
500
|
-
userOrEmpty.$gatekeeper(`uid/new/${name}`) ||
|
|
501
|
-
userOrEmpty.$gatekeeper(`uid/set/${name}`));
|
|
470
|
+
(await userOrEmpty.$gatekeeper(`uid/new/${name}`)) ||
|
|
471
|
+
(await userOrEmpty.$gatekeeper(`uid/set/${name}`)));
|
|
502
472
|
}
|
|
503
473
|
else if (type === nymph_1.TilmeldAccessLevels.READ_ACCESS) {
|
|
504
474
|
return (this.config.clientReadableUIDs.indexOf(name) !== -1 ||
|
|
505
475
|
this.config.clientEnabledUIDs.indexOf(name) !== -1 ||
|
|
506
476
|
this.config.clientSetabledUIDs.indexOf(name) !== -1 ||
|
|
507
|
-
userOrEmpty.$gatekeeper(`uid/get/${name}`) ||
|
|
508
|
-
userOrEmpty.$gatekeeper(`uid/new/${name}`) ||
|
|
509
|
-
userOrEmpty.$gatekeeper(`uid/set/${name}`));
|
|
477
|
+
(await userOrEmpty.$gatekeeper(`uid/get/${name}`)) ||
|
|
478
|
+
(await userOrEmpty.$gatekeeper(`uid/new/${name}`)) ||
|
|
479
|
+
(await userOrEmpty.$gatekeeper(`uid/set/${name}`)));
|
|
510
480
|
}
|
|
511
481
|
return false;
|
|
512
482
|
}
|
|
513
|
-
fillSession(user) {
|
|
514
|
-
const _group = user.group;
|
|
515
|
-
const _groups = user.groups;
|
|
483
|
+
async fillSession(user) {
|
|
516
484
|
this.currentUser = user;
|
|
485
|
+
this.gatekeeperCache = await user.$getGatekeeperCache();
|
|
517
486
|
this.currentUser.$updateDataProtection();
|
|
518
|
-
|
|
519
|
-
this.currentUser.group.$updateDataProtection();
|
|
520
|
-
}
|
|
521
|
-
for (let group of this.currentUser.groups ?? []) {
|
|
522
|
-
group.$updateDataProtection();
|
|
523
|
-
}
|
|
487
|
+
this.currentUser.$updateGroupDataProtection();
|
|
524
488
|
}
|
|
525
489
|
clearSession() {
|
|
526
490
|
const user = this.currentUser;
|
|
527
491
|
this.currentUser = null;
|
|
492
|
+
this.gatekeeperCache = null;
|
|
528
493
|
if (user) {
|
|
529
494
|
user.$updateDataProtection();
|
|
530
|
-
|
|
531
|
-
user.group.$updateDataProtection();
|
|
532
|
-
}
|
|
533
|
-
for (let group of user.groups ?? []) {
|
|
534
|
-
group.$updateDataProtection();
|
|
535
|
-
}
|
|
495
|
+
user.$updateGroupDataProtection();
|
|
536
496
|
}
|
|
537
497
|
}
|
|
538
498
|
async extractToken(token) {
|
|
@@ -545,89 +505,190 @@ class Tilmeld {
|
|
|
545
505
|
type: '&',
|
|
546
506
|
guid: guid,
|
|
547
507
|
});
|
|
548
|
-
if (!user || !user.guid
|
|
508
|
+
if (!user || !user.guid) {
|
|
549
509
|
return null;
|
|
550
510
|
}
|
|
551
511
|
return user;
|
|
552
512
|
}
|
|
553
|
-
authenticate(skipXsrfToken = false) {
|
|
513
|
+
async authenticate(skipXsrfToken = false) {
|
|
554
514
|
if (this.request == null) {
|
|
555
515
|
return false;
|
|
556
516
|
}
|
|
517
|
+
if (this.response &&
|
|
518
|
+
this.response.locals.user &&
|
|
519
|
+
this.response.locals.user instanceof this.User) {
|
|
520
|
+
const user = this.response.locals.user;
|
|
521
|
+
await this.fillSession(user);
|
|
522
|
+
return true;
|
|
523
|
+
}
|
|
557
524
|
const cookies = this.request.cookies ?? {};
|
|
558
525
|
let fromAuthHeader = false;
|
|
559
526
|
let authToken;
|
|
527
|
+
let switchToken;
|
|
560
528
|
if (this.request.header('x-tilmeldauth') != null) {
|
|
561
529
|
fromAuthHeader = true;
|
|
562
530
|
authToken = this.request.header('x-tilmeldauth');
|
|
531
|
+
switchToken =
|
|
532
|
+
this.request.header('x-tilmeldswitch') ||
|
|
533
|
+
'TILMELDSWITCH' in cookies
|
|
534
|
+
? cookies.TILMELDSWITCH
|
|
535
|
+
: null;
|
|
563
536
|
}
|
|
564
537
|
else if ('TILMELDAUTH' in cookies) {
|
|
565
538
|
fromAuthHeader = false;
|
|
566
539
|
authToken = cookies.TILMELDAUTH;
|
|
540
|
+
switchToken = 'TILMELDSWITCH' in cookies ? cookies.TILMELDSWITCH : null;
|
|
567
541
|
}
|
|
568
542
|
else {
|
|
569
543
|
return false;
|
|
570
544
|
}
|
|
571
|
-
let
|
|
545
|
+
let authExtract;
|
|
546
|
+
let switchExtract;
|
|
572
547
|
if (skipXsrfToken ||
|
|
573
548
|
this.request.originalUrl.startsWith(this.config.setupPath)) {
|
|
574
|
-
|
|
549
|
+
authExtract = this.config.jwtExtract(this.config, authToken);
|
|
550
|
+
switchExtract = switchToken
|
|
551
|
+
? this.config.jwtExtract(this.config, switchToken)
|
|
552
|
+
: null;
|
|
575
553
|
}
|
|
576
554
|
else {
|
|
577
555
|
const xsrfToken = this.request.header('x-xsrf-token');
|
|
578
556
|
if (xsrfToken == null && !fromAuthHeader) {
|
|
579
557
|
return false;
|
|
580
558
|
}
|
|
581
|
-
|
|
559
|
+
authExtract = this.config.jwtExtract(this.config, authToken, xsrfToken);
|
|
560
|
+
switchExtract = switchToken
|
|
561
|
+
? this.config.jwtExtract(this.config, switchToken)
|
|
562
|
+
: null;
|
|
582
563
|
}
|
|
583
|
-
if (
|
|
584
|
-
this.logout();
|
|
564
|
+
if (authExtract == null) {
|
|
565
|
+
await this.logout();
|
|
585
566
|
return false;
|
|
586
567
|
}
|
|
587
|
-
const { guid, expire } =
|
|
588
|
-
const
|
|
589
|
-
|
|
590
|
-
|
|
568
|
+
const { guid, issued, expire } = authExtract;
|
|
569
|
+
const { guid: switchGuid, expire: switchExpire } = switchExtract || {
|
|
570
|
+
guid: null,
|
|
571
|
+
expire: null,
|
|
572
|
+
};
|
|
573
|
+
const user = await this.User.factory(guid);
|
|
574
|
+
if (user.guid == null ||
|
|
575
|
+
!user.enabled ||
|
|
576
|
+
expire.getTime() <= Date.now() ||
|
|
577
|
+
issued.getTime() > Date.now() ||
|
|
578
|
+
(user.revokeTokenDate != null && issued.getTime() <= user.revokeTokenDate)) {
|
|
579
|
+
await this.logout();
|
|
591
580
|
return false;
|
|
592
581
|
}
|
|
593
|
-
if (
|
|
594
|
-
this.
|
|
582
|
+
if (switchGuid && switchExpire && switchExpire.getTime() > Date.now()) {
|
|
583
|
+
const switchUser = await this.User.factory(switchGuid);
|
|
584
|
+
if (switchUser.guid == null) {
|
|
585
|
+
await this.logoutSwitch();
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
await this.fillSession(switchUser);
|
|
589
|
+
if (this.response) {
|
|
590
|
+
this.response.locals.user = switchUser;
|
|
591
|
+
}
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (expire.getTime() < Date.now() + this.config.jwtRenew * 1000) {
|
|
596
|
+
await this.login(user, fromAuthHeader);
|
|
595
597
|
}
|
|
596
598
|
else {
|
|
597
|
-
this.fillSession(user);
|
|
599
|
+
await this.fillSession(user);
|
|
600
|
+
}
|
|
601
|
+
if (this.response) {
|
|
602
|
+
this.response.locals.user = user;
|
|
598
603
|
}
|
|
599
604
|
return true;
|
|
600
605
|
}
|
|
601
|
-
login(user, sendAuthHeader) {
|
|
606
|
+
async login(user, sendAuthHeader, sendCookie = true) {
|
|
602
607
|
if (user.guid != null && user.enabled) {
|
|
603
|
-
if (this.response
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
608
|
+
if (this.response) {
|
|
609
|
+
if (!this.response.headersSent) {
|
|
610
|
+
const token = this.config.jwtBuilder(this.config, user);
|
|
611
|
+
if (sendCookie) {
|
|
612
|
+
const appUrl = new url_1.URL(this.config.appUrl);
|
|
613
|
+
this.response.cookie('TILMELDAUTH', token, {
|
|
614
|
+
domain: this.config.cookieDomain,
|
|
615
|
+
path: this.config.cookiePath,
|
|
616
|
+
maxAge: this.config.jwtExpire * 1000,
|
|
617
|
+
secure: appUrl.protocol === 'https:',
|
|
618
|
+
httpOnly: false,
|
|
619
|
+
sameSite: appUrl.protocol === 'https:' ? 'lax' : 'strict',
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
if (sendAuthHeader) {
|
|
623
|
+
this.response.set('X-TILMELDAUTH', token);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
this.response.locals.user = user;
|
|
627
|
+
}
|
|
628
|
+
await this.fillSession(user);
|
|
629
|
+
return true;
|
|
630
|
+
}
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
async loginSwitch(user, sendAuthHeader, sendCookie = true) {
|
|
634
|
+
if (user.guid != null) {
|
|
635
|
+
if (this.response) {
|
|
636
|
+
if (!this.response.headersSent) {
|
|
637
|
+
const token = this.config.jwtBuilder(this.config, user, true);
|
|
638
|
+
if (sendCookie) {
|
|
639
|
+
const appUrl = new url_1.URL(this.config.appUrl);
|
|
640
|
+
this.response.cookie('TILMELDSWITCH', token, {
|
|
641
|
+
domain: this.config.cookieDomain,
|
|
642
|
+
path: this.config.cookiePath,
|
|
643
|
+
maxAge: this.config.jwtSwitchExpire * 1000,
|
|
644
|
+
secure: appUrl.protocol === 'https:',
|
|
645
|
+
httpOnly: false,
|
|
646
|
+
sameSite: appUrl.protocol === 'https:' ? 'lax' : 'strict',
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
if (sendAuthHeader) {
|
|
650
|
+
this.response.set('X-TILMELDSWITCH', token);
|
|
651
|
+
}
|
|
616
652
|
}
|
|
653
|
+
this.response.locals.user = user;
|
|
617
654
|
}
|
|
618
|
-
this.fillSession(user);
|
|
655
|
+
await this.fillSession(user);
|
|
619
656
|
return true;
|
|
620
657
|
}
|
|
621
658
|
return false;
|
|
622
659
|
}
|
|
623
|
-
logout() {
|
|
660
|
+
async logout(clearCookie = true) {
|
|
661
|
+
if (this.request && !this.alreadyLoggedOutSwitch) {
|
|
662
|
+
const cookies = this.request.cookies ?? {};
|
|
663
|
+
if ('TILMELDSWITCH' in cookies) {
|
|
664
|
+
this.alreadyLoggedOutSwitch = true;
|
|
665
|
+
return await this.logoutSwitch(clearCookie);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
624
668
|
this.clearSession();
|
|
625
|
-
if (this.response
|
|
626
|
-
this.response.
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
669
|
+
if (this.response) {
|
|
670
|
+
if (clearCookie && !this.response.headersSent) {
|
|
671
|
+
this.response.clearCookie('TILMELDAUTH', {
|
|
672
|
+
domain: this.config.cookieDomain,
|
|
673
|
+
path: this.config.cookiePath,
|
|
674
|
+
});
|
|
675
|
+
this.response.set('X-TILMELDAUTH', '');
|
|
676
|
+
}
|
|
677
|
+
this.response.locals.user = null;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
async logoutSwitch(clearCookie = true) {
|
|
681
|
+
this.clearSession();
|
|
682
|
+
if (this.response) {
|
|
683
|
+
if (clearCookie && !this.response.headersSent) {
|
|
684
|
+
this.response.clearCookie('TILMELDSWITCH', {
|
|
685
|
+
domain: this.config.cookieDomain,
|
|
686
|
+
path: this.config.cookiePath,
|
|
687
|
+
});
|
|
688
|
+
this.response.set('X-TILMELDSWITCH', '');
|
|
689
|
+
}
|
|
690
|
+
this.response.locals.user = null;
|
|
691
|
+
await this.authenticate();
|
|
631
692
|
}
|
|
632
693
|
}
|
|
633
694
|
}
|