@nymphjs/tilmeld 1.0.0-beta.13 → 1.0.0-beta.130
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 +598 -0
- package/README.md +21 -5
- package/dist/AbleObject.d.ts +15 -0
- package/dist/AbleObject.js +18 -6
- package/dist/AbleObject.js.map +1 -1
- package/dist/Group.d.ts +186 -19
- package/dist/Group.js +817 -440
- package/dist/Group.js.map +1 -1
- package/dist/Group.testArtifacts.d.ts +3 -0
- package/dist/Group.testArtifacts.js +120 -0
- package/dist/Group.testArtifacts.js.map +1 -0
- package/dist/Tilmeld.d.ts +241 -12
- package/dist/Tilmeld.js +725 -286
- package/dist/Tilmeld.js.map +1 -1
- package/dist/Tilmeld.types.d.ts +5 -5
- package/dist/Tilmeld.types.js +1 -2
- package/dist/User.d.ts +399 -26
- package/dist/User.js +1955 -1055
- package/dist/User.js.map +1 -1
- package/dist/User.testArtifacts.d.ts +3 -0
- package/dist/User.testArtifacts.js +175 -0
- package/dist/User.testArtifacts.js.map +1 -0
- package/dist/conf/defaults.d.ts +1 -1
- package/dist/conf/defaults.js +196 -141
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +355 -2
- package/dist/conf/index.js +2 -8
- package/dist/conf/index.js.map +1 -1
- package/dist/decorators/index.d.ts +2 -0
- package/dist/decorators/index.js +3 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/requireAbility.d.ts +10 -0
- package/dist/decorators/requireAbility.js +21 -0
- package/dist/decorators/requireAbility.js.map +1 -0
- package/dist/decorators/requireAuthentication.d.ts +8 -0
- package/dist/decorators/requireAuthentication.js +16 -0
- package/dist/decorators/requireAuthentication.js.map +1 -0
- package/dist/enforceTilmeld.d.ts +3 -0
- package/dist/enforceTilmeld.js +18 -0
- package/dist/enforceTilmeld.js.map +1 -0
- package/dist/errors/AccessControlError.d.ts +2 -1
- package/dist/errors/AccessControlError.js +3 -6
- package/dist/errors/AccessControlError.js.map +1 -1
- package/dist/errors/BadDataError.d.ts +2 -1
- package/dist/errors/BadDataError.js +3 -6
- package/dist/errors/BadDataError.js.map +1 -1
- package/dist/errors/BadEmailError.d.ts +2 -1
- package/dist/errors/BadEmailError.js +3 -6
- package/dist/errors/BadEmailError.js.map +1 -1
- package/dist/errors/BadUsernameError.d.ts +2 -1
- package/dist/errors/BadUsernameError.js +3 -6
- package/dist/errors/BadUsernameError.js.map +1 -1
- package/dist/errors/CouldNotChangeDefaultPrimaryGroupError.js +1 -5
- package/dist/errors/CouldNotChangeDefaultPrimaryGroupError.js.map +1 -1
- package/dist/errors/EmailChangeRateLimitExceededError.js +1 -5
- package/dist/errors/EmailChangeRateLimitExceededError.js.map +1 -1
- package/dist/errors/index.d.ts +6 -6
- package/dist/errors/index.js +6 -22
- package/dist/errors/index.js.map +1 -1
- package/dist/index.d.ts +12 -10
- package/dist/index.js +17 -35
- package/dist/index.js.map +1 -1
- package/dist/testArtifacts.d.ts +23 -0
- package/dist/testArtifacts.js +1317 -0
- package/dist/testArtifacts.js.map +1 -0
- package/dist/tilmeldJoiProps.d.ts +3 -3
- package/dist/tilmeldJoiProps.js +13 -18
- package/dist/tilmeldJoiProps.js.map +1 -1
- package/jest.config.js +31 -5
- package/package.json +49 -36
- package/src/AbleObject.ts +2 -2
- package/src/Group.testArtifacts.ts +159 -0
- package/src/Group.ts +647 -238
- package/src/Tilmeld.ts +550 -315
- package/src/Tilmeld.types.ts +5 -5
- package/src/User.testArtifacts.ts +226 -0
- package/src/User.ts +1657 -634
- package/src/conf/defaults.ts +172 -109
- package/src/conf/index.ts +370 -2
- package/src/decorators/index.ts +2 -0
- package/src/decorators/requireAbility.ts +26 -0
- package/src/decorators/requireAuthentication.ts +19 -0
- 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/errors/index.ts +6 -6
- package/src/index.ts +13 -10
- package/src/testArtifacts.ts +2098 -0
- package/src/tilmeldJoiProps.ts +8 -23
- package/tsconfig.json +6 -3
- package/typedoc.json +4 -0
- package/dist/Group.test.d.ts +0 -1
- package/dist/Group.test.js +0 -48
- package/dist/Group.test.js.map +0 -1
- package/dist/User.test.d.ts +0 -1
- package/dist/User.test.js +0 -51
- package/dist/User.test.js.map +0 -1
- package/dist/conf/d.d.ts +0 -55
- package/dist/conf/d.js +0 -3
- package/dist/conf/d.js.map +0 -1
- package/src/Group.test.ts +0 -58
- package/src/User.test.ts +0 -61
- package/src/conf/d.ts +0 -303
package/src/Tilmeld.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { URL } from 'node:url';
|
|
1
2
|
import {
|
|
2
3
|
Nymph,
|
|
3
4
|
EntityInterface,
|
|
@@ -5,23 +6,26 @@ import {
|
|
|
5
6
|
Options,
|
|
6
7
|
TilmeldInterface,
|
|
7
8
|
TilmeldAccessLevels,
|
|
9
|
+
TilmeldAccessRequest,
|
|
10
|
+
ACProperties,
|
|
8
11
|
} from '@nymphjs/nymph';
|
|
9
|
-
import { URL } from 'url';
|
|
10
12
|
import { Request, Response } from 'express';
|
|
11
|
-
import { xor } from 'lodash';
|
|
13
|
+
import { xor } from 'lodash-es';
|
|
14
|
+
import { splitn } from '@sciactive/splitn';
|
|
12
15
|
|
|
13
|
-
import { Config, ConfigDefaults as defaults } from './conf';
|
|
14
|
-
import { AccessControlError } from './errors';
|
|
15
|
-
import Group from './Group';
|
|
16
|
-
import User, { UserData } from './User';
|
|
17
|
-
import { AccessControlData } from './Tilmeld.types';
|
|
16
|
+
import { Config, ConfigDefaults as defaults } from './conf/index.js';
|
|
17
|
+
import { AccessControlError } from './errors/index.js';
|
|
18
|
+
import Group, { GroupData } from './Group.js';
|
|
19
|
+
import User, { UserData } from './User.js';
|
|
20
|
+
import { AccessControlData } from './Tilmeld.types.js';
|
|
21
|
+
import { enforceTilmeld } from './enforceTilmeld.js';
|
|
18
22
|
|
|
19
23
|
/**
|
|
20
24
|
* A user and group system for Nymph.js.
|
|
21
25
|
*
|
|
22
26
|
* Written by Hunter Perrin for SciActive.
|
|
23
27
|
*
|
|
24
|
-
* @author Hunter Perrin <hperrin@
|
|
28
|
+
* @author Hunter Perrin <hperrin@port87.com>
|
|
25
29
|
* @copyright SciActive Inc
|
|
26
30
|
* @see http://nymph.io/
|
|
27
31
|
*/
|
|
@@ -69,9 +73,17 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
69
73
|
public response: Response | null = null;
|
|
70
74
|
|
|
71
75
|
/**
|
|
72
|
-
*
|
|
76
|
+
* Used to avoid infinite loop.
|
|
73
77
|
*/
|
|
74
|
-
private
|
|
78
|
+
private alreadyLoggedOutSwitch = false;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Gatekeeper ability cache.
|
|
82
|
+
*
|
|
83
|
+
* Gatekeeper will cache the user's abilities that it calculates, so it can
|
|
84
|
+
* check faster if that user has been checked before.
|
|
85
|
+
*/
|
|
86
|
+
private gatekeeperCache: { [k: string]: true } | null = null;
|
|
75
87
|
|
|
76
88
|
/**
|
|
77
89
|
* Create a new instance of Tilmeld.
|
|
@@ -80,6 +92,30 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
80
92
|
*/
|
|
81
93
|
public constructor(config: Partial<Config> & { jwtSecret: string }) {
|
|
82
94
|
this.config = { ...defaults, ...config };
|
|
95
|
+
|
|
96
|
+
if (
|
|
97
|
+
this.config.emailUsernames &&
|
|
98
|
+
(!this.config.userFields.includes('email') ||
|
|
99
|
+
!this.config.regFields.includes('email'))
|
|
100
|
+
) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
'You must include email in userFields and regFields if you use emailUsernames.',
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
for (let field of this.config.regFields) {
|
|
107
|
+
if (!this.config.userFields.includes(field)) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`All fields in regFields must also be in userFields: ${field}`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (this.config.emailUsernames && this.config.domainSupport) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
'Email usernames and domain support cannot both be enabled.',
|
|
117
|
+
);
|
|
118
|
+
}
|
|
83
119
|
}
|
|
84
120
|
|
|
85
121
|
/**
|
|
@@ -92,7 +128,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
92
128
|
tilmeld.request = this.request;
|
|
93
129
|
tilmeld.response = this.response;
|
|
94
130
|
tilmeld.currentUser = this.currentUser;
|
|
95
|
-
tilmeld.
|
|
131
|
+
tilmeld.gatekeeperCache = this.gatekeeperCache;
|
|
96
132
|
|
|
97
133
|
return tilmeld;
|
|
98
134
|
}
|
|
@@ -106,32 +142,47 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
106
142
|
*/
|
|
107
143
|
public init(nymph: Nymph) {
|
|
108
144
|
this.nymph = nymph;
|
|
109
|
-
const tilmeld = this;
|
|
110
145
|
|
|
111
|
-
// Set up access control hooks when Nymph is called.
|
|
112
146
|
if (this.nymph.driver == null) {
|
|
113
147
|
throw new Error("Tilmeld can't be configured before Nymph.");
|
|
114
148
|
}
|
|
115
149
|
|
|
116
150
|
// Configure the classes.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
151
|
+
if (nymph.parent) {
|
|
152
|
+
// This is a cloned Nymph, it should have classes already.
|
|
153
|
+
this.User = this.nymph.getEntityClass(User);
|
|
154
|
+
this.Group = this.nymph.getEntityClass(Group);
|
|
155
|
+
} else {
|
|
156
|
+
// This is a new Nymph, add the classes.
|
|
157
|
+
this.User = this.nymph.addEntityClass(User);
|
|
158
|
+
this.Group = this.nymph.addEntityClass(Group);
|
|
120
159
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
160
|
+
|
|
161
|
+
// Clone the current user so its Nymph instance is correct.
|
|
162
|
+
if (this.currentUser) {
|
|
163
|
+
const currentUserGUID = this.currentUser.guid;
|
|
164
|
+
const currentUserCDate = this.currentUser.cdate;
|
|
165
|
+
const currentUserMDate = this.currentUser.mdate;
|
|
166
|
+
const currentUserTags = this.currentUser.tags;
|
|
167
|
+
const currentUserData = this.currentUser.$getData();
|
|
168
|
+
const currentUserSData = this.currentUser.$getSData();
|
|
169
|
+
|
|
170
|
+
const user = this.User.factorySync();
|
|
171
|
+
user.guid = currentUserGUID;
|
|
172
|
+
user.cdate = currentUserCDate;
|
|
173
|
+
user.mdate = currentUserMDate;
|
|
174
|
+
user.tags = currentUserTags;
|
|
175
|
+
user.$putData(currentUserData, currentUserSData);
|
|
176
|
+
user.$originalEmail = this.currentUser.$originalEmail;
|
|
177
|
+
user.$originalUsername = this.currentUser.$originalUsername;
|
|
178
|
+
this.currentUser = user;
|
|
179
|
+
this.currentUser.$updateDataProtection();
|
|
180
|
+
this.currentUser.$updateGroupDataProtection();
|
|
126
181
|
}
|
|
127
|
-
(NymphUser as any).skipOnClone = true;
|
|
128
|
-
this.Group = this.nymph.addEntityClass(NymphGroup);
|
|
129
182
|
|
|
183
|
+
// Set up access control hooks when Nymph is called.
|
|
184
|
+
this.nymph.config.debugInfo('tilmeld', 'Initializing access controls.');
|
|
130
185
|
this.initAccessControl();
|
|
131
|
-
if (this.request != null && !this.skipAuthenticate) {
|
|
132
|
-
this.authenticate();
|
|
133
|
-
}
|
|
134
|
-
this.skipAuthenticate = false;
|
|
135
186
|
}
|
|
136
187
|
|
|
137
188
|
/**
|
|
@@ -143,11 +194,17 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
143
194
|
* @param ability The ability.
|
|
144
195
|
* @returns Whether the user has the given ability.
|
|
145
196
|
*/
|
|
146
|
-
public gatekeeper(ability?: string)
|
|
147
|
-
if (this.currentUser == null) {
|
|
197
|
+
public gatekeeper(ability?: string) {
|
|
198
|
+
if (this.currentUser == null || this.gatekeeperCache == null) {
|
|
148
199
|
return false;
|
|
149
200
|
}
|
|
150
|
-
|
|
201
|
+
if (ability == null) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
return (
|
|
205
|
+
(ability in this.gatekeeperCache && !!this.gatekeeperCache[ability]) ||
|
|
206
|
+
!!this.currentUser.abilities?.includes('system/admin')
|
|
207
|
+
);
|
|
151
208
|
}
|
|
152
209
|
|
|
153
210
|
private initAccessControl() {
|
|
@@ -155,9 +212,9 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
155
212
|
const handleQuery = function (
|
|
156
213
|
nymph: Nymph,
|
|
157
214
|
options: Options,
|
|
158
|
-
selectors: FormattedSelector[]
|
|
215
|
+
selectors: FormattedSelector[],
|
|
159
216
|
) {
|
|
160
|
-
const tilmeld = nymph
|
|
217
|
+
const tilmeld = enforceTilmeld(nymph);
|
|
161
218
|
if (
|
|
162
219
|
!options ||
|
|
163
220
|
!('skipAc' in options) ||
|
|
@@ -166,13 +223,17 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
166
223
|
) {
|
|
167
224
|
if (options?.source === 'client') {
|
|
168
225
|
if (
|
|
169
|
-
!tilmeld.
|
|
226
|
+
(!tilmeld.currentUser ||
|
|
227
|
+
(!tilmeld.currentUser.abilities?.includes('tilmeld/admin') &&
|
|
228
|
+
!tilmeld.currentUser.abilities?.includes('system/admin'))) && // The user is not an admin.
|
|
170
229
|
((!tilmeld.config.enableUserSearch &&
|
|
171
230
|
options.class?.factorySync() instanceof User) || // The use is searching for a user and searching is disabled.
|
|
172
231
|
(!tilmeld.config.enableGroupSearch &&
|
|
173
232
|
options.class?.factorySync() instanceof Group)) && // Or the same, but for a group.
|
|
174
|
-
(selectors
|
|
233
|
+
(selectors.length > 1 || // There's more than one selector.
|
|
234
|
+
selectors[0] == null || // Or no selector specified.
|
|
175
235
|
selectors[0].type !== '&' || // Or the type is not and.
|
|
236
|
+
Object.keys(selectors[0]).length > 2 || // Or there's more than one clause.
|
|
176
237
|
(!('guid' in selectors[0]) && !('equal' in selectors[0])) || // Or they're searching for something other than guid or equal.
|
|
177
238
|
('guid' in selectors[0] &&
|
|
178
239
|
(!selectors[0].guid?.length ||
|
|
@@ -195,9 +256,9 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
195
256
|
// Filter entities being deleted for user permissions.
|
|
196
257
|
const checkPermissionsDelete = async function (
|
|
197
258
|
nymph: Nymph,
|
|
198
|
-
entity: EntityInterface
|
|
259
|
+
entity: EntityInterface,
|
|
199
260
|
) {
|
|
200
|
-
const tilmeld = nymph
|
|
261
|
+
const tilmeld = enforceTilmeld(nymph);
|
|
201
262
|
if (
|
|
202
263
|
typeof entity.$tilmeldDeleteSkipAC === 'function' &&
|
|
203
264
|
entity.$tilmeldDeleteSkipAC()
|
|
@@ -215,11 +276,14 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
215
276
|
const checkPermissionsDeleteByID = async function (
|
|
216
277
|
nymph: Nymph,
|
|
217
278
|
guid: string,
|
|
218
|
-
className?: string
|
|
279
|
+
className?: string,
|
|
219
280
|
) {
|
|
220
|
-
|
|
281
|
+
if (className === 'User' || className === 'Group') {
|
|
282
|
+
throw new AccessControlError("You can't delete users or groups by ID.");
|
|
283
|
+
}
|
|
284
|
+
const entity = await nymph.getEntity(
|
|
221
285
|
{ class: nymph.getEntityClass(className ?? 'Entity') },
|
|
222
|
-
{ type: '&', guid: guid }
|
|
286
|
+
{ type: '&', guid: guid },
|
|
223
287
|
);
|
|
224
288
|
if (entity != null) {
|
|
225
289
|
checkPermissionsDelete(nymph, entity);
|
|
@@ -231,9 +295,9 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
231
295
|
// disallowed changes to AC properties.
|
|
232
296
|
const checkPermissionsSaveAndFilterAcChanges = async function (
|
|
233
297
|
nymph: Nymph,
|
|
234
|
-
entity: EntityInterface & AccessControlData
|
|
298
|
+
entity: EntityInterface & AccessControlData,
|
|
235
299
|
) {
|
|
236
|
-
const tilmeld = nymph
|
|
300
|
+
const tilmeld = enforceTilmeld(nymph);
|
|
237
301
|
if (!entity) {
|
|
238
302
|
return;
|
|
239
303
|
}
|
|
@@ -249,35 +313,9 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
249
313
|
// allowing a change to ac properties.
|
|
250
314
|
|
|
251
315
|
const originalAc = entity.$getOriginalAcValues();
|
|
252
|
-
const newAc
|
|
253
|
-
user: entity.user ?? undefined,
|
|
254
|
-
group: entity.group ?? undefined,
|
|
255
|
-
acUser: entity.acUser ?? undefined,
|
|
256
|
-
acGroup: entity.acGroup ?? undefined,
|
|
257
|
-
acOther: entity.acOther ?? undefined,
|
|
258
|
-
acRead: entity.acRead ?? undefined,
|
|
259
|
-
acWrite: entity.acWrite ?? undefined,
|
|
260
|
-
acFull: entity.acFull ?? undefined,
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
const setAcProperties = (acValues: { [k: string]: any }) => {
|
|
264
|
-
for (let name in acValues) {
|
|
265
|
-
const value = acValues[name];
|
|
266
|
-
if (value == null) {
|
|
267
|
-
delete entity[name];
|
|
268
|
-
} else {
|
|
269
|
-
entity[name] = value;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
// Restore original AC properties and check permissions.
|
|
275
|
-
setAcProperties(originalAc);
|
|
316
|
+
const newAc = entity.$getCurrentAcValues();
|
|
276
317
|
|
|
277
|
-
function areAcPropertiesChanged(
|
|
278
|
-
a: AccessControlData,
|
|
279
|
-
b: AccessControlData
|
|
280
|
-
) {
|
|
318
|
+
function areAcPropertiesChanged(a: ACProperties, b: ACProperties) {
|
|
281
319
|
for (const name of ['user', 'group'] as ('user' | 'group')[]) {
|
|
282
320
|
const aVal = a[name];
|
|
283
321
|
const bVal = b[name];
|
|
@@ -286,14 +324,12 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
286
324
|
} else if (aVal == null || bVal == null) {
|
|
287
325
|
return true;
|
|
288
326
|
}
|
|
289
|
-
if (
|
|
327
|
+
if (aVal !== bVal) {
|
|
290
328
|
return true;
|
|
291
329
|
}
|
|
292
330
|
}
|
|
293
331
|
for (const name of ['acUser', 'acGroup', 'acOther'] as (
|
|
294
|
-
| '
|
|
295
|
-
| 'acGroup'
|
|
296
|
-
| 'acOther'
|
|
332
|
+
'acUser' | 'acGroup' | 'acOther'
|
|
297
333
|
)[]) {
|
|
298
334
|
const aVal = a[name];
|
|
299
335
|
const bVal = b[name];
|
|
@@ -307,9 +343,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
307
343
|
}
|
|
308
344
|
}
|
|
309
345
|
for (const name of ['acRead', 'acWrite', 'acFull'] as (
|
|
310
|
-
| '
|
|
311
|
-
| 'acWrite'
|
|
312
|
-
| 'acFull'
|
|
346
|
+
'acRead' | 'acWrite' | 'acFull'
|
|
313
347
|
)[]) {
|
|
314
348
|
const aVal = a[name];
|
|
315
349
|
const bVal = b[name];
|
|
@@ -321,12 +355,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
321
355
|
if (aVal.length !== bVal.length) {
|
|
322
356
|
return true;
|
|
323
357
|
}
|
|
324
|
-
if (
|
|
325
|
-
xor(
|
|
326
|
-
aVal.map((entity) => entity.guid),
|
|
327
|
-
bVal.map((entity) => entity.guid)
|
|
328
|
-
).length
|
|
329
|
-
) {
|
|
358
|
+
if (xor(aVal, bVal).length) {
|
|
330
359
|
return true;
|
|
331
360
|
}
|
|
332
361
|
}
|
|
@@ -335,41 +364,42 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
335
364
|
|
|
336
365
|
if (areAcPropertiesChanged(originalAc, newAc)) {
|
|
337
366
|
if (
|
|
338
|
-
!tilmeld.checkPermissions(
|
|
367
|
+
!tilmeld.checkPermissions(
|
|
368
|
+
entity,
|
|
369
|
+
TilmeldAccessLevels.FULL_ACCESS,
|
|
370
|
+
undefined,
|
|
371
|
+
originalAc,
|
|
372
|
+
)
|
|
339
373
|
) {
|
|
340
374
|
// Only allow changes to AC properties if the user has full access.
|
|
341
375
|
throw new AccessControlError(
|
|
342
|
-
'No permission to change access control properties.'
|
|
376
|
+
'No permission to change access control properties.',
|
|
343
377
|
);
|
|
344
378
|
}
|
|
345
379
|
|
|
346
380
|
if (
|
|
347
|
-
(
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
!tilmeld.gatekeeper('tilmeld/admin')
|
|
381
|
+
(originalAc.user != null && originalAc.user !== newAc.user) ||
|
|
382
|
+
(originalAc.user == null &&
|
|
383
|
+
newAc.user != null &&
|
|
384
|
+
newAc.user !== tilmeld.currentUser?.guid)
|
|
352
385
|
) {
|
|
353
386
|
throw new AccessControlError(
|
|
354
|
-
'No permission to assign to another user.'
|
|
387
|
+
'No permission to assign to another user.',
|
|
355
388
|
);
|
|
356
389
|
}
|
|
357
390
|
|
|
358
391
|
if (
|
|
359
|
-
newAc.group &&
|
|
360
|
-
|
|
392
|
+
newAc.group != null &&
|
|
393
|
+
newAc.group !== originalAc.group &&
|
|
361
394
|
!(
|
|
362
|
-
newAc.group
|
|
363
|
-
|
|
364
|
-
)
|
|
365
|
-
!tilmeld.gatekeeper('tilmeld/admin')
|
|
395
|
+
newAc.group === tilmeld.currentUser?.$getGid() ||
|
|
396
|
+
(tilmeld.currentUser?.$getGids() ?? []).includes(newAc.group)
|
|
397
|
+
)
|
|
366
398
|
) {
|
|
367
399
|
throw new AccessControlError(
|
|
368
|
-
'No permission to assign to another group.'
|
|
400
|
+
'No permission to assign to another group.',
|
|
369
401
|
);
|
|
370
402
|
}
|
|
371
|
-
|
|
372
|
-
setAcProperties(newAc);
|
|
373
403
|
}
|
|
374
404
|
}
|
|
375
405
|
|
|
@@ -398,9 +428,9 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
398
428
|
*/
|
|
399
429
|
const addAccess = async function (
|
|
400
430
|
nymph: Nymph,
|
|
401
|
-
entity: EntityInterface & AccessControlData
|
|
431
|
+
entity: EntityInterface & AccessControlData,
|
|
402
432
|
) {
|
|
403
|
-
const tilmeld = nymph
|
|
433
|
+
const tilmeld = enforceTilmeld(nymph);
|
|
404
434
|
const user = tilmeld.currentUser;
|
|
405
435
|
if (
|
|
406
436
|
user != null &&
|
|
@@ -411,12 +441,11 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
411
441
|
if (entity.user == null) {
|
|
412
442
|
entity.user = user;
|
|
413
443
|
}
|
|
414
|
-
if (
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
entity.group = user.group;
|
|
444
|
+
if (entity.group == null) {
|
|
445
|
+
const group = user.group;
|
|
446
|
+
if (group != null && group.guid != null) {
|
|
447
|
+
entity.group = group;
|
|
448
|
+
}
|
|
420
449
|
}
|
|
421
450
|
if (entity.acUser == null) {
|
|
422
451
|
entity.acUser = TilmeldAccessLevels.FULL_ACCESS;
|
|
@@ -442,7 +471,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
442
471
|
|
|
443
472
|
const validate = async function (
|
|
444
473
|
_nymph: Nymph,
|
|
445
|
-
entity: EntityInterface & AccessControlData
|
|
474
|
+
entity: EntityInterface & AccessControlData,
|
|
446
475
|
) {
|
|
447
476
|
if (!(entity instanceof User) && !(entity instanceof Group)) {
|
|
448
477
|
const ownershipAcPropertyValidator = (prop: any) => {
|
|
@@ -452,7 +481,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
452
481
|
prop > TilmeldAccessLevels.FULL_ACCESS
|
|
453
482
|
) {
|
|
454
483
|
throw new AccessControlError(
|
|
455
|
-
'Invalid access control property: ' + prop
|
|
484
|
+
'Invalid access control property: ' + prop,
|
|
456
485
|
);
|
|
457
486
|
}
|
|
458
487
|
};
|
|
@@ -460,13 +489,13 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
460
489
|
const accessAcPropertyValidator = (prop: any) => {
|
|
461
490
|
if (!Array.isArray(prop)) {
|
|
462
491
|
throw new AccessControlError(
|
|
463
|
-
'Invalid access control property: ' + prop
|
|
492
|
+
'Invalid access control property: ' + prop,
|
|
464
493
|
);
|
|
465
494
|
}
|
|
466
495
|
prop.forEach((value) => {
|
|
467
|
-
if (
|
|
496
|
+
if (typeof value !== 'string' || !value.match(/^[0-9a-f]{24}$/i)) {
|
|
468
497
|
throw new AccessControlError(
|
|
469
|
-
'Invalid access control property: ' + prop
|
|
498
|
+
'Invalid access control property: ' + prop,
|
|
470
499
|
);
|
|
471
500
|
}
|
|
472
501
|
});
|
|
@@ -475,7 +504,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
475
504
|
if ('user' in entity) {
|
|
476
505
|
if (!(entity.user instanceof User)) {
|
|
477
506
|
throw new AccessControlError(
|
|
478
|
-
'Invalid access control property: user'
|
|
507
|
+
'Invalid access control property: user',
|
|
479
508
|
);
|
|
480
509
|
}
|
|
481
510
|
}
|
|
@@ -483,7 +512,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
483
512
|
if ('group' in entity) {
|
|
484
513
|
if (!(entity.group instanceof Group)) {
|
|
485
514
|
throw new AccessControlError(
|
|
486
|
-
'Invalid access control property: group'
|
|
515
|
+
'Invalid access control property: group',
|
|
487
516
|
);
|
|
488
517
|
}
|
|
489
518
|
}
|
|
@@ -528,18 +557,10 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
528
557
|
*/
|
|
529
558
|
public addAccessControlSelectors(
|
|
530
559
|
options: Options,
|
|
531
|
-
selectors: FormattedSelector[]
|
|
560
|
+
selectors: FormattedSelector[],
|
|
532
561
|
) {
|
|
533
562
|
const user = this.currentUser;
|
|
534
563
|
|
|
535
|
-
if (
|
|
536
|
-
user != null &&
|
|
537
|
-
(user.abilities?.indexOf('system/admin') ?? -1) !== -1
|
|
538
|
-
) {
|
|
539
|
-
// The user is a system admin, so they can see everything.
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
564
|
if (options == null) {
|
|
544
565
|
throw new Error('No options in argument.');
|
|
545
566
|
} else if (
|
|
@@ -552,59 +573,112 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
552
573
|
}
|
|
553
574
|
|
|
554
575
|
if (user == null || user.guid == null) {
|
|
555
|
-
|
|
556
|
-
|
|
576
|
+
// Access requests for only public entities.
|
|
577
|
+
|
|
578
|
+
const allAccessRequests =
|
|
579
|
+
TilmeldAccessRequest.OTHER_ACCESSIBLE | TilmeldAccessRequest.UNOWNED;
|
|
580
|
+
const requestedAccess = (options.acRequest ?? 0) & allAccessRequests;
|
|
581
|
+
const defaultAccess = requestedAccess === TilmeldAccessRequest.ALL_LEVELS;
|
|
582
|
+
|
|
583
|
+
const selector: FormattedSelector = { type: '|' };
|
|
584
|
+
|
|
585
|
+
if (
|
|
586
|
+
defaultAccess ||
|
|
587
|
+
requestedAccess & TilmeldAccessRequest.OTHER_ACCESSIBLE
|
|
588
|
+
) {
|
|
557
589
|
// Other access control is sufficient.
|
|
558
|
-
gte
|
|
590
|
+
selector.gte = [['acOther', TilmeldAccessLevels.READ_ACCESS]];
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (defaultAccess || requestedAccess & TilmeldAccessRequest.UNOWNED) {
|
|
559
594
|
// The user and group are not set.
|
|
560
|
-
selector
|
|
595
|
+
selector.selector = [
|
|
561
596
|
{
|
|
562
597
|
type: '&',
|
|
563
598
|
'!defined': [['user', 'group']],
|
|
564
599
|
},
|
|
565
|
-
]
|
|
566
|
-
}
|
|
600
|
+
];
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (Object.keys(selector).length <= 1) {
|
|
604
|
+
this.nymph.config.debugError(
|
|
605
|
+
'tilmeld',
|
|
606
|
+
'No access controls were added to the query. This should not be possible.',
|
|
607
|
+
);
|
|
608
|
+
throw new Error('Access control error.');
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
selectors.push(selector);
|
|
567
612
|
} else {
|
|
568
|
-
|
|
569
|
-
|
|
613
|
+
// Access requests for public and private entities.
|
|
614
|
+
|
|
615
|
+
const allAccessRequests =
|
|
616
|
+
TilmeldAccessRequest.USER_OWNED |
|
|
617
|
+
TilmeldAccessRequest.PRIMARY_GROUP_OWNED |
|
|
618
|
+
TilmeldAccessRequest.SECONDARY_GROUP_OWNED |
|
|
619
|
+
TilmeldAccessRequest.USER_ACCESSIBLE |
|
|
620
|
+
TilmeldAccessRequest.PRIMARY_GROUP_ACCESSIBLE |
|
|
621
|
+
TilmeldAccessRequest.SECONDARY_GROUP_ACCESSIBLE |
|
|
622
|
+
TilmeldAccessRequest.OTHER_ACCESSIBLE |
|
|
623
|
+
TilmeldAccessRequest.UNOWNED;
|
|
624
|
+
const requestedAccess = (options.acRequest ?? 0) & allAccessRequests;
|
|
625
|
+
const defaultAccess = requestedAccess === TilmeldAccessRequest.ALL_LEVELS;
|
|
626
|
+
|
|
627
|
+
const subSelectors: FormattedSelector[] = [];
|
|
628
|
+
if (defaultAccess || requestedAccess & TilmeldAccessRequest.UNOWNED) {
|
|
629
|
+
subSelectors.push({
|
|
570
630
|
type: '&',
|
|
631
|
+
// The user and group are not set.
|
|
571
632
|
'!defined': [['user', 'group']],
|
|
572
|
-
}
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
// USER_OWNED is always enforced.
|
|
636
|
+
subSelectors.push(
|
|
573
637
|
// It is owned by the user.
|
|
574
638
|
{
|
|
575
639
|
type: '&',
|
|
576
640
|
ref: [['user', user.guid]],
|
|
577
641
|
gte: [['acUser', TilmeldAccessLevels.READ_ACCESS]],
|
|
578
642
|
},
|
|
579
|
-
|
|
643
|
+
);
|
|
580
644
|
const groupRefs: FormattedSelector['ref'] = [];
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
if (
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
645
|
+
const acContains: FormattedSelector['contain'] = [];
|
|
646
|
+
const gid = user.$getGid();
|
|
647
|
+
if (gid != null) {
|
|
648
|
+
if (
|
|
649
|
+
defaultAccess ||
|
|
650
|
+
requestedAccess & TilmeldAccessRequest.PRIMARY_GROUP_OWNED
|
|
651
|
+
) {
|
|
652
|
+
// It belongs to the user's primary group.
|
|
653
|
+
groupRefs.push(['group', gid]);
|
|
654
|
+
}
|
|
655
|
+
if (
|
|
656
|
+
defaultAccess ||
|
|
657
|
+
requestedAccess & TilmeldAccessRequest.PRIMARY_GROUP_ACCESSIBLE
|
|
658
|
+
) {
|
|
659
|
+
// User's primary group guid is listed in acRead, acWrite, or acFull.
|
|
660
|
+
acContains.push(['acRead', gid]);
|
|
661
|
+
acContains.push(['acWrite', gid]);
|
|
662
|
+
acContains.push(['acFull', gid]);
|
|
598
663
|
}
|
|
599
664
|
}
|
|
600
|
-
|
|
601
|
-
|
|
665
|
+
const gids = user.$getGids();
|
|
666
|
+
for (let curGid of gids ?? []) {
|
|
667
|
+
if (
|
|
668
|
+
defaultAccess ||
|
|
669
|
+
requestedAccess & TilmeldAccessRequest.SECONDARY_GROUP_OWNED
|
|
670
|
+
) {
|
|
602
671
|
// It belongs to the user's secondary group.
|
|
603
|
-
groupRefs.push(['group',
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
672
|
+
groupRefs.push(['group', curGid]);
|
|
673
|
+
}
|
|
674
|
+
if (
|
|
675
|
+
defaultAccess ||
|
|
676
|
+
requestedAccess & TilmeldAccessRequest.SECONDARY_GROUP_ACCESSIBLE
|
|
677
|
+
) {
|
|
678
|
+
// User's secondary group guid is listed in acRead, acWrite, or acFull.
|
|
679
|
+
acContains.push(['acRead', curGid]);
|
|
680
|
+
acContains.push(['acWrite', curGid]);
|
|
681
|
+
acContains.push(['acFull', curGid]);
|
|
608
682
|
}
|
|
609
683
|
}
|
|
610
684
|
// All the group refs.
|
|
@@ -612,34 +686,43 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
612
686
|
subSelectors.push({
|
|
613
687
|
type: '&',
|
|
614
688
|
gte: [['acGroup', TilmeldAccessLevels.READ_ACCESS]],
|
|
615
|
-
selector: [
|
|
616
|
-
{
|
|
617
|
-
type: '|',
|
|
618
|
-
ref: groupRefs,
|
|
619
|
-
},
|
|
620
|
-
],
|
|
689
|
+
selector: [{ type: '|', ref: groupRefs }],
|
|
621
690
|
});
|
|
622
691
|
}
|
|
623
|
-
// All the acRead, acWrite, and acFull
|
|
624
|
-
if (
|
|
625
|
-
subSelectors.push({
|
|
626
|
-
type: '|',
|
|
627
|
-
ref: acRefs,
|
|
628
|
-
});
|
|
692
|
+
// All the acRead, acWrite, and acFull contains.
|
|
693
|
+
if (acContains.length) {
|
|
694
|
+
subSelectors.push({ type: '|', contain: acContains });
|
|
629
695
|
}
|
|
630
|
-
const selector: FormattedSelector = {
|
|
631
|
-
|
|
696
|
+
const selector: FormattedSelector = { type: '|' };
|
|
697
|
+
if (
|
|
698
|
+
defaultAccess ||
|
|
699
|
+
requestedAccess & TilmeldAccessRequest.OTHER_ACCESSIBLE
|
|
700
|
+
) {
|
|
632
701
|
// Other access control is sufficient.
|
|
633
|
-
gte
|
|
634
|
-
|
|
635
|
-
|
|
702
|
+
selector.gte = [['acOther', TilmeldAccessLevels.READ_ACCESS]];
|
|
703
|
+
}
|
|
704
|
+
if (subSelectors.length) {
|
|
705
|
+
// One of the sub selectors.
|
|
706
|
+
selector.selector = subSelectors;
|
|
707
|
+
}
|
|
708
|
+
if (
|
|
709
|
+
defaultAccess ||
|
|
710
|
+
requestedAccess & TilmeldAccessRequest.USER_ACCESSIBLE
|
|
711
|
+
) {
|
|
636
712
|
// The user is listed in acRead, acWrite, or acFull.
|
|
637
|
-
|
|
713
|
+
selector.contain = [
|
|
638
714
|
['acRead', user.guid],
|
|
639
715
|
['acWrite', user.guid],
|
|
640
716
|
['acFull', user.guid],
|
|
641
|
-
]
|
|
642
|
-
}
|
|
717
|
+
];
|
|
718
|
+
}
|
|
719
|
+
if (Object.keys(selector).length <= 1) {
|
|
720
|
+
this.nymph.config.debugError(
|
|
721
|
+
'tilmeld',
|
|
722
|
+
'No access controls were added to the query. This should not be possible.',
|
|
723
|
+
);
|
|
724
|
+
throw new Error('Access control error.');
|
|
725
|
+
}
|
|
643
726
|
selectors.push(selector);
|
|
644
727
|
}
|
|
645
728
|
}
|
|
@@ -671,7 +754,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
671
754
|
* - FULL_ACCESS, the user has read, write, and delete access to the entity,
|
|
672
755
|
* as well as being able to manage its access controls and ownership.
|
|
673
756
|
*
|
|
674
|
-
* These properties
|
|
757
|
+
* These properties default to:
|
|
675
758
|
*
|
|
676
759
|
* - acUser = TilmeldAccessLevels.FULL_ACCESS
|
|
677
760
|
* - acGroup = TilmeldAccessLevels.READ_ACCESS
|
|
@@ -686,9 +769,9 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
686
769
|
* The following conditions will result in different checks, which determine
|
|
687
770
|
* whether the check passes:
|
|
688
771
|
*
|
|
689
|
-
* - The user has the "system/admin" ability. (Always true.)
|
|
690
772
|
* - It is a user or group. (True for READ_ACCESS or Tilmeld admins.)
|
|
691
|
-
* -
|
|
773
|
+
* - It is a user or group that belongs to a domain the current user is an
|
|
774
|
+
* admin for, and domain support is enabled. (Always true.)
|
|
692
775
|
* - No user is logged in. (Check other AC.)
|
|
693
776
|
* - The entity is the user. (Always true.)
|
|
694
777
|
* - It is the user's primary group. (True for READ_ACCESS.)
|
|
@@ -706,16 +789,17 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
706
789
|
* @param entity The entity to check.
|
|
707
790
|
* @param type The lowest level of permission to consider a pass.
|
|
708
791
|
* @param user The user to check permissions for. If null, uses the current user. If false, checks for public access.
|
|
792
|
+
* @param acProperties The acProperties to use instead of getting them from the entity.
|
|
709
793
|
* @returns Whether the current user has at least `type` permission for the entity.
|
|
710
794
|
*/
|
|
711
795
|
public checkPermissions(
|
|
712
|
-
entity: EntityInterface
|
|
796
|
+
entity: EntityInterface,
|
|
713
797
|
type: TilmeldAccessLevels = TilmeldAccessLevels.READ_ACCESS,
|
|
714
|
-
user?: (User & UserData) | false
|
|
798
|
+
user?: (User & UserData) | false,
|
|
799
|
+
acProperties?: ACProperties,
|
|
715
800
|
) {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
return false;
|
|
801
|
+
if (!acProperties) {
|
|
802
|
+
acProperties = entity.$getCurrentAcValues() as ACProperties;
|
|
719
803
|
}
|
|
720
804
|
|
|
721
805
|
let userOrNull: (User & UserData) | null = null;
|
|
@@ -729,30 +813,43 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
729
813
|
userOrEmpty = user;
|
|
730
814
|
}
|
|
731
815
|
|
|
732
|
-
if (userOrEmpty.$gatekeeper('system/admin')) {
|
|
733
|
-
return true;
|
|
734
|
-
}
|
|
735
|
-
|
|
736
816
|
// Users and groups are always readable. Editable by Tilmeld admins.
|
|
737
817
|
if (
|
|
738
818
|
(entity instanceof User || entity instanceof Group) &&
|
|
739
819
|
(type === TilmeldAccessLevels.READ_ACCESS ||
|
|
740
|
-
userOrEmpty
|
|
820
|
+
userOrEmpty.abilities?.includes('tilmeld/admin') ||
|
|
821
|
+
userOrEmpty.abilities?.includes('system/admin'))
|
|
741
822
|
) {
|
|
742
823
|
return true;
|
|
743
824
|
}
|
|
744
825
|
|
|
745
|
-
//
|
|
746
|
-
|
|
747
|
-
|
|
826
|
+
// Users and groups that belong to a domain are readable and editable by
|
|
827
|
+
// that domain's admins.
|
|
828
|
+
if (
|
|
829
|
+
this.config.domainSupport &&
|
|
830
|
+
(entity instanceof User || entity instanceof Group)
|
|
831
|
+
) {
|
|
832
|
+
const [_local, domain] = splitn(
|
|
833
|
+
(entity instanceof User
|
|
834
|
+
? (entity as User & UserData).username
|
|
835
|
+
: (entity as Group & GroupData).groupname) ?? '',
|
|
836
|
+
'@',
|
|
837
|
+
2,
|
|
838
|
+
);
|
|
839
|
+
if (
|
|
840
|
+
domain &&
|
|
841
|
+
userOrEmpty.abilities?.includes(`tilmeld/domain/${domain}/admin`)
|
|
842
|
+
) {
|
|
843
|
+
return true;
|
|
844
|
+
}
|
|
748
845
|
}
|
|
749
846
|
|
|
750
847
|
// Load access control, since we need it now...
|
|
751
|
-
const acUser =
|
|
752
|
-
const acGroup =
|
|
753
|
-
const acOther =
|
|
848
|
+
const acUser = acProperties.acUser ?? TilmeldAccessLevels.FULL_ACCESS;
|
|
849
|
+
const acGroup = acProperties.acGroup ?? TilmeldAccessLevels.READ_ACCESS;
|
|
850
|
+
const acOther = acProperties.acOther ?? TilmeldAccessLevels.NO_ACCESS;
|
|
754
851
|
|
|
755
|
-
if (userOrNull
|
|
852
|
+
if (userOrNull == null) {
|
|
756
853
|
return acOther >= type;
|
|
757
854
|
}
|
|
758
855
|
|
|
@@ -762,45 +859,59 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
762
859
|
}
|
|
763
860
|
|
|
764
861
|
// Check if the entity is the user's group. Always readable.
|
|
862
|
+
const gid = userOrEmpty.$getGid();
|
|
765
863
|
if (
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
userOrEmpty.group.$is(entity) &&
|
|
864
|
+
gid != null &&
|
|
865
|
+
entity.guid === gid &&
|
|
769
866
|
type === TilmeldAccessLevels.READ_ACCESS
|
|
770
867
|
) {
|
|
771
868
|
return true;
|
|
772
869
|
}
|
|
773
870
|
|
|
871
|
+
// Check if the entity is one of the user's secondary groups. Always
|
|
872
|
+
// readable.
|
|
873
|
+
const gids = userOrEmpty.$getGids();
|
|
874
|
+
if (
|
|
875
|
+
gids != null &&
|
|
876
|
+
entity.guid != null &&
|
|
877
|
+
gids.indexOf(entity.guid) !== -1 &&
|
|
878
|
+
type === TilmeldAccessLevels.READ_ACCESS
|
|
879
|
+
) {
|
|
880
|
+
return true;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// At this point, if the entity is a user or group, it should not be
|
|
884
|
+
// accessible.
|
|
885
|
+
if (entity instanceof User || entity instanceof Group) {
|
|
886
|
+
return false;
|
|
887
|
+
}
|
|
888
|
+
|
|
774
889
|
// Calculate all the groups the user belongs to.
|
|
775
|
-
let
|
|
776
|
-
|
|
777
|
-
allGroups = allGroups.concat(userOrEmpty.$getDescendantGroupsSync());
|
|
890
|
+
let allGids = gid == null ? [] : [gid];
|
|
891
|
+
allGids = allGids.concat(gids ?? []);
|
|
778
892
|
|
|
779
893
|
// Check access ac properties.
|
|
780
894
|
const checks = [
|
|
781
895
|
{
|
|
782
896
|
type: TilmeldAccessLevels.FULL_ACCESS,
|
|
783
|
-
array: [...(
|
|
897
|
+
array: [...(acProperties.acFull ?? [])],
|
|
784
898
|
},
|
|
785
899
|
{
|
|
786
900
|
type: TilmeldAccessLevels.WRITE_ACCESS,
|
|
787
|
-
array: [...(
|
|
901
|
+
array: [...(acProperties.acWrite ?? [])],
|
|
788
902
|
},
|
|
789
903
|
{
|
|
790
904
|
type: TilmeldAccessLevels.READ_ACCESS,
|
|
791
|
-
array: [...(
|
|
905
|
+
array: [...(acProperties.acRead ?? [])],
|
|
792
906
|
},
|
|
793
907
|
];
|
|
794
908
|
for (let curCheck of checks) {
|
|
795
909
|
if (type <= curCheck.type) {
|
|
796
|
-
if (
|
|
910
|
+
if (curCheck.array.includes(userOrEmpty.guid)) {
|
|
797
911
|
return true;
|
|
798
912
|
}
|
|
799
|
-
for (let
|
|
800
|
-
if (
|
|
801
|
-
typeof curGroup.$inArray === 'function' &&
|
|
802
|
-
curGroup.$inArray(curCheck.array)
|
|
803
|
-
) {
|
|
913
|
+
for (let curGid of allGids) {
|
|
914
|
+
if (curCheck.array.includes(curGid)) {
|
|
804
915
|
return true;
|
|
805
916
|
}
|
|
806
917
|
}
|
|
@@ -808,18 +919,10 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
808
919
|
}
|
|
809
920
|
|
|
810
921
|
// Check ownership ac properties.
|
|
811
|
-
if (
|
|
812
|
-
entity.user != null &&
|
|
813
|
-
typeof entity.user.$is === 'function' &&
|
|
814
|
-
entity.user.$is(userOrNull)
|
|
815
|
-
) {
|
|
922
|
+
if (acProperties.user != null && acProperties.user === userOrNull.guid) {
|
|
816
923
|
return acUser >= type;
|
|
817
924
|
}
|
|
818
|
-
if (
|
|
819
|
-
entity.group != null &&
|
|
820
|
-
typeof entity.group.$inArray === 'function' &&
|
|
821
|
-
entity.group.$inArray(allGroups)
|
|
822
|
-
) {
|
|
925
|
+
if (acProperties.group != null && allGids.includes(acProperties.group)) {
|
|
823
926
|
return acGroup >= type;
|
|
824
927
|
}
|
|
825
928
|
return acOther >= type;
|
|
@@ -849,10 +952,10 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
849
952
|
* @param user The user to check permissions for. If null, uses the current user. If false, checks for public access.
|
|
850
953
|
* @returns Whether the current user has at least `type` permission for the UID.
|
|
851
954
|
*/
|
|
852
|
-
public checkClientUIDPermissions(
|
|
955
|
+
public async checkClientUIDPermissions(
|
|
853
956
|
name: string,
|
|
854
957
|
type: TilmeldAccessLevels = TilmeldAccessLevels.READ_ACCESS,
|
|
855
|
-
user?: (User & UserData) | false
|
|
958
|
+
user?: (User & UserData) | false,
|
|
856
959
|
) {
|
|
857
960
|
let userOrEmpty: User & UserData = this.User.factorySync();
|
|
858
961
|
// Calculate the user.
|
|
@@ -862,30 +965,30 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
862
965
|
userOrEmpty = user;
|
|
863
966
|
}
|
|
864
967
|
|
|
865
|
-
if (userOrEmpty
|
|
968
|
+
if (userOrEmpty.abilities?.includes('system/admin')) {
|
|
866
969
|
return true;
|
|
867
970
|
}
|
|
868
971
|
|
|
869
972
|
if (type === TilmeldAccessLevels.FULL_ACCESS) {
|
|
870
973
|
return (
|
|
871
|
-
this.config.clientSetabledUIDs.
|
|
872
|
-
userOrEmpty.$gatekeeper(`uid/set/${name}`)
|
|
974
|
+
this.config.clientSetabledUIDs.includes(name) ||
|
|
975
|
+
(await userOrEmpty.$gatekeeper(`uid/set/${name}`))
|
|
873
976
|
);
|
|
874
977
|
} else if (type === TilmeldAccessLevels.WRITE_ACCESS) {
|
|
875
978
|
return (
|
|
876
|
-
this.config.clientEnabledUIDs.
|
|
877
|
-
this.config.clientSetabledUIDs.
|
|
878
|
-
userOrEmpty.$gatekeeper(`uid/new/${name}`) ||
|
|
879
|
-
userOrEmpty.$gatekeeper(`uid/set/${name}`)
|
|
979
|
+
this.config.clientEnabledUIDs.includes(name) ||
|
|
980
|
+
this.config.clientSetabledUIDs.includes(name) ||
|
|
981
|
+
(await userOrEmpty.$gatekeeper(`uid/new/${name}`)) ||
|
|
982
|
+
(await userOrEmpty.$gatekeeper(`uid/set/${name}`))
|
|
880
983
|
);
|
|
881
984
|
} else if (type === TilmeldAccessLevels.READ_ACCESS) {
|
|
882
985
|
return (
|
|
883
|
-
this.config.clientReadableUIDs.
|
|
884
|
-
this.config.clientEnabledUIDs.
|
|
885
|
-
this.config.clientSetabledUIDs.
|
|
886
|
-
userOrEmpty.$gatekeeper(`uid/get/${name}`) ||
|
|
887
|
-
userOrEmpty.$gatekeeper(`uid/new/${name}`) ||
|
|
888
|
-
userOrEmpty.$gatekeeper(`uid/set/${name}`)
|
|
986
|
+
this.config.clientReadableUIDs.includes(name) ||
|
|
987
|
+
this.config.clientEnabledUIDs.includes(name) ||
|
|
988
|
+
this.config.clientSetabledUIDs.includes(name) ||
|
|
989
|
+
(await userOrEmpty.$gatekeeper(`uid/get/${name}`)) ||
|
|
990
|
+
(await userOrEmpty.$gatekeeper(`uid/new/${name}`)) ||
|
|
991
|
+
(await userOrEmpty.$gatekeeper(`uid/set/${name}`))
|
|
889
992
|
);
|
|
890
993
|
}
|
|
891
994
|
|
|
@@ -897,20 +1000,13 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
897
1000
|
*
|
|
898
1001
|
* @param user The user.
|
|
899
1002
|
*/
|
|
900
|
-
public fillSession(user: User & UserData) {
|
|
901
|
-
// Read groups right now, since gatekeeper needs them, so
|
|
902
|
-
// $udpateDataProtection will fail to read them (since it runs gatekeeper).
|
|
903
|
-
const _group = user.group;
|
|
904
|
-
const _groups = user.groups;
|
|
1003
|
+
public async fillSession(user: User & UserData) {
|
|
905
1004
|
this.currentUser = user;
|
|
1005
|
+
this.gatekeeperCache = await user.$getGatekeeperCache();
|
|
1006
|
+
|
|
906
1007
|
// Now update the data protection on the user and all the groups.
|
|
907
1008
|
this.currentUser.$updateDataProtection();
|
|
908
|
-
|
|
909
|
-
this.currentUser.group.$updateDataProtection();
|
|
910
|
-
}
|
|
911
|
-
for (let group of this.currentUser.groups ?? []) {
|
|
912
|
-
group.$updateDataProtection();
|
|
913
|
-
}
|
|
1009
|
+
this.currentUser.$updateGroupDataProtection();
|
|
914
1010
|
}
|
|
915
1011
|
|
|
916
1012
|
/**
|
|
@@ -919,14 +1015,10 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
919
1015
|
public clearSession() {
|
|
920
1016
|
const user = this.currentUser;
|
|
921
1017
|
this.currentUser = null;
|
|
1018
|
+
this.gatekeeperCache = null;
|
|
922
1019
|
if (user) {
|
|
923
1020
|
user.$updateDataProtection();
|
|
924
|
-
|
|
925
|
-
user.group.$updateDataProtection();
|
|
926
|
-
}
|
|
927
|
-
for (let group of user.groups ?? []) {
|
|
928
|
-
group.$updateDataProtection();
|
|
929
|
-
}
|
|
1021
|
+
user.$updateGroupDataProtection();
|
|
930
1022
|
}
|
|
931
1023
|
}
|
|
932
1024
|
|
|
@@ -937,6 +1029,7 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
937
1029
|
* @returns The user on success, null on failure.
|
|
938
1030
|
*/
|
|
939
1031
|
public async extractToken(token: string) {
|
|
1032
|
+
this.nymph.config.debugInfo('tilmeld', 'Extracting and validating token.');
|
|
940
1033
|
const extract = this.config.jwtExtract(this.config, token);
|
|
941
1034
|
if (extract == null) {
|
|
942
1035
|
return null;
|
|
@@ -945,12 +1038,9 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
945
1038
|
|
|
946
1039
|
const user = await this.nymph.getEntity(
|
|
947
1040
|
{ class: this.User },
|
|
948
|
-
{
|
|
949
|
-
type: '&',
|
|
950
|
-
guid: guid,
|
|
951
|
-
}
|
|
1041
|
+
{ type: '&', guid },
|
|
952
1042
|
);
|
|
953
|
-
if (!user || !user.guid
|
|
1043
|
+
if (!user || !user.guid) {
|
|
954
1044
|
return null;
|
|
955
1045
|
}
|
|
956
1046
|
|
|
@@ -958,40 +1048,73 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
958
1048
|
}
|
|
959
1049
|
|
|
960
1050
|
/**
|
|
961
|
-
* Check for
|
|
1051
|
+
* Check for TILMELDAUTH and TILMELDSWITCH tokens, and, if set, authenticate
|
|
1052
|
+
* from it/them.
|
|
1053
|
+
*
|
|
1054
|
+
* You can also call this function after setting `response.locals.user` to the
|
|
1055
|
+
* user you want to authenticate. You *should* check for `user.enabled` before
|
|
1056
|
+
* setting this variable, unless you explicitly want to log in as a disabled
|
|
1057
|
+
* user. (The user must be an instance of the User class for this Tilmeld
|
|
1058
|
+
* instance.)
|
|
1059
|
+
*
|
|
1060
|
+
* This function will set `response.locals.user` to the logged in user on
|
|
1061
|
+
* successful authentication.
|
|
962
1062
|
*
|
|
963
1063
|
* @param skipXsrfToken Skip the XSRF token check.
|
|
1064
|
+
* @param skipRenew Skip the token renewal step, even if the token is close to expiration.
|
|
964
1065
|
* @returns True if a user was authenticated, false on any failure.
|
|
965
1066
|
*/
|
|
966
|
-
public authenticate(skipXsrfToken = false) {
|
|
1067
|
+
public async authenticate(skipXsrfToken = false, skipRenew = false) {
|
|
967
1068
|
if (this.request == null) {
|
|
968
1069
|
return false;
|
|
969
1070
|
}
|
|
970
1071
|
|
|
1072
|
+
if (
|
|
1073
|
+
this.response &&
|
|
1074
|
+
this.response.locals.user &&
|
|
1075
|
+
this.response.locals.user instanceof this.User
|
|
1076
|
+
) {
|
|
1077
|
+
// The user has already been authenticated through some other means.
|
|
1078
|
+
const user = this.response.locals.user;
|
|
1079
|
+
await this.fillSession(user);
|
|
1080
|
+
return true;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
971
1083
|
const cookies = this.request.cookies ?? {};
|
|
972
1084
|
|
|
973
1085
|
// If a client does't support cookies, they can use the X-TILMELDAUTH header
|
|
974
1086
|
// to provide the auth token.
|
|
975
1087
|
let fromAuthHeader = false;
|
|
976
1088
|
let authToken: string;
|
|
1089
|
+
let switchToken: string | null;
|
|
977
1090
|
if (this.request.header('x-tilmeldauth') != null) {
|
|
978
1091
|
fromAuthHeader = true;
|
|
979
1092
|
authToken = this.request.header('x-tilmeldauth') as string;
|
|
1093
|
+
switchToken =
|
|
1094
|
+
(this.request.header('x-tilmeldswitch') as string) ||
|
|
1095
|
+
'TILMELDSWITCH' in cookies
|
|
1096
|
+
? cookies.TILMELDSWITCH
|
|
1097
|
+
: null;
|
|
980
1098
|
} else if ('TILMELDAUTH' in cookies) {
|
|
981
1099
|
fromAuthHeader = false;
|
|
982
1100
|
authToken = cookies.TILMELDAUTH;
|
|
1101
|
+
switchToken = 'TILMELDSWITCH' in cookies ? cookies.TILMELDSWITCH : null;
|
|
983
1102
|
} else {
|
|
984
1103
|
return false;
|
|
985
1104
|
}
|
|
986
1105
|
|
|
987
|
-
let
|
|
1106
|
+
let authExtract: { guid: string; issued: Date; expire: Date } | null;
|
|
1107
|
+
let switchExtract: { guid: string; expire: Date } | null;
|
|
988
1108
|
if (
|
|
989
1109
|
skipXsrfToken ||
|
|
990
1110
|
this.request.originalUrl.startsWith(this.config.setupPath)
|
|
991
1111
|
) {
|
|
992
1112
|
// The request is for the setup app, or we were told to skip the XSRF
|
|
993
1113
|
// check, so don't check for the XSRF token.
|
|
994
|
-
|
|
1114
|
+
authExtract = this.config.jwtExtract(this.config, authToken);
|
|
1115
|
+
switchExtract = switchToken
|
|
1116
|
+
? this.config.jwtExtract(this.config, switchToken)
|
|
1117
|
+
: null;
|
|
995
1118
|
} else {
|
|
996
1119
|
// The request is for something else, so check for a valid XSRF token,
|
|
997
1120
|
// unless the auth token is provided by a header (instead of a cookie).
|
|
@@ -1000,28 +1123,65 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
1000
1123
|
return false;
|
|
1001
1124
|
}
|
|
1002
1125
|
|
|
1003
|
-
|
|
1126
|
+
authExtract = this.config.jwtExtract(this.config, authToken, xsrfToken);
|
|
1127
|
+
switchExtract = switchToken
|
|
1128
|
+
? this.config.jwtExtract(this.config, switchToken)
|
|
1129
|
+
: null;
|
|
1004
1130
|
}
|
|
1005
1131
|
|
|
1006
|
-
if (
|
|
1007
|
-
this.logout();
|
|
1132
|
+
if (authExtract == null) {
|
|
1133
|
+
await this.logout();
|
|
1008
1134
|
return false;
|
|
1009
1135
|
}
|
|
1010
|
-
const { guid, expire } =
|
|
1136
|
+
const { guid, issued, expire } = authExtract;
|
|
1137
|
+
const { guid: switchGuid, expire: switchExpire } = switchExtract || {
|
|
1138
|
+
guid: null,
|
|
1139
|
+
expire: null,
|
|
1140
|
+
};
|
|
1011
1141
|
|
|
1012
|
-
const user = this.User.
|
|
1013
|
-
if (
|
|
1014
|
-
|
|
1142
|
+
const user = await this.User.factory(guid);
|
|
1143
|
+
if (
|
|
1144
|
+
user.guid == null ||
|
|
1145
|
+
!user.enabled ||
|
|
1146
|
+
expire.getTime() <= Date.now() ||
|
|
1147
|
+
issued.getTime() > Date.now() ||
|
|
1148
|
+
(user.revokeTokenDate != null && issued.getTime() <= user.revokeTokenDate)
|
|
1149
|
+
) {
|
|
1150
|
+
await this.logout();
|
|
1015
1151
|
return false;
|
|
1016
1152
|
}
|
|
1017
1153
|
|
|
1018
|
-
if (
|
|
1154
|
+
if (switchGuid && switchExpire && switchExpire.getTime() > Date.now()) {
|
|
1155
|
+
// Load the switch user.
|
|
1156
|
+
const switchUser = await this.User.factory(switchGuid);
|
|
1157
|
+
if (switchUser.guid == null) {
|
|
1158
|
+
await this.logoutSwitch();
|
|
1159
|
+
} else {
|
|
1160
|
+
await this.fillSession(switchUser);
|
|
1161
|
+
|
|
1162
|
+
if (this.response) {
|
|
1163
|
+
this.response.locals.user = switchUser;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
return true;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
if (
|
|
1171
|
+
!skipRenew &&
|
|
1172
|
+
expire.getTime() < Date.now() + this.config.jwtRenew * 1000
|
|
1173
|
+
) {
|
|
1019
1174
|
// If the user is less than renew time from needing a new token, give them
|
|
1020
1175
|
// a new one.
|
|
1021
|
-
this.login(user, fromAuthHeader);
|
|
1176
|
+
await this.login(user, fromAuthHeader);
|
|
1022
1177
|
} else {
|
|
1023
|
-
this.fillSession(user);
|
|
1178
|
+
await this.fillSession(user);
|
|
1024
1179
|
}
|
|
1180
|
+
|
|
1181
|
+
if (this.response) {
|
|
1182
|
+
this.response.locals.user = user;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1025
1185
|
return true;
|
|
1026
1186
|
}
|
|
1027
1187
|
|
|
@@ -1029,27 +1189,87 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
1029
1189
|
* Logs the given user into the system.
|
|
1030
1190
|
*
|
|
1031
1191
|
* @param user The user.
|
|
1032
|
-
* @param sendAuthHeader
|
|
1192
|
+
* @param sendAuthHeader Send the auth token as a custom header.
|
|
1193
|
+
* @param sendCookie Send the auth token as a cookie.
|
|
1033
1194
|
* @returns True on success, false on failure.
|
|
1034
1195
|
*/
|
|
1035
|
-
public login(
|
|
1196
|
+
public async login(
|
|
1197
|
+
user: User & UserData,
|
|
1198
|
+
sendAuthHeader: boolean,
|
|
1199
|
+
sendCookie = true,
|
|
1200
|
+
) {
|
|
1036
1201
|
if (user.guid != null && user.enabled) {
|
|
1037
|
-
if (this.response
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1202
|
+
if (this.response) {
|
|
1203
|
+
if (!this.response.headersSent) {
|
|
1204
|
+
const token = this.config.jwtBuilder(this.config, user);
|
|
1205
|
+
|
|
1206
|
+
if (sendCookie) {
|
|
1207
|
+
const appUrl = new URL(this.config.appUrl);
|
|
1208
|
+
this.response.cookie('TILMELDAUTH', token, {
|
|
1209
|
+
domain: this.config.cookieDomain,
|
|
1210
|
+
path: this.config.cookiePath,
|
|
1211
|
+
maxAge: this.config.jwtExpire * 1000,
|
|
1212
|
+
secure: appUrl.protocol === 'https:',
|
|
1213
|
+
httpOnly: false, // Allow JS access (for CSRF protection).
|
|
1214
|
+
sameSite: appUrl.protocol === 'https:' ? 'lax' : 'strict',
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
if (sendAuthHeader) {
|
|
1219
|
+
this.response.set('X-TILMELDAUTH', token);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
this.response.locals.user = user;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
await this.fillSession(user);
|
|
1227
|
+
return true;
|
|
1228
|
+
}
|
|
1229
|
+
return false;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* Adds a switch auth token for the given user.
|
|
1234
|
+
*
|
|
1235
|
+
* This effectively logs the current user in to the system as the given user.
|
|
1236
|
+
*
|
|
1237
|
+
* @param user The user.
|
|
1238
|
+
* @param sendAuthHeader Send the auth token as a custom header.
|
|
1239
|
+
* @param sendCookie Send the auth token as a cookie.
|
|
1240
|
+
* @returns True on success, false on failure.
|
|
1241
|
+
*/
|
|
1242
|
+
public async loginSwitch(
|
|
1243
|
+
user: User & UserData,
|
|
1244
|
+
sendAuthHeader: boolean,
|
|
1245
|
+
sendCookie = true,
|
|
1246
|
+
) {
|
|
1247
|
+
if (user.guid != null) {
|
|
1248
|
+
if (this.response) {
|
|
1249
|
+
if (!this.response.headersSent) {
|
|
1250
|
+
const token = this.config.jwtBuilder(this.config, user, true);
|
|
1251
|
+
|
|
1252
|
+
if (sendCookie) {
|
|
1253
|
+
const appUrl = new URL(this.config.appUrl);
|
|
1254
|
+
this.response.cookie('TILMELDSWITCH', token, {
|
|
1255
|
+
domain: this.config.cookieDomain,
|
|
1256
|
+
path: this.config.cookiePath,
|
|
1257
|
+
maxAge: this.config.jwtSwitchExpire * 1000,
|
|
1258
|
+
secure: appUrl.protocol === 'https:',
|
|
1259
|
+
httpOnly: false, // Allow JS access (for PubSub auth token).
|
|
1260
|
+
sameSite: appUrl.protocol === 'https:' ? 'lax' : 'strict',
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
if (sendAuthHeader) {
|
|
1265
|
+
this.response.set('X-TILMELDSWITCH', token);
|
|
1266
|
+
}
|
|
1050
1267
|
}
|
|
1268
|
+
|
|
1269
|
+
this.response.locals.user = user;
|
|
1051
1270
|
}
|
|
1052
|
-
|
|
1271
|
+
|
|
1272
|
+
await this.fillSession(user);
|
|
1053
1273
|
return true;
|
|
1054
1274
|
}
|
|
1055
1275
|
return false;
|
|
@@ -1057,35 +1277,50 @@ export default class Tilmeld implements TilmeldInterface {
|
|
|
1057
1277
|
|
|
1058
1278
|
/**
|
|
1059
1279
|
* Logs the current user out of the system.
|
|
1280
|
+
*
|
|
1281
|
+
* @param clearCookie Clear the auth cookie. (Also send a header.)
|
|
1060
1282
|
*/
|
|
1061
|
-
public logout() {
|
|
1283
|
+
public async logout(clearCookie = true) {
|
|
1284
|
+
if (this.request && !this.alreadyLoggedOutSwitch) {
|
|
1285
|
+
const cookies = this.request.cookies ?? {};
|
|
1286
|
+
|
|
1287
|
+
if ('TILMELDSWITCH' in cookies) {
|
|
1288
|
+
this.alreadyLoggedOutSwitch = true;
|
|
1289
|
+
return await this.logoutSwitch(clearCookie);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1062
1293
|
this.clearSession();
|
|
1063
|
-
if (this.response
|
|
1064
|
-
this.response.
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1294
|
+
if (this.response) {
|
|
1295
|
+
if (clearCookie && !this.response.headersSent) {
|
|
1296
|
+
this.response.clearCookie('TILMELDAUTH', {
|
|
1297
|
+
domain: this.config.cookieDomain,
|
|
1298
|
+
path: this.config.cookiePath,
|
|
1299
|
+
});
|
|
1300
|
+
this.response.set('X-TILMELDAUTH', '');
|
|
1301
|
+
}
|
|
1302
|
+
this.response.locals.user = null;
|
|
1069
1303
|
}
|
|
1070
1304
|
}
|
|
1071
1305
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1306
|
+
/**
|
|
1307
|
+
* Clears the switch user out of the system.
|
|
1308
|
+
*
|
|
1309
|
+
* @param clearCookie Clear the auth cookie. (Also send a header.)
|
|
1310
|
+
*/
|
|
1311
|
+
public async logoutSwitch(clearCookie = true) {
|
|
1312
|
+
this.clearSession();
|
|
1313
|
+
if (this.response) {
|
|
1314
|
+
if (clearCookie && !this.response.headersSent) {
|
|
1315
|
+
this.response.clearCookie('TILMELDSWITCH', {
|
|
1316
|
+
domain: this.config.cookieDomain,
|
|
1317
|
+
path: this.config.cookiePath,
|
|
1318
|
+
});
|
|
1319
|
+
this.response.set('X-TILMELDSWITCH', '');
|
|
1320
|
+
}
|
|
1321
|
+
this.response.locals.user = null;
|
|
1322
|
+
|
|
1323
|
+
await this.authenticate(false, true);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1091
1326
|
}
|