Le callback Dash _update_table est un endpoint serveur global
(/_dash-update-component) invocable indépendamment du layout : la garde
is_admin() de layout() ne protégeait que l'affichage. Sans contrôle dans
le callback, un non-admin — voire un anonyme sur le chemin lecture —
pouvait lire/écrire toute la base SQLite utilisateurs.
Ajoute la garde en tête du callback + test de non-régression.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- IntegrityError (ex: email déjà utilisé) est désormais capturée et
reconvertie en ValueError, pour rester dans le funnel d'alerte
existant du callback admin au lieu de faire planter le callback Dash.
- Une UPDATE qui touche 0 ligne (ligne supprimée entre le chargement du
tableau et la soumission de l'édition) lève désormais une ValueError
au lieu d'être silencieusement traitée comme un succès (ce qui aurait
créé un log d'audit trompeur).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Covers anonymous/non-admin access (404), and the full admin flow: switch
table, edit a subscriptions.prix_ht cell through the real DataTable UI,
verify the write lands in the DB, and verify the edit is logged and
visible in the admin_actions table.
tests/users.test.sqlite is committed and shared by the whole Selenium
session, so _cleanup_user deletes every row the tests create. Beyond
resetting sqlite_sequence (already needed for the AUTOINCREMENT
counter), a plain DELETE also leaves stale, never-zeroed bytes behind
in the admin_actions/subscriptions b-tree pages once they go back to
zero rows, which byte-diffs the file even though its logical content
is unchanged. VACUUM rebuilds the file from live data only, producing
a deterministic page layout (verified empirically across independent
runs with different random test data). Re-baselined the fixture to
that canonical vacuumed state so `git status` stays clean after
running the suite.
When switching tables, the table-switch branch writes fresh data for the
new table, which re-fires the same callback with data_previous still
holding the old table's rows. find_changed_cell only checked row count
before diffing, so if the two tables happened to have the same number of
rows it would zip mismatched-schema dicts and report a spurious changed
cell (usually the PK column), producing a confusing red alert right after
switching tables.
test_admin_non_admin_gets_404 asserted the same /admin 404 that
anonymous visitors also get, without first confirming the login
actually went through. A broken login (falls back to /connexion on
bad credentials, unverified email, etc.) would leave the session
anonymous and the test would keep passing for the wrong reason,
silently degrading into a duplicate of test_admin_anonymous_gets_404.
Now waits for the post-login redirect to /compte/abonnement (this
user has no subscription) before exercising the admin guard.
Covers anonymous → 404, non-admin → 404, and the full admin flow
(list, detail, status change, journal) through a real login and a
real running app. tests/users.test.sqlite is committed and shared by
the whole Selenium session, so the test cleanup also resets the
sqlite_sequence high-water marks that plain DELETEs don't roll back,
keeping the file byte-stable across runs. This run additionally bakes
in migration 0006_create_admin_actions (new admin_actions table), the
first time any Selenium test has booted the real app since that
migration was added — the same one-time process by which migrations
0001-0005 already ended up committed in this fixture.
Wires is_admin(), SUBSCRIPTION_STATUSES/get_current/set_status, and
log_action() into POST /admin/actions/subscription-status: validates
the requested status and that subscription_id matches the user's
current subscription, applies the change, and logs an audit entry.
Registers the admin blueprint in init_auth() and documents ADMIN_EMAIL
in .template.env.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implement access control function for admin panel. Returns True only if
ADMIN_EMAIL env var is set, user is authenticated, and email matches
case-insensitively.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>