ldap-authentication 4.3.0 → 4.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.
package/test/test.spec.js CHANGED
@@ -1,6 +1,11 @@
1
- const { authenticate, LdapAuthenticationError } = require('../index.js')
2
-
3
- const url = process.env.INGITHUB ? 'ldap://localhost:1389' : 'ldap://ldap:1389'
1
+ const {
2
+ authenticate,
3
+ LdapAuthenticationError,
4
+ AUTH_RESULT_FAILURE,
5
+ AUTH_RESULT_FAILURE_IDENTITY_NOT_FOUND,
6
+ AUTH_RESULT_FAILURE_CREDENTIAL_INVALID,
7
+ } = require('../index.js')
8
+ const { url, adminDn, adminPassword, userSearchBase } = require('./config')
4
9
 
5
10
  describe('ldap-authentication test', () => {
6
11
  it('Use an admin user to check if user exists', async () => {
@@ -8,10 +13,10 @@ describe('ldap-authentication test', () => {
8
13
  ldapOpts: {
9
14
  url: url,
10
15
  },
11
- adminDn: 'cn=read-only-admin,dc=example,dc=com',
12
- adminPassword: 'password',
16
+ adminDn: adminDn,
17
+ adminPassword: adminPassword,
13
18
  verifyUserExists: true,
14
- userSearchBase: 'dc=example,dc=com',
19
+ userSearchBase: userSearchBase,
15
20
  usernameAttribute: 'uid',
16
21
  username: 'gauss',
17
22
  }
@@ -25,10 +30,10 @@ describe('ldap-authentication test', () => {
25
30
  ldapOpts: {
26
31
  url: url,
27
32
  },
28
- adminDn: 'cn=read-only-admin,dc=example,dc=com',
29
- adminPassword: 'password',
33
+ adminDn: adminDn,
34
+ adminPassword: adminPassword,
30
35
  verifyUserExists: true,
31
- userSearchBase: 'dc=example,dc=com',
36
+ userSearchBase: userSearchBase,
32
37
  usernameAttribute: 'uid',
33
38
  username: 'gauss',
34
39
  attributes: ['uid', 'sn'],
@@ -45,10 +50,10 @@ describe('ldap-authentication test', () => {
45
50
  ldapOpts: {
46
51
  url: url,
47
52
  },
48
- adminDn: 'cn=read-only-admin,dc=example,dc=com',
49
- adminPassword: 'password',
53
+ adminDn: adminDn,
54
+ adminPassword: adminPassword,
50
55
  userPassword: 'password',
51
- userSearchBase: 'dc=example,dc=com',
56
+ userSearchBase: userSearchBase,
52
57
  usernameAttribute: 'uid',
53
58
  username: 'gauss',
54
59
  }
@@ -62,10 +67,10 @@ describe('ldap-authentication test', () => {
62
67
  ldapOpts: {
63
68
  url: url,
64
69
  },
65
- adminDn: 'cn=read-only-admin,dc=example,dc=com',
66
- adminPassword: 'password',
70
+ adminDn: adminDn,
71
+ adminPassword: adminPassword,
67
72
  userPassword: 'password',
68
- userSearchBase: 'dc=example,dc=com',
73
+ userSearchBase: userSearchBase,
69
74
  usernameAttribute: 'uid',
70
75
  username: 'gauss',
71
76
  attributes: ['uid', 'sn'],
@@ -77,6 +82,28 @@ describe('ldap-authentication test', () => {
77
82
  expect(user.sn).toEqual('Bar1')
78
83
  expect(user.cn).toBeUndefined()
79
84
  })
85
+ it('Use an admin user to authenticate a user with a comma in the CN (e2e of the DN escaping)', async () => {
86
+ let options = {
87
+ ldapOpts: {
88
+ url: url,
89
+ },
90
+ adminDn: adminDn,
91
+ adminPassword: adminPassword,
92
+ userPassword: 'password',
93
+ userSearchBase: userSearchBase,
94
+ usernameAttribute: 'uid',
95
+ username: 'doe',
96
+ }
97
+
98
+ let user = await authenticate(options)
99
+ expect(user).toBeTruthy()
100
+ expect(user.uid).toEqual('doe')
101
+ // the admin finds `cn=Doe, John,ou=users,...` and must bind with the
102
+ // comma-escaped DN to succeed
103
+ expect(user.cn).toEqual('Doe, John')
104
+ expect(user.dn).toContain('cn=Doe')
105
+ expect(user.dn).toContain('ou=users,dc=example,dc=com')
106
+ })
80
107
  it('Use an regular user to authenticate iteself', async () => {
81
108
  let options = {
82
109
  ldapOpts: {
@@ -84,7 +111,7 @@ describe('ldap-authentication test', () => {
84
111
  },
85
112
  userDn: 'cn=einstein,ou=users,dc=example,dc=com',
86
113
  userPassword: 'password',
87
- userSearchBase: 'dc=example,dc=com',
114
+ userSearchBase: userSearchBase,
88
115
  usernameAttribute: 'uid',
89
116
  username: 'einstein',
90
117
  }
@@ -100,7 +127,7 @@ describe('ldap-authentication test', () => {
100
127
  },
101
128
  userDn: 'cn=einstein,ou=users,dc=example,dc=com',
102
129
  userPassword: 'password',
103
- userSearchBase: 'dc=example,dc=com',
130
+ userSearchBase: userSearchBase,
104
131
  usernameAttribute: 'uid',
105
132
  username: 'einstein',
106
133
  attributes: ['uid', 'sn'],
@@ -129,13 +156,13 @@ describe('ldap-authentication test', () => {
129
156
  ldapOpts: {
130
157
  url: url,
131
158
  },
132
- adminDn: 'cn=read-only-admin,dc=example,dc=com',
133
- adminPassword: 'password',
159
+ adminDn: adminDn,
160
+ adminPassword: adminPassword,
134
161
  userPassword: 'password',
135
- userSearchBase: 'dc=example,dc=com',
162
+ userSearchBase: userSearchBase,
136
163
  usernameAttribute: 'uid',
137
164
  username: 'gauss',
138
- groupsSearchBase: 'dc=example,dc=com',
165
+ groupsSearchBase: userSearchBase,
139
166
  groupClass: 'groupOfNames',
140
167
  groupMemberAttribute: 'member',
141
168
  groupMemberUserAttribute: 'dn',
@@ -153,10 +180,10 @@ describe('ldap-authentication test', () => {
153
180
  },
154
181
  userDn: 'cn=gauss,ou=users,dc=example,dc=com',
155
182
  userPassword: 'password',
156
- userSearchBase: 'dc=example,dc=com',
183
+ userSearchBase: userSearchBase,
157
184
  usernameAttribute: 'uid',
158
185
  username: 'gauss',
159
- groupsSearchBase: 'dc=example,dc=com',
186
+ groupsSearchBase: userSearchBase,
160
187
  groupClass: 'groupOfNames',
161
188
  groupMemberAttribute: 'member',
162
189
  groupMemberUserAttribute: 'dn',
@@ -178,10 +205,10 @@ describe('ldap-authentication test', () => {
178
205
  },
179
206
  userDn: 'cn=gauss,ou=users,dc=example,dc=com',
180
207
  userPassword: 'password',
181
- userSearchBase: 'dc=example,dc=com',
208
+ userSearchBase: userSearchBase,
182
209
  usernameAttribute: 'uid',
183
210
  username: 'gauss',
184
- groupsSearchBase: 'dc=example,dc=com',
211
+ groupsSearchBase: userSearchBase,
185
212
  groupClass: 'groupOfUniqueNames',
186
213
  }
187
214
 
@@ -200,7 +227,7 @@ describe('ldap-authentication negative test', () => {
200
227
  adminDn: 'cn=not-exist,dc=example,dc=com',
201
228
  adminPassword: 'password',
202
229
  userPassword: 'password',
203
- userSearchBase: 'dc=example,dc=com',
230
+ userSearchBase: userSearchBase,
204
231
  usernameAttribute: 'uid',
205
232
  username: 'gauss',
206
233
  }
@@ -213,20 +240,23 @@ describe('ldap-authentication negative test', () => {
213
240
  }
214
241
 
215
242
  expect(e).toBeTruthy()
243
+ expect(e).toBeInstanceOf(LdapAuthenticationError)
244
+ expect(e.code).toEqual(AUTH_RESULT_FAILURE)
216
245
  })
217
246
  it('wrong admin password should fail', async () => {
218
247
  let options = {
219
248
  ldapOpts: {
220
249
  url: url,
221
250
  },
222
- adminDn: 'cn=read-only-admin,dc=example,dc=com',
251
+ adminDn: adminDn,
223
252
  adminPassword: '',
224
253
  userPassword: 'password',
225
- userSearchBase: 'dc=example,dc=com',
254
+ userSearchBase: userSearchBase,
226
255
  usernameAttribute: 'uid',
227
256
  username: 'gauss',
228
257
  }
229
258
 
259
+ // '' is a falsy value, so this exercises the options validation, not the server
230
260
  let e = null
231
261
  try {
232
262
  await authenticate(options)
@@ -235,16 +265,17 @@ describe('ldap-authentication negative test', () => {
235
265
  }
236
266
 
237
267
  expect(e).toBeTruthy()
268
+ expect(e).toBeInstanceOf(LdapAuthenticationError)
238
269
  })
239
270
  it('admin auth wrong username should fail', async () => {
240
271
  let options = {
241
272
  ldapOpts: {
242
273
  url: url,
243
274
  },
244
- adminDn: 'cn=read-only-admin,dc=example,dc=com',
245
- adminPassword: 'password',
275
+ adminDn: adminDn,
276
+ adminPassword: adminPassword,
246
277
  userPassword: 'password',
247
- userSearchBase: 'dc=example,dc=com',
278
+ userSearchBase: userSearchBase,
248
279
  usernameAttribute: 'uid',
249
280
  username: 'wrong',
250
281
  }
@@ -257,16 +288,18 @@ describe('ldap-authentication negative test', () => {
257
288
  }
258
289
 
259
290
  expect(e).toBeTruthy()
291
+ expect(e).toBeInstanceOf(LdapAuthenticationError)
292
+ expect(e.code).toEqual(AUTH_RESULT_FAILURE_IDENTITY_NOT_FOUND)
260
293
  })
261
294
  it('admin auth wrong user password should fail', async () => {
262
295
  let options = {
263
296
  ldapOpts: {
264
297
  url: url,
265
298
  },
266
- adminDn: 'cn=read-only-admin,dc=example,dc=com',
267
- adminPassword: 'password',
299
+ adminDn: adminDn,
300
+ adminPassword: adminPassword,
268
301
  userPassword: 'wrongpassword',
269
- userSearchBase: 'dc=example,dc=com',
302
+ userSearchBase: userSearchBase,
270
303
  usernameAttribute: 'uid',
271
304
  username: 'gauss',
272
305
  }
@@ -279,6 +312,8 @@ describe('ldap-authentication negative test', () => {
279
312
  }
280
313
 
281
314
  expect(e).toBeTruthy()
315
+ expect(e).toBeInstanceOf(LdapAuthenticationError)
316
+ expect(e.code).toEqual(AUTH_RESULT_FAILURE_CREDENTIAL_INVALID)
282
317
  })
283
318
  it('user auth wrong username should fail', async () => {
284
319
  let options = {
@@ -287,7 +322,7 @@ describe('ldap-authentication negative test', () => {
287
322
  },
288
323
  userDn: 'cn=not-exist,dc=example,dc=com',
289
324
  userPassword: 'password',
290
- userSearchBase: 'dc=example,dc=com',
325
+ userSearchBase: userSearchBase,
291
326
  usernameAttribute: 'uid',
292
327
  username: 'gauss',
293
328
  }
@@ -300,6 +335,7 @@ describe('ldap-authentication negative test', () => {
300
335
  }
301
336
 
302
337
  expect(e).toBeTruthy()
338
+ expect(e).toBeInstanceOf(LdapAuthenticationError)
303
339
  })
304
340
  it('user auth wrong user password should fail', async () => {
305
341
  let options = {
@@ -308,7 +344,7 @@ describe('ldap-authentication negative test', () => {
308
344
  },
309
345
  userDn: 'cn=gauss,dc=example,dc=com',
310
346
  userPassword: 'wrongpassword',
311
- userSearchBase: 'dc=example,dc=com',
347
+ userSearchBase: userSearchBase,
312
348
  usernameAttribute: 'uid',
313
349
  username: 'gauss',
314
350
  }
@@ -321,6 +357,7 @@ describe('ldap-authentication negative test', () => {
321
357
  }
322
358
 
323
359
  expect(e).toBeTruthy()
360
+ expect(e).toBeInstanceOf(LdapAuthenticationError)
324
361
  })
325
362
  it('Use an regular user to authenticate iteself without search with wrong password should fail', async () => {
326
363
  let options = {
@@ -331,13 +368,16 @@ describe('ldap-authentication negative test', () => {
331
368
  userPassword: '',
332
369
  }
333
370
 
371
+ let e = null
334
372
  try {
335
373
  await authenticate(options)
336
374
  } catch (error) {
337
375
  e = error
338
376
  }
339
377
 
378
+ // '' is a falsy value, so this exercises the options validation, not the server
340
379
  expect(e).toBeTruthy()
380
+ expect(e).toBeInstanceOf(LdapAuthenticationError)
341
381
  })
342
382
  it('Wrong options give LdapAuthenticationError', async () => {
343
383
  let options = {
@@ -347,10 +387,11 @@ describe('ldap-authentication negative test', () => {
347
387
  userDn: 'cn=einstein,ou=users,dc=example,dc=com',
348
388
  userPassword: 'password',
349
389
  usernameAttribute: 'wrongattribute',
350
- userSearchBase: 'dc=example,dc=com',
390
+ userSearchBase: userSearchBase,
351
391
  username: 'einstein',
352
392
  }
353
393
 
394
+ let e = null
354
395
  try {
355
396
  await authenticate(options)
356
397
  } catch (error) {
@@ -369,10 +410,11 @@ describe('ldap-authentication negative test', () => {
369
410
  userDn: 'uid=einstein,dc=example,dc=com',
370
411
  userPassword: 'password',
371
412
  usernameAttribute: 'cn',
372
- userSearchBase: 'dc=example,dc=com',
413
+ userSearchBase: userSearchBase,
373
414
  username: 'einstein',
374
415
  }
375
416
 
417
+ let e = null
376
418
  try {
377
419
  await authenticate(options)
378
420
  } catch (error) {
@@ -390,11 +432,12 @@ describe('ldap-authentication negative test', () => {
390
432
  userDn: 'uid=einstein,dc=example,dc=com',
391
433
  userPassword: 'password',
392
434
  usernameAttribute: 'cn',
393
- userSearchBase: 'dc=example,dc=com',
435
+ userSearchBase: userSearchBase,
394
436
  username: 'einstein',
395
437
  starttls: true,
396
438
  }
397
439
 
440
+ let e = null
398
441
  try {
399
442
  await authenticate(options)
400
443
  } catch (error) {
@@ -410,10 +453,10 @@ describe('ldap-authentication negative test', () => {
410
453
  },
411
454
  userDn: 'cn=gauss,ou=users,dc=example,dc=com',
412
455
  userPassword: 'password',
413
- userSearchBase: 'dc=example,dc=com',
456
+ userSearchBase: userSearchBase,
414
457
  usernameAttribute: 'uid',
415
458
  username: 'gauss',
416
- groupsSearchBase: 'dc=example,dc=com',
459
+ groupsSearchBase: userSearchBase,
417
460
  groupClass: 'groupOfNames',
418
461
  groupMemberAttribute: 'member',
419
462
  groupMemberUserAttribute: 'dnWRONG',
@@ -423,4 +466,43 @@ describe('ldap-authentication negative test', () => {
423
466
  expect(user).toBeTruthy()
424
467
  expect(user.groups.length).toBeLessThan(1)
425
468
  })
469
+ it('Missing required options should throw LdapAuthenticationError listing all of them', async () => {
470
+ let e = null
471
+ try {
472
+ await authenticate({
473
+ ldapOpts: {
474
+ url: url,
475
+ },
476
+ adminDn: adminDn,
477
+ usernameAttribute: 'uid',
478
+ username: 'gauss',
479
+ })
480
+ } catch (error) {
481
+ e = error
482
+ }
483
+
484
+ expect(e).toBeTruthy()
485
+ expect(e).toBeInstanceOf(LdapAuthenticationError)
486
+ // all missing fields are reported at once, not one assert at a time
487
+ expect(e.message).toContain('adminPassword')
488
+ expect(e.message).toContain('userSearchBase')
489
+ expect(e.message).toContain('userPassword')
490
+ })
491
+ it('No adminDn and no userDn should throw LdapAuthenticationError', async () => {
492
+ let e = null
493
+ try {
494
+ await authenticate({
495
+ ldapOpts: {
496
+ url: url,
497
+ },
498
+ userPassword: 'password',
499
+ })
500
+ } catch (error) {
501
+ e = error
502
+ }
503
+
504
+ expect(e).toBeTruthy()
505
+ expect(e).toBeInstanceOf(LdapAuthenticationError)
506
+ expect(e.message).toContain('adminDn or userDn')
507
+ })
426
508
  })
@@ -1,53 +0,0 @@
1
- name: 'Publish to npm'
2
-
3
- env:
4
- INGITHUB: true
5
-
6
- on:
7
- release:
8
- types: [created]
9
-
10
- jobs:
11
- publish:
12
- runs-on: ubuntu-latest
13
- name: 'Publish ${{ github.ref_name }}'
14
-
15
- steps:
16
- - uses: actions/checkout@v3
17
-
18
- - name: Install LDAP tools
19
- run: sudo apt-get install -y ldap-utils
20
-
21
- - name: Start containers
22
- run: docker compose -f "docker-compose.yml" up -d
23
-
24
- - name: Wait for LDAP server to become available
25
- uses: nick-fields/retry@v2
26
- with:
27
- timeout_minutes: 5
28
- max_attempts: 10
29
- polling_interval_seconds: 3
30
- command: |
31
- ldapsearch -LLL -o 'ldif-wrap=no' \
32
- -H ldap://localhost:1389 \
33
- -D 'cn=read-only-admin,dc=example,dc=com' \
34
- -w password \
35
- -b 'cn=einstein,ou=users,dc=example,dc=com' DN
36
-
37
- - name: Use Node.js 22.x
38
- uses: actions/setup-node@v3
39
- with:
40
- node-version: 22.x
41
- registry-url: 'https://registry.npmjs.org'
42
-
43
- - run: npm ci
44
- - run: npm run test
45
-
46
- - name: Stop containers
47
- if: always()
48
- run: docker compose -f "docker-compose.yml" down
49
-
50
- - name: Publish to npm
51
- run: npm publish
52
- env:
53
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}