"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.escapeLdapFilterValue = escapeLdapFilterValue; exports.verifyCredentials = verifyCredentials; const ldapjs_1 = __importDefault(require("ldapjs")); /** Escape value for use inside LDAP filter (RFC4515 subset). */ function escapeLdapFilterValue(value) { return value.replace(/[\\*()\0]/g, (ch) => { const code = ch.charCodeAt(0); return `\\${code.toString(16).padStart(2, "0")}`; }); } function bindAsync(client, dn, password) { return new Promise((resolve, reject) => { client.bind(dn, password, (err) => { if (err) reject(err); else resolve(); }); }); } function unbindSafe(client) { try { client.unbind(); } catch { /* ignore */ } } function searchUserDn(client, searchBase, filter) { return new Promise((resolve, reject) => { const opts = { filter, scope: "sub", attributes: ["dn"], sizeLimit: 1, }; client.search(searchBase, opts, (err, res) => { if (err) { reject(err); return; } let dn = null; res.on("searchEntry", (entry) => { dn = entry.dn.toString(); }); res.on("error", reject); res.on("end", (result) => { if (result?.status !== 0 && result?.status !== undefined) { reject(new Error(`LDAP search end status ${result.status}`)); return; } resolve(dn); }); }); }); } /** * Verify username/password against AD/LDAP: * service bind → search user DN → bind as user with password. */ async function verifyCredentials(username, password, cfg) { const user = (username ?? "").trim(); if (!user || !password) { return { ok: false, reason: "invalid_credentials" }; } const filter = cfg.searchFilter.replace(/\{\{username\}\}/g, escapeLdapFilterValue(user)); const clientOpts = { url: cfg.url }; if (cfg.tlsOptions) { clientOpts.tlsOptions = { ...cfg.tlsOptions }; } const searchClient = ldapjs_1.default.createClient(clientOpts); try { await bindAsync(searchClient, cfg.bindDN, cfg.bindPassword); const userDn = await searchUserDn(searchClient, cfg.searchBase, filter); unbindSafe(searchClient); if (!userDn) { return { ok: false, reason: "user_not_found" }; } const verifyClient = ldapjs_1.default.createClient(clientOpts); try { await bindAsync(verifyClient, userDn, password); unbindSafe(verifyClient); return { ok: true }; } catch { unbindSafe(verifyClient); return { ok: false, reason: "invalid_credentials" }; } } catch { unbindSafe(searchClient); return { ok: false, reason: "connection_error" }; } } //# sourceMappingURL=ldapAuth.js.map