From f8f5bf80c135cfd38366b1b663480bf5ec731391 Mon Sep 17 00:00:00 2001 From: Sergey Vanyushkin Date: Thu, 4 Apr 2024 10:51:51 +0000 Subject: [PATCH] auth --- api/app_builder/dependencies.py | 26 +++++++----- api/application/usecase/auth/auth_user.py | 4 ++ api/infrastructure/dependencies/protocols.py | 26 ++++++++++++ .../repositories/user_repository.py | 13 +++++- poetry.lock | 40 ++++++++----------- pyproject.toml | 2 +- 6 files changed, 74 insertions(+), 37 deletions(-) diff --git a/api/app_builder/dependencies.py b/api/app_builder/dependencies.py index 17da4de..9185619 100644 --- a/api/app_builder/dependencies.py +++ b/api/app_builder/dependencies.py @@ -1,22 +1,23 @@ from fastapi import FastAPI -from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker +from sqlalchemy.ext.asyncio import (AsyncEngine, AsyncSession, + async_sessionmaker) from api.application.abstractions.uow import UnitOfWork from api.application.protocols.date_time import DateTimeProvider +from api.application.protocols.jwt import JwtTokenProcessor from api.application.protocols.password_hasher import PasswordHasher +from api.application.usecase.auth.auth_user import LoginUser from api.application.usecase.auth.create_user import CreateUser from api.domain.user.repository import UserRepository -from api.infrastructure.dependencies.adapters import ( - create_engine, - create_session_maker, - new_session, - new_unit_of_work, -) +from api.infrastructure.dependencies.adapters import (create_engine, + create_session_maker, + new_session, + new_unit_of_work) from api.infrastructure.dependencies.configs import app_settings -from api.infrastructure.dependencies.protocols import ( - get_date_time_provider, - get_password_hasher, -) +from api.infrastructure.dependencies.protocols import (get_date_time_provider, + get_jwt_token_processor, + get_password_hasher, + get_user_login) from api.infrastructure.dependencies.repositories import get_user_repository from api.infrastructure.dependencies.usecases import provide_create_user from api.infrastructure.settings import Settings @@ -34,6 +35,9 @@ def init_dependencies(app: FastAPI) -> None: app.dependency_overrides[DateTimeProvider] = get_date_time_provider app.dependency_overrides[PasswordHasher] = get_password_hasher + app.dependency_overrides[JwtTokenProcessor] = get_jwt_token_processor + app.dependency_overrides[LoginUser] = get_user_login + app.dependency_overrides[UserRepository] = get_user_repository app.dependency_overrides[CreateUser] = provide_create_user diff --git a/api/application/usecase/auth/auth_user.py b/api/application/usecase/auth/auth_user.py index a07b4ad..1ce19bc 100644 --- a/api/application/usecase/auth/auth_user.py +++ b/api/application/usecase/auth/auth_user.py @@ -15,12 +15,16 @@ class LoginUser: self.hasher = password_hasher async def __call__(self, request: LoginRequest) -> AuthenticationResponse: + print("__call__ request", request) user = await self.user_repository.get_user(filter={"email": request.email}) + print("__call__ user from repo", user) error = UserInvalidCredentialsError("Email or password is incorrect") if user is None: + print("user is none in LoginUser __call__") raise error if not self.hasher.verify_password(request.password, user.hashed_password): + print("wrong pass in LoginUser __call__") raise error return AuthenticationResponse( diff --git a/api/infrastructure/dependencies/protocols.py b/api/infrastructure/dependencies/protocols.py index 873dc17..2807056 100644 --- a/api/infrastructure/dependencies/protocols.py +++ b/api/infrastructure/dependencies/protocols.py @@ -1,7 +1,17 @@ +from typing import Annotated + +from fastapi import Depends + from api.application.protocols.date_time import DateTimeProvider +from api.application.protocols.jwt import JwtTokenProcessor from api.application.protocols.password_hasher import PasswordHasher +from api.application.usecase.auth.auth_user import LoginUser +from api.domain.user.repository import UserRepository +from api.infrastructure.auth.jwt_processor import JoseJwtTokenProcessor from api.infrastructure.date_time import SystemDateTimeProvider, Timezone +from api.infrastructure.dependencies.stub import Stub from api.infrastructure.security.password_hasher import Pbkdf2PasswordHasher +from api.infrastructure.settings import Settings def get_password_hasher() -> PasswordHasher: @@ -10,3 +20,19 @@ def get_password_hasher() -> PasswordHasher: def get_date_time_provider() -> DateTimeProvider: return SystemDateTimeProvider(Timezone.UTC) + + +def get_jwt_token_processor( + settings: Annotated[Settings, Depends(Stub(Settings))], + date_time_provider: Annotated[DateTimeProvider, Depends(Stub(DateTimeProvider))], +) -> JwtTokenProcessor: + return JoseJwtTokenProcessor( + jwt_options=settings.jwt, date_time_provider=date_time_provider + ) + + +def get_user_login( + user_repository: Annotated[UserRepository, Depends(Stub(UserRepository))], + password_hasher: Annotated[PasswordHasher, Depends(Stub(PasswordHasher))], +) -> LoginUser: + return LoginUser(user_repository=user_repository, password_hasher=password_hasher) diff --git a/api/infrastructure/persistence/repositories/user_repository.py b/api/infrastructure/persistence/repositories/user_repository.py index 5106fdc..c40f4cf 100644 --- a/api/infrastructure/persistence/repositories/user_repository.py +++ b/api/infrastructure/persistence/repositories/user_repository.py @@ -2,6 +2,7 @@ from sqlalchemy import text from sqlalchemy.ext.asyncio import AsyncSession from api.domain.user import User, UserRepository +from api.domain.user.model import UserEmail, UserFirstName, UserId class SqlAlchemyUserRepository(UserRepository): @@ -25,7 +26,17 @@ class SqlAlchemyUserRepository(UserRepository): ) async def get_user(self, filter: dict) -> User | None: - pass + stmt = text("""SELECT * FROM users WHERE email = :val""") + result = await self.session.execute(stmt, {"val": filter["email"]}) + if not result: + return None + result = result.mappings().one() + return User( + id=UserId(result.id), + name=UserFirstName(result.name), + email=UserEmail(result.email), + hashed_password=result.hashed_password, + ) async def get_users(self) -> list[User]: return [] diff --git a/poetry.lock b/poetry.lock index 4392949..907aa2e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -618,19 +618,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "jose" -version = "1.0.0" -description = "An implementation of the JOSE draft" -optional = false -python-versions = "*" -files = [ - {file = "jose-1.0.0.tar.gz", hash = "sha256:8436c3617cd94e1ba97828fbb1ce27c129f66c78fb855b4bb47e122b5f345fba"}, -] - -[package.dependencies] -pycrypto = ">=2.6" - [[package]] name = "magic-filter" version = "1.0.12" @@ -991,16 +978,6 @@ files = [ {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, ] -[[package]] -name = "pycrypto" -version = "2.6.1" -description = "Cryptographic modules for Python." -optional = false -python-versions = "*" -files = [ - {file = "pycrypto-2.6.1.tar.gz", hash = "sha256:f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c"}, -] - [[package]] name = "pydantic" version = "2.5.3" @@ -1180,6 +1157,20 @@ cryptography = ["cryptography (>=3.4.0)"] pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] +[[package]] +name = "python-multipart" +version = "0.0.9" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"}, + {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, +] + +[package.extras] +dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"] + [[package]] name = "pyyaml" version = "6.0.1" @@ -1205,6 +1196,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -1573,4 +1565,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "cb90b25778130dc1445290f65ee84f37029b7db4838a16b510a9f2efaf5ae841" +content-hash = "586dbb5fd80f9999d39a1cb960a06bc8f15e3b103f1c273e4d954fca0b1ffc75" diff --git a/pyproject.toml b/pyproject.toml index a8d77c0..1cb3e1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,8 +7,8 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.10" -jose = "^1.0.0" python-jose = "^3.3.0" +python-multipart = "^0.0.9"