Source code for advanced_alchemy.types.password_hash.passlib

"""Passlib Hashing Backend."""

from typing import TYPE_CHECKING, Any, Union

from advanced_alchemy.exceptions import MissingDependencyError
from advanced_alchemy.types.password_hash.base import HashingBackend
from advanced_alchemy.typing import PASSLIB_INSTALLED

if TYPE_CHECKING:
    from passlib.context import CryptContext
    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. Raises: MissingDependencyError: If the ``passlib`` package is not installed. """ if not PASSLIB_INSTALLED: raise MissingDependencyError(package="passlib") 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 needs_rehash(self, hashed: str) -> bool: """Return True if the stored hash uses an outdated scheme or cost. Args: hashed: The stored hash string. Returns: True if the hash should be regenerated; False for an unparsable or foreign hash. """ try: return self.context.needs_update(hashed) except Exception: # noqa: BLE001 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)