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

Reply via email to