ldap-authentication 2.2.7 → 2.3.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  BSD 2-Clause License
2
2
 
3
- Copyright (c) 2021, shaozi
3
+ Copyright (c) 2022, shaozi
4
4
  All rights reserved.
5
5
 
6
6
  Redistribution and use in source and binary forms, with or without
package/README.md CHANGED
@@ -9,7 +9,7 @@ Make authentication with an LDAP server easy.
9
9
 
10
10
  ## Description
11
11
 
12
- This library use `ldapjs` as the underneath library. It has two modes of authentications:
12
+ This library use `ldapjs` as the underneath library. It has three modes of authentications:
13
13
 
14
14
  1. **Admin authenticate mode**. If an admin user is provided, the library will login (ldap bind) with the admin user,
15
15
  then search for the user to be authenticated, get its DN (distinguish name), then use
@@ -23,6 +23,9 @@ This library use `ldapjs` as the underneath library. It has two modes of authent
23
23
  Otherwise, the lib does a login with the `userDn` and `userPassword` (ldap bind),
24
24
  then does a search on the user and return the user's details.
25
25
 
26
+ 3. **Verify user exists**. If an `verifyUserExists : true` is provided, the library will login (ldap bind) with the admin user,
27
+ then search for the user to be verified. If the user exists, user details will be returned (without verifying the user's password).
28
+
26
29
  ## Features
27
30
 
28
31
  - Can use an admin to search and authenticate a user
@@ -64,6 +67,20 @@ let authenticated = await authenticate({
64
67
  userSearchBase: 'dc=example,dc=com',
65
68
  usernameAttribute: 'uid',
66
69
  username: 'gauss',
70
+ attributes: ['dn', 'sn', 'cn'],
71
+ })
72
+ ```
73
+
74
+ #### User exists verification and return user details (without user's password)
75
+
76
+ ```javascript
77
+ let authenticated = await authenticate({
78
+ ldapOpts: { url: 'ldap://ldap.forumsys.com' },
79
+ userDn: 'uid=gauss,dc=example,dc=com',
80
+ verifyUserExists: true,
81
+ userSearchBase: 'dc=example,dc=com',
82
+ usernameAttribute: 'uid',
83
+ username: 'gauss',
67
84
  })
68
85
  ```
69
86
 
@@ -80,6 +97,7 @@ let authenticated = await authenticate({
80
97
  groupsSearchBase: 'dc=example,dc=com',
81
98
  groupClass: 'groupOfUniqueNames',
82
99
  groupMemberAttribute: 'uniqueMember',
100
+ // groupMemberUserAttribute: 'dn'
83
101
  })
84
102
  ```
85
103
 
@@ -138,7 +156,8 @@ auth()
138
156
  - `adminPassword`: The password of the admin.
139
157
  - `userDn`: The DN of the user to be authenticated. This is only needed if `adminDn` and `adminPassword` are not provided.
140
158
  Example: `uid=gauss,dc=example,dc=com`
141
- - `userPassword`: The password of the user,
159
+ - `userPassword`: The password of the user
160
+ - `verifyUserExists` : if `true` user existence will be verified without password
142
161
  - `userSearchBase`: The ldap base DN to search the user. Example: `dc=example,dc=com`
143
162
  - `usernameAttribute`: The ldap search equality attribute name corresponding to the user's username.
144
163
  It will be used with the value in `username` to construct an ldap filter as `({attribute}={username})`
@@ -150,7 +169,10 @@ auth()
150
169
  - `username`: The username to authenticate with. It is used together with the name in `usernameAttribute`
151
170
  to construct an ldap filter as `({attribute}={username})`
152
171
  to find the user and get user details in LDAP. Example: `some user input`
172
+ - `attributes`: A list of attributes of a user details to be returned from the LDAP server.
173
+ If is set to `[]` or ommited, all details will be returned. Example: `['sn', 'cn']`
153
174
  - `starttls`: Boolean. Use `STARTTLS` or not
154
175
  - `groupsSearchBase`: if specified with groupClass, will serve as search base for authenticated user groups
155
176
  - `groupClass`: if specified with groupsSearchBase, will be used as objectClass in search filter for authenticated user groups
156
177
  - `groupMemberAttribute`: if specified with groupClass and groupsSearchBase, will be used as member name (if not specified this defaults to `member`) in search filter for authenticated user groups
178
+ - `groupMemberUserAttribute`: if specified with groupClass and groupsSearchBase, will be used as the attribute on the user object (if not specified this defaults to `dn`) in search filter for authenticated user groups
package/index.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ declare module 'ldap-authentication' {
2
+ type LDAPOpts = {
3
+ url: string
4
+ tlsOptions?: {
5
+ rejectUnauthorized: boolean
6
+ }
7
+ connectTimeout?: number
8
+ }
9
+ export type AuthenticationOptions = {
10
+ ldapOpts: LDAPOpts
11
+ userDn?: string
12
+ adminDn?: string
13
+ adminPassword?: string
14
+ userSearchBase?: string
15
+ usernameAttribute?: string
16
+ username?: string
17
+ verifyUserExists?: boolean
18
+ starttls?: Boolean
19
+ groupsSearchBase?: string
20
+ groupClass?: string
21
+ groupMemberAttribute?: string
22
+ groupMemberUserAttribute?: string
23
+ userPassword?: string
24
+ attributes?: string[]
25
+ }
26
+
27
+ export function authenticate(options: AuthenticationOptions): Promise<any>
28
+
29
+ export class LdapAuthenticationError extends Error {
30
+ constructor(message: any)
31
+ name: string
32
+ }
33
+ }
package/index.js CHANGED
@@ -35,27 +35,26 @@ function _ldapBind(dn, password, starttls, ldapOpts) {
35
35
  resolve(client)
36
36
  })
