From bdb428a1cb8d44a48c27064b1c855c6153f67e5c Mon Sep 17 00:00:00 2001 From: andrei Date: Sun, 27 Apr 2025 16:27:58 +0500 Subject: [PATCH] =?UTF-8?q?=D1=81=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=D0=B8=20?= =?UTF-8?q?=D0=B0=D0=B2=D1=82=D0=BE=D1=80=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- API/app/application/users_repository.py | 5 ++- API/app/contollers/auth_router.py | 33 ++++++++++++++++++++ API/app/contollers/register_controller.py | 3 +- API/app/domain/entities/auth.py | 5 +++ API/app/domain/entities/token_entity.py | 11 +++++++ API/app/infrastructure/auth_service.py | 36 ++++++++++++++++++++++ API/app/main.py | 2 ++ API/app/settings.py | 3 +- API/req.txt | Bin 356 -> 386 bytes 9 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 API/app/contollers/auth_router.py create mode 100644 API/app/domain/entities/auth.py create mode 100644 API/app/domain/entities/token_entity.py create mode 100644 API/app/infrastructure/auth_service.py diff --git a/API/app/application/users_repository.py b/API/app/application/users_repository.py index 1723055..0fa8373 100644 --- a/API/app/application/users_repository.py +++ b/API/app/application/users_repository.py @@ -3,7 +3,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import joinedload from typing_extensions import Optional -from app.domain.models import User +from app.domain.models import User, Profile class UsersRepository: @@ -19,7 +19,6 @@ class UsersRepository: stmt = ( select(User) .filter_by(login=login) - .options(joinedload(User.role)) ) result = await self.db.execute(stmt) return result.scalars().first() @@ -28,4 +27,4 @@ class UsersRepository: self.db.add(user) await self.db.commit() await self.db.refresh(user) - return user \ No newline at end of file + return user diff --git a/API/app/contollers/auth_router.py b/API/app/contollers/auth_router.py new file mode 100644 index 0000000..abb3d09 --- /dev/null +++ b/API/app/contollers/auth_router.py @@ -0,0 +1,33 @@ +from fastapi import APIRouter, Response, Depends +from sqlalchemy.ext.asyncio import AsyncSession + +from app.database.session import get_db +from app.domain.entities.auth import AuthEntity +from app.domain.entities.token_entity import TokenEntity +from app.infrastructure.auth_service import AuthService + +router = APIRouter() + + +@router.post( + '/', + response_model=TokenEntity, + summary="User authentication", + description="Logs in the user and outputs the `access_token` in the `cookie'", +) +async def auth_user( + response: Response, + user_data: AuthEntity, + db: AsyncSession = Depends(get_db), +): + auth_service = AuthService(db) + token = await auth_service.authenticate_user(user_data.login, user_data.password) + + response.set_cookie( + key="users_access_token", + value=token["access_token"], + httponly=True, + samesite="Lax", + ) + + return token diff --git a/API/app/contollers/register_controller.py b/API/app/contollers/register_controller.py index e2653f0..6ec4a81 100644 --- a/API/app/contollers/register_controller.py +++ b/API/app/contollers/register_controller.py @@ -1,7 +1,6 @@ from typing import Optional -from fastapi import APIRouter -from fastapi.params import Depends +from fastapi import APIRouter, Depends from sqlalchemy.ext.asyncio import AsyncSession from app.database.session import get_db diff --git a/API/app/domain/entities/auth.py b/API/app/domain/entities/auth.py new file mode 100644 index 0000000..f197e00 --- /dev/null +++ b/API/app/domain/entities/auth.py @@ -0,0 +1,5 @@ +from app.domain.entities.base_user import BaseUserEntity + + +class AuthEntity(BaseUserEntity): + pass diff --git a/API/app/domain/entities/token_entity.py b/API/app/domain/entities/token_entity.py new file mode 100644 index 0000000..cc190c0 --- /dev/null +++ b/API/app/domain/entities/token_entity.py @@ -0,0 +1,11 @@ +from typing import Optional + +from pydantic import BaseModel + + +class TokenEntity(BaseModel): + access_token: str + user_id: int + + class Config: + from_attributes = True diff --git a/API/app/infrastructure/auth_service.py b/API/app/infrastructure/auth_service.py new file mode 100644 index 0000000..d43afac --- /dev/null +++ b/API/app/infrastructure/auth_service.py @@ -0,0 +1,36 @@ +import datetime +from typing import Optional + +import jwt +from fastapi import HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +from starlette import status + +from app.application.users_repository import UsersRepository +from app.settings import get_auth_data + + +class AuthService: + def __init__(self, db: AsyncSession): + self.users_repository = UsersRepository(db) + + async def authenticate_user(self, login: str, password: str) -> Optional[dict]: + user = await self.users_repository.get_by_login(login) + if user and user.check_password(password): + access_token = self.create_access_token({"user_id": user.id}) + return { + "access_token": access_token, + "user_id": user.id + } + + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Invalid login or password") + + @staticmethod + def create_access_token(data: dict) -> str: + to_encode = data.copy() + expire = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=30) + to_encode.update({"exp": expire}) + auth_data = get_auth_data() + encode_jwt = jwt.encode(to_encode, auth_data['secret_key'], algorithm=auth_data['algorithm']) + + return encode_jwt diff --git a/API/app/main.py b/API/app/main.py index 99b5ecf..cf1db0f 100644 --- a/API/app/main.py +++ b/API/app/main.py @@ -2,6 +2,7 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from app.contollers.register_controller import router as register_router +from app.contollers.auth_router import router as auth_router from app.settings import settings @@ -17,6 +18,7 @@ def start_app(): ) api_app.include_router(register_router, prefix=f'{settings.PREFIX}/register', tags=['register']) + api_app.include_router(auth_router, prefix=f'{settings.PREFIX}/auth', tags=['auth']) return api_app diff --git a/API/app/settings.py b/API/app/settings.py index 97b26d4..9795fd4 100644 --- a/API/app/settings.py +++ b/API/app/settings.py @@ -5,6 +5,7 @@ class Settings(BaseSettings): DATABASE_URL: str SECRET_KEY: str PREFIX: str = '' + ALGORITHM: str = 'HS256' class Config: env_file = '.env' @@ -14,7 +15,7 @@ class Settings(BaseSettings): settings = Settings() -def det_auth_data(): +def get_auth_data(): return { 'secret_key': settings.SECRET_KEY, 'algorithm': settings.ALGORITHM diff --git a/API/req.txt b/API/req.txt index 33e583fff47ca4b5cb1f2c6c357fe6fbc28ea533..05da4ade043d7161e0be96a50027565aec0c9fda 100644 GIT binary patch delta 38 ocmaFD)Wp0Yg;9=|fs3Jlp^_nsp`4+F!4?RO81xtn84SQA0H5&&jsO4v delta 7 OcmZo-e!{dNg%JP>`~rah