import pyotp from werkzeug.security import generate_password_hash from .connection import connect from .email_validation import is_valid_email from .ensure_schema import ensure_users_schema from .get_user_by_email import get_user_by_email from .is_first_user import is_first_user from .password_strength import password_strength class UserCreationError(Exception): """Erreur de validation lisible par un humain (affichée telle quelle dans le formulaire d'inscription) — jamais un détail SQL/technique.""" def create_user(email: str | None, password: str | None, nom: str | None, prenom: str | None) -> int: """Crée un compte : mot de passe fort (auth/password_strength.py) et 2FA (TOTP) rendus obligatoires — le secret est généré ici mais totp_confirmed reste à 0 tant que confirm_totp() n'a pas vérifié un premier code réellement scanné/tapé (voir routes/auth/register_2fa.py), pour ne jamais activer un compte sur un secret QR jamais réellement configuré. Le TOUT PREMIER compte créé (base vide) devient automatiquement admin (voir is_first_user.py) : pas de mot de passe par défaut à faire circuler, juste "inscris-toi en premier".""" email = (email or "").strip().lower() nom = (nom or "").strip() prenom = (prenom or "").strip() password = password or "" if not is_valid_email(email): raise UserCreationError("Adresse email invalide.") if not nom or not prenom: raise UserCreationError("Nom et prénom sont requis.") if get_user_by_email(email): raise UserCreationError("Un compte existe déjà avec cette adresse email.") if not password_strength(password)["valid"]: raise UserCreationError("Mot de passe trop faible — respecte les critères affichés.") ensure_users_schema() role = "admin" if is_first_user() else "user" totp_secret = pyotp.random_base32() conn = connect() conn.execute( """INSERT INTO _users (email, password_hash, nom, prenom, role, totp_secret, totp_confirmed) VALUES (?, ?, ?, ?, ?, ?, 0)""", (email, generate_password_hash(password), nom, prenom, role, totp_secret), ) user_id = int(conn.execute("SELECT last_insert_rowid() AS id").fetchone()["id"]) conn.commit() conn.close() return user_id