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.
Odoo security concept
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.
Sources:
General Info
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_", where "" 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 mudul ("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 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 groups "estate.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 | 1 | 0 | 0 | 0 | |
access_estate_property_manager | access_estate_property_manager | model_estate_property | 1 | 1 | 1 | 1 |
- In the "estate/manifest.py" file, add the paths to the files "estate/security/security.xml" and "estate/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="test", required=True)
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',
],
# 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 (2)
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