@nymphjs/tilmeld 1.0.0-beta.14 → 1.0.0-beta.140
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 +668 -0
- package/README.md +21 -5
- package/dist/AbleObject.d.ts +20 -2
- package/dist/AbleObject.js +25 -13
- package/dist/AbleObject.js.map +1 -1
- package/dist/Group.d.ts +190 -27
- package/dist/Group.js +827 -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 +275 -14
- package/dist/Tilmeld.js +789 -310
- package/dist/Tilmeld.js.map +1 -1
- package/dist/Tilmeld.types.d.ts +9 -9
- package/dist/Tilmeld.types.js +1 -2
- package/dist/User.d.ts +410 -41
- package/dist/User.js +2042 -1057
- 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 +215 -141
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +361 -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 +10 -14
- package/dist/index.js +11 -35
- package/dist/index.js.map +1 -1
- package/dist/testArtifacts.d.ts +26 -0
- package/dist/testArtifacts.js +1346 -0
- package/dist/testArtifacts.js.map +1 -0
- package/dist/tilmeldJoiProps.d.ts +4 -5
- 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 +17 -11
- package/src/Group.testArtifacts.ts +159 -0
- package/src/Group.ts +712 -299
- package/src/Tilmeld.ts +666 -344
- package/src/Tilmeld.types.ts +9 -9
- package/src/User.testArtifacts.ts +226 -0
- package/src/User.ts +1984 -879
- package/src/conf/defaults.ts +205 -109
- package/src/conf/index.ts +381 -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 +11 -14
- package/src/testArtifacts.ts +2148 -0
- package/src/tilmeldJoiProps.ts +9 -24
- 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/conf/defaults.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { URL } from 'url';
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { URL, fileURLToPath } from 'node:url';
|
|
3
3
|
import jwt from 'jsonwebtoken';
|
|
4
4
|
import Email from 'email-templates';
|
|
5
5
|
import Joi from 'joi';
|
|
6
6
|
import { nanoid } from '@nymphjs/guid';
|
|
7
7
|
import { nymphJoiProps } from '@nymphjs/nymph';
|
|
8
8
|
|
|
9
|
-
import { Config } from './
|
|
10
|
-
import User from '../User';
|
|
11
|
-
import Group from '../Group';
|
|
9
|
+
import type { Config } from './index.js';
|
|
10
|
+
import { User } from '../User.js';
|
|
11
|
+
import { Group } from '../Group.js';
|
|
12
|
+
|
|
13
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
14
|
|
|
13
15
|
export default {
|
|
14
16
|
appName: 'My Nymph App',
|
|
@@ -18,6 +20,7 @@ export default {
|
|
|
18
20
|
setupPath: '/tilmeld',
|
|
19
21
|
createAdmin: true,
|
|
20
22
|
emailUsernames: true,
|
|
23
|
+
domainSupport: false,
|
|
21
24
|
allowRegistration: true,
|
|
22
25
|
allowUsernameChange: true,
|
|
23
26
|
enableUserSearch: false,
|
|
@@ -49,25 +52,43 @@ export default {
|
|
|
49
52
|
validEmailRegex:
|
|
50
53
|
/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
|
|
51
54
|
validEmailRegexNotice: 'Email must be a correctly formatted address.',
|
|
55
|
+
validDomainRegex:
|
|
56
|
+
/^(((?!-))(xn--|_)?[a-z0-9\-]{0,61}[a-z0-9]{1,1}\.)*(xn--)?([a-z0-9][a-z0-9\-]{0,60}|[a-z0-9\-]{1,30}\.[a-z]{2,})$/,
|
|
57
|
+
validDomainRegexNotice: 'Domain must be a correctly formatted domain name.',
|
|
58
|
+
minUsernameLength: 1,
|
|
52
59
|
maxUsernameLength: 128,
|
|
53
60
|
jwtSecret: '',
|
|
54
|
-
jwtExpire: 60 * 60 * 24 * 7 *
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
jwtExpire: 60 * 60 * 24 * 7 * 24, // 24 weeks(ish) (6 months)
|
|
62
|
+
jwtSwitchExpire: 60 * 60 * 6, // 6 hours
|
|
63
|
+
jwtRenew: 60 * 60 * 24 * 7 * 12, // 12 weeks(ish) (3 months)
|
|
64
|
+
jwtBuilder: (config, user, switchToken, switchForUser) => {
|
|
57
65
|
const secret = config.jwtSecret;
|
|
58
66
|
if (secret === '') {
|
|
59
67
|
throw new Error('JWT secret is not configured.');
|
|
60
68
|
}
|
|
61
69
|
|
|
70
|
+
if (switchToken && (!switchForUser || switchForUser.guid == null)) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
"Issuing a switch token requires providing the user it's for.",
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
62
76
|
return jwt.sign(
|
|
63
77
|
{
|
|
64
78
|
iss: config.appUrl,
|
|
65
|
-
|
|
79
|
+
sub: switchForUser ? switchForUser.guid : user.guid,
|
|
80
|
+
nbf: Math.floor(Date.now() / 1000),
|
|
81
|
+
exp:
|
|
82
|
+
Math.floor(Date.now() / 1000) +
|
|
83
|
+
(switchToken ? config.jwtSwitchExpire : config.jwtExpire),
|
|
84
|
+
// TODO: remove this guid claim in favor of sub claim
|
|
85
|
+
// this needs to be done long enough after Nov 1 2026, when sub added
|
|
86
|
+
// also don't forget to change how switchForUser guid is stored.
|
|
66
87
|
guid: user.guid,
|
|
67
88
|
xsrfToken: 'TILMELDXSRF-' + nanoid(),
|
|
89
|
+
...(switchToken ? { switch: true } : {}),
|
|
68
90
|
},
|
|
69
91
|
secret,
|
|
70
|
-
{ expiresIn: config.jwtExpire }
|
|
71
92
|
);
|
|
72
93
|
},
|
|
73
94
|
jwtExtract: (config, token, xsrfToken?) => {
|
|
@@ -79,14 +100,21 @@ export default {
|
|
|
79
100
|
try {
|
|
80
101
|
const payload = jwt.verify(token, secret, {
|
|
81
102
|
issuer: config.appUrl,
|
|
82
|
-
clockTolerance:
|
|
103
|
+
clockTolerance: 60,
|
|
83
104
|
});
|
|
84
105
|
|
|
85
106
|
if (typeof payload === 'string') {
|
|
86
107
|
return null;
|
|
87
108
|
}
|
|
88
109
|
|
|
89
|
-
const {
|
|
110
|
+
const {
|
|
111
|
+
sub,
|
|
112
|
+
guid,
|
|
113
|
+
xsrfToken: jwtXsrfToken,
|
|
114
|
+
switch: switchBool,
|
|
115
|
+
exp,
|
|
116
|
+
nbf,
|
|
117
|
+
} = payload;
|
|
90
118
|
|
|
91
119
|
if (xsrfToken != null && xsrfToken !== jwtXsrfToken) {
|
|
92
120
|
return null;
|
|
@@ -96,13 +124,19 @@ export default {
|
|
|
96
124
|
return null;
|
|
97
125
|
}
|
|
98
126
|
|
|
127
|
+
if (nbf == null) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const issued = new Date(nbf * 1000);
|
|
132
|
+
|
|
99
133
|
if (exp == null) {
|
|
100
134
|
return null;
|
|
101
135
|
}
|
|
102
136
|
|
|
103
137
|
const expire = new Date(exp * 1000);
|
|
104
138
|
|
|
105
|
-
return { guid, expire };
|
|
139
|
+
return { sub, guid, issued, expire, switch: switchBool };
|
|
106
140
|
} catch (e: any) {
|
|
107
141
|
return null;
|
|
108
142
|
}
|
|
@@ -117,18 +151,21 @@ export default {
|
|
|
117
151
|
address: `noreply@${appUrl.hostname}`,
|
|
118
152
|
},
|
|
119
153
|
},
|
|
154
|
+
preview: {
|
|
155
|
+
openSimulator: false,
|
|
156
|
+
},
|
|
120
157
|
};
|
|
121
158
|
},
|
|
122
159
|
sendEmail: async (tilmeld, options, user) => {
|
|
123
160
|
const email = new Email(
|
|
124
|
-
await tilmeld.config.configEmail(tilmeld, options, user)
|
|
161
|
+
await tilmeld.config.configEmail(tilmeld, options, user),
|
|
125
162
|
);
|
|
126
163
|
try {
|
|
127
164
|
const result = await email.send({
|
|
128
165
|
...options,
|
|
129
166
|
template: path.join(
|
|
130
167
|
tilmeld.config.emailTemplateDir,
|
|
131
|
-
options.template ?? '.'
|
|
168
|
+
options.template ?? '.',
|
|
132
169
|
),
|
|
133
170
|
locals: {
|
|
134
171
|
// System
|
|
@@ -156,29 +193,70 @@ export default {
|
|
|
156
193
|
});
|
|
157
194
|
return !!result;
|
|
158
195
|
} catch (e: any) {
|
|
196
|
+
tilmeld.nymph.config.debugError('tilmeld', `Failed to send email: ${e}`);
|
|
159
197
|
return false;
|
|
160
198
|
}
|
|
161
199
|
},
|
|
162
200
|
userRegisteredRecipient: null,
|
|
163
|
-
|
|
201
|
+
failedLoginAttempt: (_user, _data) => {
|
|
202
|
+
// Wait 3-5 seconds to return a failure. This rate limits failed login
|
|
203
|
+
// attempts.
|
|
204
|
+
return new Promise((resolve) =>
|
|
205
|
+
setTimeout(resolve, Math.random() * 2000 + 3000),
|
|
206
|
+
);
|
|
207
|
+
},
|
|
208
|
+
validatorGroup: (tilmeld, group) => {
|
|
209
|
+
const nameFields = tilmeld.config.userFields.includes('name')
|
|
210
|
+
? {
|
|
211
|
+
name: Joi.string()
|
|
212
|
+
.trim(false)
|
|
213
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
214
|
+
name: 'control characters',
|
|
215
|
+
invert: true,
|
|
216
|
+
})
|
|
217
|
+
.max(512)
|
|
218
|
+
.required(),
|
|
219
|
+
}
|
|
220
|
+
: {};
|
|
221
|
+
const emailFields = tilmeld.config.userFields.includes('email')
|
|
222
|
+
? {
|
|
223
|
+
email: Joi.string()
|
|
224
|
+
.trim(false)
|
|
225
|
+
.email({ minDomainSegments: 1, tlds: false })
|
|
226
|
+
.required(),
|
|
227
|
+
}
|
|
228
|
+
: {};
|
|
229
|
+
const phoneFields = tilmeld.config.userFields.includes('phone')
|
|
230
|
+
? {
|
|
231
|
+
phone: Joi.string()
|
|
232
|
+
.trim(false)
|
|
233
|
+
.pattern(/^[0-9]+$/, 'numbers'),
|
|
234
|
+
}
|
|
235
|
+
: {};
|
|
236
|
+
for (let ability of group.abilities ?? []) {
|
|
237
|
+
if (ability.startsWith('system/')) {
|
|
238
|
+
throw new Error('Invalid Group: Groups cannot have system abilities.');
|
|
239
|
+
}
|
|
240
|
+
if (ability === 'tilmeld/admin') {
|
|
241
|
+
throw new Error(
|
|
242
|
+
'Invalid Group: Groups cannot have Tilmeld admin ability.',
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
if (ability === 'tilmeld/switch') {
|
|
246
|
+
throw new Error(
|
|
247
|
+
'Invalid Group: Groups cannot have Tilmeld switch ability.',
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
164
251
|
Joi.attempt(
|
|
165
252
|
group.$getValidatable(),
|
|
166
253
|
Joi.object().keys({
|
|
167
254
|
...nymphJoiProps,
|
|
255
|
+
...nameFields,
|
|
256
|
+
...emailFields,
|
|
257
|
+
...phoneFields,
|
|
168
258
|
groupname: Joi.string().trim(false).required(),
|
|
169
259
|
enabled: Joi.boolean().required(),
|
|
170
|
-
email: Joi.string()
|
|
171
|
-
.trim(false)
|
|
172
|
-
.email({ minDomainSegments: 1, tlds: false })
|
|
173
|
-
.required(),
|
|
174
|
-
name: Joi.string()
|
|
175
|
-
.trim(false)
|
|
176
|
-
.pattern(/[\x01-\x1F\x7F]/, {
|
|
177
|
-
name: 'control characters',
|
|
178
|
-
invert: true,
|
|
179
|
-
})
|
|
180
|
-
.max(512)
|
|
181
|
-
.required(),
|
|
182
260
|
avatar: Joi.string()
|
|
183
261
|
.trim(false)
|
|
184
262
|
.uri()
|
|
@@ -187,9 +265,6 @@ export default {
|
|
|
187
265
|
invert: true,
|
|
188
266
|
})
|
|
189
267
|
.max(1024),
|
|
190
|
-
phone: Joi.string()
|
|
191
|
-
.trim(false)
|
|
192
|
-
.pattern(/^[0-9]+$/, 'numbers'),
|
|
193
268
|
parent: Joi.object().instance(Group),
|
|
194
269
|
user: Joi.object().instance(User),
|
|
195
270
|
abilities: Joi.array().items(
|
|
@@ -198,56 +273,110 @@ export default {
|
|
|
198
273
|
name: 'control characters',
|
|
199
274
|
invert: true,
|
|
200
275
|
})
|
|
201
|
-
.max(256)
|
|
276
|
+
.max(256),
|
|
202
277
|
),
|
|
203
278
|
defaultPrimary: Joi.boolean(),
|
|
204
279
|
defaultSecondary: Joi.boolean(),
|
|
205
280
|
unverifiedSecondary: Joi.boolean(),
|
|
206
281
|
}),
|
|
207
|
-
'Invalid Group: '
|
|
282
|
+
'Invalid Group: ',
|
|
208
283
|
);
|
|
209
284
|
},
|
|
210
|
-
validatorUser: (user) => {
|
|
285
|
+
validatorUser: (tilmeld, user) => {
|
|
286
|
+
const nameFields = tilmeld.config.userFields.includes('name')
|
|
287
|
+
? {
|
|
288
|
+
nameFirst: Joi.string()
|
|
289
|
+
.trim(false)
|
|
290
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
291
|
+
name: 'control characters',
|
|
292
|
+
invert: true,
|
|
293
|
+
})
|
|
294
|
+
.max(512)
|
|
295
|
+
.required(),
|
|
296
|
+
nameMiddle: Joi.string()
|
|
297
|
+
.trim(false)
|
|
298
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
299
|
+
name: 'control characters',
|
|
300
|
+
invert: true,
|
|
301
|
+
})
|
|
302
|
+
.max(512),
|
|
303
|
+
nameLast: Joi.string()
|
|
304
|
+
.trim(false)
|
|
305
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
306
|
+
name: 'control characters',
|
|
307
|
+
invert: true,
|
|
308
|
+
})
|
|
309
|
+
.max(512),
|
|
310
|
+
name: Joi.string()
|
|
311
|
+
.trim(false)
|
|
312
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
313
|
+
name: 'control characters',
|
|
314
|
+
invert: true,
|
|
315
|
+
})
|
|
316
|
+
.max(512)
|
|
317
|
+
.required(),
|
|
318
|
+
}
|
|
319
|
+
: {};
|
|
320
|
+
const emailFields = tilmeld.config.userFields.includes('email')
|
|
321
|
+
? {
|
|
322
|
+
email: Joi.string()
|
|
323
|
+
.trim(false)
|
|
324
|
+
.email({ minDomainSegments: 1, tlds: false })
|
|
325
|
+
.required(),
|
|
326
|
+
secret: Joi.string()
|
|
327
|
+
.trim(false)
|
|
328
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
329
|
+
name: 'control characters',
|
|
330
|
+
invert: true,
|
|
331
|
+
})
|
|
332
|
+
.length(21),
|
|
333
|
+
emailChangeDate: Joi.number(),
|
|
334
|
+
newEmailSecret: Joi.string()
|
|
335
|
+
.trim(false)
|
|
336
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
337
|
+
name: 'control characters',
|
|
338
|
+
invert: true,
|
|
339
|
+
})
|
|
340
|
+
.length(21),
|
|
341
|
+
newEmailAddress: Joi.string()
|
|
342
|
+
.trim(false)
|
|
343
|
+
.email({ minDomainSegments: 1, tlds: false }),
|
|
344
|
+
cancelEmailSecret: Joi.string()
|
|
345
|
+
.trim(false)
|
|
346
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
347
|
+
name: 'control characters',
|
|
348
|
+
invert: true,
|
|
349
|
+
})
|
|
350
|
+
.length(21),
|
|
351
|
+
cancelEmailAddress: Joi.string()
|
|
352
|
+
.trim(false)
|
|
353
|
+
.email({ minDomainSegments: 1, tlds: false }),
|
|
354
|
+
recoverSecret: Joi.string()
|
|
355
|
+
.trim(false)
|
|
356
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
357
|
+
name: 'control characters',
|
|
358
|
+
invert: true,
|
|
359
|
+
})
|
|
360
|
+
.length(10),
|
|
361
|
+
recoverSecretDate: Joi.number(),
|
|
362
|
+
}
|
|
363
|
+
: {};
|
|
364
|
+
const phoneFields = tilmeld.config.userFields.includes('phone')
|
|
365
|
+
? {
|
|
366
|
+
phone: Joi.string()
|
|
367
|
+
.trim(false)
|
|
368
|
+
.pattern(/^[0-9]+$/, 'numbers'),
|
|
369
|
+
}
|
|
370
|
+
: {};
|
|
211
371
|
Joi.attempt(
|
|
212
372
|
user.$getValidatable(),
|
|
213
373
|
Joi.object().keys({
|
|
214
374
|
...nymphJoiProps,
|
|
375
|
+
...nameFields,
|
|
376
|
+
...emailFields,
|
|
377
|
+
...phoneFields,
|
|
215
378
|
username: Joi.string().trim(false).required(),
|
|
216
379
|
enabled: Joi.boolean().required(),
|
|
217
|
-
email: Joi.string()
|
|
218
|
-
.trim(false)
|
|
219
|
-
.email({ minDomainSegments: 1, tlds: false })
|
|
220
|
-
.required(),
|
|
221
|
-
nameFirst: Joi.string()
|
|
222
|
-
.trim(false)
|
|
223
|
-
.pattern(/[\x01-\x1F\x7F]/, {
|
|
224
|
-
name: 'control characters',
|
|
225
|
-
invert: true,
|
|
226
|
-
})
|
|
227
|
-
.max(512)
|
|
228
|
-
.required(),
|
|
229
|
-
nameMiddle: Joi.string()
|
|
230
|
-
.trim(false)
|
|
231
|
-
.pattern(/[\x01-\x1F\x7F]/, {
|
|
232
|
-
name: 'control characters',
|
|
233
|
-
invert: true,
|
|
234
|
-
})
|
|
235
|
-
.max(512),
|
|
236
|
-
nameLast: Joi.string()
|
|
237
|
-
.trim(false)
|
|
238
|
-
.pattern(/[\x01-\x1F\x7F]/, {
|
|
239
|
-
name: 'control characters',
|
|
240
|
-
invert: true,
|
|
241
|
-
})
|
|
242
|
-
.max(512),
|
|
243
|
-
name: Joi.string()
|
|
244
|
-
.trim(false)
|
|
245
|
-
.pattern(/[\x01-\x1F\x7F]/, {
|
|
246
|
-
name: 'control characters',
|
|
247
|
-
invert: true,
|
|
248
|
-
})
|
|
249
|
-
.max(512)
|
|
250
|
-
.required(),
|
|
251
380
|
avatar: Joi.string()
|
|
252
381
|
.trim(false)
|
|
253
382
|
.uri()
|
|
@@ -256,9 +385,6 @@ export default {
|
|
|
256
385
|
invert: true,
|
|
257
386
|
})
|
|
258
387
|
.max(1024),
|
|
259
|
-
phone: Joi.string()
|
|
260
|
-
.trim(false)
|
|
261
|
-
.pattern(/^[0-9]+$/, 'numbers'),
|
|
262
388
|
group: Joi.object().instance(Group),
|
|
263
389
|
groups: Joi.array().items(Joi.object().instance(Group)).required(),
|
|
264
390
|
abilities: Joi.array().items(
|
|
@@ -267,49 +393,19 @@ export default {
|
|
|
267
393
|
name: 'control characters',
|
|
268
394
|
invert: true,
|
|
269
395
|
})
|
|
270
|
-
.max(256)
|
|
396
|
+
.max(256),
|
|
271
397
|
),
|
|
272
398
|
inheritAbilities: Joi.boolean(),
|
|
273
|
-
secret: Joi.string()
|
|
274
|
-
.trim(false)
|
|
275
|
-
.pattern(/[\x01-\x1F\x7F]/, {
|
|
276
|
-
name: 'control characters',
|
|
277
|
-
invert: true,
|
|
278
|
-
})
|
|
279
|
-
.length(21),
|
|
280
|
-
emailChangeDate: Joi.number(),
|
|
281
|
-
newEmailSecret: Joi.string()
|
|
282
|
-
.trim(false)
|
|
283
|
-
.pattern(/[\x01-\x1F\x7F]/, {
|
|
284
|
-
name: 'control characters',
|
|
285
|
-
invert: true,
|
|
286
|
-
})
|
|
287
|
-
.length(21),
|
|
288
|
-
newEmailAddress: Joi.string()
|
|
289
|
-
.trim(false)
|
|
290
|
-
.email({ minDomainSegments: 1, tlds: false }),
|
|
291
|
-
cancelEmailSecret: Joi.string()
|
|
292
|
-
.trim(false)
|
|
293
|
-
.pattern(/[\x01-\x1F\x7F]/, {
|
|
294
|
-
name: 'control characters',
|
|
295
|
-
invert: true,
|
|
296
|
-
})
|
|
297
|
-
.length(21),
|
|
298
|
-
cancelEmailAddress: Joi.string()
|
|
299
|
-
.trim(false)
|
|
300
|
-
.email({ minDomainSegments: 1, tlds: false }),
|
|
301
|
-
recoverSecret: Joi.string()
|
|
302
|
-
.trim(false)
|
|
303
|
-
.pattern(/[\x01-\x1F\x7F]/, {
|
|
304
|
-
name: 'control characters',
|
|
305
|
-
invert: true,
|
|
306
|
-
})
|
|
307
|
-
.length(10),
|
|
308
|
-
recoverSecretDate: Joi.number(),
|
|
309
399
|
salt: Joi.string().trim(false),
|
|
310
400
|
password: Joi.string().trim(false).required(),
|
|
401
|
+
revokeTokenDate: Joi.number(),
|
|
402
|
+
totpSecret: Joi.string()
|
|
403
|
+
.trim(false)
|
|
404
|
+
.min(24)
|
|
405
|
+
.max(40)
|
|
406
|
+
.pattern(/^[A-Z234567]+$/, 'base32 encoded unpadded'),
|
|
311
407
|
}),
|
|
312
|
-
'Invalid User: '
|
|
408
|
+
'Invalid User: ',
|
|
313
409
|
);
|
|
314
410
|
},
|
|
315
411
|
} as Config;
|