adapt-authoring-auth-local 2.3.2 → 2.4.0

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.
@@ -1,32 +1,16 @@
1
1
  name: Release
2
+
2
3
  on:
3
4
  push:
4
5
  branches:
5
6
  - master
6
7
 
8
+ permissions:
9
+ contents: write
10
+ issues: write
11
+ pull-requests: write
12
+ id-token: write
13
+
7
14
  jobs:
8
15
  release:
9
- name: Release
10
- runs-on: ubuntu-latest
11
- permissions:
12
- contents: write # to be able to publish a GitHub release
13
- issues: write # to be able to comment on released issues
14
- pull-requests: write # to be able to comment on released pull requests
15
- id-token: write # to enable use of OIDC for trusted publishing and npm provenance
16
- steps:
17
- - name: Checkout
18
- uses: actions/checkout@v3
19
- with:
20
- fetch-depth: 0
21
- - name: Setup Node.js
22
- uses: actions/setup-node@v3
23
- with:
24
- node-version: 'lts/*'
25
- - name: Update npm
26
- run: npm install -g npm@latest
27
- - name: Install dependencies
28
- run: npm install
29
- - name: Release
30
- env:
31
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32
- run: npx semantic-release
16
+ uses: adaptlearning/semantic-release-config/.github/workflows/release.yml@master
@@ -53,7 +53,7 @@ class PasswordUtils {
53
53
  }
54
54
  const jsonschema = await App.instance.waitForModule('jsonschema')
55
55
  const schema = await jsonschema.getSchema('localpassword')
56
- await schema.validate({ password: plainPassword })
56
+ schema.validate({ password: plainPassword })
57
57
 
58
58
  const saltRounds = await PasswordUtils.getConfig('saltRounds')
59
59
  const salt = await promisify(bcrypt.genSalt)(saltRounds)
@@ -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
- const user = await users.findOne({ email: tokenData.email })
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.2",
3
+ "version": "2.4.0",
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",
@@ -26,37 +26,11 @@
26
26
  "adapt-authoring-users": "^1.0.2"
27
27
  },
28
28
  "devDependencies": {
29
+ "@adaptlearning/semantic-release-config": "^1.0.0",
29
30
  "adapt-authoring-server": "^2.1.0",
30
- "@semantic-release/git": "^10.0.1",
31
- "conventional-changelog-eslint": "^6.0.0",
32
- "semantic-release": "^25.0.2",
33
31
  "standard": "^17.1.0"
34
32
  },
35
33
  "release": {
36
- "plugins": [
37
- [
38
- "@semantic-release/commit-analyzer",
39
- {
40
- "preset": "eslint"
41
- }
42
- ],
43
- [
44
- "@semantic-release/release-notes-generator",
45
- {
46
- "preset": "eslint"
47
- }
48
- ],
49
- "@semantic-release/npm",
50
- "@semantic-release/github",
51
- [
52
- "@semantic-release/git",
53
- {
54
- "assets": [
55
- "package.json"
56
- ],
57
- "message": "Chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
58
- }
59
- ]
60
- ]
34
+ "extends": "@adaptlearning/semantic-release-config"
61
35
  }
62
36
  }
@@ -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 = {
@@ -83,7 +91,7 @@ const mockMongodb = {
83
91
 
84
92
  const mockJsonschema = {
85
93
  getSchema: async () => ({
86
- validate: async () => true
94
+ validate: () => true
87
95
  })
88
96
  }
89
97
 
@@ -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 { default: PasswordUtils } = await import('../lib/PasswordUtils.js')
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 { default: PasswordUtils } = await import('../lib/PasswordUtils.js')
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 { default: PasswordUtils } = await import('../lib/PasswordUtils.js')
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 { default: PasswordUtils } = await import('../lib/PasswordUtils.js')
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 { default: PasswordUtils } = await import('../lib/PasswordUtils.js')
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 { default: PasswordUtils } = await import('../lib/PasswordUtils.js')
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 { default: PasswordUtils } = await import('../lib/PasswordUtils.js')
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 { default: PasswordUtils } = await import('../lib/PasswordUtils.js')
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 { default: PasswordUtils } = await import('../lib/PasswordUtils.js')
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]
@@ -43,7 +43,7 @@ const mockAuthlocal = {
43
43
 
44
44
  const mockJsonschema = {
45
45
  getSchema: async () => ({
46
- validate: async () => true
46
+ validate: () => true
47
47
  })
48
48
  }
49
49
 
@@ -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