Skip to content

Attribution du super-admin : écrire contre le schéma réel de user_roles - #6

Merged
THIOYE97 merged 1 commit into
mainfrom
fix/grant-super-admin-schema-reel
Aug 7, 2026
Merged

THIOYE97 merged 1 commit into
mainfrom
fix/grant-super-admin-schema-reel

Conversation

@THIOYE97

@THIOYE97 THIOYE97 commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Le symptôme

En production, sur le shell Render :

psycopg.errors.UndefinedColumn: column "id" of relation "user_roles" does not exist

La cause

La table de production vaut exactement :

CREATE TABLE public.user_roles (
    user_id uuid NOT NULL,
    role    public.user_role NOT NULL,   -- ENUM, pas varchar
    PRIMARY KEY (user_id, role)
);

Le modèle app/models/user_role.py déclare en plus id, tenant_id et created_at. Comme le schéma de test est construit depuis les modèles (Base.metadata.create_all), l'écart est invisible en test : le code passait les 152 tests et ne pouvait qu'échouer en production. C'est exactement le piège déjà documenté en tête de app/models/screening_db.py.

Bon à savoir : SUPER_ADMIN est une valeur de l'ENUM, et user_id/role existent bien — la modification de auth_service.py livrée en PR #5 tourne donc sans risque en production. C'est ce que confirme le 401 propre observé sur /auth/login.

Le correctif

  • l'INSERT est construit d'après information_schema : valable sur le schéma de production comme sur celui des modèles ;
  • le ON CONFLICT (tenant_id, user_id, role) supposait une contrainte unique absente en production → remplacé par un contrôle d'existence explicite ;
  • les comparaisons sur role sont castées en texte, pour valoir contre l'ENUM comme contre le varchar ;
  • --list ne réclame plus created_at quand la colonne n'existe pas ;
  • le modèle porte désormais un avertissement en tête : il ne décrit pas la table de production.

Vérifications

Exécuté sur base jetable contre les deux schémas — attribution, idempotence, listage, retrait, compte introuvable :

===== SCHÉMA PRODUCTION (user_id, role enum) =====      OK
===== SCHÉMA MODÈLES (id, tenant_id, user_id, role) =====  OK

Plus un test unitaire sur la construction de l'INSERT, pour que la CI protège les deux cas — c'est le seul niveau où le test peut voir l'écart, puisque la base de test ne connaît que le schéma des modèles.

Reste ouvert

Le vrai assainissement serait d'aligner le modèle sur la base par une migration dédiée, ou d'aligner la base sur le modèle. Tant que les deux divergent, tout code écrit d'après le modèle est un piège. Hors périmètre de ce correctif.

Le script échouait en production avec « column "id" of relation "user_roles"
does not exist ». La table de production vaut exactement (user_id, role), avec
role en type ENUM ; le modèle SQLAlchemy déclare en plus id, tenant_id et
created_at. Comme le schéma de test est construit depuis les modèles, l'écart
était invisible : le code passait les tests et ne pouvait qu'échouer en prod.

L'INSERT est désormais construit d'après `information_schema`, donc valable
sur les deux schémas. Le ON CONFLICT, qui supposait une contrainte unique
(tenant_id, user_id, role) absente en production, est remplacé par un contrôle
d'existence explicite. Les comparaisons sur `role` sont castées en texte pour
valoir aussi bien contre l'ENUM que contre le varchar. `--list` ne réclame plus
created_at quand la colonne n'existe pas.

Vérifié sur base jetable contre les DEUX schémas : attribution, idempotence,
listage, retrait, compte introuvable. Test unitaire sur la construction de
l'INSERT pour que la CI protège les deux cas.

Le modèle porte désormais un avertissement : il ne décrit pas la table de
production, et tout code écrit d'après ses colonnes échouera en prod tout en
passant les tests.
@THIOYE97
THIOYE97 merged commit ae55fe6 into main Aug 7, 2026
3 checks passed
@THIOYE97
THIOYE97 deleted the fix/grant-super-admin-schema-reel branch August 7, 2026 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant