Commit Graph
4 Commits
Author SHA1 Message Date
williamandClaude Sonnet 5 3c39f1a249 Phase 1 (2/3) : route publique /jouer/<slug> + identité visiteur
Build and deploy / test (push) Failing after 8s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Ajoute la vraie route hébergée multi-joueurs qui manquait totalement
(voir le constat d'exploration : /game/<slug>/play est réservé au
créateur connecté, "Publier" ne génère qu'un exécutable mono-joueur) —
un visiteur anonyme peut maintenant jouer un jeu explicitement publié en
ligne, avec sa propre partie (variables/objets per_player posés dans le
commit précédent).

core/player_identity.py : identité visiteur via un cookie NON SIGNÉ
(forge_player_id, secrets.token_urlsafe(16), 1 an) — une simple clé de
partition, jamais un jeton d'autorisation.

routes/public_play/ : 4 routes, miroirs des routes existantes de l'aperçu
créateur mais threadées avec le vrai player_id du cookie au lieu du
sentinel PLAYER_SHARED :
- GET /jouer/<slug> (game_play_public.py)
- GET /jouer/<slug>/runtime-payload
- POST /jouer/<slug>/flow/nodes/<id>/run-data
- POST /jouer/<slug>/flow/nodes/<id>/run-variable
Chacune vérifie elle-même db.is_public_played(slug) (404 sinon) — un jeu
n'est exposé publiquement que si le créateur l'a explicitement basculé
"Publier en ligne" (nouveau db/games/is_public_played.py, réutilise la
table générique _meta, comme game_meta.py pour 'name').

core/auth_guard.py : les 4 endpoints publics ajoutés à _PUBLIC_ENDPOINTS
— la garde générique de connexion les laisse passer sans session, mais
chaque vue vérifie quand même is_public_played elle-même (défense en
profondeur, pas seulement une liste d'exceptions). CSRF (core/csrf_guard.py)
n'a besoin d'AUCUN changement : le jeton est déjà lié à la session Flask,
qui existe pour n'importe quel visiteur (connecté ou non).

templates/play.html : FORGE_PLAY_URLS (posé en Phase -1) ne construit
plus ses URLs via des noms de endpoint fixes (url_for('runtime_payload',
...)) mais reçoit des URLs déjà résolues par la route elle-même
(runtime_payload_url/flow_node_run_data_url/flow_node_run_variable_url)
— nécessaire puisque ce même template sert maintenant DEUX familles de
routes (aperçu créateur ET partie publique), chacune avec ses propres
noms de endpoint. routes/play/game_play.py (aperçu créateur, INCHANGÉ
comportement) et game_play_public.py passent chacun ses propres URLs.

templates/base.html : bascule "🌐 Publier en ligne" dans la barre de
navigation du jeu, à côté de "📦 Publier" (export .zip) — deux
fonctionnalités distinctes. db/games/game_meta.py expose maintenant
is_public_played, disponible partout où `game` est dans le contexte.

Vérifié : 237 tests passent (5 nouveaux dans test_public_play.py, dont un
bout-en-bout via HTTP avec deux VRAIS clients de test anonymes — deux
cookies forge_player_id différents — qui obtiennent des valeurs de
variable indépendantes, et un qui verrouille que l'aperçu créateur reste
inchangé). Syntaxe JS validée sur les deux variantes de play.html rendu
(aperçu créateur et partie publique).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-30 15:46:00 +02:00
williamandClaude Sonnet 5 c8c949c430 Ajoute la régénération des codes de récupération et le changement d'email
Depuis /profile :
- "Codes de récupération 2FA" : régénère les 10 codes (invalide les 10
  précédents d'un coup, voir auth.generate_recovery_codes) et les affiche
  une seule fois via le même modal qu'à l'inscription (session flash,
  core/recovery_codes_flash.py) — mot de passe actuel requis.
- "Adresse email" : change l'adresse (mêmes règles qu'à l'inscription —
  format valide, unicité — voir auth/email_validation.py, désormais
  partagé avec create_user.py au lieu d'être dupliqué). Pour un compte
  "user", le dossier de son unique projet porte le nom de son adresse
  (project_slug = slugify(email)) : il est renommé pour suivre le
  changement (db/games/move_game.py, même logique d'unicité par suffixe
  que create_game()), sans quoi project_slug ne correspondrait plus à
  aucun dossier réel. Un "admin" n'a pas de project_slug dédié : rien
  n'est renommé pour ce rôle.

18 nouveaux tests (tests/test_profile.py) : mauvais mot de passe refusé
pour les deux actions, mauvais format/email déjà pris refusés, dossier
bien renommé et project_slug mis à jour, connexion possible avec la
nouvelle adresse, anciens codes de récupération bien invalidés après
régénération, modal jamais réaffiché deux fois.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-29 06:29:38 +02:00
williamandClaude Sonnet 5 d90abc827b Ajoute l'authentification : inscription, mot de passe fort, 2FA obligatoire, isolation par utilisateur
Première des deux grandes fonctionnalités demandées (authentification
d'abord, export HTML/CSS/JS autonome ensuite) :

- Inscription (nom, prénom, email UNIQUE, mot de passe) avec schéma
  visuel du mot de passe (jauge + liste de critères qui passent au vert
  en direct — auth/password_strength.py, mêmes règles vérifiées côté
  serveur qu'affichées côté client).
- Double authentification (TOTP, compatible Google Authenticator/Authy)
  OBLIGATOIRE dès l'inscription : QR code (SVG, sans dépendance Pillow)
  à scanner puis code à confirmer avant que le compte soit utilisable —
  voir auth/create_user.py (totp_confirmed) et routes/auth/register_2fa.py.
- Connexion en 2 temps (mot de passe puis code TOTP), déconnexion.
- Isolation par utilisateur : un compte "user" est limité à un SEUL
  projet, dont le dossier est nommé d'après son adresse email (slugifiée)
  et créé automatiquement dès la 2FA confirmée — aucune page de gestion
  multi-jeux pour lui (redirigé directement vers son propre tableau de
  bord). Le rôle "admin" reste illimité, comme le moteur l'a toujours été
  (le TOUT PREMIER compte jamais créé sur une base de comptes vide devient
  automatiquement admin — voir auth/is_first_user.py — pas de mot de
  passe par défaut à faire circuler : s'inscrire en premier suffit).
  Un compte "user" ne peut pas non plus supprimer son unique projet
  (aucune façon d'en recréer un ensuite).
- Garde d'accès globale (core/auth_guard.py, un seul before_request) :
  toute page exige une connexion, sans avoir touché individuellement aux
  ~80 routes déjà existantes du moteur.

tests/conftest.py isole complètement les tests de la vraie base de
comptes (FORGE_USERS_DB_PATH/FORGE_SECRET_KEY_PATH vers un dossier
temporaire propre à la session de tests) et authentifie automatiquement
la fixture `client` partagée en tant que compte admin de test — les 155
tests déjà existants continuent de passer SANS AUCUNE modification de
leur côté, exactement comme avant l'authentification. 10 nouveaux tests
dédiés (tests/test_auth.py) : inscription/mots de passe/2FA/connexion/
isolation par projet/blocage de suppression, vérifiés en conditions
réelles (vrai client de test Flask, vraie base SQLite, vrais codes TOTP
calculés avec pyotp). 165 tests au total, tous au vert.

Nouvelles dépendances : pyotp, qrcode (requirements.txt).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-28 19:30:43 +02:00
williamandwilliam 3f4ebc4527 first commit
Build and deploy / build-and-push (push) Successful in 17s
Build and deploy / deploy (push) Successful in 10s
2026-08-21 16:23:49 +02:00