Advanced B: ACL and Record Rules
So far we have mostly concerned ourselves with implementing useful features. However in most business scenarios security quickly becomes a concern: currently,
Any employee (which is what
group_userstands for) can create, read, update or delete properties, property types, or property tags.
estate_accountis installed then only agents allowed to interact with invoicing can confirm sales as that’s necessary to create an invoice.
We do not want third parties to be able to access properties directly.
Not all our employees may be real-estate agents (e.g. administrative personnel, property managers, …), we don’t want non-agents to see the available properties.
Real-estate agents don’t need or get to decide what property types or tags are available.
Real-estate agents can have exclusive properties, we do not want one agent to be able to manage another’s exclusives.
All real-estate agents should be able to confirm the sale of a property they can manage, but we do not want them to be able to validate or mark as paid any invoice in the system.
It would not be practical to attach individual security rules to employees any time we need a change so groups link security rules and users. They correspond to roles that can be assigned to employees.
For most Odoo applications 1 a good baseline is to have user and manager (or administrator) roles: the manager can change the configuration of the application and oversee the entirety of its use while the user can well, use the application 2.
This baseline seems sufficient for us:
Real estate managers can configure the system (manage available types and tags) as well as oversee every property in the pipeline.
Real estate agents can manage the properties under their care, or properties which are not specifically under the care of any agent.
In keeping with Odoo’s data-driven nature, a group is no more than a record of the
res.groups model. They are normally part of a module’s master data, defined in one of the module’s data files.
As simple example can be found here.
Access rights were first introduced in Chapter 5: Security - A Brief Introduction.
Access rights are a way to give users access to models via groups: associate an access right to a group, then all users with that group will have the access.
For instance we don’t want real-estate agents to be able to modify what property types are available, so we would not link that access to the “user” group.
Access rights can only give access, they can’t remove it: when access is checked, the system looks to see if any access right associated with the user (via any group) grants that access.
A user with the groups A and C will be able to do anything but delete the object while one with B and C will be able to read or update, but not search or read.
Since the “demo” user was not made a real-estate agent or manager, they should not even be able to see the real-estate application. Use a private tab or window to check for this (the “demo” user has the password “demo”).
Access rights can grant access to an entire model but often we need to be more specific: while an agent can interact with properties in general we may not want them to update or even see properties managed by one of their colleagues.
Access rules provide that precision: they can grant or reject access to individual records:
The Search domains is how access is managed: if the record passes then access is granted, otherwise access is rejected.
The rule above:
Only applies to the “create”, “update” (write) and “delete” (unlink) operations: here we want every employee to be able to see other users’ records but only the author / assignee can update a record.
Is non-global so we can provide an additional rule for e.g. managers.
Allows the operation if the current user (
user.id) is set (e.g. created, or is assigned) on the record, or if the record has no associated user at all.
If you try to mark a property as “sold” as the real estate agent, you should get an access error:
This happens because
estate_account tries to create an invoice during the process, but creating an invoice requires the right to all invoice management.
We want agents to be able to confirm a sale without them having full invoicing access, which means we need to bypass the normal security checks of Odoo in order to create an invoice despite the current user not having the right to do so.
There are two main ways to bypass existing security checks in Odoo, either wilfully or as a side-effect:
sudo()method will create a new recordset in “sudo mode”, this ignores all access rules and access rights (although hard-coded group and user checks may still apply).
Performing raw SQL queries will bypass access rules and access rights as a side-effect of bypassing the ORM itself.
Programmatically checking security
In Odoo, access rights and access rules are only checked when performing data access via the ORM e.g. creating, reading, searching, writing, or unlinking a record via ORM methods. Other methods do not necessarily check against any sort of access rights.
In the previous section, we bypassed the access rules when creating the invoice in
action_sold. This bypass can be reached by any user without any access right being checked:
Add a print to
estate_accountbefore the creation of the invoice (as creating the invoice accesses the property, therefore triggers an ACL check) e.g.:
estate_account, giving it the name of your database, and the name of your version of
action_sold(unless you named it
action_soldthen it’s fine)
You should see
reached in your Odoo log, followed by an access error.
Currently the accesses are implicitly checked by accessing data on
self as well as calling
super() (which does the same and updates
self), triggering access errors and cancelling the transaction “uncreating” our invoice.
However if this changes in the future, or we add side-effects to the method (e.g. reporting the sale to a government agency), or bugs are introduced in
estate, … it would be possible for non-agents to trigger operations they should not have access to.
Therefore when performing non-CRUD operations, or legitimately bypassing the ORM or security, or when triggering other side-effects, it is extremely important to perform explicit security checks.
Explicit security checks can be performed by:
Checking who the current user is (
self.env.user) and match them against specific models or records.
Checking that the current user has specific groups hard-coded to allow or deny an operation (
check_access_rights(operation)method on a recordset, this verifies whether the current user has access to the model itself.
check_access_rule(operations)on a non-empty recordset, this verifies that the current user is allowed to perform the operation on every record of the set.
For one reason or another we might need to manage our real-estate business as multiple companies e.g. we might have largely autonomous agencies, a franchise setup, or multiple brands (possibly from having acquired other real-estate businesses) which remain legally or financially separate from one another.
Odoo can be used to manage multiple companies inside the same system, however the actual handling is up to individual modules: Odoo itself provides the tools to manage the issue of company-dependent fields and multi-company rules, which is what we’re going to concern ourselves with.
We want different agencies to be “siloed” from one another, with properties belonging to a given agency and users (whether agents or managers) only able to see properties linked to their agency.
As before, because this is based on non-trivial records it’s easier for a user to relax rules than to tighten them so it makes sense to default to a relatively stronger security model.
Multi-company rules are simply access rules based on the
company_idsis all the companies to which the current user has access
company_idis the currently active company (the one the user is currently working in / for).
Multi-company rules will usually use the former i.e. check if the record is associated with one of the companies the user has access to:
Visibility != security
Specific Odoo models can be associated directly with groups (or companies, or users). It is important to figure out whether this association is a security or a visibility feature before using it:
Visibility features mean a user can still access the model or record otherwise, either through another part of the interface or by performing operations remotely using RPC, things might just not be visible in the web interface in some contexts.
Security features mean a user can not access records, fields or operations.
Here are some examples:
Groups on model fields (in Python) are a security feature, users outside the group will not be able to retrieve the field, or even know it exists.
Example: in server actions, only system users can see or update Python code.
Groups on view elements (in XML) are a visibility feature, users outside the group will not be able to see the element or its content in the form but they will otherwise be able to interact with the object (including that field).
Groups on menus and actions are visibility features, the menu or action will not be shown in the interface but that doesn’t prevent directly interacting with the underlying object.
Despite not having access to the Property Types and Property Tags menus anymore, agents can still access the underlying objects since they can still select tags or a type to set on their properties.
An Odoo Application is a group of related modules covering a business area or field, usually composed of a base module and a number of expansions on that base to add optional or specific features, or link to other business areas.
For applications which would be used by most or every employees, the “application user” role might be done away with and its abilities granted to all employees directly e.g. generally all employees can submit expenses or take time off.