ldap-authentication 4.0.3 → 4.0.5
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/.devcontainer/docker-compose.yml +7 -8
- package/.github/workflows/publish.yml +53 -0
- package/README.md +11 -0
- package/docker/ldap/10-ldap-test-data.ldif +47 -0
- package/docker/ldap/20-ldap-test-acl.ldif +21 -0
- package/docker/ldap/Dockerfile +7 -0
- package/docker-compose.yml +10 -8
- package/index.d.ts +18 -0
- package/index.js +26 -6
- package/index.mjs +16 -0
- package/package.json +10 -1
|
@@ -8,14 +8,13 @@ services:
|
|
|
8
8
|
command: sleep infinity
|
|
9
9
|
|
|
10
10
|
ldap:
|
|
11
|
-
|
|
11
|
+
build:
|
|
12
|
+
context: ..
|
|
13
|
+
dockerfile: docker/ldap/Dockerfile
|
|
12
14
|
ports:
|
|
13
|
-
- '1389:
|
|
14
|
-
- '1636:
|
|
15
|
+
- '1389:389'
|
|
16
|
+
- '1636:636'
|
|
15
17
|
environment:
|
|
16
|
-
-
|
|
17
|
-
- LDAP_ADMIN_USERNAME=read-only-admin
|
|
18
|
+
- LDAP_DOMAIN=example.com
|
|
18
19
|
- LDAP_ADMIN_PASSWORD=password
|
|
19
|
-
-
|
|
20
|
-
- LDAP_PASSWORDS=password,password
|
|
21
|
-
- LDAP_GROUP=科学A部
|
|
20
|
+
- LDAP_TLS=true
|
|
@@ -0,0 +1,53 @@
|
|
|
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 }}
|
package/README.md
CHANGED
|
@@ -104,6 +104,14 @@ let authenticated = await authenticate({
|
|
|
104
104
|
|
|
105
105
|
#### Complete example
|
|
106
106
|
|
|
107
|
+
The library works with both CommonJS and ES modules:
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
import { authenticate } from 'ldap-authentication'
|
|
111
|
+
// or
|
|
112
|
+
const { authenticate } = require('ldap-authentication')
|
|
113
|
+
```
|
|
114
|
+
|
|
107
115
|
```javascript
|
|
108
116
|
const { authenticate } = require('ldap-authentication')
|
|
109
117
|
|
|
@@ -204,6 +212,9 @@ auth()
|
|
|
204
212
|
if this value is not set, then authenticate will return true right after user bind succeed. No user details
|
|
205
213
|
from LDAP search will be performed and returned.
|
|
206
214
|
Example: `uid`
|
|
215
|
+
- `usernameFilter`: Prioritized alternative to usernameAttribute, allows you to provide a filter where `{{username}}` will
|
|
216
|
+
be replaced with the username provided
|
|
217
|
+
Example: `(|(uid={{username}})(mail={{username}}))`
|
|
207
218
|
- `username`: The username to authenticate with. It is used together with the name in `usernameAttribute`
|
|
208
219
|
to construct an ldap filter as `({attribute}={username})`
|
|
209
220
|
to find the user and get user details in LDAP. Example: `some user input`
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Test directory seed data for ldap-authentication integration tests.
|
|
2
|
+
# Loaded by osixia/openldap at first start via
|
|
3
|
+
# /container/service/slapd/assets/config/bootstrap/ldif/custom
|
|
4
|
+
# (directory bind-mounted in docker-compose.yml).
|
|
5
|
+
# See https://github.com/osixia/container-openldap (v1, tag 1.5.0)
|
|
6
|
+
|
|
7
|
+
dn: ou=users,dc=example,dc=com
|
|
8
|
+
objectClass: organizationalUnit
|
|
9
|
+
ou: users
|
|
10
|
+
|
|
11
|
+
dn: ou=groups,dc=example,dc=com
|
|
12
|
+
objectClass: organizationalUnit
|
|
13
|
+
ou: groups
|
|
14
|
+
|
|
15
|
+
dn: cn=read-only-admin,dc=example,dc=com
|
|
16
|
+
objectClass: simpleSecurityObject
|
|
17
|
+
objectClass: organizationalRole
|
|
18
|
+
cn: read-only-admin
|
|
19
|
+
description: LDAP admin user for tests (password: password)
|
|
20
|
+
userPassword: {SSHA}u/l8yXCyiWhP3iXpt0UWMyM9ch5sZGFwLXRlc3QtMjAyNg==
|
|
21
|
+
|
|
22
|
+
dn: cn=gauss,ou=users,dc=example,dc=com
|
|
23
|
+
objectClass: inetOrgPerson
|
|
24
|
+
objectClass: posixAccount
|
|
25
|
+
cn: gauss
|
|
26
|
+
sn: Bar1
|
|
27
|
+
uid: gauss
|
|
28
|
+
uidNumber: 1000
|
|
29
|
+
gidNumber: 1000
|
|
30
|
+
homeDirectory: /home/gauss
|
|
31
|
+
userPassword: {SSHA}u/l8yXCyiWhP3iXpt0UWMyM9ch5sZGFwLXRlc3QtMjAyNg==
|
|
32
|
+
|
|
33
|
+
dn: cn=einstein,ou=users,dc=example,dc=com
|
|
34
|
+
objectClass: inetOrgPerson
|
|
35
|
+
objectClass: posixAccount
|
|
36
|
+
cn: einstein
|
|
37
|
+
sn: Bar2
|
|
38
|
+
uid: einstein
|
|
39
|
+
uidNumber: 1001
|
|
40
|
+
gidNumber: 1001
|
|
41
|
+
homeDirectory: /home/einstein
|
|
42
|
+
userPassword: {SSHA}u/l8yXCyiWhP3iXpt0UWMyM9ch5sZGFwLXRlc3QtMjAyNg==
|
|
43
|
+
|
|
44
|
+
dn: cn=科学A部,ou=groups,dc=example,dc=com
|
|
45
|
+
objectClass: groupOfNames
|
|
46
|
+
cn: 科学A部
|
|
47
|
+
member: cn=gauss,ou=users,dc=example,dc=com
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# ACLs for the test LDAP directory, applied on top of the osixia/openldap
|
|
2
|
+
# defaults (replaces the data database access rules at first start).
|
|
3
|
+
#
|
|
4
|
+
# The userPassword rule must come before the generic "to *" rule: OpenLDAP
|
|
5
|
+
# evaluates the first matching "to" clause for an attribute.
|
|
6
|
+
#
|
|
7
|
+
# - EXTERNAL (slapd local socket): full manage (used by the image bootstrap)
|
|
8
|
+
# - cn=admin,dc=example,dc=com: full manage
|
|
9
|
+
# - cn=read-only-admin,dc=example,dc=com: full manage (tests use it as the
|
|
10
|
+
# admin, including the entry modify in binary.spec.js)
|
|
11
|
+
# - normal users: read own entry and read group entries (group lookup tests)
|
|
12
|
+
# - anonymous: no access (simple bind does not consult other ACLs)
|
|
13
|
+
|
|
14
|
+
dn: olcDatabase={1}mdb,cn=config
|
|
15
|
+
changetype: modify
|
|
16
|
+
delete: olcAccess
|
|
17
|
+
-
|
|
18
|
+
add: olcAccess
|
|
19
|
+
olcAccess: to attrs=userPassword,shadowLastChange by self write by dn="cn=admin,dc=example,dc=com" manage by dn.exact="cn=read-only-admin,dc=example,dc=com" manage by anonymous auth by * none
|
|
20
|
+
olcAccess: to dn.subtree="ou=groups,dc=example,dc=com" by users read by dn.exact="cn=read-only-admin,dc=example,dc=com" read by dn="cn=admin,dc=example,dc=com" manage
|
|
21
|
+
olcAccess: to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by dn="cn=admin,dc=example,dc=com" manage by dn.exact="cn=read-only-admin,dc=example,dc=com" manage by users read by self read by * none
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
FROM osixia/openldap:1.5.0
|
|
2
|
+
|
|
3
|
+
# Seed the test directory (dc=example,dc=com) with the users, groups and
|
|
4
|
+
# admin account used by the integration tests, and grant the test admin
|
|
5
|
+
# (cn=read-only-admin) the rights it needs. Files are applied by the
|
|
6
|
+
# osixia/openldap bootstrap at the container's first start.
|
|
7
|
+
COPY docker/ldap/10-ldap-test-data.ldif docker/ldap/20-ldap-test-acl.ldif /container/service/slapd/assets/config/bootstrap/ldif/custom/
|
package/docker-compose.yml
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
services:
|
|
2
2
|
ldap:
|
|
3
|
-
|
|
3
|
+
# LDAP server used by the integration tests, based on osixia/openldap.
|
|
4
|
+
# The Dockerfile in docker/ldap/ seeds the directory with the users,
|
|
5
|
+
# groups and admin account the tests rely on.
|
|
6
|
+
build:
|
|
7
|
+
context: .
|
|
8
|
+
dockerfile: docker/ldap/Dockerfile
|
|
4
9
|
ports:
|
|
5
|
-
- '1389:
|
|
6
|
-
- '1636:
|
|
10
|
+
- '1389:389'
|
|
11
|
+
- '1636:636'
|
|
7
12
|
environment:
|
|
8
|
-
-
|
|
9
|
-
- LDAP_ADMIN_USERNAME=read-only-admin
|
|
13
|
+
- LDAP_DOMAIN=example.com
|
|
10
14
|
- LDAP_ADMIN_PASSWORD=password
|
|
11
|
-
-
|
|
12
|
-
- LDAP_PASSWORDS=password,password
|
|
13
|
-
- LDAP_GROUP=科学A部
|
|
15
|
+
- LDAP_TLS=true
|
package/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ declare module 'ldap-authentication' {
|
|
|
8
8
|
adminPassword?: string
|
|
9
9
|
userSearchBase?: string
|
|
10
10
|
usernameAttribute?: string
|
|
11
|
+
usernameFilter?:string
|
|
11
12
|
username?: string
|
|
12
13
|
verifyUserExists?: boolean
|
|
13
14
|
starttls?: boolean
|
|
@@ -20,7 +21,24 @@ declare module 'ldap-authentication' {
|
|
|
20
21
|
explicitBufferAttributes?: string[]
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
export const AUTH_RESULT_FAILURE = 0
|
|
25
|
+
export const AUTH_RESULT_SUCCESS = 1
|
|
26
|
+
export const AUTH_RESULT_FAILURE_IDENTITY_NOT_FOUND = -1
|
|
27
|
+
export const AUTH_RESULT_FAILURE_IDENTITY_AMBIGUOUS = -2
|
|
28
|
+
export const AUTH_RESULT_FAILURE_CREDENTIAL_INVALID = -3
|
|
29
|
+
export const AUTH_RESULT_FAILURE_UNCATEGORIZED = -4
|
|
30
|
+
|
|
31
|
+
export class AuthenticationResult {
|
|
32
|
+
constructor(authCode: number, identity: string, user: any, messages: string[], client: any)
|
|
33
|
+
readonly code: number
|
|
34
|
+
readonly identity: string
|
|
35
|
+
readonly user: any
|
|
36
|
+
readonly messages: string[]
|
|
37
|
+
readonly client: any
|
|
38
|
+
}
|
|
39
|
+
|
|
23
40
|
export function authenticate(options: AuthenticationOptions): Promise<any>
|
|
41
|
+
export function authenticateResult(options: AuthenticationOptions): Promise<AuthenticationResult>
|
|
24
42
|
|
|
25
43
|
export class LdapAuthenticationError extends Error {
|
|
26
44
|
constructor(message: any)
|
package/index.js
CHANGED
|
@@ -124,19 +124,30 @@ async function _ldapBind(dn, password, starttls, ldapOpts) {
|
|
|
124
124
|
return client
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
// replace username in filter
|
|
128
|
+
|
|
129
|
+
|
|
127
130
|
// search a user and return the object
|
|
128
131
|
async function _searchUser(
|
|
129
132
|
ldapClient,
|
|
130
133
|
searchBase,
|
|
134
|
+
usernameFilter,
|
|
131
135
|
usernameAttribute,
|
|
132
136
|
username,
|
|
133
137
|
attributes = null,
|
|
134
138
|
explicitBufferAttributes = null
|
|
135
139
|
) {
|
|
136
|
-
let filter
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
+
let filter;
|
|
141
|
+
if(usernameFilter){
|
|
142
|
+
filter = usernameFilter.replaceAll("{{username}}",username.replaceAll(/[&|!*()]/g,""));
|
|
143
|
+
}
|
|
144
|
+
else{
|
|
145
|
+
filter = new ldapts.EqualityFilter({
|
|
146
|
+
attribute: usernameAttribute,
|
|
147
|
+
value: username,
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
140
151
|
let searchOptions = {
|
|
141
152
|
filter: filter,
|
|
142
153
|
scope: 'sub',
|
|
@@ -266,6 +277,7 @@ async function authenticateWithAdmin(
|
|
|
266
277
|
adminDn,
|
|
267
278
|
adminPassword,
|
|
268
279
|
userSearchBase,
|
|
280
|
+
usernameFilter,
|
|
269
281
|
usernameAttribute,
|
|
270
282
|
username,
|
|
271
283
|
userPassword,
|
|
@@ -303,6 +315,7 @@ async function authenticateWithAdmin(
|
|
|
303
315
|
let searchResult = await _searchUser(
|
|
304
316
|
ldapAdminClient,
|
|
305
317
|
userSearchBase,
|
|
318
|
+
usernameFilter,
|
|
306
319
|
usernameAttribute,
|
|
307
320
|
username,
|
|
308
321
|
attributes,
|
|
@@ -368,6 +381,7 @@ async function authenticateWithAdmin(
|
|
|
368
381
|
async function authenticateWithUser(
|
|
369
382
|
userDn,
|
|
370
383
|
userSearchBase,
|
|
384
|
+
usernameFilter,
|
|
371
385
|
usernameAttribute,
|
|
372
386
|
username,
|
|
373
387
|
userPassword,
|
|
@@ -411,6 +425,7 @@ async function authenticateWithUser(
|
|
|
411
425
|
let searchResult = await _searchUser(
|
|
412
426
|
ldapUserClient,
|
|
413
427
|
userSearchBase,
|
|
428
|
+
usernameFilter,
|
|
414
429
|
usernameAttribute,
|
|
415
430
|
username,
|
|
416
431
|
attributes,
|
|
@@ -463,6 +478,7 @@ async function verifyUserExists(
|
|
|
463
478
|
adminDn,
|
|
464
479
|
adminPassword,
|
|
465
480
|
userSearchBase,
|
|
481
|
+
usernameFilter,
|
|
466
482
|
usernameAttribute,
|
|
467
483
|
username,
|
|
468
484
|
starttls,
|
|
@@ -498,6 +514,7 @@ async function verifyUserExists(
|
|
|
498
514
|
let searchResult = await _searchUser(
|
|
499
515
|
ldapAdminClient,
|
|
500
516
|
userSearchBase,
|
|
517
|
+
usernameFilter,
|
|
501
518
|
usernameAttribute,
|
|
502
519
|
username,
|
|
503
520
|
attributes,
|
|
@@ -562,8 +579,8 @@ async function authenticateResult(options) {
|
|
|
562
579
|
assert(options.adminPassword, 'Admin mode adminPassword must be provided')
|
|
563
580
|
assert(options.userSearchBase, 'Admin mode userSearchBase must be provided')
|
|
564
581
|
assert(
|
|
565
|
-
options.usernameAttribute,
|
|
566
|
-
'Admin mode usernameAttribute must be provided'
|
|
582
|
+
options.usernameAttribute || options.usernameFilter,
|
|
583
|
+
'Admin mode usernameAttribute or usernameFilter must be provided'
|
|
567
584
|
)
|
|
568
585
|
assert(options.username, 'Admin mode username must be provided')
|
|
569
586
|
} else {
|
|
@@ -584,6 +601,7 @@ async function authenticateResult(options) {
|
|
|
584
601
|
options.adminDn,
|
|
585
602
|
options.adminPassword,
|
|
586
603
|
options.userSearchBase,
|
|
604
|
+
options.usernameFilter,
|
|
587
605
|
options.usernameAttribute,
|
|
588
606
|
options.username,
|
|
589
607
|
options.starttls,
|
|
@@ -607,6 +625,7 @@ async function authenticateResult(options) {
|
|
|
607
625
|
options.adminDn,
|
|
608
626
|
options.adminPassword,
|
|
609
627
|
options.userSearchBase,
|
|
628
|
+
options.usernameFilter,
|
|
610
629
|
options.usernameAttribute,
|
|
611
630
|
options.username,
|
|
612
631
|
options.userPassword,
|
|
@@ -625,6 +644,7 @@ async function authenticateResult(options) {
|
|
|
625
644
|
return await authenticateWithUser(
|
|
626
645
|
options.userDn,
|
|
627
646
|
options.userSearchBase,
|
|
647
|
+
options.usernameFilter,
|
|
628
648
|
options.usernameAttribute,
|
|
629
649
|
options.username,
|
|
630
650
|
options.userPassword,
|
package/index.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import ldapAuthentication from './index.js'
|
|
2
|
+
|
|
3
|
+
export {
|
|
4
|
+
AUTH_RESULT_FAILURE,
|
|
5
|
+
AUTH_RESULT_FAILURE_CREDENTIAL_INVALID,
|
|
6
|
+
AUTH_RESULT_FAILURE_IDENTITY_AMBIGUOUS,
|
|
7
|
+
AUTH_RESULT_FAILURE_IDENTITY_NOT_FOUND,
|
|
8
|
+
AUTH_RESULT_FAILURE_UNCATEGORIZED,
|
|
9
|
+
AUTH_RESULT_SUCCESS,
|
|
10
|
+
AuthenticationResult,
|
|
11
|
+
LdapAuthenticationError,
|
|
12
|
+
authenticate,
|
|
13
|
+
authenticateResult,
|
|
14
|
+
} from './index.js'
|
|
15
|
+
|
|
16
|
+
export default ldapAuthentication
|
package/package.json
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ldap-authentication",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.5",
|
|
4
4
|
"description": "A simple async nodejs library for LDAP user authentication",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "./index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./index.d.ts",
|
|
10
|
+
"import": "./index.mjs",
|
|
11
|
+
"require": "./index.js",
|
|
12
|
+
"default": "./index.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
7
16
|
"engines": {
|
|
8
17
|
"node": ">=20.0.0"
|
|
9
18
|
},
|