This is an automated email from the ASF dual-hosted git repository.
shreemaan-abhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 7bae553981 fix(ldap-auth): key the consumer lookup on the escaped bind
DN (#13805)
7bae553981 is described below
commit 7bae5539816f08b30f3c534a53a43ce527771a2f
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Mon Aug 17 17:43:26 2026 +0800
fix(ldap-auth): key the consumer lookup on the escaped bind DN (#13805)
---
apisix/plugins/ldap-auth.lua | 6 +--
ci/pod/openldap/ad.ldif | 11 +++++
docs/en/latest/plugins/ldap-auth.md | 2 +
t/plugin/ldap-auth.t | 81 +++++++++++++++++++++++++++++++++++++
4 files changed, 97 insertions(+), 3 deletions(-)
diff --git a/apisix/plugins/ldap-auth.lua b/apisix/plugins/ldap-auth.lua
index 723c636a78..b22e0ed8b7 100644
--- a/apisix/plugins/ldap-auth.lua
+++ b/apisix/plugins/ldap-auth.lua
@@ -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
diff --git a/ci/pod/openldap/ad.ldif b/ci/pod/openldap/ad.ldif
index a782a83a8b..7db51a7de7 100644
--- a/ci/pod/openldap/ad.ldif
+++ b/ci/pod/openldap/ad.ldif
@@ -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
diff --git a/docs/en/latest/plugins/ldap-auth.md
b/docs/en/latest/plugins/ldap-auth.md
index 95b6884bdb..0515d59b60 100644
--- a/docs/en/latest/plugins/ldap-auth.md
+++ b/docs/en/latest/plugins/ldap-auth.md
@@ -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`.
+
For Route:
| Name | Type | Required | Default | Description
|
diff --git a/t/plugin/ldap-auth.t b/t/plugin/ldap-auth.t
index 4796057dfd..95c370832d 100644
--- a/t/plugin/ldap-auth.t
+++ b/t/plugin/ldap-auth.t
@@ -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