Source code for advanced_alchemy.types.password_hash.passlib

"""Passlib Hashing Backend."""

from typing import TYPE_CHECKING, Any, Union

from passlib.context import CryptContext  # pyright: ignore

from advanced_alchemy.types.password_hash.base import HashingBackend

if TYPE_CHECKING:
    from sqlalchemy import BinaryExpression, ColumnElement

__all__ = ("PasslibHasher",)


[docs] class PasslibHasher(HashingBackend): """Hashing backend using Passlib. Relies on the `passlib` package being installed. Install with `pip install passlib` or `uv pip install passlib`. """
[docs] def __init__(self, context: CryptContext) -> None: """Initialize PasslibBackend. Args: context: The Passlib CryptContext to use for hashing and verification. """ self.context = context
[docs] def hash(self, value: "Union[str, bytes]") -> str: """Hash the given value using the Passlib context. Args: value: The plain text value to hash. Will be converted to string. Returns: The hashed string. """ return self.context.hash(self._ensure_bytes(value))
[docs] def verify(self, plain: "Union[str, bytes]", hashed: str) -> bool: """Verify a plain text value against a hash using the Passlib context. Args: plain: The plain text value to verify. Will be converted to string. hashed: The hash to verify against. Returns: True if the plain text matches the hash, False otherwise. """ try: return self.context.verify(self._ensure_bytes(plain), hashed) except Exception: # noqa: BLE001 # Passlib can raise various errors for invalid hashes return False
[docs] def compare_expression(self, column: "ColumnElement[str]", plain: Any) -> "BinaryExpression[bool]": """Direct SQL comparison is not supported for Passlib. Raises: NotImplementedError: Always raised. """ msg = "PasslibHasher does not support direct SQL comparison." raise NotImplementedError(msg)