From 19f8236b478df81a8b24ba3dd5829f9d09850340 Mon Sep 17 00:00:00 2001 From: TheNoxium Date: Tue, 29 Apr 2025 21:16:59 +0500 Subject: [PATCH] fix: middlewaer acces token auth --- api/api/__main__.py | 5 ++ api/api/endpoints/__init__.py | 4 +- api/api/endpoints/account.py | 50 ++++++------- api/api/endpoints/auth.py | 22 +++--- api/api/endpoints/keyring.py | 54 +++++++------- api/api/endpoints/{pofile.py => profile.py} | 43 ++++------- api/api/services/access_token_validadtion.py | 14 ++-- api/api/services/middleware.py | 75 ++++++++++++++++++++ 8 files changed, 164 insertions(+), 103 deletions(-) rename api/api/endpoints/{pofile.py => profile.py} (52%) create mode 100644 api/api/services/middleware.py diff --git a/api/api/__main__.py b/api/api/__main__.py index 12d1419..ef43631 100644 --- a/api/api/__main__.py +++ b/api/api/__main__.py @@ -9,6 +9,7 @@ from uvicorn import run from api.config import get_settings, DefaultSettings from api.endpoints import list_of_routes from api.utils.common import get_hostname +from api.services.middleware import MiddlewareAccessTokenValidadtion logger = logging.getLogger() logger.setLevel(logging.DEBUG) @@ -20,6 +21,7 @@ def bind_routes(application: FastAPI, setting: DefaultSettings) -> None: application.include_router(route, prefix=setting.PATH_PREFIX) + def get_app() -> FastAPI: """Creates application and all dependable objects.""" loguru.logger.remove() @@ -78,4 +80,7 @@ app.add_middleware( allow_credentials=True, allow_methods=["GET", "POST", "OPTIONS", "DELETE", "PUT"], allow_headers=["*"], + ) + +app.add_middleware(MiddlewareAccessTokenValidadtion) diff --git a/api/api/endpoints/__init__.py b/api/api/endpoints/__init__.py index 6ac1e3f..ad6708a 100644 --- a/api/api/endpoints/__init__.py +++ b/api/api/endpoints/__init__.py @@ -1,10 +1,10 @@ from api.endpoints.auth import api_router as auth_router -from api.endpoints.pofile import api_router as pofile_router +from api.endpoints.profile import api_router as profile_router from api.endpoints.account import api_router as account_router from api.endpoints.keyring import api_router as keyring_router list_of_routes = [ auth_router, - pofile_router, + profile_router, account_router, keyring_router] diff --git a/api/api/endpoints/account.py b/api/api/endpoints/account.py index bd6bcb3..f2a9fcf 100644 --- a/api/api/endpoints/account.py +++ b/api/api/endpoints/account.py @@ -9,9 +9,6 @@ from fastapi import ( status, ) -from fastapi import FastAPI - -from fastapi_jwt_auth import AuthJWT from sqlalchemy.ext.asyncio import AsyncConnection @@ -19,10 +16,10 @@ from api.db.connection.session import get_connection_dep from api.db.logic.account import get_user_id, update_user_id, create_user,get_user_login -from api.schemas.account.account import User,Role,Status +from api.schemas.account.account import User,Status from api.schemas.endpoints.account import UserUpdate -from api.services.access_token_validadtion import AccessTokenValidadtion + from api.services.user_role_validation import db_user_role_validation from api.services.update_data_validation import update_user_data_changes @@ -36,11 +33,12 @@ api_router = APIRouter( @api_router.get("/{user_id}") async def get_account(user_id: int, - connection: AsyncConnection = Depends(get_connection_dep), - Authorize: AuthJWT = Depends()): + request: Request, + connection: AsyncConnection = Depends(get_connection_dep) + ): - current_user = AccessTokenValidadtion(Authorize) + current_user = request.state.current_user authorize_user = await db_user_role_validation(connection, current_user) user = await get_user_id(connection, user_id) @@ -55,11 +53,12 @@ async def get_account(user_id: int, @api_router.post("") async def create_account( - user: UserUpdate, - connection: AsyncConnection = Depends(get_connection_dep), - Authorize: AuthJWT = Depends() -): - current_user = AccessTokenValidadtion(Authorize) + user: UserUpdate, + request: Request, + connection: AsyncConnection = Depends(get_connection_dep) + ): + + current_user = request.state.current_user authorize_user = await db_user_role_validation(connection, current_user) @@ -81,12 +80,13 @@ async def create_account( @api_router.put("/{user_id}") async def update_account( - user_id: int, - user_update: UserUpdate, - connection: AsyncConnection = Depends(get_connection_dep), - Authorize: AuthJWT = Depends() -): - current_user = AccessTokenValidadtion(Authorize) + user_id: int, + request: Request, + user_update: UserUpdate, + connection: AsyncConnection = Depends(get_connection_dep) + ): + + current_user = request.state.current_user authorize_user = await db_user_role_validation(connection, current_user) @@ -114,11 +114,13 @@ async def update_account( @api_router.delete("/{user_id}") async def delete_account( - user_id: int, - connection: AsyncConnection = Depends(get_connection_dep), - Authorize: AuthJWT = Depends() -): - current_user = AccessTokenValidadtion(Authorize) + user_id: int, + request: Request, + connection: AsyncConnection = Depends(get_connection_dep) + ): + + + current_user = request.state.current_user authorize_user = await db_user_role_validation(connection, current_user) diff --git a/api/api/endpoints/auth.py b/api/api/endpoints/auth.py index 7f60004..b02a1ea 100644 --- a/api/api/endpoints/auth.py +++ b/api/api/endpoints/auth.py @@ -26,8 +26,6 @@ from api.services.auth import authenticate_user from api.db.logic.auth import add_new_refresh_token,upgrade_old_refresh_token -from api.db.logic.account import get_user_login - from api.schemas.endpoints.auth import Auth api_router = APIRouter( @@ -54,13 +52,13 @@ def get_config(): return Settings() -@api_router.post("/") +@api_router.post("") async def login_for_access_token( - user: Auth, - response: Response, - connection: AsyncConnection = Depends(get_connection_dep), - Authorize: AuthJWT = Depends(), -): + user: Auth, + response: Response, + connection: AsyncConnection = Depends(get_connection_dep), + Authorize: AuthJWT = Depends(), + ): """Авторизирует, выставляет токены в куки.""" @@ -109,10 +107,10 @@ async def login_for_access_token( @api_router.post("/refresh") async def refresh( - request: Request, - connection: AsyncConnection = Depends(get_connection_dep), - Authorize: AuthJWT = Depends()): - """Обновляет access токен.""" + request: Request, + connection: AsyncConnection = Depends(get_connection_dep), + Authorize: AuthJWT = Depends() + ): refresh_token = request.cookies.get("refresh_token_cookie") print("Refresh Token:", refresh_token) diff --git a/api/api/endpoints/keyring.py b/api/api/endpoints/keyring.py index ceda7f7..b8ea872 100644 --- a/api/api/endpoints/keyring.py +++ b/api/api/endpoints/keyring.py @@ -9,22 +9,19 @@ from fastapi import ( status, ) -from fastapi_jwt_auth import AuthJWT from sqlalchemy.ext.asyncio import AsyncConnection from api.db.connection.session import get_connection_dep -from api.db.logic.account import get_user_login from api.db.logic.keyring import get_key_id,create_key,update_key_id -from api.schemas.account.account import Role,Status +from api.schemas.account.account import Status from api.schemas.endpoints.account_keyring import AccountKeyringUpdate from api.schemas.account.account_keyring import AccountKeyring -from api.services.access_token_validadtion import AccessTokenValidadtion from api.services.user_role_validation import db_user_role_validation from api.services.update_data_validation import update_key_data_changes @@ -38,10 +35,11 @@ api_router = APIRouter( @api_router.get("/{user_id}/{key_id}") async def get_keyring( key_id: str, - connection: AsyncConnection = Depends(get_connection_dep), - Authorize: AuthJWT = Depends()): + request: Request, + connection: AsyncConnection = Depends(get_connection_dep) + ): - current_user = AccessTokenValidadtion(Authorize) + current_user = request.state.current_user authorize_user = await db_user_role_validation(connection, current_user) @@ -57,14 +55,14 @@ async def get_keyring( @api_router.post("/{user_id}/{key_id}") async def create_keyring( - user_id: int, - key_id: str, - key: AccountKeyringUpdate, - connection: AsyncConnection = Depends(get_connection_dep), - Authorize: AuthJWT = Depends() -): + user_id: int, + key_id: str, + request: Request, + key: AccountKeyringUpdate, + connection: AsyncConnection = Depends(get_connection_dep) + ): - current_user = AccessTokenValidadtion(Authorize) + current_user = request.state.current_user authorize_user = await db_user_role_validation(connection, current_user) @@ -84,13 +82,14 @@ async def create_keyring( @api_router.put("/{user_id}/{key_id}") async def update_keyring( - user_id: int, - key_id: str, - keyring_update: AccountKeyringUpdate, - connection: AsyncConnection = Depends(get_connection_dep), - Authorize: AuthJWT = Depends() -): - current_user = AccessTokenValidadtion(Authorize) + user_id: int, + key_id: str, + request: Request, + keyring_update: AccountKeyringUpdate, + connection: AsyncConnection = Depends(get_connection_dep) + ): + + current_user = request.state.current_user authorize_user = await db_user_role_validation(connection, current_user) @@ -119,12 +118,13 @@ async def update_keyring( @api_router.delete("/{user_id}/{key_id}") async def delete_keyring( - user_id: int, - key_id: str, - connection: AsyncConnection = Depends(get_connection_dep), - Authorize: AuthJWT = Depends() -): - current_user = AccessTokenValidadtion(Authorize) + user_id: int, + key_id: str, + request: Request, + connection: AsyncConnection = Depends(get_connection_dep) + ): + + current_user = request.state.current_user authorize_user = await db_user_role_validation(connection, current_user) diff --git a/api/api/endpoints/pofile.py b/api/api/endpoints/profile.py similarity index 52% rename from api/api/endpoints/pofile.py rename to api/api/endpoints/profile.py index 77bba39..09cf5e0 100644 --- a/api/api/endpoints/pofile.py +++ b/api/api/endpoints/profile.py @@ -9,7 +9,6 @@ from fastapi import ( status, ) -from fastapi_jwt_auth import AuthJWT from sqlalchemy.ext.asyncio import AsyncConnection @@ -19,28 +18,22 @@ from api.services.update_data_validation import update_user_data_changes from api.schemas.endpoints.account import UserUpdate -from api.services.access_token_validadtion import AccessTokenValidadtion api_router = APIRouter( - prefix="/pofile", + prefix="/profile", tags=["User accountModel"], ) @api_router.get("/{user_id}") -async def get_pofile(user_id: int, - connection: AsyncConnection = Depends(get_connection_dep), - Authorize: AuthJWT = Depends()): - - current_user = AccessTokenValidadtion(Authorize) - - authorize_user = await get_user_login(connection, current_user) - if authorize_user.id != user_id : - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, - detail="The provided data already exists in the database") - +async def get_profile( + user_id: int, + request: Request, + connection: AsyncConnection = Depends(get_connection_dep), + ): + # Извлекаем текущего пользователя из request.state + current_user = request.state.current_user user = await get_user_id(connection, user_id) @@ -53,21 +46,15 @@ async def get_pofile(user_id: int, @api_router.put("/{user_id}") -async def update_pofile( - user_id: int, - user_updata: UserUpdate, - connection: AsyncConnection = Depends(get_connection_dep), - Authorize: AuthJWT = Depends() -): +async def update_profile( + user_id: int, + request: Request, + user_updata: UserUpdate, + connection: AsyncConnection = Depends(get_connection_dep), - current_user = AccessTokenValidadtion(Authorize) + ): - - authorize_user = await get_user_login(connection, current_user) - if authorize_user.id != user_id : - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, - detail="The provided data already exists in the database") + current_user = request.state.current_user user = await get_user_id(connection, user_id) if user is None: diff --git a/api/api/services/access_token_validadtion.py b/api/api/services/access_token_validadtion.py index 8702e07..f47c761 100644 --- a/api/api/services/access_token_validadtion.py +++ b/api/api/services/access_token_validadtion.py @@ -1,15 +1,9 @@ from fastapi import HTTPException, status from fastapi_jwt_auth import AuthJWT +from fastapi.responses import JSONResponse def AccessTokenValidadtion(Authorize): - try: - Authorize.jwt_required() - current_user = Authorize.get_jwt_subject() - return current_user - except Exception: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="The access token is invalid or expired.", - headers={"WWW-Authenticate": "Bearer"}, - ) + Authorize.jwt_required() + current_user = Authorize.get_jwt_subject() + return current_user diff --git a/api/api/services/middleware.py b/api/api/services/middleware.py new file mode 100644 index 0000000..9a8ef28 --- /dev/null +++ b/api/api/services/middleware.py @@ -0,0 +1,75 @@ +from starlette.middleware.base import BaseHTTPMiddleware +from fastapi import ( + Request, + status, +) + +from fastapi.responses import JSONResponse +from api.config import get_settings + +import re +from re import escape + + +from fastapi_jwt_auth import AuthJWT + + + +class MiddlewareAccessTokenValidadtion(BaseHTTPMiddleware): + def __init__(self, app): + super().__init__(app) + + self.prefix = escape(get_settings().PATH_PREFIX) + self.excluded_routes = [ + re.compile(r'^' + re.escape(self.prefix) + r'/auth/refresh/?$'), + re.compile(r'^' + re.escape(self.prefix) + r'/auth/?$') + ] + + + + async def dispatch(self, + request: Request, + call_next): + + if request.method in ["GET", "POST", "PUT", "DELETE"]: + if any(pattern.match(request.url.path) for pattern in self.excluded_routes): + return await call_next(request) + else: + + auth_header = request.headers.get("Authorization") + if not auth_header: + return JSONResponse( + status_code=status.HTTP_401_UNAUTHORIZED, + content={"detail": "Missing authorization header."}, + headers={"WWW-Authenticate": "Bearer"} + ) + + token = auth_header.split(" ")[1] + print(token) + + Authorize = AuthJWT(request) + + try: + current_user = Authorize.get_jwt_subject() + + print(current_user) + + request.state.current_user = current_user + return await call_next(request) + + except Exception: + return JSONResponse( + status_code=status.HTTP_401_UNAUTHORIZED, + content={"detail": "The access token is invalid or expired."}, + headers={"WWW-Authenticate": "Bearer"} + ) + + + + # async with get_connection() as connection: + # authorize_user = await get_user_login(connection, current_user) + # print(authorize_user) + # if authorize_user is None : + # return JSONResponse( + # status_code=status.HTTP_404_NOT_FOUND , + # detail="User not found.")