colibri.assistant.ai / utils /password_reset.py
Gouzi Mohaled
Ajoute des fichiers et sous-dossiers supplémentaires
fe4792e
raw
history blame
5.93 kB
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."