adapt-authoring-auth-local 2.3.2 → 2.3.3
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/lib/PasswordUtils.js
CHANGED
|
@@ -124,7 +124,7 @@ class PasswordUtils {
|
|
|
124
124
|
if (new Date(tokenData.expiresAt) < new Date()) {
|
|
125
125
|
throw App.instance.errors.AUTH_TOKEN_EXPIRED
|
|
126
126
|
}
|
|
127
|
-
|
|
127
|
+
await users.findOne({ email: tokenData.email })
|
|
128
128
|
return tokenData
|
|
129
129
|
}
|
|
130
130
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adapt-authoring-auth-local",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"description": "Module which implements username/password (local) authentication",
|
|
5
5
|
"homepage": "https://github.com/adapt-security/adapt-authoring-auth-local",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -51,6 +51,13 @@ const mockUsers = {
|
|
|
51
51
|
find: async (query) => usersStore.filter(u => {
|
|
52
52
|
return Object.entries(query).every(([k, v]) => JSON.stringify(u[k]) === JSON.stringify(v))
|
|
53
53
|
}),
|
|
54
|
+
findOne: async (query) => {
|
|
55
|
+
const result = usersStore.find(u => {
|
|
56
|
+
return Object.entries(query).every(([k, v]) => JSON.stringify(u[k]) === JSON.stringify(v))
|
|
57
|
+
})
|
|
58
|
+
if (!result) throw mockErrors.NOT_FOUND.setData({ type: 'user' })
|
|
59
|
+
return result
|
|
60
|
+
},
|
|
54
61
|
update: async (query, data) => {
|
|
55
62
|
updateCalls.push({ query, data })
|
|
56
63
|
return { ...query, ...data }
|
|
@@ -59,7 +66,8 @@ const mockUsers = {
|
|
|
59
66
|
}
|
|
60
67
|
|
|
61
68
|
const mockRoles = {
|
|
62
|
-
find: async () => [{ _id: 'role-super-id', shortName: 'superuser' }]
|
|
69
|
+
find: async () => [{ _id: 'role-super-id', shortName: 'superuser' }],
|
|
70
|
+
findOne: async () => ({ _id: 'role-super-id', shortName: 'superuser' })
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
const mockMailer = {
|
|
@@ -241,6 +249,25 @@ describe('LocalAuthModule', () => {
|
|
|
241
249
|
})
|
|
242
250
|
|
|
243
251
|
describe('#authenticate()', () => {
|
|
252
|
+
let correctHash
|
|
253
|
+
|
|
254
|
+
before(async () => {
|
|
255
|
+
const { default: PasswordUtils } = await import('../lib/PasswordUtils.js')
|
|
256
|
+
correctHash = await PasswordUtils.generate('correctpass')
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
function createMockUser (overrides = {}) {
|
|
260
|
+
return {
|
|
261
|
+
_id: 'user-1',
|
|
262
|
+
password: correctHash,
|
|
263
|
+
isPermLocked: false,
|
|
264
|
+
isTempLocked: false,
|
|
265
|
+
failedLoginAttempts: 0,
|
|
266
|
+
lastFailedLoginAttempt: null,
|
|
267
|
+
...overrides
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
244
271
|
it('should throw when password is not provided in request body', async () => {
|
|
245
272
|
const req = { body: {} }
|
|
246
273
|
await assert.rejects(
|
|
@@ -250,16 +277,7 @@ describe('LocalAuthModule', () => {
|
|
|
250
277
|
})
|
|
251
278
|
|
|
252
279
|
it('should reset failed attempts on successful authentication', async () => {
|
|
253
|
-
const {
|
|
254
|
-
const hash = await PasswordUtils.generate('correctpass')
|
|
255
|
-
const user = {
|
|
256
|
-
_id: 'user-1',
|
|
257
|
-
password: hash,
|
|
258
|
-
isPermLocked: false,
|
|
259
|
-
isTempLocked: false,
|
|
260
|
-
failedLoginAttempts: 2,
|
|
261
|
-
lastFailedLoginAttempt: null
|
|
262
|
-
}
|
|
280
|
+
const user = createMockUser({ failedLoginAttempts: 2 })
|
|
263
281
|
const req = { body: { password: 'correctpass' } }
|
|
264
282
|
await mod.authenticate(user, req, {})
|
|
265
283
|
const lastUpdate = updateCalls[updateCalls.length - 1]
|
|
@@ -267,16 +285,7 @@ describe('LocalAuthModule', () => {
|
|
|
267
285
|
})
|
|
268
286
|
|
|
269
287
|
it('should increment failed attempts on wrong password', async () => {
|
|
270
|
-
const
|
|
271
|
-
const hash = await PasswordUtils.generate('correctpass')
|
|
272
|
-
const user = {
|
|
273
|
-
_id: 'user-1',
|
|
274
|
-
password: hash,
|
|
275
|
-
isPermLocked: false,
|
|
276
|
-
isTempLocked: false,
|
|
277
|
-
failedLoginAttempts: 0,
|
|
278
|
-
lastFailedLoginAttempt: null
|
|
279
|
-
}
|
|
288
|
+
const user = createMockUser()
|
|
280
289
|
const req = { body: { password: 'wrongpass' } }
|
|
281
290
|
await assert.rejects(() => mod.authenticate(user, req, {}))
|
|
282
291
|
const lastUpdate = updateCalls[updateCalls.length - 1]
|
|
@@ -284,16 +293,7 @@ describe('LocalAuthModule', () => {
|
|
|
284
293
|
})
|
|
285
294
|
|
|
286
295
|
it('should set lastFailedLoginAttempt on wrong password', async () => {
|
|
287
|
-
const
|
|
288
|
-
const hash = await PasswordUtils.generate('correctpass')
|
|
289
|
-
const user = {
|
|
290
|
-
_id: 'user-1',
|
|
291
|
-
password: hash,
|
|
292
|
-
isPermLocked: false,
|
|
293
|
-
isTempLocked: false,
|
|
294
|
-
failedLoginAttempts: 0,
|
|
295
|
-
lastFailedLoginAttempt: null
|
|
296
|
-
}
|
|
296
|
+
const user = createMockUser()
|
|
297
297
|
const req = { body: { password: 'wrongpass' } }
|
|
298
298
|
await assert.rejects(() => mod.authenticate(user, req, {}))
|
|
299
299
|
const lastUpdate = updateCalls[updateCalls.length - 1]
|
|
@@ -302,16 +302,7 @@ describe('LocalAuthModule', () => {
|
|
|
302
302
|
})
|
|
303
303
|
|
|
304
304
|
it('should temporarily lock after reaching failsUntilTemporaryLock threshold', async () => {
|
|
305
|
-
const {
|
|
306
|
-
const hash = await PasswordUtils.generate('correctpass')
|
|
307
|
-
const user = {
|
|
308
|
-
_id: 'user-1',
|
|
309
|
-
password: hash,
|
|
310
|
-
isPermLocked: false,
|
|
311
|
-
isTempLocked: false,
|
|
312
|
-
failedLoginAttempts: 4, // one more will be 5 (the threshold)
|
|
313
|
-
lastFailedLoginAttempt: null
|
|
314
|
-
}
|
|
305
|
+
const user = createMockUser({ failedLoginAttempts: 4 })
|
|
315
306
|
const req = { body: { password: 'wrongpass' } }
|
|
316
307
|
await assert.rejects(
|
|
317
308
|
() => mod.authenticate(user, req, {}),
|
|
@@ -322,16 +313,7 @@ describe('LocalAuthModule', () => {
|
|
|
322
313
|
})
|
|
323
314
|
|
|
324
315
|
it('should permanently lock after reaching failsUntilPermanentLock threshold', async () => {
|
|
325
|
-
const {
|
|
326
|
-
const hash = await PasswordUtils.generate('correctpass')
|
|
327
|
-
const user = {
|
|
328
|
-
_id: 'user-1',
|
|
329
|
-
password: hash,
|
|
330
|
-
isPermLocked: false,
|
|
331
|
-
isTempLocked: false,
|
|
332
|
-
failedLoginAttempts: 19, // one more will be 20 (the threshold)
|
|
333
|
-
lastFailedLoginAttempt: null
|
|
334
|
-
}
|
|
316
|
+
const user = createMockUser({ failedLoginAttempts: 19 })
|
|
335
317
|
const req = { body: { password: 'wrongpass' } }
|
|
336
318
|
await assert.rejects(
|
|
337
319
|
() => mod.authenticate(user, req, {}),
|
|
@@ -342,16 +324,11 @@ describe('LocalAuthModule', () => {
|
|
|
342
324
|
})
|
|
343
325
|
|
|
344
326
|
it('should throw ACCOUNT_LOCKED_PERM when user is already permanently locked', async () => {
|
|
345
|
-
const
|
|
346
|
-
const hash = await PasswordUtils.generate('correctpass')
|
|
347
|
-
const user = {
|
|
348
|
-
_id: 'user-1',
|
|
349
|
-
password: hash,
|
|
327
|
+
const user = createMockUser({
|
|
350
328
|
isPermLocked: true,
|
|
351
|
-
isTempLocked: false,
|
|
352
329
|
failedLoginAttempts: 20,
|
|
353
330
|
lastFailedLoginAttempt: new Date().toISOString()
|
|
354
|
-
}
|
|
331
|
+
})
|
|
355
332
|
const req = { body: { password: 'correctpass' } }
|
|
356
333
|
await assert.rejects(
|
|
357
334
|
() => mod.authenticate(user, req, {}),
|
|
@@ -360,16 +337,11 @@ describe('LocalAuthModule', () => {
|
|
|
360
337
|
})
|
|
361
338
|
|
|
362
339
|
it('should not increment failed attempts when account is permanently locked', async () => {
|
|
363
|
-
const
|
|
364
|
-
const hash = await PasswordUtils.generate('correctpass')
|
|
365
|
-
const user = {
|
|
366
|
-
_id: 'user-1',
|
|
367
|
-
password: hash,
|
|
340
|
+
const user = createMockUser({
|
|
368
341
|
isPermLocked: true,
|
|
369
|
-
isTempLocked: false,
|
|
370
342
|
failedLoginAttempts: 20,
|
|
371
343
|
lastFailedLoginAttempt: new Date().toISOString()
|
|
372
|
-
}
|
|
344
|
+
})
|
|
373
345
|
const req = { body: { password: 'wrongpass' } }
|
|
374
346
|
await assert.rejects(() => mod.authenticate(user, req, {}))
|
|
375
347
|
const lastUpdate = updateCalls[updateCalls.length - 1]
|
|
@@ -377,16 +349,11 @@ describe('LocalAuthModule', () => {
|
|
|
377
349
|
})
|
|
378
350
|
|
|
379
351
|
it('should not increment failed attempts during temp lock timeout', async () => {
|
|
380
|
-
const
|
|
381
|
-
const hash = await PasswordUtils.generate('correctpass')
|
|
382
|
-
const user = {
|
|
383
|
-
_id: 'user-1',
|
|
384
|
-
password: hash,
|
|
385
|
-
isPermLocked: false,
|
|
352
|
+
const user = createMockUser({
|
|
386
353
|
isTempLocked: true,
|
|
387
354
|
failedLoginAttempts: 5,
|
|
388
355
|
lastFailedLoginAttempt: new Date().toISOString()
|
|
389
|
-
}
|
|
356
|
+
})
|
|
390
357
|
const req = { body: { password: 'wrongpass' } }
|
|
391
358
|
await assert.rejects(() => mod.authenticate(user, req, {}))
|
|
392
359
|
const lastUpdate = updateCalls[updateCalls.length - 1]
|
|
@@ -394,16 +361,7 @@ describe('LocalAuthModule', () => {
|
|
|
394
361
|
})
|
|
395
362
|
|
|
396
363
|
it('should clear lastFailedLoginAttempt on successful login', async () => {
|
|
397
|
-
const
|
|
398
|
-
const hash = await PasswordUtils.generate('correctpass')
|
|
399
|
-
const user = {
|
|
400
|
-
_id: 'user-1',
|
|
401
|
-
password: hash,
|
|
402
|
-
isPermLocked: false,
|
|
403
|
-
isTempLocked: false,
|
|
404
|
-
failedLoginAttempts: 0,
|
|
405
|
-
lastFailedLoginAttempt: null
|
|
406
|
-
}
|
|
364
|
+
const user = createMockUser()
|
|
407
365
|
const req = { body: { password: 'correctpass' } }
|
|
408
366
|
await mod.authenticate(user, req, {})
|
|
409
367
|
const lastUpdate = updateCalls[updateCalls.length - 1]
|
|
@@ -72,6 +72,11 @@ const mockMongodb = {
|
|
|
72
72
|
const mockUsers = {
|
|
73
73
|
find: async (query) => {
|
|
74
74
|
return mockUsersStore.filter(u => u.email === query.email)
|
|
75
|
+
},
|
|
76
|
+
findOne: async (query) => {
|
|
77
|
+
const result = mockUsersStore.find(u => u.email === query.email)
|
|
78
|
+
if (!result) throw mockErrors.NOT_FOUND.setData({ id: query.email, type: 'user' })
|
|
79
|
+
return result
|
|
75
80
|
}
|
|
76
81
|
}
|
|
77
82
|
|