Corrige une faille d'isolation entre comptes et retire l'email des chemins de projet
Build and deploy / test-python (push) Successful in 9m39s
Build and deploy / test-js (push) Successful in 52s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

Le rôle "admin" contournait entièrement l'isolation par projet
(core/auth_guard.py) : il pouvait ouvrir/modifier/supprimer le jeu de
n'importe quel autre compte en connaissant son slug, et la page d'accueil
listait sans filtrage tous les projets de tous les comptes.

- La propriété d'un projet se vérifie désormais sur le segment
  "propriétaire" du slug (id du compte), pour tous les rôles y compris
  admin — un slug "à plat" (sans compte associé) reste réservé à
  l'admin, comportement historique conservé pour ce cas précis.
- routes/games/index.py ne liste plus que les projets du compte connecté.
- Le dossier propriétaire d'un projet est maintenant l'id numérique du
  compte, plus jamais son email slugifié (visible en clair dans chaque
  URL auparavant) — script de migration fourni et déjà exécuté sur les
  données existantes.
- Changer d'email ne renomme plus aucun dossier (n'en dépend plus).
- Deux nouveaux tests de régression, fixtures corrigées en conséquence.
- README réécrit pour refléter l'état actuel du produit (jeu 2D
  uniquement, plus de traces de l'ancien éditeur "document").
This commit is contained in:
william
2026-09-04 22:52:05 +02:00
parent 50835a18e2
commit 7ebc9b143f
16 changed files with 382 additions and 833 deletions
+3 -2
View File
@@ -11,8 +11,9 @@ def create_game(name, owner_folder=None, project_slug_override=None):
reliés entre eux, et sa base de données dédiée (nom du jeu en méta).
owner_folder : dossier PROPRIÉTAIRE (voir db/games/project_slug.py —
structure de dossiers par utilisateur), typiquement
slugify(email_du_compte) — le slug final composé
structure de dossiers par utilisateur), l'id du compte en pratique
(voir routes/games/games_new.py — opaque, jamais dérivé de l'email) —
le slug final composé
"<owner_folder>_<project_part>" résout vers un vrai chemin imbriqué
projects/<owner_folder>/<project_part>/. Omis (None) : repli sur
l'ancien comportement plat (slug à un seul segment, projects/<slug>/)
+17 -19
View File
@@ -6,27 +6,25 @@ from ..db_path import db_path
from .project_slug import build_slug
def list_games():
"""Scanne projects/ : structure par utilisateur (voir
db/games/project_slug.py) — projects/<propriétaire>/<projet>/game.db,
deux niveaux — plus le repli sur l'ancien rangement plat
projects/<slug>/game.db pour tout slug pas encore migré (voir
scripts/migrate_flat_project_slugs.py)."""
def list_games(owner_folder):
"""Scanne UNIQUEMENT projects/<owner_folder>/ (voir db/games/
project_slug.py — owner_folder est l'id du compte, voir
db/games/create_game.py) : jamais les autres comptes — voir
core/auth_guard.py, qui vérifie déjà que `owner_folder` correspond au
compte connecté avant tout accès à un slug composé de cette valeur.
Un appelant qui passerait le mauvais owner_folder ne verrait de toute
façon que SES PROPRES projets, jamais ceux d'un autre compte : cette
fonction ne lit plus jamais l'arborescence project/ en entier (avant
ce correctif, elle listait TOUS les comptes sans distinction — faille
corrigée, voir routes/games/index.py)."""
games = []
if not os.path.isdir(PROJECTS_DIR):
entry_path = os.path.join(PROJECTS_DIR, owner_folder)
if not os.path.isdir(entry_path):
return games
for entry in sorted(os.listdir(PROJECTS_DIR)):
entry_path = os.path.join(PROJECTS_DIR, entry)
if not os.path.isdir(entry_path):
continue
if os.path.isfile(os.path.join(entry_path, "game.db")):
# Repli : un ancien dossier plat porte directement game.db.
_append_game(games, entry)
continue
for project_part in sorted(os.listdir(entry_path)):
slug = build_slug(entry, project_part)
if os.path.isfile(db_path(slug)):
_append_game(games, slug)
for project_part in sorted(os.listdir(entry_path)):
slug = build_slug(owner_folder, project_part)
if os.path.isfile(db_path(slug)):
_append_game(games, slug)
return games
+12 -11
View File
@@ -6,17 +6,18 @@ from .project_slug import build_slug, split_slug
def move_game(old_slug, new_owner_folder):
"""Renomme le dossier PROPRIÉTAIRE d'un compte — utilisé quand un
utilisateur change son adresse email (voir routes/auth/profile.py,
owner_folder = slugify(email) pour un compte "user", voir
db/games/project_slug.py) : le dossier physique doit suivre. Renomme
le dossier propriétaire ENTIER en un coup (déplace tous les projets de
ce compte ensemble — prêt pour un futur multi-projet, même si un seul
aujourd'hui), pas juste un projet. Rend le nouveau dossier propriétaire
unique de la même façon que create_game() si, par un hasard extrême,
il correspond déjà à un dossier existant. Renvoie le nouveau slug
composé FINAL (même projet, propriétaire renommé), à enregistrer comme
nouveau project_slug."""
"""Renomme le dossier PROPRIÉTAIRE d'un compte — owner_folder est
l'id du compte (voir db/games/project_slug.py, routes/games/
games_new.py) : ne change donc plus jamais après coup en usage normal,
mais reste utile pour une migration ponctuelle (voir
scripts/migrate_owner_folders_to_user_id.py, qui a besoin de renommer
les anciens dossiers slugify(email) vers l'id du compte). Renomme le
dossier propriétaire ENTIER en un coup (déplace tous les projets de ce
compte ensemble), pas juste un projet. Rend le nouveau dossier
propriétaire unique de la même façon que create_game() si, par un
hasard extrême, il correspond déjà à un dossier existant. Renvoie le
nouveau slug composé FINAL (même projet, propriétaire renommé), à
enregistrer comme nouveau project_slug."""
old_owner_folder, project_part = split_slug(old_slug)
if project_part is None:
# Slug pas encore migré vers la structure par utilisateur (voir