37
37
  }
38
- });
38
+ })
39
39
 
40
40
  //Fix for issue https://github.com/shaozi/ldap-authentication/issues/13
41
41
  client.on('timeout', (err) => {
42
- reject(err);
43
- });
42
+ reject(err)
43
+ })
44
44
  client.on('connectTimeout', (err) => {
45
- reject(err);
46
- });
45
+ reject(err)
46
+ })
47
47
  client.on('error', (err) => {
48
- reject(err);
49
- });
48
+ reject(err)
49
+ })
50
50
 
51
- client.on('connectError', function(error){
51
+ client.on('connectError', function (error) {
52
52
  if (error) {
53
53
  reject(error)
54
54
  return
55
55
  }
56
- });
57
-
58
- });
56
+ })
57
+ })
59
58
  }
60
59
 
61
60
  // search a user and return the object
@@ -63,46 +62,48 @@ async function _searchUser(
63
62
  ldapClient,
64
63
  searchBase,
65
64
  usernameAttribute,
66
- username
65
+ username,
66
+ attributes = null
67
67
  ) {
68
68
  return new Promise(function (resolve, reject) {
69
69
  var filter = new ldap.filters.EqualityFilter({
70
70
  attribute: usernameAttribute,
71
71
  value: username,
72
72
  })
73
- ldapClient.search(
74
- searchBase,
75
- {
76
- filter: filter,
77
- scope: 'sub',
78
- },
79
- function (err, res) {
80
- var user = null
81
- if (err) {
82
- reject(err)
83
- ldapClient.unbind()
84
- return
85
- }
86
- res.on('searchEntry', function (entry) {
87
- user = entry.object
88
- })
89
- res.on('searchReference', function (referral) {
90
- console.log('referral: ' + referral.uris.join())
91
- })
92
- res.on('error', function (err) {
93
- reject(err)
94
- ldapClient.unbind()
95
- })
96
- res.on('end', function (result) {
97
- if (result.status != 0) {
98
- reject(new Error('ldap search status is not 0, search failed'))
99
- } else {
100
- resolve(user)
101
- }
102
- ldapClient.unbind()
103
- })
73
+ let searchOptions = {
74
+ filter: filter,
75
+ scope: 'sub',
76
+ attributes: attributes,
77
+ }
78
+ if (attributes) {
79
+ searchOptions.attributes = attributes
80
+ }
81
+ ldapClient.search(searchBase, searchOptions, function (err, res) {
82
+ var user = null
83
+ if (err) {
84
+ reject(err)
85
+ ldapClient.unbind()
86
+ return
104
87
  }
105
- )
88
+ res.on('searchEntry', function (entry) {
89
+ user = entry.object
90
+ })
91
+ res.on('searchReference', function (referral) {
92
+ console.log('referral: ' + referral.uris.join())
93
+ })
94
+ res.on('error', function (err) {
95
+ reject(err)
96
+ ldapClient.unbind()
97
+ })
98
+ res.on('end', function (result) {
99
+ if (result.status != 0) {
100
+ reject(new Error('ldap search status is not 0, search failed'))
101
+ } else {
102
+ resolve(user)
103
+ }
104
+ ldapClient.unbind()
105
+ })
106
+ })
106
107
  })
107
108
  }
108
109
 
@@ -112,13 +113,14 @@ async function _searchUserGroups(
112
113
  searchBase,
113
114
  user,
114
115
  groupClass,
115
- groupMemberAttribute = 'member'
116
+ groupMemberAttribute = 'member',
117
+ groupMemberUserAttribute = 'dn'
116
118
  ) {
117
119
  return new Promise(function (resolve, reject) {
118
120
  ldapClient.search(
119
121
  searchBase,
120
122
  {
121
- filter: `(&(objectclass=${groupClass})(${groupMemberAttribute}=${user.dn}))`,
123
+ filter: `(&(objectclass=${groupClass})(${groupMemberAttribute}=${user[groupMemberUserAttribute]}))`,
122
124
  scope: 'sub',
123
125
  },
124
126
  function (err, res) {
@@ -162,7 +164,9 @@ async function authenticateWithAdmin(
162
164
  ldapOpts,
163
165
  groupsSearchBase,
164
166
  groupClass,
165
- groupMemberAttribute = 'member'
167
+ groupMemberAttribute = 'member',
168
+ groupMemberUserAttribute = 'dn',
169
+ attributes = null
166
170
  ) {
167
171
  var ldapAdminClient
168
172
  try {
@@ -179,7 +183,8 @@ async function authenticateWithAdmin(
179
183
  ldapAdminClient,
180
184
  userSearchBase,
181
185
  usernameAttribute,
182
- username
186
+ username,
187
+ attributes
183
188
  )
184
189
  ldapAdminClient.unbind()
185
190
  if (!user || !user.dn) {
@@ -215,7 +220,8 @@ async function authenticateWithAdmin(
215
220
  groupsSearchBase,
216
221
  user,
217
222
  groupClass,
218
- groupMemberAttribute
223
+ groupMemberAttribute,
224
+ groupMemberUserAttribute
219
225
  )
220
226
  user.groups = groups
221
227
  ldapAdminClient.unbind()
@@ -233,7 +239,9 @@ async function authenticateWithUser(
233
239
  ldapOpts,
234
240
  groupsSearchBase,
235
241
  groupClass,
236
- groupMemberAttribute = 'member'
242
+ groupMemberAttribute = 'member',
243
+ groupMemberUserAttribute = 'dn',
244
+ attributes = null
237
245
  ) {
238
246
  let ldapUserClient
239
247
  try {
@@ -250,7 +258,8 @@ async function authenticateWithUser(
250
258
  ldapUserClient,
251
259
  userSearchBase,
252
260
  usernameAttribute,
253
- username
261
+ username,
262
+ attributes
254
263
  )
255
264
  if (!user || !user.dn) {
256
265
  ldapOpts.log &&
@@ -273,7 +282,8 @@ async function authenticateWithUser(
273
282
  groupsSearchBase,
274
283
  user,
275
284
  groupClass,
276
- groupMemberAttribute
285
+ groupMemberAttribute,
286
+ groupMemberUserAttribute
277
287
  )
278
288
  user.groups = groups
279
289
  ldapUserClient.unbind()
@@ -281,6 +291,73 @@ async function authenticateWithUser(
281
291
  return user
282
292
  }
283
293
 
294
+ async function verifyUserExists(
295
+ adminDn,
296
+ adminPassword,
297
+ userSearchBase,
298
+ usernameAttribute,
299
+ username,
300
+ starttls,
301
+ ldapOpts,
302
+ groupsSearchBase,
303
+ groupClass,
304
+ groupMemberAttribute = 'member',
305
+ groupMemberUserAttribute = 'dn',
306
+ attributes = null
307
+ ) {
308
+ var ldapAdminClient
309
+ try {
310
+ ldapAdminClient = await _ldapBind(
311
+ adminDn,
312
+ adminPassword,
313
+ starttls,
314
+ ldapOpts
315
+ )
316
+ } catch (error) {
317
+ throw { admin: error }
318
+ }
319
+ var user = await _searchUser(
320
+ ldapAdminClient,
321
+ userSearchBase,
322
+ usernameAttribute,
323
+ username,
324
+ attributes
325
+ )
326
+ ldapAdminClient.unbind()
327
+ if (!user || !user.dn) {
328
+ ldapOpts.log &&
329
+ ldapOpts.log.trace(
330
+ `admin did not find user! (${usernameAttribute}=${username})`
331
+ )
332
+ throw new LdapAuthenticationError(
333
+ 'user not found or usernameAttribute is wrong'
334
+ )
335
+ }
336
+ if (groupsSearchBase && groupClass && groupMemberAttribute) {
337
+ try {
338
+ ldapAdminClient = await _ldapBind(
339
+ adminDn,
340
+ adminPassword,
341
+ starttls,
342
+ ldapOpts
343
+ )
344
+ } catch (error) {
345
+ throw error
346
+ }
347
+ var groups = await _searchUserGroups(
348
+ ldapAdminClient,
349
+ groupsSearchBase,
350
+ user,
351
+ groupClass,
352
+ groupMemberAttribute,
353
+ groupMemberUserAttribute
354
+ )
355
+ user.groups = groups
356
+ ldapAdminClient.unbind()
357
+ }
358
+ return user
359
+ }
360
+
284
361
  async function authenticate(options) {
285
362
  if (!options.userDn) {
286
363
  assert(options.adminDn, 'Admin mode adminDn must be provided')
@@ -294,11 +371,31 @@ async function authenticate(options) {
294
371
  } else {
295
372
  assert(options.userDn, 'User mode userDn must be provided')
296
373
  }
297
- assert(options.userPassword, 'userPassword must be provided')
298
374
  assert(
299
375
  options.ldapOpts && options.ldapOpts.url,
300
376
  'ldapOpts.url must be provided'
301
377
  )
378
+ if (options.verifyUserExists) {
379
+ assert(options.adminDn, 'Admin mode adminDn must be provided')
380
+ assert(
381
+ options.adminPassword,
382
+ 'adminDn and adminPassword must be both provided.'
383
+ )
384
+ return await verifyUserExists(
385
+ options.adminDn,
386
+ options.adminPassword,
387
+ options.userSearchBase,
388
+ options.usernameAttribute,
389
+ options.username,
390
+ options.starttls,
391
+ options.ldapOpts,
392
+ options.groupsSearchBase,
393
+ options.groupClass,
394
+ options.groupMemberAttribute,
395
+ options.groupMemberUserAttribute
396
+ )
397
+ }
398
+ assert(options.userPassword, 'userPassword must be provided')
302
399
  if (options.adminDn) {
303
400
  assert(
304
401
  options.adminPassword,
@@ -315,7 +412,9 @@ async function authenticate(options) {
315
412
  options.ldapOpts,
316
413
  options.groupsSearchBase,
317
414
  options.groupClass,
318
- options.groupMemberAttribute
415
+ options.groupMemberAttribute,
416
+ options.groupMemberUserAttribute,
417
+ options.attributes
319
418
  )
320
419
  }
321
420
  assert(options.userDn, 'adminDn/adminPassword OR userDn must be provided')
@@ -329,7 +428,9 @@ async function authenticate(options) {
329
428
  options.ldapOpts,
330
429
  options.groupsSearchBase,
331
430
  options.groupClass,
332
- options.groupMemberAttribute
431
+ options.groupMemberAttribute,
432
+ options.groupMemberUserAttribute,
433
+ options.attributes
333
434
  )
334
435
  }
335
436
 
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "ldap-authentication",
3
- "version": "2.2.7",
3
+ "version": "2.3.0",
4
4
  "description": "A simple async nodejs library for LDAP user authentication",
5
5
  "main": "index.js",
6
+ "types": "./index.d.ts",
6
7
  "scripts": {
7
8
  "test": "node test/jasmine.js"
8
9
  },
@@ -29,16 +30,15 @@
29
30
  "ldap-authenticate"
30
31
  ],
31
32
  "author": "Jingshao Chen",
32
- "license": "MIT",
33
+ "license": "BSD-2-Clause",
33
34
  "bugs": {
34
35
  "url": "https://github.com/shaozi/ldap-authentication/issues"
35
36
  },
36
37
  "homepage": "https://github.com/shaozi/ldap-authentication#readme",
37
38
  "dependencies": {
38
- "ldapjs": "^2.2.4"
39
+ "ldapjs": "^2.3.3"
39
40
  },
40
41
  "devDependencies": {
41
- "jasmine": "^3.7.0",
42
- "jasmine-console-reporter": "^3.1.0"
42
+ "jasmine": "^4.3.0"
43
43
  }
44
44
  }
package/test/jasmine.js CHANGED
@@ -9,7 +9,55 @@ jasmine.loadConfig({
9
9
  stopSpecOnExpectationFailure: false,
10
10
  })
11
11
 
12
- const Reporter = require('jasmine-console-reporter')
12
+ class MyConsoleReporter {
13
+ constructor() {
14
+ this.suiteSeq = 1
15
+ }
13
16
 
14
- jasmine.jasmine.getEnv().addReporter(new Reporter())
17
+ _indentPrint(str) {
18
+ str
19
+ .trim()
20
+ .split('\n')
21
+ .forEach((line) => {
22
+ process.stdout.write(' ' + line + '\n')
23
+ })
24
+ }
25
+
26
+ jasmineStarted(suiteInfo) {
27
+ console.log(`Running suite with ${suiteInfo.totalSpecsDefined} tests.`)
28
+ }
29
+
30
+ suiteStarted(result) {
31
+ console.log(`\n${this.suiteSeq}. ${result.fullName}`)
32
+ this.suiteSeq++
33
+ }
34
+
35
+ specStarted(result) {
36
+ process.stdout.write(` - ${result.description} `)
37
+ }
38
+
39
+ specDone(result) {
40
+ process.stdout.write(
41
+ ` ${result.status} [${result.passedExpectations.length}] (${result.duration} ms)`
42
+ )
43
+
44
+ for (var i = 0; i < result.failedExpectations.length; i++) {
45
+ console.log()
46
+ this._indentPrint(result.failedExpectations[i].message)
47
+ this._indentPrint(result.failedExpectations[i].stack)
48
+ }
49
+
50
+ process.stdout.write(`\n`)
51
+ }
52
+
53
+ jasmineDone(result) {
54
+ console.log('Overall result: ' + result.overallStatus)
55
+ for (var i = 0; i < result.failedExpectations.length; i++) {
56
+ console.log('Global ' + result.failedExpectations[i].message)
57
+ console.log(result.failedExpectations[i].stack)
58
+ }
59
+ }
60
+ }
61
+
62
+ jasmine.jasmine.getEnv().addReporter(new MyConsoleReporter())
15
63
  jasmine.execute()
package/test/test.js CHANGED
@@ -1,6 +1,22 @@
1
1
  const { authenticate, LdapAuthenticationError } = require('../index.js')
2
2
 
3
3
  describe('ldap-authentication test', () => {
4
+ it('Use an admin user to check if user exists', async () => {
5
+ let options = {
6
+ ldapOpts: {
7
+ url: 'ldap://ldap.forumsys.com',
8
+ },
9
+ adminDn: 'cn=read-only-admin,dc=example,dc=com',
10
+ adminPassword: 'password',
11
+ verifyUserExists: true,
12
+ userSearchBase: 'dc=example,dc=com',
13
+ usernameAttribute: 'uid',
14
+ username: 'gauss',
15
+ }
16
+ let user = await authenticate(options)
17
+ expect(user).toBeTruthy()
18
+ expect(user.uid).toEqual('gauss')
19
+ })
4
20
  it('Use an admin user to authenticate a regular user', async () => {
5
21
  let options = {
6
22
  ldapOpts: {
@@ -17,6 +33,25 @@ describe('ldap-authentication test', () => {
17
33
  expect(user).toBeTruthy()
18
34
  expect(user.uid).toEqual('gauss')
19
35
  })
36
+ it('Use an admin user to authenticate a regular user and return attrubutes', async () => {
37
+ let options = {
38
+ ldapOpts: {
39
+ url: 'ldap://ldap.forumsys.com',
40
+ },
41
+ adminDn: 'cn=read-only-admin,dc=example,dc=com',
42
+ adminPassword: 'password',
43
+ userPassword: 'password',
44
+ userSearchBase: 'dc=example,dc=com',
45
+ usernameAttribute: 'uid',
46
+ username: 'gauss',
47
+ attributes: ['uid', 'sn'],
48
+ }
49
+ let user = await authenticate(options)
50
+ expect(user).toBeTruthy()
51
+ expect(user.uid).toEqual('gauss')
52
+ expect(user.sn).toEqual('Gauss')
53
+ expect(user.cn).toBeUndefined()
54
+ })
20
55
  it('Use an regular user to authenticate iteself', async () => {
21
56
  let options = {
22
57
  ldapOpts: {
@@ -32,6 +67,24 @@ describe('ldap-authentication test', () => {
32
67
  expect(user).toBeTruthy()
33
68
  expect(user.uid).toEqual('einstein')
34
69
  })
70
+ it('Use an regular user to authenticate iteself and return attributes', async () => {
71
+ let options = {
72
+ ldapOpts: {
73
+ url: 'ldap://ldap.forumsys.com',
74
+ },
75
+ userDn: 'uid=einstein,dc=example,dc=com',
76
+ userPassword: 'password',
77
+ userSearchBase: 'dc=example,dc=com',
78
+ usernameAttribute: 'uid',
79
+ username: 'einstein',
80
+ attributes: ['uid', 'sn'],
81
+ }
82
+ let user = await authenticate(options)
83
+ expect(user).toBeTruthy()
84
+ expect(user.uid).toEqual('einstein')
85
+ expect(user.sn).toEqual('Einstein')
86
+ expect(user.cn).toBeUndefined()
87
+ })
35
88
  it('Use an regular user to authenticate iteself without search', async () => {
36
89
  let options = {
37
90
  ldapOpts: {
@@ -57,6 +110,7 @@ describe('ldap-authentication test', () => {
57
110
  groupsSearchBase: 'dc=example,dc=com',
58
111
  groupClass: 'groupOfUniqueNames',
59
112
  groupMemberAttribute: 'uniqueMember',
113
+ groupMemberUserAttribute: 'dn',
60
114
  }
61
115
  let user = await authenticate(options)
62
116
  expect(user).toBeTruthy()
@@ -75,12 +129,13 @@ describe('ldap-authentication test', () => {
75
129
  groupsSearchBase: 'dc=example,dc=com',
76
130
  groupClass: 'groupOfUniqueNames',
77
131
  groupMemberAttribute: 'uniqueMember',
132
+ groupMemberUserAttribute: 'dn',
78
133
  }
79
134
  let user = await authenticate(options)
80
135
  expect(user).toBeTruthy()
81
136
  expect(user.groups.length).toBeGreaterThan(0)
82
137
  })
83
- it('Not specifying groupMemberAttribute should not cause an error and fallback do default value', async () => {
138
+ it('Not specifying groupMemberAttribute or groupMemberUserAttribute should not cause an error and fallback to default values', async () => {
84
139
  let options = {
85
140
  ldapOpts: {
86
141
  url: 'ldap://ldap.forumsys.com',
@@ -275,14 +330,14 @@ describe('ldap-authentication negative test', () => {
275
330
  let options = {
276
331
  ldapOpts: {
277
332
  url: 'ldap://x.forumsys.com',
278
- connectTimeout: 2000
333
+ connectTimeout: 2000,
279
334
  },
280
335
  userDn: 'uid=einstein,dc=example,dc=com',
281
336
  userPassword: 'password',
282
337
  usernameAttribute: 'cn',
283
338
  userSearchBase: 'dc=example,dc=com',
284
- username: 'einstein',
285
- starttls: true
339
+ username: 'einstein',
340
+ starttls: true,
286
341
  }
287
342
  try {
288
343
  await authenticate(options)
@@ -291,4 +346,23 @@ describe('ldap-authentication negative test', () => {
291
346
  }
292
347
  expect(e).toBeTruthy()
293
348
  })
349
+ it('Unmatched supplied groupMemberUserAttribute should return empty group list', async () => {
350
+ let options = {
351
+ ldapOpts: {
352
+ url: 'ldap://ldap.forumsys.com',
353
+ },
354
+ userDn: 'uid=gauss,dc=example,dc=com',
355
+ userPassword: 'password',
356
+ userSearchBase: 'dc=example,dc=com',
357
+ usernameAttribute: 'uid',
358
+ username: 'gauss',
359
+ groupsSearchBase: 'dc=example,dc=com',
360
+ groupClass: 'groupOfUniqueNames',
361
+ groupMemberAttribute: 'uniqueMember',
362
+ groupMemberUserAttribute: 'notARealGroupMemberUserAttribute',
363
+ }
364
+ let user = await authenticate(options)
365
+ expect(user).toBeTruthy()
366
+ expect(user.groups.length).toBeLessThan(1)
367
+ })
294
368
  })