ldap-authentication 2.2.9 → 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
@@ -24,7 +24,7 @@ This library use `ldapjs` as the underneath library. It has two modes of authent
24
24
  then does a search on the user and return the user's details.
25
25
 
26
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).
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
28
 
29
29
  ## Features
30
30
 
@@ -67,6 +67,7 @@ let authenticated = await authenticate({
67
67
  userSearchBase: 'dc=example,dc=com',
68
68
  usernameAttribute: 'uid',
69
69
  username: 'gauss',
70
+ attributes: ['dn', 'sn', 'cn'],
70
71
  })
71
72
  ```
72
73
 
@@ -76,7 +77,7 @@ let authenticated = await authenticate({
76
77
  let authenticated = await authenticate({
77
78
  ldapOpts: { url: 'ldap://ldap.forumsys.com' },
78
79
  userDn: 'uid=gauss,dc=example,dc=com',
79
- verifyUserExists : true,
80
+ verifyUserExists: true,
80
81
  userSearchBase: 'dc=example,dc=com',
81
82
  usernameAttribute: 'uid',
82
83
  username: 'gauss',
@@ -156,7 +157,7 @@ auth()
156
157
  - `userDn`: The DN of the user to be authenticated. This is only needed if `adminDn` and `adminPassword` are not provided.
157
158
  Example: `uid=gauss,dc=example,dc=com`
158
159
  - `userPassword`: The password of the user
159
- - `verifyUserExists` : if `true` user existence will be verified without password
160
+ - `verifyUserExists` : if `true` user existence will be verified without password
160
161
  - `userSearchBase`: The ldap base DN to search the user. Example: `dc=example,dc=com`
161
162
  - `usernameAttribute`: The ldap search equality attribute name corresponding to the user's username.
162
163
  It will be used with the value in `username` to construct an ldap filter as `({attribute}={username})`
@@ -168,6 +169,8 @@ auth()
168
169
  - `username`: The username to authenticate with. It is used together with the name in `usernameAttribute`
169
170
  to construct an ldap filter as `({attribute}={username})`
170
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']`
171
174
  - `starttls`: Boolean. Use `STARTTLS` or not
172
175
  - `groupsSearchBase`: if specified with groupClass, will serve as search base for authenticated user groups
173
176
  - `groupClass`: if specified with groupsSearchBase, will be used as objectClass 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
 
@@ -164,7 +165,8 @@ async function authenticateWithAdmin(
164
165
  groupsSearchBase,
165
166
  groupClass,
166
167
  groupMemberAttribute = 'member',
167
- groupMemberUserAttribute
168
+ groupMemberUserAttribute = 'dn',
169
+ attributes = null
168
170
  ) {
169
171
  var ldapAdminClient
170
172
  try {
@@ -181,7 +183,8 @@ async function authenticateWithAdmin(
181
183
  ldapAdminClient,
182
184
  userSearchBase,
183
185
  usernameAttribute,
184
- username
186
+ username,
187
+ attributes
185
188
  )
186
189
  ldapAdminClient.unbind()
187
190
  if (!user || !user.dn) {
@@ -237,7 +240,8 @@ async function authenticateWithUser(
237
240
  groupsSearchBase,
238
241
  groupClass,
239
242
  groupMemberAttribute = 'member',
240
- groupMemberUserAttribute
243
+ groupMemberUserAttribute = 'dn',
244
+ attributes = null
241
245
  ) {
242
246
  let ldapUserClient
243
247
  try {
@@ -254,7 +258,8 @@ async function authenticateWithUser(
254
258
  ldapUserClient,
255
259
  userSearchBase,
256
260
  usernameAttribute,
257
- username
261
+ username,
262
+ attributes
258
263
  )
259
264
  if (!user || !user.dn) {
260
265
  ldapOpts.log &&
@@ -297,7 +302,8 @@ async function verifyUserExists(
297
302
  groupsSearchBase,
298
303
  groupClass,
299
304
  groupMemberAttribute = 'member',
300
- groupMemberUserAttribute
305
+ groupMemberUserAttribute = 'dn',
306
+ attributes = null
301
307
  ) {
302
308
  var ldapAdminClient
303
309
  try {
@@ -314,7 +320,8 @@ async function verifyUserExists(
314
320
  ldapAdminClient,
315
321
  userSearchBase,
316
322
  usernameAttribute,
317
- username
323
+ username,
324
+ attributes
318
325
  )
319
326
  ldapAdminClient.unbind()
320
327
  if (!user || !user.dn) {
@@ -406,7 +413,8 @@ async function authenticate(options) {
406
413
  options.groupsSearchBase,
407
414
  options.groupClass,
408
415
  options.groupMemberAttribute,
409
- options.groupMemberUserAttribute
416
+ options.groupMemberUserAttribute,
417
+ options.attributes
410
418
  )
411
419
  }
412
420
  assert(options.userDn, 'adminDn/adminPassword OR userDn must be provided')
@@ -421,7 +429,8 @@ async function authenticate(options) {
421
429
  options.groupsSearchBase,
422
430
  options.groupClass,
423
431
  options.groupMemberAttribute,
424
- options.groupMemberUserAttribute
432
+ options.groupMemberUserAttribute,
433
+ options.attributes
425
434
  )
426
435
  }
427
436
 
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "ldap-authentication",
3
- "version": "2.2.9",
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
  },
@@ -35,9 +36,9 @@
35
36
  },
36
37
  "homepage": "https://github.com/shaozi/ldap-authentication#readme",
37
38
  "dependencies": {
38
- "ldapjs": "^2.3.1"
39
+ "ldapjs": "^2.3.3"
39
40
  },
40
41
  "devDependencies": {
41
- "jasmine": "^3.9.0"
42
+ "jasmine": "^4.3.0"
42
43
  }
43
44
  }
package/test/test.js CHANGED
@@ -33,6 +33,25 @@ describe('ldap-authentication test', () => {
33
33
  expect(user).toBeTruthy()
34
34
  expect(user.uid).toEqual('gauss')
35
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
+ })
36
55
  it('Use an regular user to authenticate iteself', async () => {
37
56
  let options = {
38
57
  ldapOpts: {
@@ -48,6 +67,24 @@ describe('ldap-authentication test', () => {
48
67
  expect(user).toBeTruthy()
49
68
  expect(user.uid).toEqual('einstein')
50
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
+ })
51
88
  it('Use an regular user to authenticate iteself without search', async () => {
52
89
  let options = {
53
90
  ldapOpts: {