import streamlit as st from datetime import datetime, timedelta import pytz import secrets import bcrypt from supabase import create_client import os from dotenv import load_dotenv from utils.email_utils import send_email from urllib.parse import urljoin, urlparse from utils.logging_utils import log_to_file # remplace "import logging" car maintenat les logging sont définis et importés depuis le fichier dédié: logging_utils.py load_dotenv() supabase = create_client(os.getenv("SUPABASE_URL"), os.getenv("SUPABASE_KEY")) # Fonction pour générer un token de réinitialisation de mot de passe def generate_reset_token() -> str: """Génère un token sécurisé pour la réinitialisation de mot de passe.""" return secrets.token_urlsafe(32) # Fonction pour créer un token de réinitialisation de mot de passe pour un utilisateur spécifique def create_reset_token(user_id: int) -> tuple[bool, str]: """ Crée un token de réinitialisation de mot de passe. """ try: token = generate_reset_token() expires_at = datetime.now(pytz.UTC) + timedelta(hours=24) # Invalider les tokens précédents supabase.table("user_tokens").update({ "used": True }).eq("user_id", user_id).eq("type", "reset_password").execute() # Créer un nouveau token supabase.table("user_tokens").insert({ "user_id": user_id, "token": token, "type": "reset_password", "expires_at": expires_at.isoformat() }).execute() return True, token except Exception as e: log_to_file(f"Erreur lors de la création du token : {str(e)}") return False, "" # Fonction pour envoyer l'email de réinitialisation def send_reset_email(email: str, reset_url: str) -> bool: """ Envoie l'email de réinitialisation. """ subject = "Réinitialisation de votre mot de passe" html_content = f"""
Vous avez demandé la réinitialisation de votre mot de passe.
Cliquez sur le lien ci-dessous pour définir un nouveau mot de passe :
Réinitialiser mon mot de passe
Ce lien est valable pendant 24 heures.
Si vous n'avez pas demandé cette réinitialisation, ignorez cet email.
""" return send_email(email, subject, html_content) # Fonction pour initier le processus de réinitialisation du mot de passe def initiate_password_reset(email: str) -> tuple[bool, str]: """ Initie le processus de réinitialisation de mot de passe. """ try: # Vérifier si l'utilisateur existe user = supabase.table("users").select("id").eq("email", email).execute() if not user.data: return False, "Aucun compte associé à cet email." user_id = user.data[0]['id'] success, token = create_reset_token(user_id) if not success: return False, "Erreur lors de la création du token." # Construire l'URL de réinitialisation de manière sécurisée base_url = os.getenv('APP_URL', '').rstrip('/') # Nettoyer l'URL de base if base_url.startswith('http://'): base_url = base_url[7:] elif base_url.startswith('https://'): base_url = base_url[8:] base_url = base_url.strip('/') reset_url = f"http://{base_url}/reset_password?token={token}" # Envoyer l'email if send_reset_email(email, reset_url): return True, "Instructions envoyées par email." return False, "Erreur lors de l'envoi de l'email." except Exception as e: log_to_file(f"Erreur lors de l'initiation de la réinitialisation : {str(e)}") return False, "Une erreur est survenue." # Fonction pour vérifier la validité d'un token de réinitialisation def verify_reset_token(token: str) -> tuple[bool, int]: """ Vérifie la validité d'un token de réinitialisation. """ try: response = supabase.table("user_tokens").select("*").eq( "token", token ).eq("type", "reset_password").eq("used", False).execute() if not response.data: return False, 0 token_data = response.data[0] expires_at = datetime.fromisoformat(token_data['expires_at'].replace('Z', '+00:00')) if expires_at < datetime.now(pytz.UTC): return False, 0 return True, token_data['user_id'] except Exception as e: log_to_file(f"Erreur lors de la vérification du token : {str(e)}") return False, 0 # Fonction pour réinitialiser le mot de passe avec le token de réinitialisation def reset_password(token: str, new_password: str) -> tuple[bool, str]: """ Réinitialise le mot de passe avec le token. """ try: valid, user_id = verify_reset_token(token) if not valid: return False, "Token invalide ou expiré." # Hasher le nouveau mot de passe hashed_password = bcrypt.hashpw(new_password.encode(), bcrypt.gensalt()).decode() # Mettre à jour le mot de passe supabase.table("users").update({ "password": hashed_password }).eq("id", user_id).execute() # Marquer le token comme utilisé supabase.table("user_tokens").update({ "used": True }).eq("token", token).execute() return True, "Mot de passe mis à jour avec succès." except Exception as e: log_to_file(f"Erreur lors de la réinitialisation du mot de passe : {str(e)}") return False, "Une erreur est survenue."