Skip to content

Odoo 19 Security Groups & Record Rules: Complete Guide

DeployMonkey Team · March 22, 2026 14 min read

Odoo Security Layers

Odoo has three layers of access control, each more granular than the last:

  1. Security Groups — Who can access which menus and features
  2. Model Access (ir.model.access) — CRUD permissions per model per group
  3. Record Rules (ir.rule) — Which specific records a user can see/edit

Security Groups

Groups define roles: Sales User, Sales Manager, Accounting User, etc. Users belong to one or more groups.

<!-- Define a custom group -->
<record id="group_equipment_user" model="res.groups">
    <field name="name">Equipment User</field>
    <field name="category_id" ref="base.module_category_operations"/>
</record>

<record id="group_equipment_manager" model="res.groups">
    <field name="name">Equipment Manager</field>
    <field name="category_id" ref="base.module_category_operations"/>
    <field name="implied_ids" eval="[(4, ref('group_equipment_user'))]"/>
</record>

implied_ids creates inheritance: Equipment Manager automatically gets all Equipment User permissions.

Model Access (ir.model.access.csv)

Controls CRUD at the model level — can this group create/read/update/delete records on this model?

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_equip_user,equipment.user,model_equipment_item,group_equipment_user,1,1,1,0
access_equip_manager,equipment.manager,model_equipment_item,group_equipment_manager,1,1,1,1

Users can read/write/create but NOT delete. Managers can do everything.

Record Rules (ir.rule)

Controls which records a user can see — row-level security.

<!-- Users see only their department's equipment -->
<record id="rule_equipment_department" model="ir.rule">
    <field name="name">Equipment: own department</field>
    <field name="model_id" ref="model_equipment_item"/>
    <field name="groups" eval="[(4, ref('group_equipment_user'))]"/>
    <field name="domain_force">
        [('department_id.member_ids.user_id', 'in', [user.id])]
    </field>
    <field name="perm_read" eval="True"/>
    <field name="perm_write" eval="True"/>
    <field name="perm_create" eval="True"/>
    <field name="perm_unlink" eval="True"/>
</record>

<!-- Managers see all equipment (global rule) -->
<record id="rule_equipment_manager" model="ir.rule">
    <field name="name">Equipment: manager sees all</field>
    <field name="model_id" ref="model_equipment_item"/>
    <field name="groups" eval="[(4, ref('group_equipment_manager'))]"/>
    <field name="domain_force">[(1, '=', 1)]</field>
</record>

Global vs Group Rules

Typegroups fieldBehavior
Global ruleEmpty (no groups)AND — applies to everyone, restricts access
Group ruleHas groupsOR — within same group, rules are combined with OR

Multi-Company Record Rule

<record id="rule_equipment_multi_company" model="ir.rule">
    <field name="name">Equipment: multi-company</field>
    <field name="model_id" ref="model_equipment_item"/>
    <field name="domain_force">[('company_id', 'in', company_ids)]</field>
</record>

This is a global rule (no groups) — it restricts ALL users to seeing only their company's records.

Field-Level Access

<!-- Only managers can see purchase cost -->
<field name="purchase_cost" groups="equipment_tracking.group_equipment_manager"/>

The groups attribute on fields works in both Python models and XML views.

Common Patterns

"Own Records Only" Rule

[('user_id', '=', user.id)]

"Same Company" Rule

[('company_id', 'in', company_ids)]

"Active Records Only" Rule

[('active', '=', True)]

Debugging Access Issues

# Check what groups a user belongs to
user.groups_id.mapped('full_name')

# Check model access
self.env['ir.model.access'].check('equipment.item', 'read')

# Check record rules (returns domain)
self.env['ir.rule']._compute_domain('equipment.item', 'read')

# Bypass security (use carefully!)
self.env['equipment.item'].sudo().search([])

Common Mistakes

  • No ir.model.access for custom model → AccessError for all users
  • Global rule too restrictive → Even admin cannot see records
  • Using sudo() to fix access issues → Masks the real problem, creates security holes
  • Forgetting company_id rule → Data leaks between companies

DeployMonkey Security

DeployMonkey's AI agent understands Odoo security. Ask it to diagnose access errors, generate security rules, or audit your custom module's permissions.