"""Authentication handlers for the SQLI binary login PDU. Auth in modern Informix has several mechanisms (plain password, password obfuscation, PAM challenge/response, GSSAPI/Kerberos, trusted context). For Phase 1 we ship the simplest one — plain password — and expose a pluggable shape so later phases add new methods without touching ``connections.py``. Each handler contributes the username/password section to the login PDU. The rest of the PDU (markers, capabilities, env vars, process info) is assembled by ``connections.py`` and is auth-method-independent. """ from __future__ import annotations from collections.abc import Callable from ._protocol import IfxStreamWriter # Type alias: an auth handler appends its credentials section to the writer. AuthHandler = Callable[[IfxStreamWriter, str, str | None], None] def write_plain_password( writer: IfxStreamWriter, username: str, password: str | None, ) -> None: """Write the username + password section of the login PDU. Layout (matches ``Connection.encodeAscBinary`` lines that emit username and password): ``[short username.len+1][bytes username][nul]`` then either ``[short 0]`` (no password) or ``[short password.len+1][bytes password][nul]``. Plain password is sent inline; the server compares it directly to its user database. There is no salt, no hash, no challenge round-trip. Use TLS (Phase 6+) if the network is untrusted. """ writer.write_string_with_nul(username) if password is None: writer.write_short(0) else: writer.write_string_with_nul(password) # Dispatch table — Phase 6+ adds: "obfuscate" (SHA-256+nonstd-base64), # "pam" (challenge/response), "gssapi" (Kerberos). HANDLERS: dict[str, AuthHandler] = { "plain": write_plain_password, } def get_handler(method: str) -> AuthHandler: """Look up an auth handler by name. Raises KeyError if unknown.""" return HANDLERS[method]