@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/CHANGELOG.md +189 -0
- package/README.md +13 -0
- package/dist/Group.d.ts +4 -7
- package/dist/Group.js +107 -83
- package/dist/Group.js.map +1 -1
- package/dist/Tilmeld.d.ts +10 -9
- package/dist/Tilmeld.js +226 -175
- package/dist/Tilmeld.js.map +1 -1
- package/dist/User.d.ts +66 -13
- package/dist/User.js +421 -128
- package/dist/User.js.map +1 -1
- package/dist/conf/d.d.ts +3 -1
- package/dist/conf/defaults.js +15 -4
- 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 +24 -21
- package/src/Group.ts +140 -102
- package/src/Tilmeld.ts +312 -221
- package/src/User.ts +600 -145
- package/src/conf/d.ts +16 -7
- package/src/conf/defaults.ts +17 -4
- package/src/enforceTilmeld.ts +20 -0
- package/src/index.ts +2 -0
- package/typedoc.json +4 -0
package/src/Tilmeld.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
Options,
|
|
6
6
|
TilmeldInterface,
|
|
7
7
|
TilmeldAccessLevels,
|
|
8
|
+
ACProperties,
|
|
8
9
|
} from '@nymphjs/nymph';
|
|
9
10
|
import { URL } from 'url';
|
|
10
11
|
import { Request, Response } from 'express';
|
|
@@ -15,6 +16,7 @@ import { AccessControlError } from './errors';
|
|
|
15
16
|
import Group from './Group';
|
|
16
17
|
import User, { UserData } from './User';
|
|
17
18
|
import { AccessControlData } from './Tilmeld.types';
|
|
19
|
+
import { enforceTilmeld } from './enforceTilmeld';
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* A user and group system for Nymph.js.
|
|
@@ -69,9 +71,9 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
69
71
|
public response: Response | null = null;
|
|
70
72
|
|
|
71
73
|
/**
|
|
72
|
-
*
|
|
74
|
+
* Used to avoid infinite loop.
|
|
73
75
|
*/
|
|
74
|
-
private
|
|
76
|
+
private alreadyLoggedOutSwitch = false;
|
|
75
77
|
|
|
76
78
|
/**
|
|
77
79
|
* Create a new instance of Tilmeld.
|
|
@@ -84,6 +86,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
84
86
|
|
|
85
87
|
/**
|
|
86
88
|
* This is used internally by Nymph. Don't call it yourself.
|
|
89
|
+
*
|
|
87
90
|
* @returns A clone of this instance.
|
|
88
91
|
*/
|
|
89
92
|
public clone() {
|
|
@@ -91,7 +94,6 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
91
94
|
tilmeld.request = this.request;
|
|
92
95
|
tilmeld.response = this.response;
|
|
93
96
|
tilmeld.currentUser = this.currentUser;
|
|
94
|
-
tilmeld.skipAuthenticate = true;
|
|
95
97
|
|
|
96
98
|
return tilmeld;
|
|
97
99
|
}
|
|
@@ -106,30 +108,41 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
106
108
|
public init(nymph: Nymph) {
|
|
107
109
|
this.nymph = nymph;
|
|
108
110
|
|
|
109
|
-
// Set up access control hooks when Nymph is called.
|
|
110
111
|
if (this.nymph.driver == null) {
|
|
111
112
|
throw new Error("Tilmeld can't be configured before Nymph.");
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
// Configure the classes.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
(NymphUser as any).skipOnClone = true;
|
|
125
|
-
this.Group = NymphGroup;
|
|
126
|
-
this.nymph.addEntityClass(NymphGroup);
|
|
116
|
+
if (nymph.parent) {
|
|
117
|
+
// This is a cloned Nymph, it should have classes already.
|
|
118
|
+
this.User = this.nymph.getEntityClass(User);
|
|
119
|
+
this.Group = this.nymph.getEntityClass(Group);
|
|
120
|
+
} else {
|
|
121
|
+
// This is a new Nymph, add the classes.
|
|
122
|
+
this.User = this.nymph.addEntityClass(User);
|
|
123
|
+
this.Group = this.nymph.addEntityClass(Group);
|
|
124
|
+
}
|
|
127
125
|
|
|
128
|
-
|
|
129
|
-
if (this.
|
|
130
|
-
this.
|
|
126
|
+
// Clone the current user so its Nymph instance is correct.
|
|
127
|
+
if (this.currentUser) {
|
|
128
|
+
const currentUserGUID = this.currentUser.guid;
|
|
129
|
+
const currentUserCDate = this.currentUser.cdate;
|
|
130
|
+
const currentUserMDate = this.currentUser.mdate;
|
|
131
|
+
const currentUserTags = this.currentUser.tags;
|
|
132
|
+
const currentUserData = this.currentUser.$getData();
|
|
133
|
+
const currentUserSData = this.currentUser.$getSData();
|
|
134
|
+
|
|
135
|
+
const user = this.User.factorySync();
|
|
136
|
+
user.guid = currentUserGUID;
|
|
137
|
+
user.cdate = currentUserCDate;
|
|
138
|
+
user.mdate = currentUserMDate;
|
|
139
|
+
user.tags = currentUserTags;
|
|
140
|
+
user.$putData(currentUserData, currentUserSData);
|
|
141
|
+
this.fillSession(user);
|
|
131
142
|
}
|
|
132
|
-
|
|
143
|
+
|
|
144
|
+
// Set up access control hooks when Nymph is called.
|
|
145
|
+
this.initAccessControl();
|
|
133
146
|
}
|
|
134
147
|
|
|
135
148
|
/**
|
|
@@ -141,11 +154,11 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
141
154
|
* @param ability The ability.
|
|
142
155
|
* @returns Whether the user has the given ability.
|
|
143
156
|
*/
|
|
144
|
-
public gatekeeper(ability?: string): boolean {
|
|
157
|
+
public async gatekeeper(ability?: string): Promise<boolean> {
|
|
145
158
|
if (this.currentUser == null) {
|
|
146
159
|
return false;
|
|
147
160
|
}
|
|
148
|
-
return this.currentUser.$gatekeeper(ability);
|
|
161
|
+
return await this.currentUser.$gatekeeper(ability);
|
|
149
162
|
}
|
|
150
163
|
|
|
151
164
|
private initAccessControl() {
|
|
@@ -155,7 +168,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
155
168
|
options: Options,
|
|
156
169
|
selectors: FormattedSelector[]
|
|
157
170
|
) {
|
|
158
|
-
const tilmeld = nymph
|
|
171
|
+
const tilmeld = enforceTilmeld(nymph);
|
|
159
172
|
if (
|
|
160
173
|
!options ||
|
|
161
174
|
!('skipAc' in options) ||
|
|
@@ -164,7 +177,9 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
164
177
|
) {
|
|
165
178
|
if (options?.source === 'client') {
|
|
166
179
|
if (
|
|
167
|
-
!tilmeld.
|
|
180
|
+
(!tilmeld.currentUser ||
|
|
181
|
+
(!tilmeld.currentUser.abilities?.includes('tilmeld/admin') &&
|
|
182
|
+
!tilmeld.currentUser.abilities?.includes('system/admin'))) && // The user is not an admin.
|
|
168
183
|
((!tilmeld.config.enableUserSearch &&
|
|
169
184
|
options.class?.factorySync() instanceof User) || // The use is searching for a user and searching is disabled.
|
|
170
185
|
(!tilmeld.config.enableGroupSearch &&
|
|
@@ -195,7 +210,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
195
210
|
nymph: Nymph,
|
|
196
211
|
entity: EntityInterface
|
|
197
212
|
) {
|
|
198
|
-
const tilmeld = nymph
|
|
213
|
+
const tilmeld = enforceTilmeld(nymph);
|
|
199
214
|
if (
|
|
200
215
|
typeof entity.$tilmeldDeleteSkipAC === 'function' &&
|
|
201
216
|
entity.$tilmeldDeleteSkipAC()
|
|
@@ -215,7 +230,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
215
230
|
guid: string,
|
|
216
231
|
className?: string
|
|
217
232
|
) {
|
|
218
|
-
const entity = nymph.
|
|
233
|
+
const entity = await nymph.getEntity(
|
|
219
234
|
{ class: nymph.getEntityClass(className ?? 'Entity') },
|
|
220
235
|
{ type: '&', guid: guid }
|
|
221
236
|
);
|
|
@@ -231,7 +246,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
231
246
|
nymph: Nymph,
|
|
232
247
|
entity: EntityInterface & AccessControlData
|
|
233
248
|
) {
|
|
234
|
-
const tilmeld = nymph
|
|
249
|
+
const tilmeld = enforceTilmeld(nymph);
|
|
235
250
|
if (!entity) {
|
|
236
251
|
return;
|
|
237
252
|
}
|
|
@@ -247,35 +262,9 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
247
262
|
// allowing a change to ac properties.
|
|
248
263
|
|
|
249
264
|
const originalAc = entity.$getOriginalAcValues();
|
|
250
|
-
const newAc
|
|
251
|
-
user: entity.user ?? undefined,
|
|
252
|
-
group: entity.group ?? undefined,
|
|
253
|
-
acUser: entity.acUser ?? undefined,
|
|
254
|
-
acGroup: entity.acGroup ?? undefined,
|
|
255
|
-
acOther: entity.acOther ?? undefined,
|
|
256
|
-
acRead: entity.acRead ?? undefined,
|
|
257
|
-
acWrite: entity.acWrite ?? undefined,
|
|
258
|
-
acFull: entity.acFull ?? undefined,
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
const setAcProperties = (acValues: { [k: string]: any }) => {
|
|
262
|
-
for (let name in acValues) {
|
|
263
|
-
const value = acValues[name];
|
|
264
|
-
if (value == null) {
|
|
265
|
-
delete entity[name];
|
|
266
|
-
} else {
|
|
267
|
-
entity[name] = value;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
// Restore original AC properties and check permissions.
|
|
273
|
-
setAcProperties(originalAc);
|
|
265
|
+
const newAc = entity.$getCurrentAcValues();
|
|
274
266
|
|
|
275
|
-
function areAcPropertiesChanged(
|
|
276
|
-
a: AccessControlData,
|
|
277
|
-
b: AccessControlData
|
|
278
|
-
) {
|
|
267
|
+
function areAcPropertiesChanged(a: ACProperties, b: ACProperties) {
|
|
279
268
|
for (const name of ['user', 'group'] as ('user' | 'group')[]) {
|
|
280
269
|
const aVal = a[name];
|
|
281
270
|
const bVal = b[name];
|
|
@@ -284,7 +273,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
284
273
|
} else if (aVal == null || bVal == null) {
|
|
285
274
|
return true;
|
|
286
275
|
}
|
|
287
|
-
if (
|
|
276
|
+
if (aVal !== bVal) {
|
|
288
277
|
return true;
|
|
289
278
|
}
|
|
290
279
|
}
|
|
@@ -319,12 +308,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
319
308
|
if (aVal.length !== bVal.length) {
|
|
320
309
|
return true;
|
|
321
310
|
}
|
|
322
|
-
if (
|
|
323
|
-
xor(
|
|
324
|
-
aVal.map((entity) => entity.guid),
|
|
325
|
-
bVal.map((entity) => entity.guid)
|
|
326
|
-
).length
|
|
327
|
-
) {
|
|
311
|
+
if (xor(aVal, bVal).length) {
|
|
328
312
|
return true;
|
|
329
313
|
}
|
|
330
314
|
}
|
|
@@ -333,7 +317,12 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
333
317
|
|
|
334
318
|
if (areAcPropertiesChanged(originalAc, newAc)) {
|
|
335
319
|
if (
|
|
336
|
-
!tilmeld.checkPermissions(
|
|
320
|
+
!tilmeld.checkPermissions(
|
|
321
|
+
entity,
|
|
322
|
+
TilmeldAccessLevels.FULL_ACCESS,
|
|
323
|
+
undefined,
|
|
324
|
+
originalAc
|
|
325
|
+
)
|
|
337
326
|
) {
|
|
338
327
|
// Only allow changes to AC properties if the user has full access.
|
|
339
328
|
throw new AccessControlError(
|
|
@@ -342,11 +331,10 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
342
331
|
}
|
|
343
332
|
|
|
344
333
|
if (
|
|
345
|
-
(
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
!tilmeld.gatekeeper('tilmeld/admin')
|
|
334
|
+
(originalAc.user && originalAc.user !== newAc.user) ||
|
|
335
|
+
(!originalAc.user &&
|
|
336
|
+
newAc.user &&
|
|
337
|
+
newAc.user !== tilmeld.currentUser)
|
|
350
338
|
) {
|
|
351
339
|
throw new AccessControlError(
|
|
352
340
|
'No permission to assign to another user.'
|
|
@@ -355,19 +343,15 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
355
343
|
|
|
356
344
|
if (
|
|
357
345
|
newAc.group &&
|
|
358
|
-
|
|
359
|
-
!(
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
) &&
|
|
363
|
-
!tilmeld.gatekeeper('tilmeld/admin')
|
|
346
|
+
newAc.group !== originalAc.group &&
|
|
347
|
+
!(newAc.group =
|
|
348
|
+
tilmeld.currentUser?.$getGid() ||
|
|
349
|
+
(tilmeld.currentUser?.$getGids() ?? []).includes(newAc.group))
|
|
364
350
|
) {
|
|
365
351
|
throw new AccessControlError(
|
|
366
352
|
'No permission to assign to another group.'
|
|
367
353
|
);
|
|
368
354
|
}
|
|
369
|
-
|
|
370
|
-
setAcProperties(newAc);
|
|
371
355
|
}
|
|
372
356
|
}
|
|
373
357
|
|
|
@@ -398,7 +382,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
398
382
|
nymph: Nymph,
|
|
399
383
|
entity: EntityInterface & AccessControlData
|
|
400
384
|
) {
|
|
401
|
-
const tilmeld = nymph
|
|
385
|
+
const tilmeld = enforceTilmeld(nymph);
|
|
402
386
|
const user = tilmeld.currentUser;
|
|
403
387
|
if (
|
|
404
388
|
user != null &&
|
|
@@ -409,12 +393,11 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
409
393
|
if (entity.user == null) {
|
|
410
394
|
entity.user = user;
|
|
411
395
|
}
|
|
412
|
-
if (
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
entity.group = user.group;
|
|
396
|
+
if (entity.group == null) {
|
|
397
|
+
const group = user.group;
|
|
398
|
+
if (group != null && group.guid != null) {
|
|
399
|
+
entity.group = group;
|
|
400
|
+
}
|
|
418
401
|
}
|
|
419
402
|
if (entity.acUser == null) {
|
|
420
403
|
entity.acUser = TilmeldAccessLevels.FULL_ACCESS;
|
|
@@ -530,14 +513,6 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
530
513
|
) {
|
|
531
514
|
const user = this.currentUser;
|
|
532
515
|
|
|
533
|
-
if (
|
|
534
|
-
user != null &&
|
|
535
|
-
(user.abilities?.indexOf('system/admin') ?? -1) !== -1
|
|
536
|
-
) {
|
|
537
|
-
// The user is a system admin, so they can see everything.
|
|
538
|
-
return;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
516
|
if (options == null) {
|
|
542
517
|
throw new Error('No options in argument.');
|
|
543
518
|
} else if (
|
|
@@ -577,33 +552,23 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
577
552
|
];
|
|
578
553
|
const groupRefs: FormattedSelector['ref'] = [];
|
|
579
554
|
const acRefs: FormattedSelector['ref'] = [];
|
|
580
|
-
|
|
555
|
+
const gid = user.$getGid();
|
|
556
|
+
if (gid != null) {
|
|
581
557
|
// It belongs to the user's primary group.
|
|
582
|
-
groupRefs.push(['group',
|
|
558
|
+
groupRefs.push(['group', gid]);
|
|
583
559
|
// User's primary group is listed in acRead, acWrite, or acFull.
|
|
584
|
-
acRefs.push(['acRead',
|
|
585
|
-
acRefs.push(['acWrite',
|
|
586
|
-
acRefs.push(['acFull',
|
|
587
|
-
}
|
|
588
|
-
for (let curSecondaryGroup of user.groups ?? []) {
|
|
589
|
-
if (curSecondaryGroup != null && curSecondaryGroup.guid != null) {
|
|
590
|
-
// It belongs to the user's secondary group.
|
|
591
|
-
groupRefs.push(['group', curSecondaryGroup.guid]);
|
|
592
|
-
// User's secondary group is listed in acRead, acWrite, or acFull.
|
|
593
|
-
acRefs.push(['acRead', curSecondaryGroup.guid]);
|
|
594
|
-
acRefs.push(['acWrite', curSecondaryGroup.guid]);
|
|
595
|
-
acRefs.push(['acFull', curSecondaryGroup.guid]);
|
|
596
|
-
}
|
|
560
|
+
acRefs.push(['acRead', gid]);
|
|
561
|
+
acRefs.push(['acWrite', gid]);
|
|
562
|
+
acRefs.push(['acFull', gid]);
|
|
597
563
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
}
|
|
564
|
+
const gids = user.$getGids();
|
|
565
|
+
for (let curGid of gids ?? []) {
|
|
566
|
+
// It belongs to the user's secondary group.
|
|
567
|
+
groupRefs.push(['group', curGid]);
|
|
568
|
+
// User's secondary group is listed in acRead, acWrite, or acFull.
|
|
569
|
+
acRefs.push(['acRead', curGid]);
|
|
570
|
+
acRefs.push(['acWrite', curGid]);
|
|
571
|
+
acRefs.push(['acFull', curGid]);
|
|
607
572
|
}
|
|
608
573
|
// All the group refs.
|
|
609
574
|
if (groupRefs.length) {
|
|
@@ -669,7 +634,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
669
634
|
* - FULL_ACCESS, the user has read, write, and delete access to the entity,
|
|
670
635
|
* as well as being able to manage its access controls and ownership.
|
|
671
636
|
*
|
|
672
|
-
* These properties
|
|
637
|
+
* These properties default to:
|
|
673
638
|
*
|
|
674
639
|
* - acUser = TilmeldAccessLevels.FULL_ACCESS
|
|
675
640
|
* - acGroup = TilmeldAccessLevels.READ_ACCESS
|
|
@@ -684,9 +649,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
684
649
|
* The following conditions will result in different checks, which determine
|
|
685
650
|
* whether the check passes:
|
|
686
651
|
*
|
|
687
|
-
* - The user has the "system/admin" ability. (Always true.)
|
|
688
652
|
* - It is a user or group. (True for READ_ACCESS or Tilmeld admins.)
|
|
689
|
-
* - The entity has no "user" and no "group". (Always true.)
|
|
690
653
|
* - No user is logged in. (Check other AC.)
|
|
691
654
|
* - The entity is the user. (Always true.)
|
|
692
655
|
* - It is the user's primary group. (True for READ_ACCESS.)
|
|
@@ -704,16 +667,17 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
704
667
|
* @param entity The entity to check.
|
|
705
668
|
* @param type The lowest level of permission to consider a pass.
|
|
706
669
|
* @param user The user to check permissions for. If null, uses the current user. If false, checks for public access.
|
|
670
|
+
* @param acProperties The acProperties to use instead of getting them from the entity.
|
|
707
671
|
* @returns Whether the current user has at least `type` permission for the entity.
|
|
708
672
|
*/
|
|
709
673
|
public checkPermissions(
|
|
710
|
-
entity: EntityInterface
|
|
674
|
+
entity: EntityInterface,
|
|
711
675
|
type: TilmeldAccessLevels = TilmeldAccessLevels.READ_ACCESS,
|
|
712
|
-
user?: (User & UserData) | false
|
|
676
|
+
user?: (User & UserData) | false,
|
|
677
|
+
acProperties?: ACProperties
|
|
713
678
|
) {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
return false;
|
|
679
|
+
if (!acProperties) {
|
|
680
|
+
acProperties = entity.$getCurrentAcValues() as ACProperties;
|
|
717
681
|
}
|
|
718
682
|
|
|
719
683
|
let userOrNull: (User & UserData) | null = null;
|
|
@@ -727,30 +691,22 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
727
691
|
userOrEmpty = user;
|
|
728
692
|
}
|
|
729
693
|
|
|
730
|
-
if (userOrEmpty.$gatekeeper('system/admin')) {
|
|
731
|
-
return true;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
694
|
// Users and groups are always readable. Editable by Tilmeld admins.
|
|
735
695
|
if (
|
|
736
696
|
(entity instanceof User || entity instanceof Group) &&
|
|
737
697
|
(type === TilmeldAccessLevels.READ_ACCESS ||
|
|
738
|
-
userOrEmpty
|
|
698
|
+
userOrEmpty.abilities?.includes('tilmeld/admin') ||
|
|
699
|
+
userOrEmpty.abilities?.includes('system/admin'))
|
|
739
700
|
) {
|
|
740
701
|
return true;
|
|
741
702
|
}
|
|
742
703
|
|
|
743
|
-
// Entities with no owners are always editable.
|
|
744
|
-
if (entity.user == null && entity.group == null) {
|
|
745
|
-
return true;
|
|
746
|
-
}
|
|
747
|
-
|
|
748
704
|
// Load access control, since we need it now...
|
|
749
|
-
const acUser =
|
|
750
|
-
const acGroup =
|
|
751
|
-
const acOther =
|
|
705
|
+
const acUser = acProperties.acUser ?? TilmeldAccessLevels.FULL_ACCESS;
|
|
706
|
+
const acGroup = acProperties.acGroup ?? TilmeldAccessLevels.READ_ACCESS;
|
|
707
|
+
const acOther = acProperties.acOther ?? TilmeldAccessLevels.NO_ACCESS;
|
|
752
708
|
|
|
753
|
-
if (userOrNull
|
|
709
|
+
if (userOrNull == null) {
|
|
754
710
|
return acOther >= type;
|
|
755
711
|
}
|
|
756
712
|
|
|
@@ -760,45 +716,41 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
760
716
|
}
|
|
761
717
|
|
|
762
718
|
// Check if the entity is the user's group. Always readable.
|
|
719
|
+
const gid = userOrEmpty.$getGid();
|
|
763
720
|
if (
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
userOrEmpty.group.$is(entity) &&
|
|
721
|
+
gid != null &&
|
|
722
|
+
entity.guid === gid &&
|
|
767
723
|
type === TilmeldAccessLevels.READ_ACCESS
|
|
768
724
|
) {
|
|
769
725
|
return true;
|
|
770
726
|
}
|
|
771
727
|
|
|
772
728
|
// Calculate all the groups the user belongs to.
|
|
773
|
-
let
|
|
774
|
-
|
|
775
|
-
allGroups = allGroups.concat(userOrEmpty.$getDescendantGroupsSync());
|
|
729
|
+
let allGids = gid == null ? [] : [gid];
|
|
730
|
+
allGids = allGids.concat(userOrEmpty.$getGids() ?? []);
|
|
776
731
|
|
|
777
732
|
// Check access ac properties.
|
|
778
733
|
const checks = [
|
|
779
734
|
{
|
|
780
735
|
type: TilmeldAccessLevels.FULL_ACCESS,
|
|
781
|
-
array: [...(
|
|
736
|
+
array: [...(acProperties.acFull ?? [])],
|
|
782
737
|
},
|
|
783
738
|
{
|
|
784
739
|
type: TilmeldAccessLevels.WRITE_ACCESS,
|
|
785
|
-
array: [...(
|
|
740
|
+
array: [...(acProperties.acWrite ?? [])],
|
|
786
741
|
},
|
|
787
742
|
{
|
|
788
743
|
type: TilmeldAccessLevels.READ_ACCESS,
|
|
789
|
-
array: [...(
|
|
744
|
+
array: [...(acProperties.acRead ?? [])],
|
|
790
745
|
},
|
|
791
746
|
];
|
|
792
747
|
for (let curCheck of checks) {
|
|
793
748
|
if (type <= curCheck.type) {
|
|
794
|
-
if (
|
|
749
|
+
if (curCheck.array.includes(userOrEmpty.guid)) {
|
|
795
750
|
return true;
|
|
796
751
|
}
|
|
797
|
-
for (let
|
|
798
|
-
if (
|
|
799
|
-
typeof curGroup.$inArray === 'function' &&
|
|
800
|
-
curGroup.$inArray(curCheck.array)
|
|
801
|
-
) {
|
|
752
|
+
for (let curGid of allGids) {
|
|
753
|
+
if (curCheck.array.includes(curGid)) {
|
|
802
754
|
return true;
|
|
803
755
|
}
|
|
804
756
|
}
|
|
@@ -806,18 +758,10 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
806
758
|
}
|
|
807
759
|
|
|
808
760
|
// Check ownership ac properties.
|
|
809
|
-
if (
|
|
810
|
-
entity.user != null &&
|
|
811
|
-
typeof entity.user.$is === 'function' &&
|
|
812
|
-
entity.user.$is(userOrNull)
|
|
813
|
-
) {
|
|
761
|
+
if (acProperties.user != null && acProperties.user === userOrNull.guid) {
|
|
814
762
|
return acUser >= type;
|
|
815
763
|
}
|
|
816
|
-
if (
|
|
817
|
-
entity.group != null &&
|
|
818
|
-
typeof entity.group.$inArray === 'function' &&
|
|
819
|
-
entity.group.$inArray(allGroups)
|
|
820
|
-
) {
|
|
764
|
+
if (acProperties.group != null && allGids.includes(acProperties.group)) {
|
|
821
765
|
return acGroup >= type;
|
|
822
766
|
}
|
|
823
767
|
return acOther >= type;
|
|
@@ -847,7 +791,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
847
791
|
* @param user The user to check permissions for. If null, uses the current user. If false, checks for public access.
|
|
848
792
|
* @returns Whether the current user has at least `type` permission for the UID.
|
|
849
793
|
*/
|
|
850
|
-
public checkClientUIDPermissions(
|
|
794
|
+
public async checkClientUIDPermissions(
|
|
851
795
|
name: string,
|
|
852
796
|
type: TilmeldAccessLevels = TilmeldAccessLevels.READ_ACCESS,
|
|
853
797
|
user?: (User & UserData) | false
|
|
@@ -860,30 +804,30 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
860
804
|
userOrEmpty = user;
|
|
861
805
|
}
|
|
862
806
|
|
|
863
|
-
if (userOrEmpty
|
|
807
|
+
if (userOrEmpty.abilities?.includes('system/admin')) {
|
|
864
808
|
return true;
|
|
865
809
|
}
|
|
866
810
|
|
|
867
811
|
if (type === TilmeldAccessLevels.FULL_ACCESS) {
|
|
868
812
|
return (
|
|
869
813
|
this.config.clientSetabledUIDs.indexOf(name) !== -1 ||
|
|
870
|
-
userOrEmpty.$gatekeeper(`uid/set/${name}`)
|
|
814
|
+
(await userOrEmpty.$gatekeeper(`uid/set/${name}`))
|
|
871
815
|
);
|
|
872
816
|
} else if (type === TilmeldAccessLevels.WRITE_ACCESS) {
|
|
873
817
|
return (
|
|
874
818
|
this.config.clientEnabledUIDs.indexOf(name) !== -1 ||
|
|
875
819
|
this.config.clientSetabledUIDs.indexOf(name) !== -1 ||
|
|
876
|
-
userOrEmpty.$gatekeeper(`uid/new/${name}`) ||
|
|
877
|
-
userOrEmpty.$gatekeeper(`uid/set/${name}`)
|
|
820
|
+
(await userOrEmpty.$gatekeeper(`uid/new/${name}`)) ||
|
|
821
|
+
(await userOrEmpty.$gatekeeper(`uid/set/${name}`))
|
|
878
822
|
);
|
|
879
823
|
} else if (type === TilmeldAccessLevels.READ_ACCESS) {
|
|
880
824
|
return (
|
|
881
825
|
this.config.clientReadableUIDs.indexOf(name) !== -1 ||
|
|
882
826
|
this.config.clientEnabledUIDs.indexOf(name) !== -1 ||
|
|
883
827
|
this.config.clientSetabledUIDs.indexOf(name) !== -1 ||
|
|
884
|
-
userOrEmpty.$gatekeeper(`uid/get/${name}`) ||
|
|
885
|
-
userOrEmpty.$gatekeeper(`uid/new/${name}`) ||
|
|
886
|
-
userOrEmpty.$gatekeeper(`uid/set/${name}`)
|
|
828
|
+
(await userOrEmpty.$gatekeeper(`uid/get/${name}`)) ||
|
|
829
|
+
(await userOrEmpty.$gatekeeper(`uid/new/${name}`)) ||
|
|
830
|
+
(await userOrEmpty.$gatekeeper(`uid/set/${name}`))
|
|
887
831
|
);
|
|
888
832
|
}
|
|
889
833
|
|
|
@@ -896,19 +840,10 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
896
840
|
* @param user The user.
|
|
897
841
|
*/
|
|
898
842
|
public fillSession(user: User & UserData) {
|
|
899
|
-
// Read groups right now, since gatekeeper needs them, so
|
|
900
|
-
// $udpateDataProtection will fail to read them (since it runs gatekeeper).
|
|
901
|
-
const _group = user.group;
|
|
902
|
-
const _groups = user.groups;
|
|
903
843
|
this.currentUser = user;
|
|
904
844
|
// Now update the data protection on the user and all the groups.
|
|
905
845
|
this.currentUser.$updateDataProtection();
|
|
906
|
-
|
|
907
|
-
this.currentUser.group.$updateDataProtection();
|
|
908
|
-
}
|
|
909
|
-
for (let group of this.currentUser.groups ?? []) {
|
|
910
|
-
group.$updateDataProtection();
|
|
911
|
-
}
|
|
846
|
+
this.currentUser.$updateGroupDataProtection();
|
|
912
847
|
}
|
|
913
848
|
|
|
914
849
|
/**
|
|
@@ -919,12 +854,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
919
854
|
this.currentUser = null;
|
|
920
855
|
if (user) {
|
|
921
856
|
user.$updateDataProtection();
|
|
922
|
-
|
|
923
|
-
user.group.$updateDataProtection();
|
|
924
|
-
}
|
|
925
|
-
for (let group of user.groups ?? []) {
|
|
926
|
-
group.$updateDataProtection();
|
|
927
|
-
}
|
|
857
|
+
user.$updateGroupDataProtection();
|
|
928
858
|
}
|
|
929
859
|
}
|
|
930
860
|
|
|
@@ -948,7 +878,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
948
878
|
guid: guid,
|
|
949
879
|
}
|
|
950
880
|
);
|
|
951
|
-
if (!user || !user.guid
|
|
881
|
+
if (!user || !user.guid) {
|
|
952
882
|
return null;
|
|
953
883
|
}
|
|
954
884
|
|
|
@@ -956,40 +886,72 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
956
886
|
}
|
|
957
887
|
|
|
958
888
|
/**
|
|
959
|
-
* Check for
|
|
889
|
+
* Check for TILMELDAUTH and TILMELDSWITCH tokens, and, if set, authenticate
|
|
890
|
+
* from it/them.
|
|
891
|
+
*
|
|
892
|
+
* You can also call this function after setting `response.locals.user` to the
|
|
893
|
+
* user you want to authenticate. You *should* check for `user.enabled` before
|
|
894
|
+
* setting this variable, unless you explicitly want to log in as a disabled
|
|
895
|
+
* user. (The user must be an instance of the User class for this Tilmeld
|
|
896
|
+
* instance.)
|
|
897
|
+
*
|
|
898
|
+
* This function will set `response.locals.user` to the logged in user on
|
|
899
|
+
* successful authentication.
|
|
960
900
|
*
|
|
961
901
|
* @param skipXsrfToken Skip the XSRF token check.
|
|
962
902
|
* @returns True if a user was authenticated, false on any failure.
|
|
963
903
|
*/
|
|
964
|
-
public authenticate(skipXsrfToken = false) {
|
|
904
|
+
public async authenticate(skipXsrfToken = false) {
|
|
965
905
|
if (this.request == null) {
|
|
966
906
|
return false;
|
|
967
907
|
}
|
|
968
908
|
|
|
909
|
+
if (
|
|
910
|
+
this.response &&
|
|
911
|
+
this.response.locals.user &&
|
|
912
|
+
this.response.locals.user instanceof this.User
|
|
913
|
+
) {
|
|
914
|
+
// The user has already been authenticated through some other means.
|
|
915
|
+
const user = this.response.locals.user;
|
|
916
|
+
this.fillSession(user);
|
|
917
|
+
return true;
|
|
918
|
+
}
|
|
919
|
+
|
|
969
920
|
const cookies = this.request.cookies ?? {};
|
|
970
921
|
|
|
971
922
|
// If a client does't support cookies, they can use the X-TILMELDAUTH header
|
|
972
923
|
// to provide the auth token.
|
|
973
924
|
let fromAuthHeader = false;
|
|
974
925
|
let authToken: string;
|
|
926
|
+
let switchToken: string | null;
|
|
975
927
|
if (this.request.header('x-tilmeldauth') != null) {
|
|
976
928
|
fromAuthHeader = true;
|
|
977
929
|
authToken = this.request.header('x-tilmeldauth') as string;
|
|
930
|
+
switchToken =
|
|
931
|
+
(this.request.header('x-tilmeldswitch') as string) ||
|
|
932
|
+
'TILMELDSWITCH' in cookies
|
|
933
|
+
? cookies.TILMELDSWITCH
|
|
934
|
+
: null;
|
|
978
935
|
} else if ('TILMELDAUTH' in cookies) {
|
|
979
936
|
fromAuthHeader = false;
|
|
980
937
|
authToken = cookies.TILMELDAUTH;
|
|
938
|
+
switchToken = 'TILMELDSWITCH' in cookies ? cookies.TILMELDSWITCH : null;
|
|
981
939
|
} else {
|
|
982
940
|
return false;
|
|
983
941
|
}
|
|
984
942
|
|
|
985
|
-
let
|
|
943
|
+
let authExtract: { guid: string; issued: Date; expire: Date } | null;
|
|
944
|
+
let switchExtract: { guid: string; expire: Date } | null;
|
|
986
945
|
if (
|
|
987
946
|
skipXsrfToken ||
|
|
988
947
|
this.request.originalUrl.startsWith(this.config.setupPath)
|
|
989
948
|
) {
|
|
990
949
|
// The request is for the setup app, or we were told to skip the XSRF
|
|
991
950
|
// check, so don't check for the XSRF token.
|
|
992
|
-
|
|
951
|
+
authExtract = this.config.jwtExtract(this.config, authToken);
|
|
952
|
+
switchExtract = switchToken
|
|
953
|
+
? this.config.jwtExtract(this.config, switchToken)
|
|
954
|
+
: null;
|
|
993
955
|
} else {
|
|
994
956
|
// The request is for something else, so check for a valid XSRF token,
|
|
995
957
|
// unless the auth token is provided by a header (instead of a cookie).
|
|
@@ -998,28 +960,62 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
998
960
|
return false;
|
|
999
961
|
}
|
|
1000
962
|
|
|
1001
|
-
|
|
963
|
+
authExtract = this.config.jwtExtract(this.config, authToken, xsrfToken);
|
|
964
|
+
switchExtract = switchToken
|
|
965
|
+
? this.config.jwtExtract(this.config, switchToken)
|
|
966
|
+
: null;
|
|
1002
967
|
}
|
|
1003
968
|
|
|
1004
|
-
if (
|
|
1005
|
-
this.logout();
|
|
969
|
+
if (authExtract == null) {
|
|
970
|
+
await this.logout();
|
|
1006
971
|
return false;
|
|
1007
972
|
}
|
|
1008
|
-
const { guid, expire } =
|
|
973
|
+
const { guid, issued, expire } = authExtract;
|
|
974
|
+
const { guid: switchGuid, expire: switchExpire } = switchExtract || {
|
|
975
|
+
guid: null,
|
|
976
|
+
expire: null,
|
|
977
|
+
};
|
|
1009
978
|
|
|
1010
|
-
const user = this.User.
|
|
1011
|
-
if (
|
|
1012
|
-
|
|
979
|
+
const user = await this.User.factory(guid);
|
|
980
|
+
if (
|
|
981
|
+
user.guid == null ||
|
|
982
|
+
!user.enabled ||
|
|
983
|
+
expire.getTime() <= Date.now() ||
|
|
984
|
+
issued.getTime() > Date.now() ||
|
|
985
|
+
(user.revokeTokenDate != null && issued.getTime() <= user.revokeTokenDate)
|
|
986
|
+
) {
|
|
987
|
+
await this.logout();
|
|
1013
988
|
return false;
|
|
1014
989
|
}
|
|
1015
990
|
|
|
1016
|
-
if (
|
|
991
|
+
if (switchGuid && switchExpire && switchExpire.getTime() > Date.now()) {
|
|
992
|
+
// Load the switch user.
|
|
993
|
+
const switchUser = await this.User.factory(switchGuid);
|
|
994
|
+
if (switchUser.guid == null) {
|
|
995
|
+
await this.logoutSwitch();
|
|
996
|
+
} else {
|
|
997
|
+
this.fillSession(switchUser);
|
|
998
|
+
|
|
999
|
+
if (this.response) {
|
|
1000
|
+
this.response.locals.user = switchUser;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
return true;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
if (expire.getTime() < Date.now() + this.config.jwtRenew * 1000) {
|
|
1017
1008
|
// If the user is less than renew time from needing a new token, give them
|
|
1018
1009
|
// a new one.
|
|
1019
1010
|
this.login(user, fromAuthHeader);
|
|
1020
1011
|
} else {
|
|
1021
1012
|
this.fillSession(user);
|
|
1022
1013
|
}
|
|
1014
|
+
|
|
1015
|
+
if (this.response) {
|
|
1016
|
+
this.response.locals.user = user;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1023
1019
|
return true;
|
|
1024
1020
|
}
|
|
1025
1021
|
|
|
@@ -1027,26 +1023,86 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
1027
1023
|
* Logs the given user into the system.
|
|
1028
1024
|
*
|
|
1029
1025
|
* @param user The user.
|
|
1030
|
-
* @param sendAuthHeader
|
|
1026
|
+
* @param sendAuthHeader Send the auth token as a custom header.
|
|
1027
|
+
* @param sendCookie Send the auth token as a cookie.
|
|
1031
1028
|
* @returns True on success, false on failure.
|
|
1032
1029
|
*/
|
|
1033
|
-
public login(
|
|
1030
|
+
public login(
|
|
1031
|
+
user: User & UserData,
|
|
1032
|
+
sendAuthHeader: boolean,
|
|
1033
|
+
sendCookie = true
|
|
1034
|
+
) {
|
|
1034
1035
|
if (user.guid != null && user.enabled) {
|
|
1035
|
-
if (this.response
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1036
|
+
if (this.response) {
|
|
1037
|
+
if (!this.response.headersSent) {
|
|
1038
|
+
const token = this.config.jwtBuilder(this.config, user);
|
|
1039
|
+
|
|
1040
|
+
if (sendCookie) {
|
|
1041
|
+
const appUrl = new URL(this.config.appUrl);
|
|
1042
|
+
this.response.cookie('TILMELDAUTH', token, {
|
|
1043
|
+
domain: this.config.cookieDomain,
|
|
1044
|
+
path: this.config.cookiePath,
|
|
1045
|
+
maxAge: this.config.jwtExpire * 1000,
|
|
1046
|
+
secure: appUrl.protocol === 'https',
|
|
1047
|
+
httpOnly: false, // Allow JS access (for CSRF protection).
|
|
1048
|
+
sameSite: appUrl.protocol === 'https' ? 'lax' : 'strict',
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
if (sendAuthHeader) {
|
|
1053
|
+
this.response.set('X-TILMELDAUTH', token);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
this.response.locals.user = user;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
this.fillSession(user);
|
|
1061
|
+
return true;
|
|
1062
|
+
}
|
|
1063
|
+
return false;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* Adds a switch auth token for the given user.
|
|
1068
|
+
*
|
|
1069
|
+
* This effectively logs the current user in to the system as the given user.
|
|
1070
|
+
*
|
|
1071
|
+
* @param user The user.
|
|
1072
|
+
* @param sendAuthHeader Send the auth token as a custom header.
|
|
1073
|
+
* @param sendCookie Send the auth token as a cookie.
|
|
1074
|
+
* @returns True on success, false on failure.
|
|
1075
|
+
*/
|
|
1076
|
+
public loginSwitch(
|
|
1077
|
+
user: User & UserData,
|
|
1078
|
+
sendAuthHeader: boolean,
|
|
1079
|
+
sendCookie = true
|
|
1080
|
+
) {
|
|
1081
|
+
if (user.guid != null) {
|
|
1082
|
+
if (this.response) {
|
|
1083
|
+
if (!this.response.headersSent) {
|
|
1084
|
+
const token = this.config.jwtBuilder(this.config, user, true);
|
|
1085
|
+
|
|
1086
|
+
if (sendCookie) {
|
|
1087
|
+
const appUrl = new URL(this.config.appUrl);
|
|
1088
|
+
this.response.cookie('TILMELDSWITCH', token, {
|
|
1089
|
+
domain: this.config.cookieDomain,
|
|
1090
|
+
path: this.config.cookiePath,
|
|
1091
|
+
maxAge: this.config.jwtSwitchExpire * 1000,
|
|
1092
|
+
secure: appUrl.protocol === 'https',
|
|
1093
|
+
httpOnly: false, // Allow JS access (for PubSub auth token).
|
|
1094
|
+
sameSite: appUrl.protocol === 'https' ? 'lax' : 'strict',
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
if (sendAuthHeader) {
|
|
1099
|
+
this.response.set('X-TILMELDSWITCH', token);
|
|
1100
|
+
}
|
|
1048
1101
|
}
|
|
1102
|
+
|
|
1103
|
+
this.response.locals.user = user;
|
|
1049
1104
|
}
|
|
1105
|
+
|
|
1050
1106
|
this.fillSession(user);
|
|
1051
1107
|
return true;
|
|
1052
1108
|
}
|
|
@@ -1055,15 +1111,50 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
1055
1111
|
|
|
1056
1112
|
/**
|
|
1057
1113
|
* Logs the current user out of the system.
|
|
1114
|
+
*
|
|
1115
|
+
* @param clearCookie Clear the auth cookie. (Also send a header.)
|
|
1058
1116
|
*/
|
|
1059
|
-
public logout() {
|
|
1117
|
+
public async logout(clearCookie = true) {
|
|
1118
|
+
if (this.request && !this.alreadyLoggedOutSwitch) {
|
|
1119
|
+
const cookies = this.request.cookies ?? {};
|
|
1120
|
+
|
|
1121
|
+
if ('TILMELDSWITCH' in cookies) {
|
|
1122
|
+
this.alreadyLoggedOutSwitch = true;
|
|
1123
|
+
return await this.logoutSwitch(clearCookie);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1060
1127
|
this.clearSession();
|
|
1061
|
-
if (this.response
|
|
1062
|
-
this.response.
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1128
|
+
if (this.response) {
|
|
1129
|
+
if (clearCookie && !this.response.headersSent) {
|
|
1130
|
+
this.response.clearCookie('TILMELDAUTH', {
|
|
1131
|
+
domain: this.config.cookieDomain,
|
|
1132
|
+
path: this.config.cookiePath,
|
|
1133
|
+
});
|
|
1134
|
+
this.response.set('X-TILMELDAUTH', '');
|
|
1135
|
+
}
|
|
1136
|
+
this.response.locals.user = null;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* Clears the switch user out of the system.
|
|
1142
|
+
*
|
|
1143
|
+
* @param clearCookie Clear the auth cookie. (Also send a header.)
|
|
1144
|
+
*/
|
|
1145
|
+
public async logoutSwitch(clearCookie = true) {
|
|
1146
|
+
this.clearSession();
|
|
1147
|
+
if (this.response) {
|
|
1148
|
+
if (clearCookie && !this.response.headersSent) {
|
|
1149
|
+
this.response.clearCookie('TILMELDSWITCH', {
|
|
1150
|
+
domain: this.config.cookieDomain,
|
|
1151
|
+
path: this.config.cookiePath,
|
|
1152
|
+
});
|
|
1153
|
+
this.response.set('X-TILMELDSWITCH', '');
|
|
1154
|
+
}
|
|
1155
|
+
this.response.locals.user = null;
|
|
1156
|
+
|
|
1157
|
+
await this.authenticate();
|
|
1067
1158
|
}
|
|
1068
1159
|
}
|
|
1069
1160
|
|