maj
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
"""
|
||||
DragonBank - Couche Service (Utilisateur)
|
||||
=========================================
|
||||
Ce module implémente le pattern 'Service Layer' (Couche Métier).
|
||||
|
||||
Choix architecturaux :
|
||||
1. Séparation des responsabilités (Separation of Concerns) :
|
||||
Les routes Flask (Controllers) ne font que recevoir les requêtes HTTP.
|
||||
C'est cette classe Service qui concentre TOUTES les règles métier complexes
|
||||
(validation des mots de passe bcrypt, règles de profil, audits de sécurité).
|
||||
2. Gestion Transactionnelle (ACID) :
|
||||
Chaque méthode du service encapsule l'ouverture et la fermeture de la connexion DB.
|
||||
Si une exception survient pendant une mise à jour, le bloc `except` appelle un `conn.rollback()`
|
||||
pour garantir que la base de données ne reste jamais dans un état instable.
|
||||
3. Abstraction des Données (DAO) :
|
||||
Le service instancie la classe `UtilisateurDAO` pour lire et écrire les données.
|
||||
Il ignore totalement la complexité des requêtes SQL (Pattern Repository/DAO).
|
||||
"""
|
||||
|
||||
from database import creer_connexion, enregistrer_audit
|
||||
from auth import obtenir_ip_client, verifier_mot_de_passe, hacher_mot_de_passe
|
||||
from config import CHAMPS_PROFIL_MODIFIABLES
|
||||
from validators import valider_email, valider_mot_de_passe
|
||||
from models.utilisateur import UtilisateurDAO
|
||||
|
||||
class UtilisateurService:
|
||||
def __init__(self):
|
||||
self.utilisateur_dao = UtilisateurDAO()
|
||||
|
||||
def obtenir_profil(self, id_utilisateur_courant):
|
||||
conn = creer_connexion()
|
||||
try:
|
||||
utilisateur = self.utilisateur_dao.trouver_par_id(conn, id_utilisateur_courant)
|
||||
if not utilisateur:
|
||||
raise ValueError('Utilisateur introuvable')
|
||||
return utilisateur
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def modifier_profil(self, id_utilisateur_courant, donnees):
|
||||
donnees = donnees or {}
|
||||
champs = {
|
||||
k: v.strip()
|
||||
for k, v in donnees.items()
|
||||
if k in CHAMPS_PROFIL_MODIFIABLES and isinstance(v, str)
|
||||
}
|
||||
if not champs:
|
||||
raise ValueError('Aucun champ valide a mettre a jour')
|
||||
|
||||
conn = creer_connexion()
|
||||
try:
|
||||
mis_a_jour = self.utilisateur_dao.mettre_a_jour_profil(conn, id_utilisateur_courant, champs)
|
||||
enregistrer_audit(conn.cursor(), id_utilisateur_courant, 'UPDATE_PROFILE',
|
||||
{'champs': list(champs.keys())}, obtenir_ip_client())
|
||||
conn.commit()
|
||||
return mis_a_jour
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def changer_mot_de_passe(self, id_utilisateur_courant, donnees):
|
||||
donnees = donnees or {}
|
||||
ancien_mot_de_passe = donnees.get('current_password')
|
||||
nouveau_mot_de_passe = donnees.get('new_password')
|
||||
|
||||
if not ancien_mot_de_passe or not nouveau_mot_de_passe:
|
||||
raise ValueError('Le mot de passe actuel et le nouveau mot de passe sont requis')
|
||||
|
||||
valider_mot_de_passe(nouveau_mot_de_passe)
|
||||
|
||||
conn = creer_connexion()
|
||||
try:
|
||||
utilisateur = self.utilisateur_dao.trouver_par_id(conn, id_utilisateur_courant)
|
||||
if not utilisateur:
|
||||
raise ValueError('Utilisateur introuvable')
|
||||
|
||||
utilisateur_complet = self.utilisateur_dao.trouver_par_email(conn, utilisateur['email'])
|
||||
|
||||
if not verifier_mot_de_passe(ancien_mot_de_passe, utilisateur_complet['password_hash']):
|
||||
raise PermissionError('Le mot de passe actuel est incorrect')
|
||||
|
||||
nouveau_hash = hacher_mot_de_passe(nouveau_mot_de_passe)
|
||||
self.utilisateur_dao.mettre_a_jour_mot_de_passe(conn, id_utilisateur_courant, nouveau_hash)
|
||||
|
||||
enregistrer_audit(conn.cursor(), id_utilisateur_courant, 'UPDATE_PASSWORD', {}, obtenir_ip_client())
|
||||
conn.commit()
|
||||
return True
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def changer_email(self, id_utilisateur_courant, donnees):
|
||||
donnees = donnees or {}
|
||||
mot_de_passe = donnees.get('password')
|
||||
nouvel_email = donnees.get('new_email')
|
||||
|
||||
if not mot_de_passe or not nouvel_email:
|
||||
raise ValueError('Le mot de passe et le nouvel email sont requis')
|
||||
|
||||
email_valide = valider_email(nouvel_email)
|
||||
|
||||
conn = creer_connexion()
|
||||
try:
|
||||
utilisateur = self.utilisateur_dao.trouver_par_id(conn, id_utilisateur_courant)
|
||||
if not utilisateur:
|
||||
raise ValueError('Utilisateur introuvable')
|
||||
|
||||
if utilisateur['email'] == email_valide:
|
||||
raise ValueError("Le nouvel email est identique a l'actuel")
|
||||
|
||||
if self.utilisateur_dao.email_existe(conn, email_valide):
|
||||
raise ValueError('Cette adresse email est deja utilisee')
|
||||
|
||||
utilisateur_complet = self.utilisateur_dao.trouver_par_email(conn, utilisateur['email'])
|
||||
if not verifier_mot_de_passe(mot_de_passe, utilisateur_complet['password_hash']):
|
||||
raise PermissionError('Mot de passe incorrect')
|
||||
|
||||
self.utilisateur_dao.mettre_a_jour_email(conn, id_utilisateur_courant, email_valide)
|
||||
|
||||
enregistrer_audit(conn.cursor(), id_utilisateur_courant, 'UPDATE_EMAIL',
|
||||
{'ancien': utilisateur['email'], 'nouveau': email_valide},
|
||||
obtenir_ip_client())
|
||||
conn.commit()
|
||||
return True
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
finally:
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user