How a Growing SME Secured Its Odoo Environment: Role‑Based Access vs. Record Rules
How a Growing SME Secured Its Odoo Environment: Role‑Based Access vs. Record Rules
When a midsize distributor expanded from a single warehouse to three regional hubs, the existing Odoo setup suddenly became a liability. Every new employee received a generic admin login, and sensitive data – from purchase orders to bank statements – was visible to anyone who opened the system. The finance director demanded an immediate lock‑down, but the IT admin was wary of breaking existing workflows. This case scenario walks through the problem, evaluates two common security approaches, and provides a practical recommendation that balances protection with operational agility.
Understanding the Core Problem
In Odoo, security is enforced at two levels:
- Access Rights (Groups): Define which models a user can create, read, write, or delete.
- Record Rules: Filter the rows a user sees within a model, based on domain expressions.
The distributor’s initial configuration relied only on the default groups (e.g., Sales / User: Own Documents Only, Purchase / Manager) and granted the “Settings / Administrator” role to all power users. This caused two issues:
- Over‑exposure of data – junior staff could view confidential vendor contracts.
- Process friction – managers often had to manually re‑assign records because the default “Own Documents Only” rule prevented them from seeing cross‑departmental transactions.
Approach 1 – Relying Solely on Role‑Based Access (Groups)
How It Works
Odoo’s group mechanism lives under Settings → Users & Companies → Users. For each user you can assign one or more groups. The underlying fields are group_id (many2many) on the res.users model, and the permission matrix is stored in ir.model.access with fields perm_read, perm_write, perm_create, and perm_unlink.
Pros
- Simple to administer – Adding a user to the “Purchase / Manager” group instantly grants full rights on
purchase.order. - Clear audit trail – Group changes are logged in the “User Log” view.
- Low performance impact – No additional domain evaluation at runtime.
Cons
- Coarse granularity – All records of a model are treated equally. You cannot hide a specific purchase order from a manager while still allowing them to edit others.
- Maintenance overhead – As the organization adds new modules (e.g., Manufacturing, Project), the admin must constantly create custom groups to match every nuance.
- Risk of privilege creep – Users often accumulate groups over time, leading to unintended access.
Approach 2 – Adding Record Rules on Top of Groups
How It Works
Record rules are defined under Settings → Technical → Security → Record Rules. Each rule references a model (model_id) and a domain expression that filters rows. For example, a rule that limits a sales rep to his own opportunities uses the domain [('user_id','=',uid)]. The fields global and perm_read control whether the rule applies to all users and which operations it affects.
Pros
- Fine‑grained control – You can hide a specific vendor’s invoices from a regional manager while still exposing the rest of the finance data.
- Scalable across modules – One rule can protect all models that share a common field (e.g.,
company_id). - Reduced group proliferation – Fewer custom groups are needed because the same group can rely on different rules to see appropriate records.
Cons
- Complexity – Writing correct domain expressions requires familiarity with Odoo’s ORM syntax.
- Performance considerations – Each rule adds a domain filter to every query, which can affect response time on large tables.
- Potential for rule conflicts – Multiple rules on the same model are combined with an OR, which can unintentionally broaden access if not carefully designed.
Choosing the Right Mix for a Growing Business
In practice, the most robust security model blends both approaches: use groups to define *what* a user can do (CRUD permissions) and record rules to define *which* records they can see. The distributor’s IT team followed this hybrid path, guided by three practical steps.
Step 1 – Consolidate Groups First
Audit the existing groups:
- Navigate to Settings → Users & Companies → Users and export the
group_idcolumn for a quick overview. - Remove obsolete groups such as “Purchase / User” that were never used.
- Create three core groups that reflect the organization’s hierarchy:
- Operations Manager – Full rights on Inventory, Sales, and Purchase.
- Finance Officer – Full rights on Accounting, limited rights on Sales.
- Regional Sales Rep – Read‑only on Accounting, full rights on own sales orders.
Step 2 – Implement Targeted Record Rules
For each core group, add a rule that respects the company and user context. Sample rule for “Regional Sales Rep” on sale.order:
<record id="sale_order_rule_sales_rep" model="ir.rule">
<field name="name">Sales Rep – Own Orders Only</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="domain_force">[('user_id','=',uid)]</field>
<field name="groups" eval="[(4, ref('custom_group_sales_rep'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_unlink" eval="False"/>
</record>
Similar rules are added for the Finance Officer, limiting visibility to the company_id of the user, ensuring that a regional office cannot see the headquarters’ bank statements.
Step 3 – Test in a Staging Environment
Before rolling out to production, the team cloned the live database to a staging server. Because the Odoo instance runs on a virtual machine, they opted for a reliable Cloud VPS that mirrors the production specs (2 vCPU, 4 GB RAM, 50 GB SSD). This allowed them to validate security rules without risking live data. The staging environment also gave them the chance to benchmark query performance after adding the new record rules.
Final Recommendations
- Start with a clean group structure. Too many groups lead to privilege creep. Align groups with business functions rather than individual users.
- Use record rules sparingly and deliberately. Write one rule per business need, test the resulting domain, and document the purpose in the rule’s description field.
- Leverage the
company_idfield for multi‑company segregation. Odoo already filters most models by company when the “Multi‑Company” setting is enabled. - Maintain a change log. Record every addition or modification to
ir.model.accessandir.rulein a version‑controlled module (e.g.,custom_security) so that future updates can be audited. - Periodically review access. Quarterly audits using the “Users” list view, filtered by “Groups”, help spot users who have accumulated unnecessary permissions.
Conclusion
For a growing SME, the temptation to grant broad admin rights to keep operations humming is understandable, but it creates a hidden security risk that can quickly become costly. By first consolidating role‑based groups and then layering precise record rules, the distributor achieved a “least‑privilege” posture without sacrificing the speed of daily transactions. The hybrid approach scales as new modules are added, and the disciplined audit process keeps the security model transparent. Most importantly, the transition was executed on a modest Cloud VPS, demonstrating that robust Odoo security does not require heavyweight infrastructure – just a clear strategy and the right combination of Odoo’s native tools.