Odoo 20 introduces a major change in how access rights are defined and evaluated.
If you’ve worked with Odoo 19 or earlier, you already know the traditional security model:
- Model-level permissions using ACLs (ir.model.access)
- Record-level filtering using Record Rules (ir.rule)
- Separate security definitions that can sometimes be difficult to debug
With the new security architecture, Odoo brings these concepts together using a single model:
ir.access
๐ฅ What Changed in Odoo 20?
Previously:
- ir.model.access โ Can the user access this model?
- ir.rule โ Which records can the user access?
Now:
- ir.access โ Model + CRUD operations + record domain + optional group
The new security definitions are primarily maintained through:
security/ir.access.csv
๐ง Understanding the Old Model
Before Odoo 20, security was split between ACLs and Record Rules. For example:
ACL:
Create = Yes
Read = Yes
Write = Yes
Delete = No
And a Record Rule could restrict the records:
[('user_id', '=', user.id)]
So the user could access the model, but only their own records.
๐ New ir.access Model
The same logic can now be defined together:
crm_rule_personal_lead,Personal Leads, crm.lead,sales_team.group_sale_salesman,cru,"['|',('user_id','=',user.id),('user_id','=',False)]"
This defines:
- Model: crm.lead
- Group: Salesperson
- Operation: cru
- Domain: user_id = current user
So the salesperson can:
- Create leads
- Read leads
- Update leads
- Access only their own leads
๐ What Happened to CRUD?
The old ACL format used separate fields:
- perm_read
- perm_write
- perm_create
- perm_unlink
The new operation field combines them.
| Old ACL | New operation |
|---|---|
| perm_create | c |
| perm_read | r |
| perm_write | u |
| perm_unlink | d |
For example:
cru
means:
Create + Read + Update
while:
crud
means full CRUD access.
๐ What Happened to R?
Previously, a Record Rule could look like:
<record id="crm_rule_personal_lead"
<field name="name">Personal Leads</field>
<field name="model_id" ref="model_crm_lead"/>
<field name="groups"
eval="[(4, ref('sales_team.group_sale_salesman'))]"/>
<field name="domain_force">
['|',('user_id','=',user.id),('user_id','=',False)]
</field>
</record>
In the new structure, the domain becomes
crm_rule_personal_lead,Personal Leads,crm.lead,sales_team.group_sale_salesman,cru,"['|',('user_id','=',user.id),('user_id','=',False)]"
So instead of maintaining:
ACL + Record Rule
the access definition contains:
Model + Group + Operation + Domain
๐ง Field Mapping
| Odoo 19 | Odoo 20 | Meaning |
|---|---|---|
| ir.model.access | ir.access | Model access |
| ir.rule | ir.access | Record access |
| model_id | model_id/id | Model |
| groups | group_id/id | Security group |
| perm_create | operation = c | Create |
| perm_read | operation = r | Read |
| perm_write | operation = u | Update |
| perm_unlink | operation = d | Delete |
| domain_force | domain | Record filter |
๐ Need Help Migrating Odoo 19 to Odoo 20?
Planning an Odoo 19 โ Odoo 20 migration
We can help with:
- Custom module migration
- ir.model.access โ ir.access
- ir.rule migration
- Python/API changes
- XML & view migration
- Migration testing and bug fixing
WhatsApp: +91 77779 09289
Email: info@codespheretech.in
๐ฉ Contact us if you want help reviewing custom Odoo 19 modules to Odoo 20.


