From a120512baf2b7581111230443d6511010190ab7f Mon Sep 17 00:00:00 2001 From: Zvonimir Rudinski Date: Sun, 16 Nov 2025 16:42:20 +0100 Subject: [PATCH] extract password hashing into separate module --- database.py | 66 ++++++++++++++++++++++++++++++----------------------- security.py | 13 +++++++++++ 2 files changed, 51 insertions(+), 28 deletions(-) create mode 100644 security.py diff --git a/database.py b/database.py index e4906fc..1500269 100644 --- a/database.py +++ b/database.py @@ -1,62 +1,76 @@ from models import User -from passlib.context import CryptContext from settings import settings from fastapi import HTTPException, status, Request import sqlite3 import jwt import datetime +import security connection = sqlite3.connect('database.db') connection.row_factory = sqlite3.Row cursor = connection.cursor() -password_context = CryptContext(schemes=["sha256_crypt"], deprecated="auto") - def init() -> None: - # create users table + """Initializes the database.""" + + # Create users table cursor.execute(''' - CREATE TABLE IF NOT EXISTS users ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL UNIQUE, - password TEXT NOT NULL - ) - ''') + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL UNIQUE, + password TEXT NOT NULL + ) + ''') + + # Create logs table + cursor.execute(''' + CREATE TABLE IF NOT EXISTS logs ( + id INTEGER PRIMARY KEY, + user_id INTEGER NOT NULL, + calories DOUBLE NOT NULL, + description TEXT, + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users (id) + ) + ''') def close() -> None: + """Closes the database connection.""" connection.close() def register(user: User) -> None: - password = password_context.hash(user.password) + """Registers a new user in the database.""" cursor.execute( "INSERT INTO users (name, password) VALUES (?, ?)", (user.name, - password)) + security.hash_password(user.password)) connection.commit() def get_user_by_token(request: Request) -> User: - token = request.headers.get("Authorization") + """Retrieves a user from the database using a JWT token.""" + token=request.headers.get("Authorization") if not token or not token.startswith("Bearer "): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated" ) - token = token.split(" ")[1] - payload = jwt.decode( + token=token.split(" ")[1] + payload=jwt.decode( token, key=settings.jwt_secret, algorithms=[ settings.jwt_algorithm]) - connection = sqlite3.connect('database.db') - connection.row_factory = sqlite3.Row - cursor = connection.cursor() + connection=sqlite3.connect('database.db') + connection.row_factory=sqlite3.Row + cursor=connection.cursor() cursor.execute( "SELECT id, name, password FROM users WHERE id = ?", (payload["id"],)) - row = cursor.fetchone() + row=cursor.fetchone() connection.close() if not row: @@ -69,24 +83,20 @@ def get_user_by_token(request: Request) -> User: def login(user: User) -> str: + """Logs in a user and returns a JWT token.""" cursor.execute( "SELECT id, name, password FROM users WHERE name = ?", (user.name,)) - row = cursor.fetchone() + row=cursor.fetchone() - if not row: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Invalid credentials" - ) - if not password_context.verify(user.password, row["password"]): + if not row or not security.verify_password(user.password, row["password"]): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials" ) - exp = datetime.datetime.now( + exp=datetime.datetime.now( datetime.timezone.utc) + datetime.timedelta(hours=1) - payload = { + payload={ "id": row["id"], "exp": exp } diff --git a/security.py b/security.py new file mode 100644 index 0000000..159d748 --- /dev/null +++ b/security.py @@ -0,0 +1,13 @@ +from passlib.context import CryptContext + +password_context = CryptContext(schemes=["sha256_crypt"], deprecated="auto") + + +def hash_password(password: str) -> str: + """Hashes a plain text password.""" + return password_context.hash(password) + + +def verify_password(plain_password: str, hashed_password: str) -> bool: + """Verifies a plain text password against a hashed password.""" + return password_context.verify(plain_password, hashed_password)