diff options
Diffstat (limited to 'src/expense/auth/password.py')
-rw-r--r-- | src/expense/auth/password.py | 45 |
1 files changed, 45 insertions, 0 deletions
diff --git a/src/expense/auth/password.py b/src/expense/auth/password.py new file mode 100644 index 0000000..0c7090c --- /dev/null +++ b/src/expense/auth/password.py @@ -0,0 +1,45 @@ +from dataclasses import dataclass +from sqlalchemy import Text, TypeDecorator +from werkzeug.security import check_password_hash, generate_password_hash + + +@dataclass(eq=False) +class PasswordHash: + """ + Generate hash for password storage. It wraps arround the werkzeug.security + module. + """ + + hash: str + + def __eq__(self, candidate) -> bool: + return check_password_hash(self.hash, candidate) + + @classmethod + def new(cls, password: str, method: str = "pbkdf2", salt_length: int = 16): + """Wrapper over generate_password""" + hashed_password = generate_password_hash(password, method, salt_length) + return cls(hashed_password) + + +class Password(TypeDecorator): + """The SQLAlchemy Addapter for PasswordHash""" + + impl = Text + + def process_bind_param(self, value, dialect) -> str | None: + if value is not None: + if isinstance(value, PasswordHash): + return value.hash + raise ValueError("Invalid value type. Expected PasswordHash object.") + return None + + def process_result_value(self, value: str, dialect) -> PasswordHash | None: + if value is not None: + return PasswordHash(value) + return None + + def coerce_compared_value(self, op, value): + if isinstance(value, PasswordHash): + return value.hash + return super().coerce_compared_value(op, value) |