1. Attribuez une responsabilité à chaque couche
Gardez ce tableau sous les yeux pendant les tests. Une couche peut collaborer avec la suivante, mais elle ne remplace pas son rôle.
| Couche | Responsabilité dans ce parcours | Ce qu’elle ne doit pas faire |
|---|---|---|
| Session Odoo ou clé d’API Bearer | Établir l’identité. La session identifie le compte connecté ; la clé Bearer identifie le compte Odoo qui la possède. Odoo place ensuite cet utilisateur dans env.user ou request.env.user. | Recevoir un user_id choisi par le navigateur ou accorder un droit supplémentaire. |
Modèle training.note | Lorsque les clients du parcours n’envoient pas user_id, remplir ce champ avec l’utilisateur courant grâce à sa valeur par défaut. | Considérer cette valeur par défaut comme une validation d’un user_id envoyé par un formulaire, Owl ou l’API. |
| ACL | Autoriser ou refuser une opération sur le modèle : lire, créer, modifier ou supprimer training.note pour le groupe Training Notes User. | Choisir quelles lignes précises l’utilisateur peut atteindre. |
| Règle sur les enregistrements (record rule dans Odoo) | Limiter les enregistrements accessibles par son domaine [('user_id', '=', user.id)]. Elle répond à la question « quelles notes ? ». | Accorder une opération que l’ACL n’autorise pas. |
| ORM | Exécuter la recherche ou l’écriture dans l’environnement authentifié et appliquer le modèle, l’ACL et la règle sur les enregistrements avant l’accès SQL. | Contourner les contrôles avec sudo(). |
| PostgreSQL | Stocker les lignes de la table training_note, notamment l’identifiant entier de la relation user_id. | Déduire l’identité depuis une saisie du navigateur ou remplacer les contrôles de l’ORM. |
| Interface | Présenter et saisir les données : vue standard, page QWeb ou action cliente Owl. | Devenir une frontière de sécurité grâce à un champ masqué, un bouton absent ou un filtre JavaScript. |
2. Exécutez la matrice à deux utilisateurs
Utilisez les notes nommées dans la section 3. Dans chaque session, consignez seulement le résultat observé : la liste de la personne connectée contient-elle sa note et exclut-elle celle de l’autre ?
| Parcours | Test avec Alex | Test avec Sam | Preuve à observer |
|---|---|---|---|
| Vue standard | Création de Alex · vue standard acceptée ; Sam · vue standard est absente. | Création de Sam · vue standard acceptée ; Alex · vue standard est absente. | Le champ Propriétaire affiche le bon compte, mais la vue n’a pas choisi l’identité. |
Page QWeb /training/notes | Le POST crée Alex · page QWeb, puis la redirection 303 affiche cette note seulement. | Le POST crée Sam · page QWeb, puis la page n’affiche pas la note d’Alex. | La même URL et le même gabarit reçoivent des enregistrements différents après le filtrage de l’ORM. |
| Action cliente Owl | Alex · Owl est créé et relu ; aucune note de Sam n’apparaît. | Sam · Owl est créé et relu ; aucune note d’Alex n’apparaît. | searchRead et this.orm.create utilisent la session Odoo, sans propriétaire fourni. |
API Bearer /training/api/notes | Avec la clé d’Alex, le GET répond 200 avec ses notes et n’inclut pas celles de Sam. | Avec la clé de Sam, le GET répond 200 avec ses notes et n’inclut pas celles d’Alex. | La clé établit l’identité ; le contrôleur et l’ORM appliquent ensuite la même règle de propriétaire. |
Les créations acceptées reçoivent automatiquement le propriétaire de la session. Les lectures croisées restent absentes ou refusées dans les trois interfaces et l’API. Les différences de HTML, d’action JavaScript ou de JSON sont des différences de présentation et de transport, pas de politique.
3. Vérifiez aussi un accès direct et les appels API refusés
- Dans la session de Sam, ouvrez la fiche Sam · vue standard et copiez son URL. Connectez-vous comme Alex et ouvrez cette URL : Odoo doit refuser l’accès ou signaler que l’enregistrement est indisponible, sans révéler son titre ni son contenu.
- Dans la liste standard d’Alex, recherchez exactement Sam · vue standard : aucun résultat ne doit apparaître. Ce résultat vient de la règle sur les enregistrements, pas d’un filtre ajouté à la vue.
- Appelez
/training/api/notessans en-têteAuthorization, comme au Jour 4 : Odoo répond401. Une clé valide est nécessaire pour établir l’identité. - Rejouez ensuite le GET avec les clés d’Alex et de Sam, à l’étape précédente. Les deux requêtes visent la même URL, mais chacune reçoit seulement les notes de son propriétaire.
Un champ en lecture seule ou masqué, un bouton supprimé et un filtre côté client améliorent l’expérience, mais ne sont pas des frontières de sécurité. Aucun navigateur ni client ne fournit un user_id digne de confiance. Dans ce parcours, les clients ne l’envoient pas et le défaut utilise env.user. Un module qui accepterait ce champ devrait le vérifier côté serveur. Aucune route ne doit utiliser sudo() pour faire disparaître un refus.
4. Diagnostiquez dans cet ordre
Si une observation ne correspond pas à la matrice, contrôlez uniquement les éléments déjà démontrés dans le parcours :
- Modèle : dans
custom_addons/training_hello/models/training_note.py, vérifiez le champuser_id, son défaut fondé surself.env.useret l’absence decreate()ouwrite()ajoutés pour cet exercice. Redémarrez Odoo après une correction Python. - ACL : dans
custom_addons/training_hello/security/ir.model.access.csv, vérifiez la ligne detraining.note:group_training_note_useret les quatre permissions CRUD valent1. Une modification demande une mise à niveau de Hello Odoo. - Règle : dans
custom_addons/training_hello/security/training_note_security.xml, contrôlezdomain_forceet le domaine exact[('user_id', '=', user.id)]. Mettez le module à niveau pour recharger le XML. - Chargement : dans
custom_addons/training_hello/__manifest__.py, vérifiez que la listedatacontient l’ACL puis le XML de sécurité, avant les vues et les gabarits. Mettez le module à niveau après cette correction. - Page QWeb : dans
custom_addons/training_hello/controllers/training_notes.py, vérifiezauth="user",request.env["training.note"]et les routes/training/notes. Le contrôleur ne doit ni recevoiruser_idni appelersudo(). - Owl : dans
custom_addons/training_hello/static/src/training_notes/training_notes.js, vérifiezsearchRead,this.orm.create("training.note", ...)et l’absence de propriétaire dans les valeurs envoyées. Mettez le module à niveau, puis rechargez les assets du navigateur. - API : dans
custom_addons/training_hello/controllers/notes_api.py, vérifiezauth="bearer",request.env.useret le domaine du propriétaire. Le contrôleur ne doit ni recevoiruser_idni appelersudo().
Après chaque correction, rejouez la ligne concernée de la matrice avec Alex et Sam. Ne contournez pas un refus en ajoutant sudo() ou un identifiant envoyé par le client : vous masqueriez précisément le défaut à diagnostiquer.
Ce que vous devez retenir
La session ou la clé Bearer établit l’identité. L’ACL autorise des opérations sur le modèle training.note. La règle sur les enregistrements limite les lignes à leur propriétaire. Dans les créations du parcours, l’absence de user_id laisse le défaut du modèle utiliser cette identité, puis l’ORM fait respecter les droits avant que PostgreSQL ne stocke la ligne. Les vues standard, QWeb, Owl et l’API sont quatre chemins vers la même donnée protégée.
Vous avez vérifié la même politique avec Alex et Sam dans trois interfaces et l’API. Chaque note appartient au compte authentifié, les notes de l’autre compte restent inaccessibles et aucun client ne choisit l’identité de confiance.
Dans Odoo, révoquez les deux clés temporaires du Jour 5, celle d’Alex et celle de Sam. Effacez TRAINING_API_KEY_ALEX, TRAINING_API_KEY_SAM et TRAINING_DATABASE du terminal. Ne placez jamais ces secrets dans les sources, les captures ou les archives.