@nymphjs/tilmeld 1.0.0-beta.8 → 1.0.0-beta.81
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 +368 -0
- package/README.md +14 -1
- package/dist/AbleObject.d.ts +15 -0
- package/dist/AbleObject.js +16 -1
- package/dist/AbleObject.js.map +1 -1
- package/dist/Group.d.ts +130 -10
- package/dist/Group.js +311 -129
- package/dist/Group.js.map +1 -1
- package/dist/Group.test.js +77 -2
- package/dist/Group.test.js.map +1 -1
- package/dist/Tilmeld.d.ts +237 -9
- package/dist/Tilmeld.js +569 -201
- package/dist/Tilmeld.js.map +1 -1
- package/dist/User.d.ts +344 -16
- package/dist/User.js +979 -313
- package/dist/User.js.map +1 -1
- package/dist/User.test.js +63 -2
- package/dist/User.test.js.map +1 -1
- package/dist/conf/d.d.ts +250 -3
- package/dist/conf/defaults.js +146 -98
- 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/errors/AccessControlError.d.ts +2 -1
- package/dist/errors/AccessControlError.js +3 -2
- package/dist/errors/AccessControlError.js.map +1 -1
- package/dist/errors/BadDataError.d.ts +2 -1
- package/dist/errors/BadDataError.js +3 -2
- package/dist/errors/BadDataError.js.map +1 -1
- package/dist/errors/BadEmailError.d.ts +2 -1
- package/dist/errors/BadEmailError.js +3 -2
- package/dist/errors/BadEmailError.js.map +1 -1
- package/dist/errors/BadUsernameError.d.ts +2 -1
- package/dist/errors/BadUsernameError.js +3 -2
- package/dist/errors/BadUsernameError.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +30 -26
- package/src/AbleObject.ts +2 -2
- package/src/Group.test.ts +102 -4
- package/src/Group.ts +274 -158
- package/src/Tilmeld.ts +383 -249
- package/src/User.test.ts +80 -4
- package/src/User.ts +1061 -382
- package/src/conf/d.ts +26 -13
- package/src/conf/defaults.ts +151 -104
- 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/index.ts +2 -0
- package/src/tilmeldJoiProps.ts +9 -9
- package/tsconfig.json +3 -3
- package/typedoc.json +4 -0
package/src/conf/d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import User, { UserData } from '../User';
|
|
|
9
9
|
*/
|
|
10
10
|
export interface Config {
|
|
11
11
|
/**
|
|
12
|
-
* The name of the app. Used in emails sent to users.
|
|
12
|
+
* The name of the app. Used in emails sent to users and as the TOTP issuer.
|
|
13
13
|
*/
|
|
14
14
|
appName: string;
|
|
15
15
|
/**
|
|
@@ -206,7 +206,11 @@ export interface Config {
|
|
|
206
206
|
*/
|
|
207
207
|
validEmailRegexNotice: string;
|
|
208
208
|
/**
|
|
209
|
-
* The
|
|
209
|
+
* The minimum length for usernames.
|
|
210
|
+
*/
|
|
211
|
+
minUsernameLength: number;
|
|
212
|
+
/**
|
|
213
|
+
* The maximum length for usernames. `Infinity` for unlimited.
|
|
210
214
|
*/
|
|
211
215
|
maxUsernameLength: number;
|
|
212
216
|
/**
|
|
@@ -217,6 +221,11 @@ export interface Config {
|
|
|
217
221
|
* How long from current time, in seconds, the JWT token expires.
|
|
218
222
|
*/
|
|
219
223
|
jwtExpire: number;
|
|
224
|
+
/**
|
|
225
|
+
* How long from current time, in seconds, the JWT token for a user switch
|
|
226
|
+
* expires.
|
|
227
|
+
*/
|
|
228
|
+
jwtSwitchExpire: number;
|
|
220
229
|
/**
|
|
221
230
|
* How long, in seconds, before the JWT token expires to give the user a new
|
|
222
231
|
* one.
|
|
@@ -225,21 +234,25 @@ export interface Config {
|
|
|
225
234
|
/**
|
|
226
235
|
* Function to build the JWT for user sessions.
|
|
227
236
|
*/
|
|
228
|
-
jwtBuilder: (
|
|
237
|
+
jwtBuilder: (
|
|
238
|
+
config: Config,
|
|
239
|
+
user: User & UserData,
|
|
240
|
+
switchToken?: boolean,
|
|
241
|
+
) => string;
|
|
229
242
|
/**
|
|
230
|
-
* Function to verify that a JWT was signed with the secret key,
|
|
231
|
-
* data, validate the XSRF token, and extract the GUID.
|
|
243
|
+
* Function to verify that a JWT was signed with the secret key, validate its
|
|
244
|
+
* data, validate the XSRF token, and extract the GUID and dates.
|
|
232
245
|
*
|
|
233
246
|
* If no XSRF token is supplied, ignore it.
|
|
234
247
|
*
|
|
235
|
-
* Return false if the JWT is not valid, or an
|
|
236
|
-
*
|
|
248
|
+
* Return false if the JWT is not valid, or an object with GUID, issued date,
|
|
249
|
+
* and expire date otherwise.
|
|
237
250
|
*/
|
|
238
251
|
jwtExtract: (
|
|
239
252
|
config: Config,
|
|
240
253
|
token: string,
|
|
241
|
-
xsrfToken?: string
|
|
242
|
-
) => { guid: string; expire: Date } | null;
|
|
254
|
+
xsrfToken?: string,
|
|
255
|
+
) => { guid: string; issued: Date; expire: Date } | null;
|
|
243
256
|
/**
|
|
244
257
|
* The absolute path to the email template directory. Used by the default
|
|
245
258
|
* email sender.
|
|
@@ -255,7 +268,7 @@ export interface Config {
|
|
|
255
268
|
configEmail: (
|
|
256
269
|
tilmeld: Tilmeld,
|
|
257
270
|
options: EmailOptions,
|
|
258
|
-
user: User & UserData
|
|
271
|
+
user: User & UserData,
|
|
259
272
|
) => Promise<EmailConfig>;
|
|
260
273
|
/**
|
|
261
274
|
* Send an email to a user. Uses `email-templates` by default.
|
|
@@ -285,7 +298,7 @@ export interface Config {
|
|
|
285
298
|
sendEmail: (
|
|
286
299
|
tilmeld: Tilmeld,
|
|
287
300
|
options: EmailOptions,
|
|
288
|
-
user: User & UserData
|
|
301
|
+
user: User & UserData,
|
|
289
302
|
) => Promise<boolean>;
|
|
290
303
|
/**
|
|
291
304
|
* The address you'd like to receive a notification of registered users, if
|
|
@@ -295,9 +308,9 @@ export interface Config {
|
|
|
295
308
|
/**
|
|
296
309
|
* The validator used to check groups before saving.
|
|
297
310
|
*/
|
|
298
|
-
validatorGroup: (group: Group & GroupData) => void;
|
|
311
|
+
validatorGroup: (tilmeld: Tilmeld, group: Group & GroupData) => void;
|
|
299
312
|
/**
|
|
300
313
|
* The validator used to check users before saving.
|
|
301
314
|
*/
|
|
302
|
-
validatorUser: (user: User & UserData) => void;
|
|
315
|
+
validatorUser: (tilmeld: Tilmeld, user: User & UserData) => void;
|
|
303
316
|
}
|
package/src/conf/defaults.ts
CHANGED
|
@@ -49,11 +49,13 @@ export default {
|
|
|
49
49
|
validEmailRegex:
|
|
50
50
|
/^[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
51
|
validEmailRegexNotice: 'Email must be a correctly formatted address.',
|
|
52
|
+
minUsernameLength: 1,
|
|
52
53
|
maxUsernameLength: 128,
|
|
53
54
|
jwtSecret: '',
|
|
54
|
-
jwtExpire: 60 * 60 * 24 * 7 *
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
jwtExpire: 60 * 60 * 24 * 7 * 24, // 24 weeks(ish) (6 months)
|
|
56
|
+
jwtSwitchExpire: 60 * 60 * 6, // 6 hours
|
|
57
|
+
jwtRenew: 60 * 60 * 24 * 7 * 12, // 12 weeks(ish) (3 months)
|
|
58
|
+
jwtBuilder: (config, user, switchToken) => {
|
|
57
59
|
const secret = config.jwtSecret;
|
|
58
60
|
if (secret === '') {
|
|
59
61
|
throw new Error('JWT secret is not configured.');
|
|
@@ -62,12 +64,14 @@ export default {
|
|
|
62
64
|
return jwt.sign(
|
|
63
65
|
{
|
|
64
66
|
iss: config.appUrl,
|
|
65
|
-
nbf: Date.now() / 1000,
|
|
67
|
+
nbf: Math.floor(Date.now() / 1000),
|
|
68
|
+
exp:
|
|
69
|
+
Math.floor(Date.now() / 1000) +
|
|
70
|
+
(switchToken ? config.jwtSwitchExpire : config.jwtExpire),
|
|
66
71
|
guid: user.guid,
|
|
67
72
|
xsrfToken: 'TILMELDXSRF-' + nanoid(),
|
|
68
73
|
},
|
|
69
74
|
secret,
|
|
70
|
-
{ expiresIn: config.jwtExpire }
|
|
71
75
|
);
|
|
72
76
|
},
|
|
73
77
|
jwtExtract: (config, token, xsrfToken?) => {
|
|
@@ -79,14 +83,14 @@ export default {
|
|
|
79
83
|
try {
|
|
80
84
|
const payload = jwt.verify(token, secret, {
|
|
81
85
|
issuer: config.appUrl,
|
|
82
|
-
clockTolerance:
|
|
86
|
+
clockTolerance: 60,
|
|
83
87
|
});
|
|
84
88
|
|
|
85
89
|
if (typeof payload === 'string') {
|
|
86
90
|
return null;
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
const { guid, xsrfToken: jwtXsrfToken, exp } = payload;
|
|
93
|
+
const { guid, xsrfToken: jwtXsrfToken, exp, nbf } = payload;
|
|
90
94
|
|
|
91
95
|
if (xsrfToken != null && xsrfToken !== jwtXsrfToken) {
|
|
92
96
|
return null;
|
|
@@ -96,13 +100,19 @@ export default {
|
|
|
96
100
|
return null;
|
|
97
101
|
}
|
|
98
102
|
|
|
103
|
+
if (nbf == null) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const issued = new Date(nbf * 1000);
|
|
108
|
+
|
|
99
109
|
if (exp == null) {
|
|
100
110
|
return null;
|
|
101
111
|
}
|
|
102
112
|
|
|
103
113
|
const expire = new Date(exp * 1000);
|
|
104
114
|
|
|
105
|
-
return { guid, expire };
|
|
115
|
+
return { guid, issued, expire };
|
|
106
116
|
} catch (e: any) {
|
|
107
117
|
return null;
|
|
108
118
|
}
|
|
@@ -121,14 +131,14 @@ export default {
|
|
|
121
131
|
},
|
|
122
132
|
sendEmail: async (tilmeld, options, user) => {
|
|
123
133
|
const email = new Email(
|
|
124
|
-
await tilmeld.config.configEmail(tilmeld, options, user)
|
|
134
|
+
await tilmeld.config.configEmail(tilmeld, options, user),
|
|
125
135
|
);
|
|
126
136
|
try {
|
|
127
137
|
const result = await email.send({
|
|
128
138
|
...options,
|
|
129
139
|
template: path.join(
|
|
130
140
|
tilmeld.config.emailTemplateDir,
|
|
131
|
-
options.template ?? '.'
|
|
141
|
+
options.template ?? '.',
|
|
132
142
|
),
|
|
133
143
|
locals: {
|
|
134
144
|
// System
|
|
@@ -156,29 +166,48 @@ export default {
|
|
|
156
166
|
});
|
|
157
167
|
return !!result;
|
|
158
168
|
} catch (e: any) {
|
|
169
|
+
tilmeld.nymph.config.debugError('tilmeld', `Failed to send email: ${e}`);
|
|
159
170
|
return false;
|
|
160
171
|
}
|
|
161
172
|
},
|
|
162
173
|
userRegisteredRecipient: null,
|
|
163
|
-
validatorGroup: (group) => {
|
|
174
|
+
validatorGroup: (tilmeld, group) => {
|
|
175
|
+
const nameFields = tilmeld.config.userFields.includes('name')
|
|
176
|
+
? {
|
|
177
|
+
name: Joi.string()
|
|
178
|
+
.trim(false)
|
|
179
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
180
|
+
name: 'control characters',
|
|
181
|
+
invert: true,
|
|
182
|
+
})
|
|
183
|
+
.max(512)
|
|
184
|
+
.required(),
|
|
185
|
+
}
|
|
186
|
+
: {};
|
|
187
|
+
const emailFields = tilmeld.config.userFields.includes('email')
|
|
188
|
+
? {
|
|
189
|
+
email: Joi.string()
|
|
190
|
+
.trim(false)
|
|
191
|
+
.email({ minDomainSegments: 1, tlds: false })
|
|
192
|
+
.required(),
|
|
193
|
+
}
|
|
194
|
+
: {};
|
|
195
|
+
const phoneFields = tilmeld.config.userFields.includes('phone')
|
|
196
|
+
? {
|
|
197
|
+
phone: Joi.string()
|
|
198
|
+
.trim(false)
|
|
199
|
+
.pattern(/^[0-9]+$/, 'numbers'),
|
|
200
|
+
}
|
|
201
|
+
: {};
|
|
164
202
|
Joi.attempt(
|
|
165
203
|
group.$getValidatable(),
|
|
166
204
|
Joi.object().keys({
|
|
167
205
|
...nymphJoiProps,
|
|
206
|
+
...nameFields,
|
|
207
|
+
...emailFields,
|
|
208
|
+
...phoneFields,
|
|
168
209
|
groupname: Joi.string().trim(false).required(),
|
|
169
210
|
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
211
|
avatar: Joi.string()
|
|
183
212
|
.trim(false)
|
|
184
213
|
.uri()
|
|
@@ -187,9 +216,6 @@ export default {
|
|
|
187
216
|
invert: true,
|
|
188
217
|
})
|
|
189
218
|
.max(1024),
|
|
190
|
-
phone: Joi.string()
|
|
191
|
-
.trim(false)
|
|
192
|
-
.pattern(/^[0-9]+$/, 'numbers'),
|
|
193
219
|
parent: Joi.object().instance(Group),
|
|
194
220
|
user: Joi.object().instance(User),
|
|
195
221
|
abilities: Joi.array().items(
|
|
@@ -198,56 +224,110 @@ export default {
|
|
|
198
224
|
name: 'control characters',
|
|
199
225
|
invert: true,
|
|
200
226
|
})
|
|
201
|
-
.max(256)
|
|
227
|
+
.max(256),
|
|
202
228
|
),
|
|
203
229
|
defaultPrimary: Joi.boolean(),
|
|
204
230
|
defaultSecondary: Joi.boolean(),
|
|
205
231
|
unverifiedSecondary: Joi.boolean(),
|
|
206
232
|
}),
|
|
207
|
-
'Invalid Group: '
|
|
233
|
+
'Invalid Group: ',
|
|
208
234
|
);
|
|
209
235
|
},
|
|
210
|
-
validatorUser: (user) => {
|
|
236
|
+
validatorUser: (tilmeld, user) => {
|
|
237
|
+
const nameFields = tilmeld.config.userFields.includes('name')
|
|
238
|
+
? {
|
|
239
|
+
nameFirst: Joi.string()
|
|
240
|
+
.trim(false)
|
|
241
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
242
|
+
name: 'control characters',
|
|
243
|
+
invert: true,
|
|
244
|
+
})
|
|
245
|
+
.max(512)
|
|
246
|
+
.required(),
|
|
247
|
+
nameMiddle: Joi.string()
|
|
248
|
+
.trim(false)
|
|
249
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
250
|
+
name: 'control characters',
|
|
251
|
+
invert: true,
|
|
252
|
+
})
|
|
253
|
+
.max(512),
|
|
254
|
+
nameLast: Joi.string()
|
|
255
|
+
.trim(false)
|
|
256
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
257
|
+
name: 'control characters',
|
|
258
|
+
invert: true,
|
|
259
|
+
})
|
|
260
|
+
.max(512),
|
|
261
|
+
name: Joi.string()
|
|
262
|
+
.trim(false)
|
|
263
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
264
|
+
name: 'control characters',
|
|
265
|
+
invert: true,
|
|
266
|
+
})
|
|
267
|
+
.max(512)
|
|
268
|
+
.required(),
|
|
269
|
+
}
|
|
270
|
+
: {};
|
|
271
|
+
const emailFields = tilmeld.config.userFields.includes('email')
|
|
272
|
+
? {
|
|
273
|
+
email: Joi.string()
|
|
274
|
+
.trim(false)
|
|
275
|
+
.email({ minDomainSegments: 1, tlds: false })
|
|
276
|
+
.required(),
|
|
277
|
+
secret: Joi.string()
|
|
278
|
+
.trim(false)
|
|
279
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
280
|
+
name: 'control characters',
|
|
281
|
+
invert: true,
|
|
282
|
+
})
|
|
283
|
+
.length(21),
|
|
284
|
+
emailChangeDate: Joi.number(),
|
|
285
|
+
newEmailSecret: Joi.string()
|
|
286
|
+
.trim(false)
|
|
287
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
288
|
+
name: 'control characters',
|
|
289
|
+
invert: true,
|
|
290
|
+
})
|
|
291
|
+
.length(21),
|
|
292
|
+
newEmailAddress: Joi.string()
|
|
293
|
+
.trim(false)
|
|
294
|
+
.email({ minDomainSegments: 1, tlds: false }),
|
|
295
|
+
cancelEmailSecret: Joi.string()
|
|
296
|
+
.trim(false)
|
|
297
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
298
|
+
name: 'control characters',
|
|
299
|
+
invert: true,
|
|
300
|
+
})
|
|
301
|
+
.length(21),
|
|
302
|
+
cancelEmailAddress: Joi.string()
|
|
303
|
+
.trim(false)
|
|
304
|
+
.email({ minDomainSegments: 1, tlds: false }),
|
|
305
|
+
recoverSecret: Joi.string()
|
|
306
|
+
.trim(false)
|
|
307
|
+
.pattern(/[\x01-\x1F\x7F]/, {
|
|
308
|
+
name: 'control characters',
|
|
309
|
+
invert: true,
|
|
310
|
+
})
|
|
311
|
+
.length(10),
|
|
312
|
+
recoverSecretDate: Joi.number(),
|
|
313
|
+
}
|
|
314
|
+
: {};
|
|
315
|
+
const phoneFields = tilmeld.config.userFields.includes('phone')
|
|
316
|
+
? {
|
|
317
|
+
phone: Joi.string()
|
|
318
|
+
.trim(false)
|
|
319
|
+
.pattern(/^[0-9]+$/, 'numbers'),
|
|
320
|
+
}
|
|
321
|
+
: {};
|
|
211
322
|
Joi.attempt(
|
|
212
323
|
user.$getValidatable(),
|
|
213
324
|
Joi.object().keys({
|
|
214
325
|
...nymphJoiProps,
|
|
326
|
+
...nameFields,
|
|
327
|
+
...emailFields,
|
|
328
|
+
...phoneFields,
|
|
215
329
|
username: Joi.string().trim(false).required(),
|
|
216
330
|
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
331
|
avatar: Joi.string()
|
|
252
332
|
.trim(false)
|
|
253
333
|
.uri()
|
|
@@ -256,9 +336,6 @@ export default {
|
|
|
256
336
|
invert: true,
|
|
257
337
|
})
|
|
258
338
|
.max(1024),
|
|
259
|
-
phone: Joi.string()
|
|
260
|
-
.trim(false)
|
|
261
|
-
.pattern(/^[0-9]+$/, 'numbers'),
|
|
262
339
|
group: Joi.object().instance(Group),
|
|
263
340
|
groups: Joi.array().items(Joi.object().instance(Group)).required(),
|
|
264
341
|
abilities: Joi.array().items(
|
|
@@ -267,49 +344,19 @@ export default {
|
|
|
267
344
|
name: 'control characters',
|
|
268
345
|
invert: true,
|
|
269
346
|
})
|
|
270
|
-
.max(256)
|
|
347
|
+
.max(256),
|
|
271
348
|
),
|
|
272
349
|
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
350
|
salt: Joi.string().trim(false),
|
|
310
351
|
password: Joi.string().trim(false).required(),
|
|
352
|
+
revokeTokenDate: Joi.number(),
|
|
353
|
+
totpSecret: Joi.string()
|
|
354
|
+
.trim(false)
|
|
355
|
+
.min(24)
|
|
356
|
+
.max(40)
|
|
357
|
+
.pattern(/^[A-Z234567]+$/, 'base32 encoded unpadded'),
|
|
311
358
|
}),
|
|
312
|
-
'Invalid User: '
|
|
359
|
+
'Invalid User: ',
|
|
313
360
|
);
|
|
314
361
|
},
|
|
315
362
|
} as Config;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Entity, EntityConstructor, Nymph } from '@nymphjs/nymph';
|
|
2
|
+
|
|
3
|
+
import Tilmeld from './Tilmeld';
|
|
4
|
+
|
|
5
|
+
export function enforceTilmeld(object: Nymph | Entity | EntityConstructor) {
|
|
6
|
+
let tilmeld: Tilmeld | undefined;
|
|
7
|
+
if (object instanceof Entity) {
|
|
8
|
+
tilmeld = object.$nymph.tilmeld as Tilmeld | undefined;
|
|
9
|
+
} else if (object instanceof Nymph) {
|
|
10
|
+
tilmeld = object.tilmeld as Tilmeld | undefined;
|
|
11
|
+
} else {
|
|
12
|
+
tilmeld = object.nymph.tilmeld as Tilmeld | undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (tilmeld == null) {
|
|
16
|
+
throw new Error('Tilmeld was not configured on this Nymph instance!');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return tilmeld;
|
|
20
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import { HttpError } from '@nymphjs/server';
|
|
2
|
+
|
|
3
|
+
export class AccessControlError extends HttpError {
|
|
2
4
|
constructor(message: string) {
|
|
3
|
-
super(message);
|
|
5
|
+
super(message, 403);
|
|
4
6
|
this.name = 'AccessControlError';
|
|
5
7
|
}
|
|
6
8
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import { HttpError } from '@nymphjs/server';
|
|
2
|
+
|
|
3
|
+
export class BadUsernameError extends HttpError {
|
|
2
4
|
constructor(message: string) {
|
|
3
|
-
super(message);
|
|
5
|
+
super(message, 400);
|
|
4
6
|
this.name = 'BadUsernameError';
|
|
5
7
|
}
|
|
6
8
|
}
|
package/src/index.ts
CHANGED
package/src/tilmeldJoiProps.ts
CHANGED
|
@@ -11,36 +11,36 @@ export default {
|
|
|
11
11
|
TilmeldAccessLevels.NO_ACCESS,
|
|
12
12
|
TilmeldAccessLevels.READ_ACCESS,
|
|
13
13
|
TilmeldAccessLevels.WRITE_ACCESS,
|
|
14
|
-
TilmeldAccessLevels.FULL_ACCESS
|
|
14
|
+
TilmeldAccessLevels.FULL_ACCESS,
|
|
15
15
|
),
|
|
16
16
|
acGroup: Joi.number().valid(
|
|
17
17
|
TilmeldAccessLevels.NO_ACCESS,
|
|
18
18
|
TilmeldAccessLevels.READ_ACCESS,
|
|
19
19
|
TilmeldAccessLevels.WRITE_ACCESS,
|
|
20
|
-
TilmeldAccessLevels.FULL_ACCESS
|
|
20
|
+
TilmeldAccessLevels.FULL_ACCESS,
|
|
21
21
|
),
|
|
22
22
|
acOther: Joi.number().valid(
|
|
23
23
|
TilmeldAccessLevels.NO_ACCESS,
|
|
24
24
|
TilmeldAccessLevels.READ_ACCESS,
|
|
25
25
|
TilmeldAccessLevels.WRITE_ACCESS,
|
|
26
|
-
TilmeldAccessLevels.FULL_ACCESS
|
|
26
|
+
TilmeldAccessLevels.FULL_ACCESS,
|
|
27
27
|
),
|
|
28
28
|
acRead: Joi.array().items(
|
|
29
29
|
Joi.alternatives().try(
|
|
30
30
|
Joi.object().instance(User),
|
|
31
|
-
Joi.object().instance(Group)
|
|
32
|
-
)
|
|
31
|
+
Joi.object().instance(Group),
|
|
32
|
+
),
|
|
33
33
|
),
|
|
34
34
|
acWrite: Joi.array().items(
|
|
35
35
|
Joi.alternatives().try(
|
|
36
36
|
Joi.object().instance(User),
|
|
37
|
-
Joi.object().instance(Group)
|
|
38
|
-
)
|
|
37
|
+
Joi.object().instance(Group),
|
|
38
|
+
),
|
|
39
39
|
),
|
|
40
40
|
acFull: Joi.array().items(
|
|
41
41
|
Joi.alternatives().try(
|
|
42
42
|
Joi.object().instance(User),
|
|
43
|
-
Joi.object().instance(Group)
|
|
44
|
-
)
|
|
43
|
+
Joi.object().instance(Group),
|
|
44
|
+
),
|
|
45
45
|
),
|
|
46
46
|
};
|
package/tsconfig.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"extends": "@tsconfig/recommended/tsconfig.json",
|
|
3
3
|
|
|
4
4
|
"compilerOptions": {
|
|
5
|
-
"lib": ["
|
|
6
|
-
"target": "
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"target": "ES2022",
|
|
7
7
|
"noImplicitAny": true,
|
|
8
|
-
"removeComments":
|
|
8
|
+
"removeComments": false,
|
|
9
9
|
"sourceMap": true,
|
|
10
10
|
"outDir": "dist",
|
|
11
11
|
"resolveJsonModule": true,
|
package/typedoc.json
ADDED