adapt-authoring-auth-local 2.3.1 → 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/LocalAuthModule.js
CHANGED
|
@@ -99,7 +99,7 @@ class LocalAuthModule extends AbstractAuthModule {
|
|
|
99
99
|
*/
|
|
100
100
|
async registerSuper (data) {
|
|
101
101
|
const [roles, users] = await this.app.waitForModule('roles', 'users')
|
|
102
|
-
const
|
|
102
|
+
const superRole = await roles.findOne({ shortName: 'superuser' })
|
|
103
103
|
const superUsers = await users.find({ roles: [superRole._id] })
|
|
104
104
|
if (superUsers.length) {
|
|
105
105
|
throw this.app.errors.SUPER_USER_EXISTS
|
|
@@ -256,7 +256,7 @@ class LocalAuthModule extends AbstractAuthModule {
|
|
|
256
256
|
// allow for a specific email to be passed via body, falling back to the email from the auth data
|
|
257
257
|
email = req.body.email || req.auth.user.email
|
|
258
258
|
// validate the existing password for security
|
|
259
|
-
const
|
|
259
|
+
const user = await this.users.findOne({ email })
|
|
260
260
|
await compare(req.body.oldPassword, user.password)
|
|
261
261
|
} else { // no authenticated, so should expect body data
|
|
262
262
|
const tokenData = await PasswordUtils.validateReset(req.body.token)
|
package/lib/PasswordUtils.js
CHANGED
|
@@ -69,11 +69,7 @@ class PasswordUtils {
|
|
|
69
69
|
*/
|
|
70
70
|
static async createReset (email, lifespan) {
|
|
71
71
|
const [mongodb, users] = await App.instance.waitForModule('mongodb', 'users')
|
|
72
|
-
const
|
|
73
|
-
if (!user) {
|
|
74
|
-
throw App.instance.errors.NOT_FOUND
|
|
75
|
-
.setData({ type: 'user', id: email })
|
|
76
|
-
}
|
|
72
|
+
const user = await users.findOne({ email })
|
|
77
73
|
if (user.authType !== 'local') {
|
|
78
74
|
const authlocal = await App.instance.waitForModule('auth-local')
|
|
79
75
|
authlocal.log('error', `Failed to reset ${user._id} password, not authenticated with local auth`)
|
|
@@ -128,11 +124,7 @@ class PasswordUtils {
|
|
|
128
124
|
if (new Date(tokenData.expiresAt) < new Date()) {
|
|
129
125
|
throw App.instance.errors.AUTH_TOKEN_EXPIRED
|
|
130
126
|
}
|
|
131
|
-
|
|
132
|
-
if (!user) {
|
|
133
|
-
throw App.instance.errors.NOT_FOUND
|
|
134
|
-
.setData({ type: 'user', id: tokenData.email })
|
|
135
|
-
}
|
|
127
|
+
await users.findOne({ email: tokenData.email })
|
|
136
128
|
return tokenData
|
|
137
129
|
}
|
|
138
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
|
|