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 +1 -1
- package/README.md +7 -4
- package/index.d.ts +33 -0
- package/index.js +61 -52
- package/package.json +4 -3
- package/test/test.js +37 -0
package/LICENSE
CHANGED
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
|
|
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
|
-
|
|
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
|
|
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
|
-
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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.
|
|
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.
|
|
39
|
+
"ldapjs": "^2.3.3"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
|
-
"jasmine": "^3.
|
|
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: {
|