diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 200a4e7..516f8b4 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,7 @@ // README at: https://github.com/devcontainers/templates/tree/main/src/postgres { "name": "Python 3 & PostgreSQL", - "dockerComposeFile": "docker-compose.yml", + "dockerComposeFile": "./docker-compose.yml", "service": "app", "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", @@ -14,11 +14,14 @@ // "forwardPorts": [5000, 5432], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "pip install --user -r ./MediaManager/src/requirements.txt", + "postCreateCommand": "pip install --user -r ./MediaManager/src/requirements.txt", // Configure tool-specific properties. "customizations" : { "jetbrains" : { + "settings": { + "com.intellij:app:HttpConfigurable.use_proxy_pac": true + }, "backend" : "PyCharm" } }, diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index bf1e2ae..028007b 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.8' - services: app: build: diff --git a/MediaManager/res/.env b/MediaManager/res/.env index e69de29..73a7b6e 100644 --- a/MediaManager/res/.env +++ b/MediaManager/res/.env @@ -0,0 +1,3 @@ +DB_USERNAME=MediaManager +DB_PASSWORD=MediaManager +DB_NAME=MediaManager \ No newline at end of file diff --git a/MediaManager/src/auth/password.py b/MediaManager/src/auth/password.py index 091b6b2..2a02508 100644 --- a/MediaManager/src/auth/password.py +++ b/MediaManager/src/auth/password.py @@ -1,6 +1,7 @@ from datetime import datetime, timedelta, timezone from typing import Annotated +import bcrypt import jwt from fastapi import Depends, FastAPI, HTTPException, status, APIRouter from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm @@ -37,7 +38,6 @@ class Token(BaseModel): class TokenData(BaseModel): username: str | None = None -pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") @@ -45,11 +45,17 @@ app = APIRouter() def verify_password(plain_password, hashed_password): - return pwd_context.verify(plain_password, hashed_password) + return bcrypt.checkpw( + bytes(plain_password, encoding="utf-8"), + bytes(hashed_password, encoding="utf-8"), + ) def get_password_hash(password): - return pwd_context.hash(password) + return bcrypt.hashpw( + bytes(password, encoding="utf-8"), + bcrypt.gensalt(), + ) def authenticate_user(email: str, password: str) -> UserInternal: @@ -105,6 +111,6 @@ async def login_for_access_token( ) access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( - data={"sub": user.username}, expires_delta=access_token_expires + data={"sub": user.email}, expires_delta=access_token_expires ) return Token(access_token=access_token, token_type="bearer") \ No newline at end of file diff --git a/MediaManager/src/database.py b/MediaManager/src/database.py index a2d5639..395414b 100644 --- a/MediaManager/src/database.py +++ b/MediaManager/src/database.py @@ -9,7 +9,6 @@ import psycopg from pydantic import BaseModel log = getLogger(__name__) -log.addHandler(logging.StreamHandler(sys.stdout)) log.level = logging.DEBUG @@ -53,6 +52,7 @@ class PgDatabase(Database): def connect_to_database(self): return self.driver.connect( + autocommit=True, host=os.getenv("DB_HOST"), port=os.getenv("DB_PORT"), user=os.getenv("DB_USERNAME"), @@ -60,7 +60,6 @@ class PgDatabase(Database): dbname=os.getenv("DB_NAME") ) - def init_db(): with PgDatabase() as db: db.connection.execute(""" @@ -83,23 +82,24 @@ def drop_tables() -> None: def create_user(user: UserInternal) -> bool: with PgDatabase() as db: - try: - db.connection.execute( - """ - INSERT INTO users (id, name, lastname, email, hashed_password) - VALUES (%s, %s, %s, %s, %s) - """, - (user.id, user.name, user.lastname, user.email, user.hashed_password) - ) - except psycopg.errors.UniqueViolation: - return False + try: + db.connection.execute( + """ + INSERT INTO users (id, name, lastname, email, hashed_password) + VALUES (%s, %s, %s, %s, %s) + """, + (user.id, user.name, user.lastname, user.email, user.hashed_password) + ) + except psycopg.errors.UniqueViolation as e: + log.error(e) + return False log.info("User inserted successfully") - log.debug(f"User {user.model_dump()} created successfully") + log.debug(f"Inserted following User:", user.model_dump()) return True -def get_user(email: str) -> UserInternal: +def get_user(email: str) -> UserInternal | None: with PgDatabase() as db: result = db.connection.execute( "SELECT id, name, lastname, email, hashed_password FROM users WHERE email=%s", @@ -110,5 +110,5 @@ def get_user(email: str) -> UserInternal: return None user = UserInternal.model_construct(**dict(zip(["id", "name", "lastname", "email", "hashed_password"], result))) - log.debug(f"User {user.model_dump()} retrieved successfully") + log.debug(f"Retrieved User succesfully: {user.model_dump()} ") return user diff --git a/MediaManager/src/main.py b/MediaManager/src/main.py index 98cb2b1..856e816 100644 --- a/MediaManager/src/main.py +++ b/MediaManager/src/main.py @@ -1,10 +1,22 @@ -from fastapi import FastAPI +import logging + +import uvicorn +from fastapi import FastAPI, Depends +from pydantic import BaseModel import database +from fastapi.testclient import TestClient from routers import users from auth import password +from routers.users import CreateUser app = FastAPI() + +logging.info("OIDA") app.include_router(users.router, tags=["users"]) app.include_router(password.app, tags=["authentication"]) -database.__init__() \ No newline at end of file + + + +if __name__ == "__main__": + uvicorn.run(app, host="127.0.0.1", port=5049) \ No newline at end of file diff --git a/MediaManager/src/requirements.txt b/MediaManager/src/requirements.txt index e77be7d..87c8a06 100644 Binary files a/MediaManager/src/requirements.txt and b/MediaManager/src/requirements.txt differ diff --git a/MediaManager/src/routers/users.py b/MediaManager/src/routers/users.py index 0999e54..b6df4f0 100644 --- a/MediaManager/src/routers/users.py +++ b/MediaManager/src/routers/users.py @@ -2,6 +2,7 @@ import logging from fastapi import APIRouter from fastapi import Depends +from pydantic import BaseModel import database from auth.password import authenticate_user, get_password_hash @@ -15,26 +16,32 @@ router = APIRouter( class CreateUser(User): password: str +log = logging.getLogger(__name__) +log.level = logging.DEBUG -log = logging.getLogger(__file__) - - -@router.post("/", response_model=User) -async def create_user(user: CreateUser): +@router.post("/") +async def create_user( + user: CreateUser = Depends(CreateUser), +): internal_user = UserInternal(name=user.name, lastname=user.lastname, email=user.email, hashed_password=get_password_hash(user.password)) - database.create_user(internal_user) - return user + if database.create_user(internal_user): + log.info("Created new user",internal_user.model_dump()) + return user + else: + log.warning("Failed to create new user", internal_user.model_dump()) + return {"error": "Failed to create new user"} -@router.get("/me/", response_model=User) + +@router.get("/me", response_model=User) async def read_users_me( current_user: User = Depends(authenticate_user), ): return current_user -@router.get("/me/items/") +@router.get("/me/items") async def read_own_items( current_user: User = Depends(authenticate_user), ):