@nymphjs/tilmeld 1.0.0-beta.5 → 1.0.0-beta.51
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 +242 -0
- package/README.md +14 -1
- package/dist/AbleObject.js.map +1 -1
- package/dist/Group.d.ts +4 -7
- package/dist/Group.js +124 -86
- package/dist/Group.js.map +1 -1
- package/dist/Tilmeld.d.ts +12 -9
- package/dist/Tilmeld.js +240 -179
- package/dist/Tilmeld.js.map +1 -1
- package/dist/User.d.ts +69 -13
- package/dist/User.js +492 -169
- package/dist/User.js.map +1 -1
- package/dist/conf/d.d.ts +4 -1
- package/dist/conf/defaults.js +22 -8
- 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 +29 -26
- package/src/AbleObject.ts +1 -1
- package/src/Group.test.ts +1 -1
- package/src/Group.ts +180 -130
- package/src/Tilmeld.ts +355 -244
- package/src/User.test.ts +1 -1
- package/src/User.ts +717 -230
- package/src/conf/d.ts +24 -11
- package/src/conf/defaults.ts +30 -14
- package/src/enforceTilmeld.ts +20 -0
- package/src/index.ts +2 -0
- package/src/tilmeldJoiProps.ts +9 -9
- 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
|
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
|
|
@@ -198,13 +208,13 @@ export default {
|
|
|
198
208
|
name: 'control characters',
|
|
199
209
|
invert: true,
|
|
200
210
|
})
|
|
201
|
-
.max(256)
|
|
211
|
+
.max(256),
|
|
202
212
|
),
|
|
203
213
|
defaultPrimary: Joi.boolean(),
|
|
204
214
|
defaultSecondary: Joi.boolean(),
|
|
205
215
|
unverifiedSecondary: Joi.boolean(),
|
|
206
216
|
}),
|
|
207
|
-
'Invalid Group: '
|
|
217
|
+
'Invalid Group: ',
|
|
208
218
|
);
|
|
209
219
|
},
|
|
210
220
|
validatorUser: (user) => {
|
|
@@ -267,7 +277,7 @@ export default {
|
|
|
267
277
|
name: 'control characters',
|
|
268
278
|
invert: true,
|
|
269
279
|
})
|
|
270
|
-
.max(256)
|
|
280
|
+
.max(256),
|
|
271
281
|
),
|
|
272
282
|
inheritAbilities: Joi.boolean(),
|
|
273
283
|
secret: Joi.string()
|
|
@@ -308,8 +318,14 @@ export default {
|
|
|
308
318
|
recoverSecretDate: Joi.number(),
|
|
309
319
|
salt: Joi.string().trim(false),
|
|
310
320
|
password: Joi.string().trim(false).required(),
|
|
321
|
+
revokeTokenDate: Joi.number(),
|
|
322
|
+
totpSecret: Joi.string()
|
|
323
|
+
.trim(false)
|
|
324
|
+
.min(24)
|
|
325
|
+
.max(40)
|
|
326
|
+
.pattern(/^[A-Z234567]+$/, 'base32 encoded unpadded'),
|
|
311
327
|
}),
|
|
312
|
-
'Invalid User: '
|
|
328
|
+
'Invalid User: ',
|
|
313
329
|
);
|
|
314
330
|
},
|
|
315
331
|
} 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
|
+
}
|
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/typedoc.json
ADDED