@nymphjs/tilmeld 1.0.0-beta.3 → 1.0.0-beta.31
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 +150 -0
- package/README.md +13 -0
- package/dist/Group.d.ts +0 -2
- package/dist/Group.js +57 -28
- package/dist/Group.js.map +1 -1
- package/dist/Tilmeld.d.ts +5 -2
- package/dist/Tilmeld.js +164 -63
- package/dist/Tilmeld.js.map +1 -1
- package/dist/User.d.ts +56 -5
- package/dist/User.js +331 -66
- 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 +22 -19
- package/src/Group.ts +80 -32
- package/src/Tilmeld.ts +241 -78
- package/src/User.ts +508 -74
- 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/dist/User.js
CHANGED
|
@@ -3,12 +3,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const guid_1 = require("@nymphjs/guid");
|
|
6
7
|
const strtotime_1 = __importDefault(require("locutus/php/datetime/strtotime"));
|
|
7
8
|
const enc_base64_1 = __importDefault(require("crypto-js/enc-base64"));
|
|
8
9
|
const sha256_1 = __importDefault(require("crypto-js/sha256"));
|
|
9
10
|
const md5_1 = __importDefault(require("crypto-js/md5"));
|
|
10
11
|
const lodash_1 = require("lodash");
|
|
11
|
-
const
|
|
12
|
+
const otpauth_1 = require("otpauth");
|
|
13
|
+
const qrcode_1 = require("qrcode");
|
|
14
|
+
const enforceTilmeld_1 = require("./enforceTilmeld");
|
|
12
15
|
const AbleObject_1 = __importDefault(require("./AbleObject"));
|
|
13
16
|
const errors_1 = require("./errors");
|
|
14
17
|
class User extends AbleObject_1.default {
|
|
@@ -49,13 +52,15 @@ class User extends AbleObject_1.default {
|
|
|
49
52
|
}
|
|
50
53
|
}
|
|
51
54
|
static current(returnObjectIfNotExist) {
|
|
52
|
-
|
|
55
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
56
|
+
if (tilmeld.currentUser == null) {
|
|
53
57
|
return returnObjectIfNotExist ? this.factorySync() : null;
|
|
54
58
|
}
|
|
55
|
-
return
|
|
59
|
+
return tilmeld.currentUser;
|
|
56
60
|
}
|
|
57
61
|
static async sendRecovery(data) {
|
|
58
|
-
|
|
62
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
63
|
+
if (!tilmeld.config.pwRecovery) {
|
|
59
64
|
return {
|
|
60
65
|
result: false,
|
|
61
66
|
message: 'Account recovery is not allowed.',
|
|
@@ -63,9 +68,8 @@ class User extends AbleObject_1.default {
|
|
|
63
68
|
}
|
|
64
69
|
let user;
|
|
65
70
|
const options = {};
|
|
66
|
-
if (!
|
|
67
|
-
|
|
68
|
-
const getUser = await this.nymph.getEntity({ class: this.tilmeld.User, skipAc: true }, {
|
|
71
|
+
if (!tilmeld.config.emailUsernames && data.recoveryType === 'username') {
|
|
72
|
+
const getUser = await this.nymph.getEntity({ class: tilmeld.User, skipAc: true }, {
|
|
69
73
|
type: '&',
|
|
70
74
|
ilike: ['email', data.account.replace(/([\\%_])/g, (s) => `\\${s}`)],
|
|
71
75
|
});
|
|
@@ -86,7 +90,7 @@ class User extends AbleObject_1.default {
|
|
|
86
90
|
options.locals = {};
|
|
87
91
|
}
|
|
88
92
|
else if (data.recoveryType === 'password') {
|
|
89
|
-
const getUser = await
|
|
93
|
+
const getUser = await tilmeld.User.factoryUsername(data.account);
|
|
90
94
|
if (getUser.guid == null) {
|
|
91
95
|
return {
|
|
92
96
|
result: false,
|
|
@@ -108,13 +112,13 @@ class User extends AbleObject_1.default {
|
|
|
108
112
|
};
|
|
109
113
|
options.locals = {
|
|
110
114
|
recoverCode: getUser.recoverSecret,
|
|
111
|
-
timeLimit:
|
|
115
|
+
timeLimit: tilmeld.config.pwRecoveryTimeLimit,
|
|
112
116
|
};
|
|
113
117
|
}
|
|
114
118
|
else {
|
|
115
119
|
return { result: false, message: 'Invalid recovery type.' };
|
|
116
120
|
}
|
|
117
|
-
if (await
|
|
121
|
+
if (await tilmeld.config.sendEmail(tilmeld, options, user)) {
|
|
118
122
|
return {
|
|
119
123
|
result: true,
|
|
120
124
|
message: "We've sent an email to your registered address. Please " +
|
|
@@ -126,17 +130,18 @@ class User extends AbleObject_1.default {
|
|
|
126
130
|
}
|
|
127
131
|
}
|
|
128
132
|
static async recover(data) {
|
|
129
|
-
|
|
133
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
134
|
+
if (!tilmeld.config.pwRecovery) {
|
|
130
135
|
return {
|
|
131
136
|
result: false,
|
|
132
137
|
message: 'Account recovery is not allowed.',
|
|
133
138
|
};
|
|
134
139
|
}
|
|
135
|
-
const user = await
|
|
140
|
+
const user = await tilmeld.User.factoryUsername(data.username);
|
|
136
141
|
if (user.guid == null ||
|
|
137
142
|
user.recoverSecret == null ||
|
|
138
143
|
data.secret !== user.recoverSecret ||
|
|
139
|
-
(0, strtotime_1.default)('+' +
|
|
144
|
+
(0, strtotime_1.default)('+' + tilmeld.config.pwRecoveryTimeLimit, Math.floor((user.recoverSecretDate ?? 0) / 1000)) *
|
|
140
145
|
1000 <
|
|
141
146
|
Date.now()) {
|
|
142
147
|
return {
|
|
@@ -161,22 +166,24 @@ class User extends AbleObject_1.default {
|
|
|
161
166
|
}
|
|
162
167
|
}
|
|
163
168
|
static getClientConfig() {
|
|
169
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
164
170
|
return {
|
|
165
|
-
regFields:
|
|
166
|
-
userFields:
|
|
167
|
-
emailUsernames:
|
|
168
|
-
allowRegistration:
|
|
169
|
-
allowUsernameChange:
|
|
170
|
-
pwRecovery:
|
|
171
|
-
verifyEmail:
|
|
172
|
-
unverifiedAccess:
|
|
171
|
+
regFields: tilmeld.config.regFields,
|
|
172
|
+
userFields: tilmeld.config.userFields,
|
|
173
|
+
emailUsernames: tilmeld.config.emailUsernames,
|
|
174
|
+
allowRegistration: tilmeld.config.allowRegistration,
|
|
175
|
+
allowUsernameChange: tilmeld.config.allowUsernameChange,
|
|
176
|
+
pwRecovery: tilmeld.config.pwRecovery,
|
|
177
|
+
verifyEmail: tilmeld.config.verifyEmail,
|
|
178
|
+
unverifiedAccess: tilmeld.config.unverifiedAccess,
|
|
173
179
|
};
|
|
174
180
|
}
|
|
175
181
|
static async loginUser(data) {
|
|
182
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
176
183
|
if (!('username' in data) || !data.username.length) {
|
|
177
184
|
return { result: false, message: 'Incorrect login/password.' };
|
|
178
185
|
}
|
|
179
|
-
const user = await
|
|
186
|
+
const user = await tilmeld.User.factoryUsername(data.username);
|
|
180
187
|
const result = await user.$login(data);
|
|
181
188
|
if (result.result) {
|
|
182
189
|
user.$updateDataProtection();
|
|
@@ -185,7 +192,7 @@ class User extends AbleObject_1.default {
|
|
|
185
192
|
return result;
|
|
186
193
|
}
|
|
187
194
|
async $login(data) {
|
|
188
|
-
const tilmeld = this
|
|
195
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
189
196
|
if (this.guid == null) {
|
|
190
197
|
return { result: false, message: 'Incorrect login/password.' };
|
|
191
198
|
}
|
|
@@ -198,6 +205,20 @@ class User extends AbleObject_1.default {
|
|
|
198
205
|
if (!this.$checkPassword(data.password)) {
|
|
199
206
|
return { result: false, message: 'Incorrect login/password.' };
|
|
200
207
|
}
|
|
208
|
+
if (this.$data.totpSecret != null) {
|
|
209
|
+
if (data.code == null ||
|
|
210
|
+
typeof data.code !== 'string' ||
|
|
211
|
+
data.code.length !== 6) {
|
|
212
|
+
return {
|
|
213
|
+
result: false,
|
|
214
|
+
needTOTP: true,
|
|
215
|
+
message: 'You need to provide a 2FA code.',
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (!this.$checkTOTPCode(data.code)) {
|
|
219
|
+
return { result: false, message: 'Incorrect 2FA code.' };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
201
222
|
try {
|
|
202
223
|
for (let callback of this.constructor
|
|
203
224
|
.beforeLoginCallbacks) {
|
|
@@ -223,8 +244,63 @@ class User extends AbleObject_1.default {
|
|
|
223
244
|
}
|
|
224
245
|
return { result: true, message: 'You are logged in.' };
|
|
225
246
|
}
|
|
247
|
+
async $switchUser(data) {
|
|
248
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
249
|
+
if (this.guid == null) {
|
|
250
|
+
return { result: false, message: 'Incorrect login/password.' };
|
|
251
|
+
}
|
|
252
|
+
let user = tilmeld.User.current();
|
|
253
|
+
if (!user || user.guid == null) {
|
|
254
|
+
return { result: false, message: "You're not logged in." };
|
|
255
|
+
}
|
|
256
|
+
const isCurrentUser = user != null && user.$is(this);
|
|
257
|
+
if (isCurrentUser) {
|
|
258
|
+
return { result: false, message: "You can't switch to yourself." };
|
|
259
|
+
}
|
|
260
|
+
const toAbilities = this.$data.abilities || [];
|
|
261
|
+
const fromAbilities = user.abilities || [];
|
|
262
|
+
if (fromAbilities.indexOf('tilmeld/switch') === -1 &&
|
|
263
|
+
fromAbilities.indexOf('system/admin') === -1) {
|
|
264
|
+
return {
|
|
265
|
+
result: false,
|
|
266
|
+
message: "You don't have permission to switch users.",
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
if (toAbilities.indexOf('system/admin') !== -1 ||
|
|
270
|
+
toAbilities.indexOf('tilmeld/admin') !== -1) {
|
|
271
|
+
return { result: false, message: "You can't switch to an admin." };
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
for (let callback of this.constructor
|
|
275
|
+
.beforeLoginCallbacks) {
|
|
276
|
+
if (callback) {
|
|
277
|
+
await callback(this, {
|
|
278
|
+
username: this.$data.username || '',
|
|
279
|
+
password: '',
|
|
280
|
+
...(data || {}),
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
catch (e) {
|
|
286
|
+
return {
|
|
287
|
+
result: false,
|
|
288
|
+
message: e.message,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
if (!tilmeld.loginSwitch(this, true)) {
|
|
292
|
+
return { result: false, message: 'Incorrect login/password.' };
|
|
293
|
+
}
|
|
294
|
+
for (let callback of this.constructor
|
|
295
|
+
.afterLoginCallbacks) {
|
|
296
|
+
if (callback) {
|
|
297
|
+
await callback(this);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return { result: true, message: 'You are logged in.' };
|
|
301
|
+
}
|
|
226
302
|
async $logout() {
|
|
227
|
-
const tilmeld = this
|
|
303
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
228
304
|
try {
|
|
229
305
|
for (let callback of this.constructor
|
|
230
306
|
.beforeLogoutCallbacks) {
|
|
@@ -284,25 +360,49 @@ class User extends AbleObject_1.default {
|
|
|
284
360
|
return this.$descendantGroups;
|
|
285
361
|
}
|
|
286
362
|
$jsonAcceptData(input, allowConflict = false) {
|
|
287
|
-
const tilmeld = this
|
|
363
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
288
364
|
this.$referenceWake();
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
|
|
365
|
+
if ('abilities' in input.data &&
|
|
366
|
+
input.data.abilities?.includes('system/admin') !==
|
|
367
|
+
this.$data.abilities?.includes('system/admin') &&
|
|
368
|
+
!tilmeld.gatekeeper('system/admin')) {
|
|
369
|
+
throw new errors_1.BadDataError("You don't have the authority to grant or revoke system/admin.");
|
|
370
|
+
}
|
|
371
|
+
if ('abilities' in input.data &&
|
|
372
|
+
input.data.abilities?.includes('tilmeld/admin') !==
|
|
373
|
+
this.$data.abilities?.includes('tilmeld/admin') &&
|
|
292
374
|
!tilmeld.gatekeeper('system/admin')) {
|
|
293
|
-
throw new errors_1.BadDataError("You don't have the authority to
|
|
375
|
+
throw new errors_1.BadDataError("You don't have the authority to grant or revoke tilmeld/admin.");
|
|
376
|
+
}
|
|
377
|
+
if ('abilities' in input.data &&
|
|
378
|
+
input.data.abilities?.includes('tilmeld/switch') !==
|
|
379
|
+
this.$data.abilities?.includes('tilmeld/switch') &&
|
|
380
|
+
!tilmeld.gatekeeper('system/admin')) {
|
|
381
|
+
throw new errors_1.BadDataError("You don't have the authority to grant or revoke tilmeld/switch.");
|
|
294
382
|
}
|
|
295
383
|
this.$originalEmail = this.$data.email;
|
|
296
384
|
super.$jsonAcceptData(input, allowConflict);
|
|
297
385
|
}
|
|
298
386
|
$jsonAcceptPatch(patch, allowConflict = false) {
|
|
299
|
-
const tilmeld = this
|
|
387
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
300
388
|
this.$referenceWake();
|
|
301
|
-
if (
|
|
302
|
-
|
|
303
|
-
|
|
389
|
+
if ('abilities' in patch.set &&
|
|
390
|
+
patch.set.abilities?.includes('system/admin') !==
|
|
391
|
+
this.$data.abilities?.includes('system/admin') &&
|
|
392
|
+
!tilmeld.gatekeeper('system/admin')) {
|
|
393
|
+
throw new errors_1.BadDataError("You don't have the authority to grant or revoke system/admin.");
|
|
394
|
+
}
|
|
395
|
+
if ('abilities' in patch.set &&
|
|
396
|
+
patch.set.abilities?.includes('tilmeld/admin') !==
|
|
397
|
+
this.$data.abilities?.includes('tilmeld/admin') &&
|
|
304
398
|
!tilmeld.gatekeeper('system/admin')) {
|
|
305
|
-
throw new errors_1.BadDataError("You don't have the authority to
|
|
399
|
+
throw new errors_1.BadDataError("You don't have the authority to grant or revoke tilmeld/admin.");
|
|
400
|
+
}
|
|
401
|
+
if ('abilities' in patch.set &&
|
|
402
|
+
patch.set.abilities?.includes('tilmeld/switch') !==
|
|
403
|
+
this.$data.abilities?.includes('tilmeld/switch') &&
|
|
404
|
+
!tilmeld.gatekeeper('system/admin')) {
|
|
405
|
+
throw new errors_1.BadDataError("You don't have the authority to grant or revoke tilmeld/switch.");
|
|
306
406
|
}
|
|
307
407
|
this.$originalEmail = this.$data.email;
|
|
308
408
|
super.$jsonAcceptPatch(patch, allowConflict);
|
|
@@ -312,7 +412,7 @@ class User extends AbleObject_1.default {
|
|
|
312
412
|
this.$updateDataProtection();
|
|
313
413
|
}
|
|
314
414
|
$updateDataProtection(givenUser) {
|
|
315
|
-
const tilmeld = this
|
|
415
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
316
416
|
let user = givenUser ?? tilmeld.User.current();
|
|
317
417
|
this.$clientEnabledMethods = [...User.DEFAULT_CLIENT_ENABLED_METHODS];
|
|
318
418
|
this.$privateData = [...User.DEFAULT_PRIVATE_DATA];
|
|
@@ -328,13 +428,18 @@ class User extends AbleObject_1.default {
|
|
|
328
428
|
if (isCurrentUser) {
|
|
329
429
|
this.$clientEnabledMethods.push('$gatekeeper');
|
|
330
430
|
this.$clientEnabledMethods.push('$changePassword');
|
|
431
|
+
this.$clientEnabledMethods.push('$revokeCurrentTokens');
|
|
331
432
|
this.$clientEnabledMethods.push('$logout');
|
|
332
433
|
this.$clientEnabledMethods.push('$sendEmailVerification');
|
|
434
|
+
this.$clientEnabledMethods.push('$hasTOTPSecret');
|
|
435
|
+
this.$clientEnabledMethods.push('$getNewTOTPSecret');
|
|
436
|
+
this.$clientEnabledMethods.push('$saveTOTPSecret');
|
|
437
|
+
this.$clientEnabledMethods.push('$removeTOTPSecret');
|
|
333
438
|
}
|
|
334
439
|
if (user != null &&
|
|
335
440
|
(abilities.indexOf('tilmeld/admin') !== -1 ||
|
|
336
441
|
abilities.indexOf('system/admin') !== -1)) {
|
|
337
|
-
this.$privateData = ['password', 'salt'];
|
|
442
|
+
this.$privateData = ['password', 'salt', 'totpSecret'];
|
|
338
443
|
this.$allowlistData = undefined;
|
|
339
444
|
}
|
|
340
445
|
else if (isCurrentUser || isNewUser) {
|
|
@@ -355,20 +460,29 @@ class User extends AbleObject_1.default {
|
|
|
355
460
|
this.$allowlistData.push('phone');
|
|
356
461
|
}
|
|
357
462
|
this.$privateData = [
|
|
463
|
+
'enabled',
|
|
358
464
|
'secret',
|
|
465
|
+
'emailChangeDate',
|
|
359
466
|
'newEmailSecret',
|
|
360
|
-
'cancelEmailAddress',
|
|
361
467
|
'cancelEmailSecret',
|
|
362
|
-
'
|
|
468
|
+
'cancelEmailAddress',
|
|
363
469
|
'recoverSecret',
|
|
364
470
|
'recoverSecretDate',
|
|
365
|
-
'password',
|
|
366
471
|
'salt',
|
|
472
|
+
'password',
|
|
473
|
+
'passwordTemp',
|
|
474
|
+
'totpSecret',
|
|
367
475
|
];
|
|
368
476
|
}
|
|
477
|
+
if (user != null &&
|
|
478
|
+
!isCurrentUser &&
|
|
479
|
+
(abilities.indexOf('tilmeld/switch') !== -1 ||
|
|
480
|
+
abilities.indexOf('system/admin') !== -1)) {
|
|
481
|
+
this.$clientEnabledMethods.push('$switchUser');
|
|
482
|
+
}
|
|
369
483
|
}
|
|
370
484
|
$gatekeeper(ability) {
|
|
371
|
-
const tilmeld = this
|
|
485
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
372
486
|
if (ability == null) {
|
|
373
487
|
return tilmeld.User.current()?.$is(this) ?? false;
|
|
374
488
|
}
|
|
@@ -389,7 +503,7 @@ class User extends AbleObject_1.default {
|
|
|
389
503
|
this.$gatekeeperCache = Object.fromEntries(abilities.map((ability) => [ability, true]));
|
|
390
504
|
}
|
|
391
505
|
return ((ability in this.$gatekeeperCache && !!this.$gatekeeperCache[ability]) ||
|
|
392
|
-
!!this.$
|
|
506
|
+
!!this.$data.abilities?.includes('system/admin'));
|
|
393
507
|
}
|
|
394
508
|
$clearCache() {
|
|
395
509
|
const ret = super.$clearCache();
|
|
@@ -398,7 +512,7 @@ class User extends AbleObject_1.default {
|
|
|
398
512
|
return ret;
|
|
399
513
|
}
|
|
400
514
|
async $sendEmailVerification() {
|
|
401
|
-
const tilmeld = this
|
|
515
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
402
516
|
if (this.guid == null) {
|
|
403
517
|
return false;
|
|
404
518
|
}
|
|
@@ -472,7 +586,7 @@ class User extends AbleObject_1.default {
|
|
|
472
586
|
return true;
|
|
473
587
|
}
|
|
474
588
|
$checkPassword(password) {
|
|
475
|
-
const tilmeld = this
|
|
589
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
476
590
|
switch (tilmeld.config.pwMethod) {
|
|
477
591
|
case 'plain':
|
|
478
592
|
return this.$data.password === password;
|
|
@@ -531,7 +645,14 @@ class User extends AbleObject_1.default {
|
|
|
531
645
|
return { result: false, message: 'Incorrect password.' };
|
|
532
646
|
}
|
|
533
647
|
this.$data.passwordTemp = data.newPassword;
|
|
648
|
+
if (data.revokeCurrentTokens) {
|
|
649
|
+
this.$data.revokeTokenDate = Date.now();
|
|
650
|
+
}
|
|
534
651
|
if (await this.$save()) {
|
|
652
|
+
if (data.revokeCurrentTokens) {
|
|
653
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
654
|
+
tilmeld.login(this, true);
|
|
655
|
+
}
|
|
535
656
|
return { result: true, message: 'Your password has been changed.' };
|
|
536
657
|
}
|
|
537
658
|
else {
|
|
@@ -539,7 +660,7 @@ class User extends AbleObject_1.default {
|
|
|
539
660
|
}
|
|
540
661
|
}
|
|
541
662
|
$password(password) {
|
|
542
|
-
const tilmeld = this
|
|
663
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
543
664
|
switch (tilmeld.config.pwMethod) {
|
|
544
665
|
case 'plain':
|
|
545
666
|
delete this.$data.salt;
|
|
@@ -557,8 +678,137 @@ class User extends AbleObject_1.default {
|
|
|
557
678
|
}
|
|
558
679
|
return this.$data.password;
|
|
559
680
|
}
|
|
681
|
+
async $revokeCurrentTokens(data) {
|
|
682
|
+
if (!this.$checkPassword(data.password ?? '')) {
|
|
683
|
+
return { result: false, message: 'Incorrect password.' };
|
|
684
|
+
}
|
|
685
|
+
this.$data.revokeTokenDate = Date.now();
|
|
686
|
+
if (await this.$save()) {
|
|
687
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
688
|
+
tilmeld.login(this, true);
|
|
689
|
+
return {
|
|
690
|
+
result: true,
|
|
691
|
+
message: 'You have logged out of all other sessions.',
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
else {
|
|
695
|
+
return { result: false, message: "Couldn't save revocation date." };
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
async $hasTOTPSecret() {
|
|
699
|
+
return this.$data.totpSecret != null;
|
|
700
|
+
}
|
|
701
|
+
$checkTOTPCode(code) {
|
|
702
|
+
if (this.$data.totpSecret == null) {
|
|
703
|
+
return false;
|
|
704
|
+
}
|
|
705
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
706
|
+
const totp = new otpauth_1.TOTP({
|
|
707
|
+
issuer: tilmeld.config.appName,
|
|
708
|
+
label: this.$data.username,
|
|
709
|
+
algorithm: 'SHA1',
|
|
710
|
+
digits: 6,
|
|
711
|
+
period: 30,
|
|
712
|
+
secret: this.$data.totpSecret,
|
|
713
|
+
});
|
|
714
|
+
return totp.validate({ token: code, window: 2 }) != null;
|
|
715
|
+
}
|
|
716
|
+
async $getNewTOTPSecret() {
|
|
717
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
718
|
+
if (this.$data.totpSecret != null) {
|
|
719
|
+
throw new errors_1.BadDataError('You already have a 2FA secret.');
|
|
720
|
+
}
|
|
721
|
+
const secret = new otpauth_1.Secret();
|
|
722
|
+
const totp = new otpauth_1.TOTP({
|
|
723
|
+
issuer: tilmeld.config.appName,
|
|
724
|
+
label: this.$data.username,
|
|
725
|
+
algorithm: 'SHA1',
|
|
726
|
+
digits: 6,
|
|
727
|
+
period: 30,
|
|
728
|
+
secret,
|
|
729
|
+
});
|
|
730
|
+
const uri = totp.toString();
|
|
731
|
+
const qrcode = await (0, qrcode_1.toDataURL)(uri);
|
|
732
|
+
return {
|
|
733
|
+
uri,
|
|
734
|
+
qrcode,
|
|
735
|
+
secret: secret.base32,
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
async $saveTOTPSecret(data) {
|
|
739
|
+
if (!this.$checkPassword(data.password ?? '')) {
|
|
740
|
+
return { result: false, message: 'Incorrect password.' };
|
|
741
|
+
}
|
|
742
|
+
if (this.$data.totpSecret != null) {
|
|
743
|
+
return { result: false, message: 'You already have a 2FA secret.' };
|
|
744
|
+
}
|
|
745
|
+
if (data.secret == null ||
|
|
746
|
+
typeof data.secret !== 'string' ||
|
|
747
|
+
data.secret === '') {
|
|
748
|
+
return { result: false, message: '2FA secret is invalid.' };
|
|
749
|
+
}
|
|
750
|
+
if (data.code == null ||
|
|
751
|
+
typeof data.code !== 'string' ||
|
|
752
|
+
data.code.length !== 6) {
|
|
753
|
+
return { result: false, message: '2FA code is invalid.' };
|
|
754
|
+
}
|
|
755
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
756
|
+
const totp = new otpauth_1.TOTP({
|
|
757
|
+
issuer: tilmeld.config.appName,
|
|
758
|
+
label: this.$data.username,
|
|
759
|
+
algorithm: 'SHA1',
|
|
760
|
+
digits: 6,
|
|
761
|
+
period: 30,
|
|
762
|
+
secret: data.secret,
|
|
763
|
+
});
|
|
764
|
+
if (totp.validate({ token: data.code, window: 2 }) == null) {
|
|
765
|
+
return { result: false, message: '2FA code is incorrect.' };
|
|
766
|
+
}
|
|
767
|
+
this.$data.totpSecret = data.secret;
|
|
768
|
+
if (await this.$save()) {
|
|
769
|
+
return { result: true, message: 'Your two factor secret is now set.' };
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
return { result: false, message: "Couldn't save two factor secret." };
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
async $removeTOTPSecret(data) {
|
|
776
|
+
if (!this.$checkPassword(data.password ?? '')) {
|
|
777
|
+
return { result: false, message: 'Incorrect password.' };
|
|
778
|
+
}
|
|
779
|
+
if (this.$data.totpSecret == null) {
|
|
780
|
+
return { result: false, message: "You don't have a 2FA secret." };
|
|
781
|
+
}
|
|
782
|
+
if (data.code == null ||
|
|
783
|
+
typeof data.code !== 'string' ||
|
|
784
|
+
data.code.length !== 6) {
|
|
785
|
+
return { result: false, message: '2FA code is invalid.' };
|
|
786
|
+
}
|
|
787
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
788
|
+
const totp = new otpauth_1.TOTP({
|
|
789
|
+
issuer: tilmeld.config.appName,
|
|
790
|
+
label: this.$data.username,
|
|
791
|
+
algorithm: 'SHA1',
|
|
792
|
+
digits: 6,
|
|
793
|
+
period: 30,
|
|
794
|
+
secret: this.$data.totpSecret,
|
|
795
|
+
});
|
|
796
|
+
if (totp.validate({ token: data.code, window: 2 }) == null) {
|
|
797
|
+
return { result: false, message: '2FA code is incorrect.' };
|
|
798
|
+
}
|
|
799
|
+
delete this.$data.totpSecret;
|
|
800
|
+
if (await this.$save()) {
|
|
801
|
+
return {
|
|
802
|
+
result: true,
|
|
803
|
+
message: 'Your two factor secret has been removed.',
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
return { result: false, message: "Couldn't remove two factor secret." };
|
|
808
|
+
}
|
|
809
|
+
}
|
|
560
810
|
async $checkUsername() {
|
|
561
|
-
const tilmeld = this
|
|
811
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
562
812
|
if (!tilmeld.config.emailUsernames) {
|
|
563
813
|
if (this.$data.username == null || !this.$data.username.length) {
|
|
564
814
|
return { result: false, message: 'Please specify a username.' };
|
|
@@ -598,6 +848,17 @@ class User extends AbleObject_1.default {
|
|
|
598
848
|
if (test != null) {
|
|
599
849
|
return { result: false, message: 'That username is taken.' };
|
|
600
850
|
}
|
|
851
|
+
for (let callback of this.constructor
|
|
852
|
+
.checkUsernameCallbacks) {
|
|
853
|
+
if (callback) {
|
|
854
|
+
const result = await callback(this, {
|
|
855
|
+
username: this.$data.username,
|
|
856
|
+
});
|
|
857
|
+
if (!result.result) {
|
|
858
|
+
return { message: 'Username is not available.', ...result };
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
601
862
|
return {
|
|
602
863
|
result: true,
|
|
603
864
|
message: this.guid != null ? 'Username is valid.' : 'Username is available!',
|
|
@@ -621,7 +882,7 @@ class User extends AbleObject_1.default {
|
|
|
621
882
|
}
|
|
622
883
|
}
|
|
623
884
|
async $checkEmail() {
|
|
624
|
-
const tilmeld = this
|
|
885
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
625
886
|
if (this.$data.email == null || !this.$data.email.length) {
|
|
626
887
|
if (tilmeld.config.verifyEmail) {
|
|
627
888
|
return { result: false, message: 'Please specify an email.' };
|
|
@@ -656,7 +917,7 @@ class User extends AbleObject_1.default {
|
|
|
656
917
|
};
|
|
657
918
|
}
|
|
658
919
|
async $checkPhone() {
|
|
659
|
-
const tilmeld = this
|
|
920
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
660
921
|
if (this.$data.phone == null || !this.$data.phone.length) {
|
|
661
922
|
return { result: false, message: 'Please specify a phone number.' };
|
|
662
923
|
}
|
|
@@ -684,7 +945,7 @@ class User extends AbleObject_1.default {
|
|
|
684
945
|
};
|
|
685
946
|
}
|
|
686
947
|
async $register(data) {
|
|
687
|
-
let tilmeld = this
|
|
948
|
+
let tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
688
949
|
if (!tilmeld.config.allowRegistration) {
|
|
689
950
|
return {
|
|
690
951
|
result: false,
|
|
@@ -742,7 +1003,7 @@ class User extends AbleObject_1.default {
|
|
|
742
1003
|
const nymph = this.$nymph;
|
|
743
1004
|
const tnymph = await this.$nymph.startTransaction(transaction);
|
|
744
1005
|
this.$nymph = tnymph;
|
|
745
|
-
tilmeld = this
|
|
1006
|
+
tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
746
1007
|
try {
|
|
747
1008
|
let generatedPrimaryGroup = null;
|
|
748
1009
|
if (tilmeld.config.generatePrimary) {
|
|
@@ -909,13 +1170,13 @@ class User extends AbleObject_1.default {
|
|
|
909
1170
|
}
|
|
910
1171
|
}
|
|
911
1172
|
async $save() {
|
|
912
|
-
let tilmeld = this
|
|
1173
|
+
let tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
913
1174
|
if (this.$data.username == null || !this.$data.username.trim().length) {
|
|
914
1175
|
return false;
|
|
915
1176
|
}
|
|
916
1177
|
if (tilmeld.gatekeeper('tilmeld/admin') &&
|
|
917
1178
|
!tilmeld.gatekeeper('system/admin') &&
|
|
918
|
-
this.$
|
|
1179
|
+
this.$data.abilities?.includes('system/admin')) {
|
|
919
1180
|
throw new errors_1.BadDataError("You don't have the authority to modify system admins.");
|
|
920
1181
|
}
|
|
921
1182
|
let sendVerification = false;
|
|
@@ -1062,7 +1323,7 @@ class User extends AbleObject_1.default {
|
|
|
1062
1323
|
const nymph = this.$nymph;
|
|
1063
1324
|
const tnymph = await this.$nymph.startTransaction(transaction);
|
|
1064
1325
|
this.$nymph = tnymph;
|
|
1065
|
-
tilmeld = this
|
|
1326
|
+
tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
1066
1327
|
if (this.$data.group != null &&
|
|
1067
1328
|
this.$data.group.user != null &&
|
|
1068
1329
|
this.$is(this.$data.group.user)) {
|
|
@@ -1106,8 +1367,8 @@ class User extends AbleObject_1.default {
|
|
|
1106
1367
|
this.$descendantGroups = undefined;
|
|
1107
1368
|
this.$gatekeeperCache = undefined;
|
|
1108
1369
|
await tnymph.commit(transaction);
|
|
1109
|
-
if (nymph.
|
|
1110
|
-
nymph.
|
|
1370
|
+
if ((0, enforceTilmeld_1.enforceTilmeld)(nymph).User.current(true).$is(this)) {
|
|
1371
|
+
(0, enforceTilmeld_1.enforceTilmeld)(nymph).fillSession(this);
|
|
1111
1372
|
}
|
|
1112
1373
|
}
|
|
1113
1374
|
else {
|
|
@@ -1128,12 +1389,12 @@ class User extends AbleObject_1.default {
|
|
|
1128
1389
|
return false;
|
|
1129
1390
|
}
|
|
1130
1391
|
async $delete() {
|
|
1131
|
-
const tilmeld = this
|
|
1392
|
+
const tilmeld = (0, enforceTilmeld_1.enforceTilmeld)(this);
|
|
1132
1393
|
if (!tilmeld.gatekeeper('tilmeld/admin')) {
|
|
1133
1394
|
throw new errors_1.BadDataError("You don't have the authority to delete users.");
|
|
1134
1395
|
}
|
|
1135
1396
|
if (!tilmeld.gatekeeper('system/admin') &&
|
|
1136
|
-
this.$
|
|
1397
|
+
this.$data.abilities?.includes('system/admin')) {
|
|
1137
1398
|
throw new errors_1.BadDataError("You don't have the authority to delete system admins.");
|
|
1138
1399
|
}
|
|
1139
1400
|
if (tilmeld.User.current(true).$is(this)) {
|
|
@@ -1161,9 +1422,9 @@ class User extends AbleObject_1.default {
|
|
|
1161
1422
|
return true;
|
|
1162
1423
|
}
|
|
1163
1424
|
}
|
|
1164
|
-
exports.default = User;
|
|
1165
1425
|
User.ETYPE = 'tilmeld_user';
|
|
1166
1426
|
User.class = 'User';
|
|
1427
|
+
User.checkUsernameCallbacks = [];
|
|
1167
1428
|
User.beforeRegisterCallbacks = [];
|
|
1168
1429
|
User.afterRegisterCallbacks = [];
|
|
1169
1430
|
User.beforeLoginCallbacks = [];
|
|
@@ -1178,23 +1439,26 @@ User.DEFAULT_CLIENT_ENABLED_METHODS = [
|
|
|
1178
1439
|
'$register',
|
|
1179
1440
|
];
|
|
1180
1441
|
User.DEFAULT_PRIVATE_DATA = [
|
|
1442
|
+
'abilities',
|
|
1181
1443
|
'email',
|
|
1182
|
-
'originalEmail',
|
|
1183
1444
|
'phone',
|
|
1184
1445
|
'group',
|
|
1185
1446
|
'groups',
|
|
1186
|
-
'abilities',
|
|
1187
1447
|
'inheritAbilities',
|
|
1188
|
-
'
|
|
1189
|
-
'recoverSecretDate',
|
|
1190
|
-
'password',
|
|
1191
|
-
'salt',
|
|
1448
|
+
'enabled',
|
|
1192
1449
|
'secret',
|
|
1193
|
-
'
|
|
1450
|
+
'emailChangeDate',
|
|
1194
1451
|
'newEmailSecret',
|
|
1195
|
-
'
|
|
1452
|
+
'newEmailAddress',
|
|
1196
1453
|
'cancelEmailSecret',
|
|
1197
|
-
'
|
|
1454
|
+
'cancelEmailAddress',
|
|
1455
|
+
'recoverSecret',
|
|
1456
|
+
'recoverSecretDate',
|
|
1457
|
+
'salt',
|
|
1458
|
+
'password',
|
|
1459
|
+
'passwordTemp',
|
|
1460
|
+
'revokeTokenDate',
|
|
1461
|
+
'totpSecret',
|
|
1198
1462
|
];
|
|
1199
1463
|
User.DEFAULT_ALLOWLIST_DATA = [];
|
|
1200
1464
|
User.clientEnabledStaticMethods = [
|
|
@@ -1205,4 +1469,5 @@ User.clientEnabledStaticMethods = [
|
|
|
1205
1469
|
'getClientConfig',
|
|
1206
1470
|
];
|
|
1207
1471
|
User.searchRestrictedData = [...User.DEFAULT_PRIVATE_DATA];
|
|
1472
|
+
exports.default = User;
|
|
1208
1473
|
//# sourceMappingURL=User.js.map
|