import { Client, InvalidCredentialsError } from 'ldapts' const LDAP_URL = process.env.LDAP_URL || 'ldap://localhost:3890' const LDAP_BASE_DN = process.env.LDAP_BASE_DN || 'dc=tlyq,dc=ai' // 从环境变量获取 LLDAP admin 密码(容器内无法执行 docker exec,见 LESSONS-LEARNED #41) function getLdapAdminPassword(): string { return process.env.LLDAP_ADMIN_PASSWORD || 'admin123' } export interface LdapResult { success: boolean unreachable: boolean username?: string displayName?: string email?: string } export async function ldapAuth( username: string, password: string ): Promise { const userDn = `uid=${username},ou=people,${LDAP_BASE_DN}` const client = new Client({ url: LDAP_URL, timeout: 5000 }) try { await client.bind(userDn, password) try { const { searchEntries } = await client.search(LDAP_BASE_DN, { scope: 'sub', filter: `(uid=${username})`, attributes: ['displayName', 'mail'], timeLimit: 3, }) const entry = searchEntries[0] as any const displayName = entry?.displayName || username const email = entry?.mail || null return { success: true, unreachable: false, username, displayName, email } } catch { return { success: true, unreachable: false, username, displayName: username } } } catch (err) { if (err instanceof InvalidCredentialsError) { return { success: false, unreachable: false } } return { success: false, unreachable: true } } finally { await client.unbind() } } // 从 LLDAP 获取用户信息(displayName + email),不可达返回 null export async function ldapGetUserInfo(username: string): Promise<{ displayName: string; email: string | null } | null> { const adminDn = process.env.LDAP_ADMIN_DN || 'uid=admin,ou=people,dc=tlyq,dc=ai' const adminPass = getLdapAdminPassword() const client = new Client({ url: LDAP_URL, timeout: 5000 }) try { await client.bind(adminDn, adminPass) const { searchEntries } = await client.search(LDAP_BASE_DN, { scope: 'sub', filter: `(uid=${username})`, attributes: ['displayName', 'mail'], timeLimit: 3, }) const entry = searchEntries[0] as any return entry ? { displayName: entry.displayName || username, email: entry.mail || null } : null } catch { return null } finally { await client.unbind() } } // Q1: 检查 LLDAP 中用户是否存在(用 admin bind 搜索,不在/不可达均返回 true 保证容错) export async function ldapUserExists(username: string): Promise { const adminDn = process.env.LDAP_ADMIN_DN || 'uid=admin,ou=people,dc=tlyq,dc=ai' const adminPass = getLdapAdminPassword() const client = new Client({ url: LDAP_URL, timeout: 5000 }) try { await client.bind(adminDn, adminPass) const { searchEntries } = await client.search(LDAP_BASE_DN, { scope: 'sub', filter: `(uid=${username})`, timeLimit: 3, }) return searchEntries.length > 0 } catch { return true // LLDAP 不可达 → 不阻断,容错放行 } finally { await client.unbind() } }