{"record":{"id":"d07189feb8d2c387","repo":"risingwavelabs/risingwave","slug":"ldap-search-failed","errorCode":null,"errorMessage":"LDAP search failed","messagePattern":"LDAP search failed","errorType":"exception","errorClass":"PsqlError","httpStatus":null,"severity":"error","filePath":"src/utils/pgwire/src/ldap_auth.rs","lineNumber":516,"sourceCode":"        // Build search filter\n        let search_filter = if let Some(filter_template) = &self.config.search_filter {\n            // Use custom filter template with $username placeholder\n            // SECURITY: Escape username to prevent LDAP filter injection\n            let escaped_username = ldap_escape(username);\n            filter_template.replace(\"$username\", &escaped_username)\n        } else {\n            // Default filter using search_attribute (defaults to \"uid\" if not configured)\n            // SECURITY: Escape username to prevent LDAP filter injection\n            let escaped_username = ldap_escape(username);\n            let attr = self.config.search_attribute.as_deref().unwrap_or(\"uid\");\n            format!(\"({}={})\", attr, escaped_username)\n        };\n\n        let rs = ldap\n            .search(base_dn, Scope::Subtree, &search_filter, vec![\"dn\"])\n            .await\n            .map_err(|e| {\n                PsqlError::StartupError(anyhow!(e).context(\"LDAP search failed\").into())\n            })?;\n\n        // If no user found, authentication fails\n        let search_entries: Vec<SearchEntry> =\n            rs.0.into_iter().map(SearchEntry::construct).collect();\n        if search_entries.is_empty() {\n            return Ok(false);\n        }\n\n        // Attempt to bind with the user's DN and password\n        let user_dn = &search_entries[0].dn;\n\n        let bind_result = ldap\n            .simple_bind(user_dn, password)\n            .await\n            .map_err(|e| PsqlError::StartupError(anyhow!(e).context(\"LDAP bind failed\").into()));\n\n        // Explicitly unbind the connection","sourceCodeStart":498,"sourceCodeEnd":534,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/utils/pgwire/src/ldap_auth.rs#L498-L534","documentation":"After binding as the search user, search_and_bind runs an LDAP subtree search for the user's entry using the configured base_dn and search_filter. Any error from the search operation (network drop, malformed filter, insufficient search permissions, base_dn not found) is wrapped as this StartupError. It is a directory-query failure, not an authentication failure.","triggerScenarios":"ldap.search(base_dn, Scope::Subtree, &search_filter, vec![\"dn\"]) awaits or resolves with Err — bad filter syntax, wrong base_dn, search privileges missing for the bind account","commonSituations":"Search filter template with unescaped special characters in username ((uid=j.o'brien) breaks filter syntax); base_dn typo'd or points at a non-existent subtree; bind account lacks read/search ACLs on the user subtree; AD requires the filter to use sAMAccountName instead of uid.","solutions":["Test the search manually: ldapsearch -H ... -D bind_dn -w pass -b base_dn '(uid=testuser)' dn","Escape special characters in interpolated usernames (RFC 4515 filter escaping)","Verify base_dn exists and matches the directory naming context","Grant the bind account read/search permissions on the user OU"],"exampleFix":"// before (unescaped interpolation breaks filter)\nsearch_filter = '(uid={username})'\n// after (escaped via library interpolation; correct attribute for AD)\nsearch_filter = '(sAMAccountName={username})'","handlingStrategy":"try-catch","validationCode":"// verify the search works manually before configuring\n# ldapsearch -H ldaps://ldap.corp:636 -D \"$BIND_DN\" -w \"$BIND_PASS\" \\\n#   -b 'ou=users,dc=corp,dc=com' '(sAMAccountName=testuser)' dn","typeGuard":null,"tryCatchPattern":"catch PsqlError::StartupError 'LDAP search failed'; differentiate filter-syntax errors (fix escaping) from ACL/base_dn errors (fix directory config) using the inner error text","preventionTips":["LDAP-escape all interpolated usernames (RFC 4515) before building filters","Copy base_dn directly from the directory's naming context, never by hand","Grant the bind account explicit read/search ACLs on the user subtree","Keep an ldapsearch-based integration test for the search phase"],"tags":["ldap","search","query","permissions"],"backgroundTag":"database-query-failed","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}