From 5b5f11805779b59d82d4f7f32c6ba888159664d0 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 18 Aug 2026 16:46:42 +0200 Subject: [PATCH] update main.py kaasislekker --- backend/auth.py | 37 +++++++++++++++- backend/email_service.py | 94 +++++++++++++++++++++++++++++++++++++++- database/db_init.py | 26 +++++++++++ 3 files changed, 155 insertions(+), 2 deletions(-) diff --git a/backend/auth.py b/backend/auth.py index 5e73183..b7f341c 100644 --- a/backend/auth.py +++ b/backend/auth.py @@ -6,7 +6,14 @@ from dotenv import load_dotenv import os from fastapi import Depends from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials -from email_service import create_verification, send_verification_email +from email_service import ( + create_verification, + send_verification_email, + send_password_reset_email, + create_password_reset, +) + +import secrets security = HTTPBearer() load_dotenv() @@ -235,3 +242,31 @@ def delete_me(user_id, password): return { "status": "deleted" } + +def reset_password(email: str): + email = email.strip().lower() + + user = fetch_one( + """ + SELECT id + FROM users + WHERE email = ? + """, + (email,), + ) + + if user is None: + return { + "status": "sent", + } + + token = create_password_reset(user["id"]) + + send_password_reset_email( + email, + token, + ) + + return { + "status": "sent", + } \ No newline at end of file diff --git a/backend/email_service.py b/backend/email_service.py index fa905bb..34aa750 100644 --- a/backend/email_service.py +++ b/backend/email_service.py @@ -268,4 +268,96 @@ If you did not request this, you can ignore this email. smtp_username, smtp_password, ) - smtp.send_message(message) \ No newline at end of file + smtp.send_message(message) + +def send_password_reset_email(email: str, token: str): + smtp_host = os.getenv("SMTP_HOST") + smtp_port = int(os.getenv("SMTP_PORT", "587")) + smtp_username = os.getenv("SMTP_USERNAME") + smtp_password = os.getenv("SMTP_PASSWORD") + smtp_from = os.getenv("SMTP_FROM") + + if not all([ + smtp_host, + smtp_username, + smtp_password, + smtp_from, + ]): + raise RuntimeError("SMTP configuration is incomplete") + + password_reset_url = ( + f"https://ben.de-roo.org/api/update-password?token={token}" + ) + + message = EmailMessage() + + message["From"] = smtp_from + message["To"] = email + message["Subject"] = "Reset your password" + + message.set_content( + f"""Hello, + +You requested to use this email address for reseting your Calendar password. + +Reset your password using this link: + +{password_reset_url} + +This link expires after 24 hours. + +If you did not request this, you can ignore this email. +""" + ) + + with smtplib.SMTP(smtp_host, smtp_port) as smtp: + smtp.starttls() + smtp.login( + smtp_username, + smtp_password, + ) + smtp.send_message(message) + +def create_password_reset(user_id: int): + token = secrets.token_urlsafe(32) + token_hash = hash_token(token) + + expires_at = ( + datetime.now(timezone.utc) + + timedelta(hours=1) + ).isoformat() + + conn = get_connection() + cur = conn.cursor() + + try: + cur.execute( + """ + DELETE FROM password_reset_tokens + WHERE user_id = ? + """, + (user_id,), + ) + + cur.execute( + """ + INSERT INTO password_reset_tokens ( + user_id, + token_hash, + expires_at + ) + VALUES (?, ?, ?) + """, + ( + user_id, + token_hash, + expires_at, + ), + ) + + conn.commit() + + return token + + finally: + conn.close() \ No newline at end of file diff --git a/database/db_init.py b/database/db_init.py index 6b7c921..3cd95e0 100644 --- a/database/db_init.py +++ b/database/db_init.py @@ -37,6 +37,21 @@ CREATE TABLE IF NOT EXISTS email_verifications ( """) +cur.execute(""" +CREATE TABLE IF NOT EXISTS password_reset_tokens ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + token_hash TEXT NOT NULL UNIQUE, + expires_at TEXT NOT NULL, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + + FOREIGN KEY (user_id) + REFERENCES users(id) + ON DELETE CASCADE +); +""") + + cur.execute(""" CREATE TABLE IF NOT EXISTS rooms ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -155,32 +170,43 @@ CREATE INDEX IF NOT EXISTS idx_room_members_user ON room_members(user_id); """) + cur.execute(""" CREATE INDEX IF NOT EXISTS idx_events_room ON events(room_id); """) + cur.execute(""" CREATE INDEX IF NOT EXISTS idx_event_invitations_user ON event_invitations(user_id); """) + cur.execute(""" CREATE INDEX IF NOT EXISTS idx_event_invitations_event ON event_invitations(event_id); """) + cur.execute(""" CREATE INDEX IF NOT EXISTS idx_event_signups_user ON event_signups(user_id); """) + cur.execute(""" CREATE INDEX IF NOT EXISTS idx_email_verifications_user ON email_verifications(user_id); """) +cur.execute(""" +CREATE INDEX IF NOT EXISTS idx_password_reset_tokens_user +ON password_reset_tokens(user_id); +""") + + conn.commit() print("Database initialized.")