From b638d333b55ada5e2abb262e0e7985517bc4395c Mon Sep 17 00:00:00 2001 From: Michel Date: Sun, 9 Nov 2025 00:48:01 +0100 Subject: [PATCH] =?UTF-8?q?Ajout=20notification=20par=20mail=20=C3=A0=20la?= =?UTF-8?q?=20cr=C3=A9ation=20d'user?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/creer_user.py | 86 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 12 deletions(-) diff --git a/app/creer_user.py b/app/creer_user.py index cd27646..9220b1b 100644 --- a/app/creer_user.py +++ b/app/creer_user.py @@ -130,14 +130,17 @@ def hash_password(plain: str, rounds: int = 12) -> str: salt = bcrypt.gensalt(rounds=rounds) return bcrypt.hashpw(plain.encode("utf-8"), salt).decode("utf-8") -def user_exists(cur, username: str, email: str) -> bool: - cur.execute( - "SELECT COUNT(*) FROM Utilisateurs WHERE NomUtilisateur=%s OR email=%s", - (username, email), - ) +def user_exists(cur, username: str) -> bool: + cur.execute("SELECT COUNT(*) FROM Utilisateurs WHERE NomUtilisateur=%s", (username,)) (count,) = cur.fetchone() return count > 0 - +def find_users_by_email(cnx, email: str): + with cnx.cursor(dictionary=True) as cur: + cur.execute( + "SELECT NomUtilisateur, Site FROM Utilisateurs WHERE email=%s ORDER BY NomUtilisateur", + (email,), + ) + return cur.fetchall() def list_users(cnx, limit: int = 500, include_password=False): fields = [ "NomUtilisateur", "Nom_complet", "Site", "DateExpiration", @@ -159,8 +162,10 @@ def insert_user(cnx, username, full_name, site, password, expires, phone, email, pwd_hash = hash_password(password) with cnx.cursor() as cur: - if user_exists(cur, username, email): - raise RuntimeError("Nom d'utilisateur ou email déjà existant.") + # ✅ vérif uniquement sur NomUtilisateur + if user_exists(cur, username): + raise RuntimeError("Nom d'utilisateur déjà existant.") + if store_plain: cur.execute( """ @@ -181,6 +186,7 @@ def insert_user(cnx, username, full_name, site, password, expires, phone, email, ) return pwd_hash + def get_user_details(cnx, username: str): with cnx.cursor(dictionary=True) as cur: cur.execute( @@ -362,8 +368,10 @@ with tab_list: except Exception as e: st.warning(f"Impossible de lister les utilisateurs : {e}") +# ----------------------- +# ONGLET CREER +# ----------------------- -# --- Onglet Créer --- with tab_create: with st.form("create_user_form", clear_on_submit=False): st.subheader("Nouveau compte") @@ -381,19 +389,33 @@ with tab_create: c7, c8 = st.columns(2) password = c7.text_input("Mot de passe", type="password") password2 = c8.text_input("Confirmer", type="password") - store_plain = st.checkbox("Stocker le mot de passe en clair (déconseillé)", value=False) + + col_cb1, col_cb2 = st.columns([1.2, 1]) + # si tu stockes encore le mot de passe en clair dans la table + store_plain = col_cb1.checkbox("Stocker le mot de passe en clair (déconseillé)", value=False) + # ✅ nouvelle case pour l'e-mail de bienvenue à la création + notify_welcome = col_cb2.checkbox("Envoyer un e-mail de bienvenue", value=True, + help="Enverra l'identifiant, le nom, le site et le mot de passe en clair") submitted = st.form_submit_button("Créer l'utilisateur", use_container_width=True) if submitted: if not username or not full_name or not site or not email: st.error("Champs requis manquants.") + elif not EMAIL_RE.match(email): + st.error("Format d’e-mail invalide.") elif password != password2: st.error("Les mots de passe ne correspondent pas.") else: try: cnx = pool.get_connection() try: + # 🔎 avertir si l'e-mail existe déjà (mais on n'empêche pas) + dup = find_users_by_email(cnx, email) + if dup: + liste = ", ".join(f"{u['NomUtilisateur']}@{u['Site']}" for u in dup) + st.info(f"Cet e-mail est déjà utilisé par : {liste}") + pwd_hash = insert_user( cnx, username=username, full_name=full_name, site=site, password=password, expires=expires, phone=phone, email=email, @@ -401,9 +423,44 @@ with tab_create: ) finally: cnx.close() + st.success("Utilisateur créé avec succès ✅") st.caption("Hash (MotDePasseHash) :") st.code(pwd_hash) + + # ✉️ Mail de bienvenue (optionnel) + if notify_welcome: + try: + subj = f"[Compte créé] Vos accès — {site}" + body_txt = ( + "Bonjour,\n\n" + "Votre compte a été créé.\n\n" + f"Nom d’utilisateur : {username}\n" + f"Nom complet : {full_name}\n" + f"Site : {site}\n" + f"Mot de passe : {password}\n" + f"Date d'expiration: {expires.strftime('%Y-%m-%d')}\n\n" + "Cordialement." + ) + body_html = f""" + +

Bonjour,

+

Votre compte a été créé.

+ + + + + + +
Nom d’utilisateur{username}
Nom complet{full_name}
Site{site}
Mot de passe{password}
Date d'expiration{expires.strftime('%Y-%m-%d')}
+

Cordialement.

+ + """ + send_mail(email, subj, body_txt, body_html) + st.success(f"✉️ E-mail de bienvenue envoyé à {email}") + except Exception as e_mail: + st.warning(f"E-mail non envoyé : {e_mail}") + except mysql.connector.Error as db_err: if db_err.errno == errorcode.ER_ACCESS_DENIED_ERROR: st.error("Identifiants MySQL invalides.") @@ -412,7 +469,10 @@ with tab_create: except Exception as e: st.error(f"Erreur : {e}") -# --- Onglet Modifier --- +# ----------------------- +# ONGLET MODIFIER +# ----------------------- + with tab_edit: st.subheader("Modifier un utilisateur existant") try: @@ -495,8 +555,10 @@ with tab_edit: st.error(f"Erreur MySQL : {db_err}") except Exception as e: st.error(f"Erreur : {e}") +# ----------------------- +# ONGLET SECURITE +# ----------------------- -# --- Onglet Sécurité --- with tab_security: st.subheader("Réinitialiser le mot de passe (bcrypt)") try: