File size: 5,932 Bytes
fe4792e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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."