/*Disclaimer
I haven't written tutorials for a few years, and
recently a fellow friend advised me to learn Odoo
and look for a job in this field, so I decided to
practice and rewrite Odoo security concept in a
different style. I'm sure I've lost the skill to
do this kind of work and might have missed some
details. In any case, reasoned criticism in an
acceptable form is welcome.*/
Sources:
General Info
The security concept in Odoo is based on the mechanism of user groups and access rights. Access rights define which users can view, create, modify or delete records in the database. User groups unite users with common access rights. A user can belong to multiple groups, and each group can have its own access rights.
Access Rights
These are access rights that define what operations (create, read, update, delete) a user can perform on data models. Access Rights are applied at the model level, i.e. to all records in the model. Access Rights are defined in the file ir.model.access.access.csv
, which must be located in the security folder of the module. The file ir.model.access.csv
contains the following fields:
- id – unique identifier of access right
- name – access right description
- model_id – model to which the right of access applies
- group_id – user group to which the right of access is granted
- perm_read – read permission (1 or 0)
- perm_write – update permission (1 or 0)
- perm_create – create permission (1 or 0)
- perm_unlink – delete permission (1 or 0)
For example, to give the "Library / User" user group the right to read and create books, but not to update and delete them, you can add the following line to the file
ir.model.access.csv
:
id | name | model_id:id | group_id:id | perm_read | perm_write | perm_create | perm_unlink |
---|---|---|---|---|---|---|---|
access_library_book_user | library.book.user | model_library_book | library.group_library_user | 1 | 0 | 1 | 0 |
When No Access Rights Are Specified
If we run the program without defined access rights for a module, we will see a warning in the terminal, for example, the following:
2024-02-02 11:48:53,572 2533261 WARNING rd-mydb odoo.modules.loading:
The models ['estate_property'] have no access rules in module estate,
consider adding some, like:
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
estate.access_estate_property,access_estate_property,estate.model_estate_property,base.group_user,1,0,0,0
To add access rights for a module, you must create a file named ir.model.access.csv
in the security folder of the current module, for example estate/security/ir.model.access.csv
. In this file, you can define different access rights for different user groups. For example, you can define that the user group "managers" has the right to read, write and delete records, while the user group "users" has only the right to read.
Access rights without groups
Creating access rights
You can define access rights in the file security/ir.model.access.csv
using the following format:
id | name | model_id:id | group_id:id | perm_read | perm_write | perm_create | perm_unlink |
---|---|---|---|---|---|---|---|
access_estate_property_user | access_estate_property_user | model_estate_property | 1 | 0 | 0 | 0 | |
access_estate_property_manager | access_estate_property_manager | model_estate_property | 1 | 1 | 1 | 1 |
If there is an entry in the group_id
field, for example group_estate_manager
or estate.group_estate_manager
, then the rules for it are defined in the security/security.xml
file. But for now we'll leave the group_id
field empty, so that the rule will apply to all users.
In this example we define two types of access: one for regular users (access_estate_property_user
), which is read-only (perm_read=1
), and one for managers (access_estate_property_manager
), which has all permissions.
Once this file is created, you need to make sure it is included in the manifest of the current module. Add the file path to the 'data' section of the manifest.
File estate/__manifest__.py
:
{
...
'data': [
'security/ir.model.access.csv'
],
...
}
Installing or updating a module
Odoo will automatically apply these access rights to the appropriate data models. For example, you can use the command to update -u estate
:
./odoo-bin --addons-path="addons, custom" -d rd-mydb -u estate
If you have correctly created access rights for your module, the warning will no longer appear in the terminal.
Csv fields, identifiers and references
Fields of the csv file
The "id" and "name" fields in the ir.model.access.csv
file are required and they must be unique. They are used to identify the access rule.
"id"
This is a unique identifier for the access rule. You can choose any value, but it must be unique in the context of the entire ir.model.access.csv
file. In our case, access_estate_property_user
and access_estate_property_manager
are unique identifiers for two different access rules.
"name"
This is the name for the access rule. It is usually the same as "id", but can be different if you want to use a name that describes the rule in more detail. In our case, access_estate_property_user
and access_estate_property_manager
are also names for two different access rules.
"model_id:id"
Refers to the data model to which the access rule applies. In our case, model_estate_property
is a reference to the data model that was defined in the "TestModel" class using the _name
attribute.
File estate/models/estate_test.py
:
class TestModel(models.Model):
_name = "estate_property"
"group_id:id"
Refers to the user group to which the access rule applies. If this field is left empty, the rule applies to all users. This may be convenient for testing, but in real situations it is necessary to restrict access to the model for certain groups of users. For this purpose you can use existing groups from the base module, for example, base.group_user
or base.group_system
, or you can create your own groups in the file security.xml
.
Identifiers
In Odoo, model_id:id
and group_id:id
are used to refer to external model and group identifiers. The :
is used to separate the name of the external identifier (model_id
or group_id
) from the identifier itself.
The usual way of naming a model in Odoo is model_<model_name>
, where <model_name>
is the _name
of the model, where .
is replaced by _
. For example, sale.order
becomes model_sale_order
.
Similarly, group_id:id
refers to the group that will apply the access right. The value of id
here is the group's external identifier, which was created automatically when the group was created. This approach allows you to reference a model or group without knowing their internal identifiers in the database.
Reference to a group
The reference can be either to a group within the "estate" module (group_estate_manager
), or to the same group but from another module (estate.group_estate_manager
). The format group_estate_manager
without the module prefix is usually used within a module where it refers to a group defined in the current module. In the latter case, estate
is the name of the module and group_estate_manager
is the group identifier within that module.
Access rights for groups
Creating a "group_id"
Instead of using existing groups from the base module, such as base.group_user
or base.group_system
, you can create your own groups:
- In
estate/security/security.xml
create two user groups using the<record>
tag and specify their IDs, names, categories, and parent groups (if any):
<odoo>
<record id="group_estate_manager" model="res.groups">
<field name="name">Estate Manager</field>
<field name="category_id" ref="base.module_category_hidden"/>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
<record id="group_estate_user" model="res.groups">
<field name="name">Estate User</field>
<field name="category_id" ref="base.module_category_hidden"/>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
</odoo>
The parent groups for each of the created groups group_estate_manager
and group_estate_user
are specified here. This is done using the implied_ids
field. For each group, it is specified that they include the base.group_user
group, which is the default user group in Odoo. So any user who is in the group_estate_manager
or group_estate_user
group will automatically also be in the base.group_user
group.
- In the file
estate/security/ir.model.access.csv
set external identifiers for user groupsestate.group_estate_manager
иestate.group_estate_user
:
id | name | model_id:id | group_id:id | perm_read | perm_write | perm_create | perm_unlink |
---|---|---|---|---|---|---|---|
access_estate_property_user | access_estate_property_user | model_estate_property | group_estate_user | 1 | 0 | 0 | 0 |
access_estate_property_manager | access_estate_property_manager | model_estate_property | group_estate_manager | 1 | 1 | 1 | 1 |
- In the
estate/manifest.py
file, add the paths to the filesestate/security/security.xml
andestate/security/ir.model.access.csv
to the 'data' list so that they will be loaded when the module is installed:
'data': [
'security/security.xml',
'security/ir.model.access.csv',
],
Then restart Odoo with a command like
./odoo-bin --addons-path="addons, custom" -d rd-mydb -u estate
The module is now running with updated access rights.
Example of demo module
File custom/estate/models/estate_test.py
from odoo import fields, models
class TestModel(models.Model):
_name = "estate_property"
_description = "Estate test module"
name = fields.Char(required=True)
description = fields.Text()
postcode = fields.Char()
date_availability = fields.Date()
expected_price = fields.Float(
required=True,
help="Some helpful information")
selling_price = fields.Float()
bedrooms = fields.Integer()
living_area = fields.Integer()
facades = fields.Integer()
garage = fields.Boolean()
garden = fields.Boolean()
garden_area = fields.Integer()
garden_orientation = fields.Selection(
string="Type",
required=False,
selection=[('south', 'South'),
('north', 'North'),
('east', 'East'),
('west', 'West')],
)
File custom/estate/__manifest__.py
{
'name': "estate",
'version': '1.0',
'license': 'LGPL-3',
'depends': ['base'],
'author': "Author Name",
'category': 'Estate',
'description': """
Description Text
""",
# data files always loaded at installation
'data': [
'security/security.xml',
'security/ir.model.access.csv',
'views/estate_view.xml',
'views/estate_menu.xml',
],
# data files containing optionally loaded demonstration data
'demo': [],
}
File custom/estate/__init__.py
from . import models
File custom/estate/models/__init__.py
from . import estate_test
File security/security.xml
<odoo>
<record id="group_estate_manager" model="res.groups">
<field name="name">Estate Manager</field>
<field name="category_id" ref="base.module_category_hidden"/>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
<record id="group_estate_user" model="res.groups">
<field name="name">Estate User</field>
<field name="category_id" ref="base.module_category_hidden"/>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
</odoo>
File security/ir.model.access.csv
id | name | model_id:id | group_id:id | perm_read | perm_write | perm_create | perm_unlink |
---|---|---|---|---|---|---|---|
access_estate_property_user | access_estate_property_user | model_estate_property | group_estate_user | 1 | 0 | 0 | 0 |
access_estate_property_manager | access_estate_property_manager | model_estate_property | group_estate_manager | 1 | 1 | 1 | 1 |
Top comments (4)
thanks for the explanation
I'm glad you liked it. I can see that you have a lot more experience than I do, so that's even surprising
no one is perfect, one can always learn from others
Learn or degrade 😅