From 20ffc180448d75b38e331da0c42211432af6d87f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Doma=C5=84ski?= Date: Thu, 2 Apr 2026 21:33:20 +0200 Subject: [PATCH] [password_helper] implement --- fooder/auth.py | 4 ++-- fooder/context.py | 2 +- fooder/domain/base.py | 14 +++++-------- fooder/settings.py | 6 ++++-- fooder/test/utils/__init__.py | 0 fooder/test/utils/test_password_helper.py | 25 +++++++++++++++++++++++ fooder/utils/password_helper.py | 16 +++++++++++++++ 7 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 fooder/test/utils/__init__.py create mode 100644 fooder/test/utils/test_password_helper.py create mode 100644 fooder/utils/password_helper.py diff --git a/fooder/auth.py b/fooder/auth.py index 6dc7c20..73b5992 100644 --- a/fooder/auth.py +++ b/fooder/auth.py @@ -22,11 +22,11 @@ AsyncSessionDependency = Annotated[async_sessionmaker, Depends(get_db_session)] TokenDependency = Annotated[str, Depends(oauth2_scheme)] -def verify_password(plain_password: str, hashed_password: str) -> bool: +def verify(plain_password: str, hashed_password: str) -> bool: return pwd_context.verify(plain_password, hashed_password) -def get_password_hash(password: str) -> str: +def hash(password: str) -> str: return pwd_context.hash(password) diff --git a/fooder/context.py b/fooder/context.py index f824dce..566453b 100644 --- a/fooder/context.py +++ b/fooder/context.py @@ -10,7 +10,7 @@ class Context: """ def __init__(self, repo: Repository) -> None: - self.repo = Repository + self.repo = repo class ContextDependency: diff --git a/fooder/domain/base.py b/fooder/domain/base.py index 68859a3..bcaf75b 100644 --- a/fooder/domain/base.py +++ b/fooder/domain/base.py @@ -1,4 +1,5 @@ from sqlalchemy.orm import DeclarativeBase, Mapped, declared_attr, mapped_column +from ..utils.password_helper import password_helper class Base(DeclarativeBase): @@ -30,13 +31,8 @@ class PasswordMixin: hashed_password: Mapped[str] - def set_password(self, password) -> None: - """set_password. - - :param password: - :rtype: None - """ - - from ..auth import password_helper - + def set_password(self, password: str) -> None: self.hashed_password = password_helper.hash(password) + + def verify_password(self, password: str) -> bool: + return password_helper.verify(password, self.hashed_password) diff --git a/fooder/settings.py b/fooder/settings.py index 261f038..5a73629 100644 --- a/fooder/settings.py +++ b/fooder/settings.py @@ -1,4 +1,4 @@ -from typing import List +from typing import Any from pydantic_settings import BaseSettings @@ -15,9 +15,11 @@ class Settings(BaseSettings): ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 REFRESH_TOKEN_EXPIRE_DAYS: int = 120 - ALLOWED_ORIGINS: List[str] = ["*"] + ALLOWED_ORIGINS: list[str] = ["*"] API_KEY: str + PASSWORD_SCHEMES: list[str] = ["bcrypt"] + settings = Settings() diff --git a/fooder/test/utils/__init__.py b/fooder/test/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fooder/test/utils/test_password_helper.py b/fooder/test/utils/test_password_helper.py new file mode 100644 index 0000000..59bac85 --- /dev/null +++ b/fooder/test/utils/test_password_helper.py @@ -0,0 +1,25 @@ +from fooder.utils.password_helper import password_helper +import pytest + + +def test_password_hash_and_verify(faker): + password = faker.password() + hash = password_helper.hash(password) + + assert password_helper.verify(password, hash) + + +def test_wrong_password_doesnt_verify(faker): + password = faker.password() + hash = password_helper.hash(password) + + password2 = faker.password() + assert not password_helper.verify(password2, hash) + + +def test_wrong_hash_breaks(faker): + password = faker.password() + hash = password_helper.hash(password) + + with pytest.raises(Exception, match="malformed"): + assert not password_helper.verify(password, hash + password) diff --git a/fooder/utils/password_helper.py b/fooder/utils/password_helper.py new file mode 100644 index 0000000..a28aedd --- /dev/null +++ b/fooder/utils/password_helper.py @@ -0,0 +1,16 @@ +from passlib.context import CryptContext +from ..settings import settings + + +class PasswordHelper: + def __init__(self, pwd_context: CryptContext): + self.pwd_context = pwd_context + + def verify(self, plain_password: str, hashed_password: str) -> bool: + return self.pwd_context.verify(plain_password, hashed_password) + + def hash(self, password: str) -> str: + return self.pwd_context.hash(password) + + +password_helper = PasswordHelper(CryptContext(settings.PASSWORD_SCHEMES))