Ajoute une page de profil (modifier ses infos, mot de passe, supprimer son compte)

Le nom affiché dans la barre de navigation (base.html) devient un lien
vers /profile : informations du compte (email en lecture seule, nom/
prénom modifiables), changement de mot de passe (mot de passe actuel
requis + mêmes règles de force qu'à l'inscription/la réinitialisation),
et une section "danger" pour demander la suppression du compte.

La suppression exige le mot de passe actuel ET la saisie exacte de
"SUPPRIMER" (deux confirmations distinctes pour une action irréversible)
— routes/auth/profile.py. Pour un compte "user" (limité à un seul projet,
créé automatiquement et impossible à supprimer autrement, voir
core/auth_guard.py), supprimer le compte supprime aussi son unique projet
sur le disque, faute de quoi il resterait orphelin sans plus aucun
propriétaire. Un compte "admin" peut posséder plusieurs jeux qui ne lui
sont pas dédiés de la même façon : ses projets ne sont jamais touchés.
L'unique compte administrateur ne peut pas être supprimé (auth/
count_admins.py) — le supprimer bloquerait la création d'un nouveau
compte admin (réservée au tout premier compte jamais créé, base vide).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-08-28 21:03:47 +02:00
co-authored by Claude Sonnet 5
parent d5a84413d1
commit 0d46abe193
9 changed files with 379 additions and 2 deletions
+4
View File
@@ -26,6 +26,9 @@ from .password_reset import (
TOKEN_TTL_MINUTES,
)
from .send_email import send_password_reset_email, EmailNotConfiguredError
from .update_profile import update_profile
from .count_admins import count_admins
from .delete_user import delete_user
__all__ = [
"users_db_path", "ensure_users_schema", "is_first_user",
@@ -40,4 +43,5 @@ __all__ = [
"set_password", "create_password_reset_token", "get_user_id_for_valid_token",
"consume_password_reset_token", "TOKEN_TTL_MINUTES",
"send_password_reset_email", "EmailNotConfiguredError",
"update_profile", "count_admins", "delete_user",
]
+8
View File
@@ -0,0 +1,8 @@
from .connection import connect
def count_admins():
conn = connect()
n = conn.execute("SELECT COUNT(*) AS n FROM _users WHERE role = 'admin'").fetchone()["n"]
conn.close()
return n
+16
View File
@@ -0,0 +1,16 @@
from .connection import connect
def delete_user(user_id):
"""Supprime le compte et tout ce qui lui est directement rattaché
(codes de récupération, jetons de réinitialisation encore en cours) —
le dossier de projet, lui, est géré par l'appelant (voir
routes/auth/profile.py) : un compte "admin" peut posséder plusieurs
jeux qu'il n'a pas forcément envie de perdre en même temps que son
compte."""
conn = connect()
conn.execute("DELETE FROM _recovery_codes WHERE user_id = ?", (user_id,))
conn.execute("DELETE FROM _password_reset_tokens WHERE user_id = ?", (user_id,))
conn.execute("DELETE FROM _users WHERE id = ?", (user_id,))
conn.commit()
conn.close()
+8
View File
@@ -0,0 +1,8 @@
from .connection import connect
def update_profile(user_id, nom, prenom):
conn = connect()
conn.execute("UPDATE _users SET nom = ?, prenom = ? WHERE id = ?", (nom, prenom, user_id))
conn.commit()
conn.close()