Depuis le 17 juin 2026, la branche master d'Odoo ne connaît plus ir.model.access ni ir.rule. Un seul modèle, ir.access, porte désormais à la fois les droits d'accès par modèle et les règles d'enregistrement1. Pour un intégrateur, la nouvelle touche le dossier security/ de chaque module maison, et elle mérite qu'on la lise dans le code plutôt que dans les résumés.
Ce que le commit fondateur introduit
Le commit 77c17e7, signé par Raphael Collet et Adrien Dieudonné, s'intitule sans détour « new model ir.access that merges ir.model.access and ir.rule ». Il fait partie de la pull request #166359, liée à des PR jumelles dans les dépôts enterprise, upgrade et upgrade-util. Un second commit du même jour adapte le reste du code de base et retire la classe ir.model.access de ir_model.py, ainsi que la vue des règles ir_rule_views.xml.
La description de la PR résume le modèle en une phrase : chaque enregistrement possède un modèle, un sous-ensemble d'opérations CRUD, un domaine et un groupe optionnel. Tout le reste découle de ce groupe optionnel.
Permission ou restriction : le groupe décide
Quand le groupe est renseigné, l'accès accorde aux membres de ce groupe les opérations listées, sur les enregistrements qui satisfont le domaine. Le code stocke alors kind = 'permission'. Quand le groupe est vide, l'accès restreint les opérations aux enregistrements du domaine, pour tous les utilisateurs, et devient une restriction.
La PR donne la formule de combinaison :
(p1.domain OR p2.domain OR ...) AND (r1.domain AND r2.domain AND ...)
Les permissions s'additionnent, les restrictions se cumulent, et les restrictions l'emportent. Les habitués y retrouveront la logique des anciennes règles globales et des règles de groupe. La différence tient au fait que le droit de toucher au modèle et le filtre sur les lignes vivent maintenant dans la même ligne de configuration.
Un point demande de l'attention lors de la conversion. Dans ir_access.py, la fonction _get_groups_with_access ignore les accès sans groupe quand elle calcule les groupes autorisés. Une ligne sans groupe n'ouvre donc rien à personne : elle ne fait que filtrer. Un module qui comptait sur un ancien ACL sans groupe pour ouvrir un modèle à tous les utilisateurs devra déclarer explicitement le groupe concerné.
Le nouveau fichier CSV
Les modules standards ont migré. Dans addons/sale/security, le fichier s'appelle désormais ir.access.csv et son en-tête tient en six colonnes :
id,name,model_id,group_id/id,operation,domain
access_account_account_salesman,account_account salesman,account.account,sales_team.group_sale_salesman,r,
sale_order_comp_rule,Sales Order multi-company,sale.order,,crud,"[('company_id', 'in', company_ids)]"
La première ligne correspond à un ancien ACL en lecture seule. La seconde reprend l'ancienne règle multi-société de sale.order : aucun groupe, toutes les opérations, et un domaine sur company_ids. On compte cinq lignes de ce type, sans groupe, dans le fichier de sale2.
Le tableau suivant résume la correspondance que nous utilisons pour relire nos propres modules.
| Avant | Dans ir.access |
|---|---|
ACL perm_read=1, perm_write=1, perm_create=1, perm_unlink=0 | operation = cru |
| Règle globale (sans groupe) | ligne sans group_id, avec domaine |
| Règle de groupe | ligne avec group_id et domaine |
| ACL + règle de groupe sur le même modèle | une seule ligne avec groupe et domaine |
Champ active de la règle | champ active de l'accès |
Le champ operation accepte les quinze combinaisons non vides des lettres c, r, u et d.
L'opérateur access dans les domaines
Le nouveau modèle s'accompagne d'un opérateur de domaine access. Le commit 49c1f7d du 2 juillet 2026 en montre l'usage : un domaine [('categ_id', 'access', 'read')] limite l'accès aux enregistrements dont l'utilisateur peut lire la catégorie liée. Ce commit corrige justement le calcul des groupes autorisés, qui ignorait ces conditions et ouvrait un modèle à des groupes privés d'accès au comodèle.
Pour un module métier, cet opérateur évite de recopier dans chaque modèle enfant les filtres du modèle parent. Nous le réserverons pourtant aux cas lisibles : un domaine récursif devient vite difficile à auditer.
Cache, personnalisation et débogage
Les accès actifs sont chargés une fois dans un cache ormcache dit « stable », indexé par modèle. Toute création, modification ou suppression d'un accès vide ce cache et invalide l'environnement. La méthode customize() copie un accès standard, désactive l'original et ouvre la copie. Une modification fonctionnelle ne remplace donc plus la donnée du module, ce qui protège les mises à jour.
Le commit 6601aa2 du 7 septembre 2026 ajoute la pile d'appels dans les erreurs d'accès en mode débogage. Le détail des accès en échec reste réservé au groupe base.group_no_one.
Ce qu'il faut prévoir côté migration
La PR ajoute un script upgrade_code chargé de convertir les fichiers de sécurité. Nous le lancerons sur chaque module, puis nous relirons le résultat ligne par ligne. Pendant la revue, un contributeur s'est étonné d'une règle convertie touchant base.group_partner_manager, et un mainteneur a répondu que la conversion était correcte, même si elle ne correspondait pas forcément à ce qu'il attendait3. Un script peut produire une sécurité équivalente qui reste surprenante à la lecture.
Nous voyons trois chantiers concrets :
- les fichiers
ir.model.access.csvet les règles XML à fusionner enir.access.csv; - le code Python qui appelait
env['ir.model.access'].check(...)ou lisaitir.rule, qu'il faudra réécrire ; - les tests de sécurité, à rejouer avec des utilisateurs de chaque groupe.
Le numéro de version exact qui embarquera ce changement dépend du calendrier de branches d'Odoo. Il est confirmé dans master, la lignée qui mènera à Odoo 20, mais nous ne l'avons pas vu documenté dans une note de version officielle.
Notre lecture
Le nouveau modèle simplifie réellement la lecture de la sécurité, puisque chaque droit tient sur une ligne et que la règle de combinaison est écrite noir sur blanc. Pour un consultant qui audite une base, c'est un vrai progrès. Le coût se paie à la migration, surtout sur les modules qui superposaient ACL, règles globales et règles de groupe sans les documenter.
Nous conseillons de cartographier dès maintenant la sécurité de vos modules spécifiques sous Odoo 19 : quels groupes ouvrent quels modèles, et quelles règles filtrent quelles lignes. Cet inventaire servira de référence pour comparer le résultat du script de conversion, plutôt que de lui faire confiance à l'aveugle.
Sources
- Odoo, PR #166359 « [IMP] core: new model merging ACLs and record rules » : https://github.com/odoo/odoo/pull/166359
- Odoo, commit 77c17e7 « [ADD] core: new model ir.access that merges ir.model.access and ir.rule » : https://github.com/odoo/odoo/commit/77c17e7111c18fffe06fea9b9149cc6d3119e893
- Odoo, commit 26cc6ac « [IMP] base: adapt code to use new model ir.access » : https://github.com/odoo/odoo/commit/26cc6ac18fe9e29e034d8ccf34b63c4ad650307e
- Odoo, commit 49c1f7d « [FIX] base: get groups with access respects 'access' operator » : https://github.com/odoo/odoo/commit/49c1f7db0341e1cb4193ec412352a523627b4c9a
- Odoo, code source de ir_access.py (master) : https://github.com/odoo/odoo/blob/master/odoo/addons/base/models/ir_access.py
- Odoo, historique des commits de ir_access.py : https://github.com/odoo/odoo/commits/master/odoo/addons/base/models/ir_access.py
- Odoo, ir.access.csv du module sale (master) : https://github.com/odoo/odoo/blob/master/addons/sale/security/ir.access.csv
Footnotes
-
Constat établi à partir du code source de la branche master d'Odoo (fichier
odoo/addons/base/models/ir_access.py) et de l'historique de ses commits, consultés le 22 octobre 2026. ↩ -
Comptage réalisé sur
addons/sale/security/ir.access.csvdans la branche master à la même date. Le contenu peut évoluer. ↩ -
Échange visible dans les commentaires de la pull request odoo/odoo #166359. ↩