Repository: mitmproxy/mitmproxy

Issue:
Insufficient flexibility of LDAP Proxy Auth
Insufficient flexibility of LDAP Proxy Auth

I noticed that in the Ldap Validator object, the username passed in during authentication queries will be set to cn. However, in our AD, CN is highly likely to be a Chinese name. Is there a solution to pass in other keys, such as SamAccountName

https://github.com/mitmproxy/mitmproxy/blob/main/mitmproxy/addons/proxyauth.py#L212

![image](https://github.com/mitmproxy/mitmproxy/assets/80510016/a3b0eb31-2d9b-4fc9-8756-43fe0272d973)



Discussion:
Not our focus right now, but happy to look at a PR for this. :)

Resolving patch:
diff --git a/mitmproxy/addons/proxyauth.py b/mitmproxy/addons/proxyauth.py
--- a/mitmproxy/addons/proxyauth.py
+++ b/mitmproxy/addons/proxyauth.py
@@ -40,7 +40,7 @@ def load(self, loader):
             "username:pass",
             "any" to accept any user/pass combination,
             "@path" to use an Apache htpasswd file,
-            or "ldap[s]:url_server_ldap[:port]:dn_auth:password:dn_subtree" for LDAP authentication.
+            or "ldap[s]:url_server_ldap[:port]:dn_auth:password:dn_subtree[?search_filter_key=...]" for LDAP authentication.
             """,
         )
 
@@ -213,6 +213,7 @@ class Ldap(Validator):
     conn: ldap3.Connection
     server: ldap3.Server
     dn_subtree: str
+    filter_key: str
 
     def __init__(self, proxyauth: str):
         (
@@ -222,6 +223,7 @@ def __init__(self, proxyauth: str):
             ldap_user,
             ldap_pass,
             self.dn_subtree,
+            self.filter_key,
         ) = self.parse_spec(proxyauth)
         server = ldap3.Server(url, port=port, use_ssl=use_ssl)
         conn = ldap3.Connection(server, ldap_user, ldap_pass, auto_bind=True)
@@ -229,7 +231,7 @@ def __init__(self, proxyauth: str):
         self.server = server
 
     @staticmethod
-    def parse_spec(spec: str) -> tuple[bool, str, int | None, str, str, str]:
+    def parse_spec(spec: str) -> tuple[bool, str, int | None, str, str, str, str]:
         try:
             if spec.count(":") > 4:
                 (
@@ -245,6 +247,16 @@ def parse_spec(spec: str) -> tuple[bool, str, int | None, str, str, str]:
                 security, url, ldap_user, ldap_pass, dn_subtree = spec.split(":")
                 port = None
 
+            if "?" in dn_subtree:
+                dn_subtree, search_str = dn_subtree.split("?")
+                key, value = search_str.split("=")
+                if key == "search_filter_key":
+                    search_filter_key = value
+                else:
+                    raise ValueError
+            else:
+                search_filter_key = "cn"
+
             if security == "ldaps":
                 use_ssl = True
             elif security == "ldap":
@@ -252,14 +264,22 @@ def parse_spec(spec: str) -> tuple[bool, str, int | None, str, str, str]:
             else:
                 raise ValueError
 
-            return use_ssl, url, port, ldap_user, ldap_pass, dn_subtree
+            return (
+                use_ssl,
+                url,
+                port,
+                ldap_user,
+                ldap_pass,
+                dn_subtree,
+                search_filter_key,
+            )
         except ValueError:
             raise exceptions.OptionsError(f"Invalid LDAP specification: {spec}")
 
     def __call__(self, username: str, password: str) -> bool:
         if not username or not password:
             return False
-        self.conn.search(self.dn_subtree, f"(cn={username})")
+        self.conn.search(self.dn_subtree, f"({self.filter_key}={username})")
         if self.conn.response:
             c = ldap3.Connection(
                 self.server, self.conn.response[0]["dn"], password, auto_bind=True
diff --git a/mitmproxy/utils/arg_check.py b/mitmproxy/utils/arg_check.py
--- a/mitmproxy/utils/arg_check.py
+++ b/mitmproxy/utils/arg_check.py
@@ -127,7 +127,7 @@ def check():
                 "Please use `--proxyauth SPEC` instead.\n"
                 'SPEC Format: "username:pass", "any" to accept any user/pass combination,\n'
                 '"@path" to use an Apache htpasswd file, or\n'
-                '"ldap[s]:url_server_ldap:dn_auth:password:dn_subtree" '
+                '"ldap[s]:url_server_ldap[:port]:dn_auth:password:dn_subtree[?search_filter_key=...]" '
                 "for LDAP authentication.".format(option)
             )
 


Test patch:
diff --git a/test/mitmproxy/addons/test_proxyauth.py b/test/mitmproxy/addons/test_proxyauth.py
--- a/test/mitmproxy/addons/test_proxyauth.py
+++ b/test/mitmproxy/addons/test_proxyauth.py
@@ -165,11 +165,25 @@ def test_configure(self, monkeypatch, tdata):
             )
             assert isinstance(pa.validator, proxyauth.Ldap)
 
+            ctx.configure(
+                pa,
+                proxyauth="ldap:localhost:1234:cn=default,dc=cdhdt,dc=com:password:dc=cdhdt,dc=com?search_filter_key=SamAccountName",
+            )
+            assert isinstance(pa.validator, proxyauth.Ldap)
+
             with pytest.raises(
                 exceptions.OptionsError, match="Invalid LDAP specification"
             ):
                 ctx.configure(pa, proxyauth="ldap:test:test:test")
 
+            with pytest.raises(
+                exceptions.OptionsError, match="Invalid LDAP specification"
+            ):
+                ctx.configure(
+                    pa,
+                    proxyauth="ldap:localhost:1234:cn=default,dc=cdhdt,dc=com:password:ou=application,dc=cdhdt,dc=com?key=1",
+                )
+
             with pytest.raises(
                 exceptions.OptionsError, match="Invalid LDAP specification"
             ):
@@ -231,6 +245,7 @@ def test_handlers(self):
     [
         "ldaps:localhost:cn=default,dc=cdhdt,dc=com:password:ou=application,dc=cdhdt,dc=com",
         "ldap:localhost:1234:cn=default,dc=cdhdt,dc=com:password:ou=application,dc=cdhdt,dc=com",
+        "ldap:localhost:1234:cn=default,dc=cdhdt,dc=com:password:ou=application,dc=cdhdt,dc=com?search_filter_key=cn",
     ],
 )
 def test_ldap(monkeypatch, spec):
diff --git a/test/mitmproxy/utils/test_arg_check.py b/test/mitmproxy/utils/test_arg_check.py
--- a/test/mitmproxy/utils/test_arg_check.py
+++ b/test/mitmproxy/utils/test_arg_check.py
@@ -35,7 +35,7 @@
             "Please use `--proxyauth SPEC` instead.\n"
             'SPEC Format: "username:pass", "any" to accept any user/pass combination,\n'
             '"@path" to use an Apache htpasswd file, or\n'
-            '"ldap[s]:url_server_ldap:dn_auth:password:dn_subtree" '
+            '"ldap[s]:url_server_ldap[:port]:dn_auth:password:dn_subtree[?search_filter_key=...]" '
             "for LDAP authentication.",
         ),
         (