Spaces:
Sleeping
Sleeping
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""" | |
<html> | |
<body> | |
<h2>Réinitialisation de votre mot de passe</h2> | |
<p>Vous avez demandé la réinitialisation de votre mot de passe.</p> | |
<p>Cliquez sur le lien ci-dessous pour définir un nouveau mot de passe :</p> | |
<p><a href="{reset_url}">Réinitialiser mon mot de passe</a></p> | |
<p>Ce lien est valable pendant 24 heures.</p> | |
<p>Si vous n'avez pas demandé cette réinitialisation, ignorez cet email.</p> | |
</body> | |
</html> | |
""" | |
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." |