From 2418f613b5fac1e66e2c82ae41b4106afc58c0f2 Mon Sep 17 00:00:00 2001 From: Alix JEUDI--LEMOINE Date: Tue, 17 Dec 2024 11:07:26 +0100 Subject: [PATCH] :boom: Removed default timedelta + fixed user_id in register + comment out the logout route (maybe unuseful) + added ObjectID format try except to every route that needs an id --- app/main.py | 89 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/app/main.py b/app/main.py index 7ce1364..bd0da6c 100644 --- a/app/main.py +++ b/app/main.py @@ -1,3 +1,4 @@ +import bson from fastapi import FastAPI, Depends, HTTPException, status from fastapi.responses import JSONResponse from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm @@ -15,7 +16,7 @@ import app.serializers as serializers # Import all serializers (detailed in __in from app.models import User, Pin, Friend, Token, TokenData, HTTPError # Import all DTOs (detailed in __init__.py) -from app.dto import FriendAddDTO, FriendListDTO, UserDTO, UserRegisterDTO +from app.dto import FriendAddDTO, FriendListDTO, UserDTO, UserRegisterDTO, PinDTO # Contains all constants import app.config as config @@ -45,13 +46,10 @@ pins_collection = db["pins"] friends_collection = db["friends"] # Token management -def create_access_token(data: dict, expires_delta: Optional[timedelta] = None): +def create_access_token(data: dict, expires_delta: timedelta): to_encode = data.copy() - if expires_delta: - expire = datetime.now() + expires_delta - else: - expire = datetime.now() + timedelta(minutes=15) + expire = datetime.now() + expires_delta to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, config.SECRET_KEY, algorithm=config.ALGORITHM) @@ -101,7 +99,7 @@ async def register(user: UserRegisterDTO): access_token_expires = timedelta(minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token(data={"sub": user.username}, expires_delta=access_token_expires) - return {"access_token": access_token, "token_type": "bearer", "user_id": str(user_id)} + return {"access_token": access_token, "token_type": "bearer", "user_id": str(user_id.inserted_id)} @app.post( path="/login", @@ -122,20 +120,28 @@ async def login(form_data: OAuth2PasswordRequestForm = Depends()): return {"access_token": access_token, "token_type": "bearer", "user_id": str(user["_id"])} +""" Is it really usefull ? idk. @app.get( path="/logout", responses={401: {"model": HTTPError}} ) async def logout(current_user: User = Depends(get_current_user)): - # TODO: find usecase / what to do ?? return {"message": "Logged out"} +""" @app.get( path="/pin/{id}", - responses={401: {"model": HTTPError}, 404: {"model": HTTPError}} + responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}} ) async def get_pin(id: str, current_user: User = Depends(get_current_user)): - pin = pins_collection.find_one({"_id": ObjectId(id)}) + try: + pin = pins_collection.find_one({"_id": ObjectId(id)}) + except bson.errors.InvalidId: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="The ObjectID is misformatted" + ) + if pin is None: raise HTTPException(status_code=404, detail="Pin not found") @@ -143,10 +149,17 @@ async def get_pin(id: str, current_user: User = Depends(get_current_user)): @app.patch( path="/pin/{id}", - responses={401: {"model": HTTPError}, 404: {"model": HTTPError}} + responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}} ) -async def update_pin(id: str, pin: Pin, current_user: User = Depends(get_current_user)): - result = pins_collection.update_one({"_id": ObjectId(id)}, {"$set": pin.model_dump()}) +async def update_pin(id: str, pin: PinDTO, current_user: User = Depends(get_current_user)): + try: + result = pins_collection.update_one({"_id": ObjectId(id)}, {"$set": pin.model_dump()}) + except bson.errors.InvalidId: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="The ObjectID is misformatted" + ) + if result.matched_count == 0: raise HTTPException(status_code=404, detail="Pin not found") @@ -156,7 +169,7 @@ async def update_pin(id: str, pin: Pin, current_user: User = Depends(get_current path="/pin/add", responses={401: {"model": HTTPError}} ) -async def add_pin(pin: Pin, current_user: User = Depends(get_current_user)): +async def add_pin(pin: PinDTO, current_user: User = Depends(get_current_user)): pin_id = pins_collection.insert_one(pin.model_dump()).inserted_id return {"id": str(pin_id)} @@ -175,13 +188,20 @@ def friend_not_found(): @app.get( path="/friend/{id}", - responses={401: {"model": HTTPError}, 404: {"model": HTTPError}} + responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}} ) async def get_friend(id: str, current_user: User = Depends(get_current_user)): - friend = friends_collection.find_one({"_id": ObjectId(id)}) + try: + friend = friends_collection.find_one({"_id": ObjectId(id)}) + except bson.errors.InvalidId: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="The ObjectID is misformatted" + ) + if friend is None: friend_not_found() - return friend + return serializers.friend_serialize(friend) @app.post( path="/friend/add", @@ -191,8 +211,6 @@ async def add_friend(friend_to_add: FriendAddDTO, current_user: User = Depends(g # TODO: test if exists friend: Friend = friend_to_add.model_dump() - print(current_user) - if(current_user.uid == friend["friend_user_id"]): raise HTTPException( status_code=status.HTTP_409_CONFLICT, @@ -205,30 +223,51 @@ async def add_friend(friend_to_add: FriendAddDTO, current_user: User = Depends(g @app.delete( path="/friend/{id}/delete", - responses={401: {"model": HTTPError}, 404: {"model": HTTPError}} + responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}} ) async def delete_friend(id: str, current_user: User = Depends(get_current_user)): - result = friends_collection.delete_one({"_id": ObjectId(id)}) + try: + result = friends_collection.delete_one({"_id": ObjectId(id)}) + except bson.errors.InvalidId: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="The ObjectID is misformatted" + ) + if result.deleted_count == 0: friend_not_found() return {"message": "Friend deleted"} @app.patch( path="/friend/{id}/accept", - responses={401: {"model": HTTPError}, 404: {"model": HTTPError}} + responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}} ) async def accept_friend(id: str, current_user: User = Depends(get_current_user)): - result = friends_collection.update_one({"_id": ObjectId(id)}, {"$set": {"status": "accepted"}}) + try: + result = friends_collection.update_one({"_id": ObjectId(id)}, {"$set": {"status": "accepted"}}) + except bson.errors.InvalidId: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="The ObjectID is misformatted" + ) + if result.matched_count == 0: friend_not_found() return {"message": "Friend request accepted"} @app.post( path="/friend/{id}/deny", - responses={401: {"model": HTTPError}, 404: {"model": HTTPError}} + responses={401: {"model": HTTPError}, 404: {"model": HTTPError}, 422: {"model": HTTPError}} ) async def deny_friend(id: str, current_user: User = Depends(get_current_user)): - result = friends_collection.update_one({"_id": ObjectId(id)}, {"$set": {"status": "denied"}}) + try: + result = friends_collection.update_one({"_id": ObjectId(id)}, {"$set": {"status": "denied"}}) + except bson.errors.InvalidId: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="The ObjectID is misformatted" + ) + if result.matched_count == 0: friend_not_found() return {"message": "Friend request denied"}