Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apisix/plugins/ldap-auth.lua
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,15 @@ function _M.rewrite(conf, ctx)
attribute = conf.uid,
keepalive = 60000,
}
local res, err = ldap.ldap_authenticate(user.username, user.password, ldapconf)
-- the third return value is the bind DN the client assembled, with the
-- username escaped per RFC 4514. Rebuilding it here would drop that escaping.
local res, err, user_dn = ldap.ldap_authenticate(user.username, user.password, ldapconf)
if not res then
core.log.warn("ldap-auth failed: ", err)
core.response.set_header("WWW-Authenticate", "Basic realm=\"" .. conf.realm .. "\"")
return 401, { message = "Invalid user authorization" }
end

local user_dn = conf.uid .. "=" .. user.username .. "," .. conf.base_dn

-- 3. Retrieve consumer for authorization plugin
local consumer_conf = consumer_mod.plugin(plugin_name)
if not consumer_conf then
Expand Down
11 changes: 11 additions & 0 deletions ci/pod/openldap/ad.ldif
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,14 @@ cn: Secret User
sn: Secret
uid: secretuser
userPassword: secretpass

# The RDN value carries a literal comma, so its DN only round-trips when the
# username is escaped per RFC 4514.
dn: cn=comma\,user,ou=users,dc=example,dc=org
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
cn: comma,user
sn: Comma
uid: commauser
userPassword: commapass
2 changes: 2 additions & 0 deletions docs/en/latest/plugins/ldap-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ For Consumer:
| ------- | ------ | -------- | -------------------------------------------------------------------------------- |
| user_dn | string | True | User dn of the LDAP client. For example, `cn=user01,ou=users,dc=example,dc=org`. This field supports saving the value in Secret Manager using the [APISIX Secret](../terminology/secret.md) resource. |

The Plugin builds the user dn as `<uid>=<username>,<base_dn>`, taking the username from the `Authorization` header. Characters that carry structural meaning in a distinguished name (`,` `+` `=` `<` `>` `;` `"` `\`) are escaped per [RFC 4514](https://datatracker.ietf.org/doc/html/rfc4514#section-2.4), so `user_dn` must use the escaped form to match. A user named `comma,user` under `ou=users,dc=example,dc=org` is configured as `cn=comma\,user,ou=users,dc=example,dc=org`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add it to the Chinese documentation.


For Route:

| Name | Type | Required | Default | Description |
Expand Down
81 changes: 81 additions & 0 deletions t/plugin/ldap-auth.t
Original file line number Diff line number Diff line change
Expand Up @@ -650,3 +650,84 @@ Authorization: bASiC dXNlcjAxOnBhc3N3b3JkMQ==
hello world
--- error_log
find consumer user01



=== TEST 29: add consumer whose user_dn drops the RDN escaping
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers',
ngx.HTTP_PUT,
[[{
"username": "commauser",
"plugins": {
"ldap-auth": {
"user_dn": "cn=comma,user,ou=users,dc=example,dc=org"
}
}
}]]
)

if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed



=== TEST 30: a username holding a comma does not match the unescaped user_dn
--- request
GET /hello
--- more_headers
Authorization: Basic Y29tbWEsdXNlcjpjb21tYXBhc3M=
--- error_code: 401
--- response_body
{"message":"Invalid user authorization"}
--- no_error_log
find consumer commauser



=== TEST 31: repoint the consumer at the escaped user_dn
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/consumers',
ngx.HTTP_PUT,
[[{
"username": "commauser",
"plugins": {
"ldap-auth": {
"user_dn": "cn=comma\\,user,ou=users,dc=example,dc=org"
}
}
}]]
)

if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed



=== TEST 32: verify against the escaped user_dn
--- request
GET /hello
--- more_headers
Authorization: Basic Y29tbWEsdXNlcjpjb21tYXBhc3M=
--- response_body
hello world
--- error_log
find consumer commauser
Loading