From 6e3c24f265d551782e6aee955757bb6716ce7b69 Mon Sep 17 00:00:00 2001 From: Benjamin Lin Date: Tue, 29 Oct 2024 16:34:57 -0400 Subject: [PATCH] Bearer token creation --- backend/api/decoder.py | 45 ++++++++++++++++++++++++++++++---- backend/api/resource.py | 26 +++++++++++--------- backend/api/service.py | 29 +++++++++++++--------- backend/api/tag.py | 24 +++++++++--------- backend/services/exceptions.py | 4 +++ 5 files changed, 88 insertions(+), 40 deletions(-) diff --git a/backend/api/decoder.py b/backend/api/decoder.py index 1b55664..d5c8f68 100644 --- a/backend/api/decoder.py +++ b/backend/api/decoder.py @@ -1,13 +1,48 @@ # token_utils.py import jwt from jwt import PyJWTError -from fastapi import HTTPException, status -from workspace.backend.models.user_model import User -from ..services import UserService +from datetime import datetime, timedelta +from fastapi import HTTPException, status, Depends +from backend.models.user_model import User +from ..services import UserService +from passlib.context import CryptContext + +from supabase import create_client, Client + +# Supabase configuration +SUPABASE_URL = "placeholder" +SUPABASE_KEY = "sample key" +supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) SECRET = "SECRET_KEY" ALGORITHM = "HS256" +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + + +def create_bearer_token(user_uuid: str, expires_delta: timedelta = None) -> str: + if expires_delta: + expire = datetime.now(datetime.UTC) + expires_delta + else: + expire = datetime.now(datetime.UTC) + timedelta(minutes=180) + + to_encode = { + "sub": user_uuid, + "exp": expire, + } + + token = jwt.encode(to_encode, SECRET, algorithm=ALGORITHM) + + expires_at = expire.isoformat() + token_data = { + "user_uuid": user_uuid, + "token": token, + "expires_at": expires_at, + } + + response = supabase.table("user_tokens").insert(token_data).execute() + + def decode_token(token: str) -> User: try: payload = jwt.decode(token, SECRET, algorithms=[ALGORITHM]) @@ -19,7 +54,7 @@ def decode_token(token: str) -> User: headers={"WWW-Authenticate": "Bearer"}, ) - user_data = UserService.get_user_by_uuid(user_uuid) + user_data = UserService.get_user_by_uuid(user_uuid) if user_data is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, @@ -45,4 +80,4 @@ def decode_token(token: str) -> User: status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired token", headers={"WWW-Authenticate": "Bearer"}, - ) \ No newline at end of file + ) diff --git a/backend/api/resource.py b/backend/api/resource.py index 04e6bc1..c04d4ea 100644 --- a/backend/api/resource.py +++ b/backend/api/resource.py @@ -1,8 +1,8 @@ from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer -from decoder import decode_token +from backend.api.decoder import decode_token -from workspace.backend.models.user_model import User +from backend.models.user_model import User from ..services import ResourceService, UserService from ..models.resource_model import Resource @@ -17,6 +17,7 @@ openapi_tags = { oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") + def get_current_user(token: str = Depends(oauth2_scheme)) -> User: user = decode_token(token) if not user: @@ -27,38 +28,39 @@ def get_current_user(token: str = Depends(oauth2_scheme)) -> User: ) return user + # TODO: Add security using HTTP Bearer Tokens # TODO: Enable authorization by passing user uuid to API # TODO: Create custom exceptions @api.post("", response_model=Resource, tags=["Resource"]) def create( subject: User = Depends(get_current_user), - resource: Resource, - resource_svc: ResourceService = Depends() + resource: Resource = Depends(), + resource_svc: ResourceService = Depends(), ): return resource_svc.create(subject, resource) @api.get("", response_model=List[Resource], tags=["Resource"]) def get_all( - subject: User = Depends(get_current_user), - resource_svc: ResourceService = Depends()): + subject: User = Depends(get_current_user), resource_svc: ResourceService = Depends() +): return resource_svc.get_resource_by_user(subject) @api.put("", response_model=Resource, tags=["Resource"]) def update( - subject: User = Depends(get_current_user), - resource: Resource, - resource_svc: ResourceService = Depends() + subject: User = Depends(get_current_user), + resource: Resource = Depends(), + resource_svc: ResourceService = Depends(), ): return resource_svc.update(subject, resource) @api.delete("", response_model=None, tags=["Resource"]) def delete( - subject: User = Depends(get_current_user), - resource: Resource, - resource_svc: ResourceService = Depends() + subject: User = Depends(get_current_user), + resource: Resource = Depends(), + resource_svc: ResourceService = Depends(), ): resource_svc.delete(subject, resource) diff --git a/backend/api/service.py b/backend/api/service.py index ebb3442..57f3240 100644 --- a/backend/api/service.py +++ b/backend/api/service.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer -from decoder import decode_token +from backend.api.decoder import decode_token from backend.models.user_model import User from ..services import ServiceService, UserService @@ -18,6 +18,7 @@ openapi_tags = { # Creates an OAuth instance oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") + def get_current_user(token: str = Depends(oauth2_scheme)) -> User: user = decode_token(token) if not user: @@ -28,37 +29,41 @@ def get_current_user(token: str = Depends(oauth2_scheme)) -> User: ) return user + # TODO: Add security using HTTP Bearer Tokens # TODO: Enable authorization by passing user uuid to API # TODO: Create custom exceptions + + @api.post("", response_model=Service, tags=["Service"]) def create( - subject: User = Depends(getUser), - service: Service, - service_svc: ServiceService = Depends() + subject: User = Depends(get_current_user), + service: Service = Depends(), + service_svc: ServiceService = Depends(), ): return service_svc.create(subject, service) @api.get("", response_model=List[Service], tags=["Service"]) def get_all( - subject: User = Depends(getUser), - service_svc: ServiceService = Depends() + subject: User = Depends(get_current_user), service_svc: ServiceService = Depends() ): return service_svc.get_service_by_user(subject) + @api.put("", response_model=Service, tags=["Service"]) def update( - subject: User = Depends(getUser), - service: Service, - service_svc: ServiceService = Depends() + subject: User = Depends(get_current_user), + service: Service = Depends(), + service_svc: ServiceService = Depends(), ): return service_svc.update(subject, service) + @api.delete("", response_model=None, tags=["Service"]) def delete( - subject: User = Depends(getUser), - service: Service, - service_svc: ServiceService = Depends() + subject: User = Depends(get_current_user), + service: Service = Depends(), + service_svc: ServiceService = Depends(), ): service_svc.delete(subject, service) diff --git a/backend/api/tag.py b/backend/api/tag.py index 1da302f..0679c64 100644 --- a/backend/api/tag.py +++ b/backend/api/tag.py @@ -1,6 +1,6 @@ from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer -from decoder import decode_token +from backend.api.decoder import decode_token from backend.models.tag_model import Tag from backend.models.user_model import User @@ -19,6 +19,7 @@ openapi_tags = { oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") + def get_current_user(token: str = Depends(oauth2_scheme)) -> User: user = decode_token(token) if not user: @@ -29,36 +30,37 @@ def get_current_user(token: str = Depends(oauth2_scheme)) -> User: ) return user + # TODO: Add security using HTTP Bearer Tokens # TODO: Enable authorization by passing user uuid to API # TODO: Create custom exceptions @api.post("", response_model=Tag, tags=["Tag"]) def create( subject: User = Depends(get_current_user), - tag: Tag, - tag_service: TagService=Depends() + tag: Tag = Depends(), + tag_service: TagService = Depends(), ): return tag_service.create(subject, tag) + @api.get("", response_model=List[Tag], tags=["Tag"]) -def get_all( - subject: User = Depends(get_current_user), - tag_svc: TagService=Depends() -): +def get_all(subject: User = Depends(get_current_user), tag_svc: TagService = Depends()): return tag_svc.get_all() + @api.put("", response_model=Tag, tags=["Tag"]) def update( subject: User = Depends(get_current_user), - tag: Tag, - tag_svc: TagService=Depends() + tag: Tag = Depends(), + tag_svc: TagService = Depends(), ): return tag_svc.delete(subject, tag) + @api.delete("", response_model=None, tags=["Tag"]) def delete( subject: User = Depends(get_current_user), - tag: Tag, - tag_svc: TagService=Depends() + tag: Tag = Depends(), + tag_svc: TagService = Depends(), ): tag_svc.delete(subject, tag) diff --git a/backend/services/exceptions.py b/backend/services/exceptions.py index 069bff1..5fb87ed 100644 --- a/backend/services/exceptions.py +++ b/backend/services/exceptions.py @@ -23,3 +23,7 @@ class ServiceNotFoundException(Exception): class ProgramNotAssignedException(Exception): """Exception for when the user does not have correct access for requested services.""" + + +class TagNotFoundException(Exception): + """Exception for when the user does not have correct access for requested services."""