--- name: odoo-security-rules description: 'Expert in Odoo access control: ir.model.access.csv, record rules (ir.rule), groups, and multi-company security patterns.' metadata: aas-risk: safe aas-source: self aas-date-added: '2026-03-04' --- # Odoo Security Rules ## Overview Security in Odoo is managed at two levels: **model-level access** (who can read/write which models) and **record-level rules** (which records a user can see). This skill helps you write correct `ir.model.access.csv` entries and `ir.rule` domain-based record rules. ## When to Use This Skill - Setting up access rights for a new custom module. - Restricting records so users only see their own data or their company's data. - Debugging "Access Denied" or "You are not allowed to access" errors. - Implementing multi-company record visibility rules. ## How It Works 1. **Activate**: Mention `@odoo-security-rules` and describe the access scenario. 2. **Generate**: Get correct CSV access lines and XML record rules. 3. **Debug**: Paste an access error and get a diagnosis with the fix. ## Examples ### Example 1: ir.model.access.csv ```csv id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_hospital_patient_user,hospital.patient.user,model_hospital_patient,base.group_user,1,0,0,0 access_hospital_patient_manager,hospital.patient.manager,model_hospital_patient,hospital_management.group_hospital_manager,1,1,1,1 ``` > **Note:** Define the dedicated module manager group before loading the CSV (replace `hospital_management` with the module ID). Do not reuse broad settings/administration groups for patient-data roles. This group example targets Odoo 18; verify group fields for the installed version: > > ```xml > > Hospital Manager > > > ``` ### Example 2: Record Rule — Users See Only Their Own Records ```xml Hospital Patient: Own Records Only [('create_uid', '=', user.id)] ``` > **Important:** No groups makes a rule global (superuser mode bypasses record rules). Global rules intersect; group rules union within those bounds. `perm_unlink=False` means this rule is not checked for deletion, not that deletion is denied. ACLs grant operations additively, and record rules default to allow when none apply. ### Example 3: Multi-Company Record Rule ```xml Hospital Patient: Multi-Company ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] ``` ## Best Practices - ✅ **Do:** Start with the most restrictive access and open up as needed. - ✅ **Do:** Use `company_ids` (plural) in multi-company rules — it reflects the companies active in the current environment; test company switching. - ✅ **Do:** Test rules using a non-admin user in debug mode — `sudo()` bypasses all record rules entirely. - ✅ **Do:** Create dedicated security groups per module rather than reusing core Odoo groups. - ❌ **Don't:** Give `perm_unlink = 1` to regular users unless deletion is explicitly required by the business process. - ❌ **Don't:** Leave `group_id` blank in `ir.model.access.csv` unless you intend to grant public (unauthenticated) access. - ❌ **Don't:** Use `base.group_system` for module managers — that grants full technical access including server configurations. ## Validation and sources Test as ordinary users with overlapping groups: own/other records, every CRUD operation, active company switching, and portal/public roles. The company rule assumes a `company_id` field; allowing `False` deliberately shares unowned records. See [Odoo security](https://www.odoo.com/documentation/18.0/developer/reference/backend/security.html) and [multi-company rules](https://www.odoo.com/documentation/18.0/developer/howtos/company.html). ## Limitations - Field access can be restricted using the ORM field `groups` attribute; hiding a field only in OWL/views is not server-side access control. - **Portal and public user** access rules have additional nuances not fully covered here; test carefully with `base.group_portal`. - Record rules are **bypassed by `sudo()`** — any code running in superuser context ignores all `ir.rule` entries. - Does not cover **row-level security via PostgreSQL** (RLS) — Odoo manages all security at the ORM layer.