/**
 * directory/port — the four directory operations `directory-password` needs,
 * as a port, so the rules run over a fake directory in tests and over LDAPS
 * (`ldapDirectory`, through `ldapts`) in a deployment.
 */
/// <reference types="node" />
/// <reference types="node" />
import type { AdSubCodeName } from '../../../hosting/signin/audit.js';
/** One entry a search found. Binary attributes (objectGUID, objectSid) arrive as bytes. */
export interface DirectoryEntry {
    readonly dn: string;
    readonly objectGUID?: Buffer;
    readonly displayName?: string;
}
/**
 * A refused bind that says WHY: Active Directory's sub-code from the LDAP
 * error (`… data 52e, v1db1` → `'52e'`). For the operator's audit record only —
 * the person gets one answer whatever it says.
 */
export interface DirectoryBindRefused {
    readonly kind: 'invalid';
    /** AD's sub-code, lower-case hex, when the error carried one. */
    readonly adSubCode?: string;
}
/** What a bind answered: `'ok'`, or a refusal — bare, or with AD's sub-code. */
export type DirectoryBind = 'ok' | 'invalid' | DirectoryBindRefused;
/** One connection, used for exactly one sign-in attempt, then closed. */
export interface DirectorySession {
    /**
     * A simple bind, sent ONCE — never retried. `'ok'` when the directory
     * authenticated the name and password; `'invalid'` (or
     * `{ kind: 'invalid', adSubCode }`) for ANY wrong credential. The sub-code
     * reaches the audit record and never the person. Throws only when it could
     * not ask.
     */
    bind(name: string, password: string): Promise<DirectoryBind>;
    /** RFC 4532 "Who am I?" — the authzId the directory says this connection is, e.g. `u:CORP\\alice`. Empty = anonymous. */
    whoAmI(): Promise<string>;
    /** A search below `base` (subtree) or AT `base` (`scope: 'base'`). */
    search(base: string, filter: string, scope: 'sub' | 'base'): Promise<readonly DirectoryEntry[]>;
    close(): Promise<void>;
}
/** Where connections come from. */
export interface Directory {
    open(): Promise<DirectorySession>;
}
/**
 * What an AD bind sub-code means: `52e` bad password, `775` locked, `532`
 * password expired, `773` must change at next logon, `533` disabled, `701`
 * account expired; anything else (`525` no such user, `530`/`531` logon
 * hours/workstation, a code a later AD invents) is `'unknown'`.
 */
export declare function adSubCodeName(code: string): AdSubCodeName;
/**
 * RFC 4515 §3: escape a value for an LDAP search filter. A string escapes `*`,
 * `(`, `)`, `\` and NUL (and every non-ASCII byte, so nothing depends on how a
 * server reads UTF-8 in a filter); bytes are escaped whole.
 */
export declare function escapeFilterValue(value: string | Buffer): string;
/**
 * A SID in SDDL string form (`S-1-5-21-…-1104`) as the binary `objectSid` a
 * filter compares, or `undefined` when it is not one.
 */
export declare function sidToBytes(sid: string): Buffer | undefined;
