DEV Community

Cover image for Odoo security concept
Antonov Mike
Antonov Mike

Posted on

Odoo security concept

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
Enter fullscreen mode Exit fullscreen mode

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'
    ],
    ...
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

"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>
Enter fullscreen mode Exit fullscreen mode

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.

Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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': [],
}
Enter fullscreen mode Exit fullscreen mode

File custom/estate/__init__.py

from . import models
Enter fullscreen mode Exit fullscreen mode

File custom/estate/models/__init__.py

from . import estate_test
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
jeevanizm profile image
Jeevachaithanyan Sivanandan

thanks for the explanation

Collapse
 
antonov_mike profile image
Antonov Mike

I'm glad you liked it. I can see that you have a lot more experience than I do, so that's even surprising