In our in-depth look at the Odoo 19 security features, we discussed the role of user groups, the security of access, and record rules as the basis for managing data access on the system. There is, however, a much more fundamental area we must check out in more detail: the files themselves. Documents attached to communications, invoices, contracts, employee records, product photos, and other binary data files could very well represent your most protected pieces of business data, but are not managed in the same way as other system security records.
Odoo 19 maintains its file attachments either on the file system or in the database using records from the model `ir.attachment`, making them susceptible to breaches in case suitable security measures are not implemented. The following tutorial intends to describe different methods of securing file access using `ir.attachment`, field-level security, and best practices of storing files.
Why File Security Matters in Odoo
Odoo files are more than just pictures and PDF files because they contain extremely important business documents such as customer signatures, contracts related to employees and human resource management, financial proofs of delivery or sales or other business operations, internal business designs, or confidential business communications.
If there are security loopholes with respect to files in Odoo or any business organization,
- A sales representative might be able to access the HR resumes electronically appended to employee files
- External portal users were able to download internal financial invoices
- Warehouse personnel could regard confidential agreements between vendors
- “Competitors could gain access to confidential product information through compromised accounts.”
The attachments are treated as records of the `ir.attachment`model by Odoo, which implies that they inherit the different security layers discussed in our pillar blog page, including groups, record rules, and field constraints. The presence of files introduces an added layer of complexity, considering that they are related to other models, accessed using URL addresses, and can be reached from outside using portal access and public addresses.
Understanding Odoo's File Storage Architecture
Before moving ahead into security setups, an understanding regarding Odoo's internal storage of files is crucial.
So, every time you add a document to any record within Odoo, whether it is adding a PDF copy of an invoice to a vendor bill or adding a resume to the employee record, what happens is that the system automatically inserts a record into the model “ir.attachment.” This model is essentially a repository through which all documents are stored along with some essential information:
- The original file name and MIME type.
- What model and file the attachment is associated with (res_model, res_id)
- The user who uploaded it (create_uid)
- Created and updated timestamps
- Data from the actual file, or a path to the storage location
Through this centralized approach, security for files can be dealt with systematically, unlike the current system, where security for files is spread over various modules.
Understanding Odoo 19's New Security Group Structure
We should consider the fact that before implementing the security on files, there has been a major change made by the new version of Odoo, that is, version 19, regarding the structuring of user groups.
The New 3-Layer Hierarchy
Odoo 18 and earlier used a simple 2-layer structure:
Odoo 19 introduces a new 3-layer hierarchy:
- Category < Privilege < Group
This new intermediate layer called Privilege (res.groups.privilege) decouples the concept of an access level from the technical implementation. The privilege determines which "row" or selection widget the group appears in within the User form settings.
Why This Matters for File Security
During the creation of custom security groups, while managing files within Odoo 19, you are now supposed to:
- Define a Category: The main section heading
- Create a Privilege - this is the specific field/dropdown
- Create Groups linked to that privilege - the real access levels
This structure creates better organization in the User Access matrix and enforces tighter, more standardized security mechanisms.
Securing Files Through ir.attachment Access Rights
Like all other models within the Odoo framework, the attachment model, *ir.attachment*, utilizes group permissions, which you create using access control files. We are going to go through the entire process with the new layout offered in Odoo version 19.
Step 1: Create the Category, Privilege, and File Security Groups
Inside your custom module's security/ folder, create the complete hierarchy:
<!-- security/file_security.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<!-- Step 1: Define the Category -->
<record id="module_category_file_management" model="ir.module.category">
<field name="name">File Management</field>
<field name="description">Manage file and attachment security</field>
<field name="sequence">20</field>
</record>
<!-- Step 2: Define the Privilege (New in Odoo 19) -->
<record id="privilege_file_access" model="res.groups.privilege">
<field name="name">File Access Control</field>
<field name="category_id" ref="module_category_file_management"/>
</record>
<!-- Step 3: Define Groups linked to the Privilege -->
<record model="res.groups" id="group_file_viewer">
<field name="name">File Viewer</field>
<field name="privilege_id" ref="privilege_file_access"/>
<field name="comment">Can view and download files</field>
</record>
<record model="res.groups" id="group_file_manager">
<field name="name">File Manager</field>
<field name="privilege_id" ref="privilege_file_access"/>
<field name="implied_ids" eval="[(4, ref('group_file_viewer'))]"/>
<field name="comment">Can create, edit, and manage files</field>
</record>
</data>
</odoo>
Key changes from Odoo 18:
- Rather than using category_id on groups, we now use privilege_id
- Privileges are referred to by their `id` which is actually linked
- The privilege record then references the category through category_id
- This establishes the correct order of the 3-layer hierarchy as follows: Category > Privilege > Groups
Don't forget to include this file in your __manifest__.py file, in the 'data' dictionary.
Step 2: Define Access Rights for ir.attachment
Create or edit your security/ir.model.access.csv file:
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_attachment_file_viewer,access.attachment.file.viewer,base.model_ir_attachment,group_file_viewer,1,0,0,0
access_attachment_file_manager,access.attachment.file.manager,base.model_ir_attachment,group_file_manager,1,1,1,0
access_attachment_base_user,access.attachment.base.user,base.model_ir_attachment,base.group_user,1,0,0,0
It allows for three levels of access:
- File Viewer group: Read-only access to permitted files
- File Manager group: Read, write, create, but no delete permission
- Base users: Basic read access, which we'll further restrict with record rules
Step 3: Assign Users to Groups
Go to Settings > Users & Companies > Users, pick a user, and in the Access Rights tab you will now have:
- File Management section - your Category
- File Access Control dropdown field where you will select your Privilege
- File Viewer or File Manager Options are your Groups
With this new structure, the access level of each user is much clearer and also provides a nicer-looking organization in the user interface.
Record Rules: Governing the Viewability of Files
Record rules let administrators determine
Access rights are what determine the user level on the ir.attachment model, but record rules determine what files the user can view. It is here that file security comes into its own.
Example 1: Sales Representatives See Only Their Own Quotes' Attachments
Create security/file_record_rules.xml:
<record id="rule_attachment_sales_user" model="ir.rule">
<field name="name">Sales: Own Attachments Only</field>
<field name="model_id" ref="base.model_ir_attachment"/>
<field name="groups" eval="[(4, ref('sales_team.group_sale_salesman'))]"/>
<field name="domain_force">[('res_model', '=', 'sale.order'), ('create_uid', '=', user.id)]</field>
</record>
This rule allows the sales person to only see attachments related to their sales orders that they have authored. The <code>domain_force</code> helps to filter the attachments automatically every time the sales person attempts to view any attachment.
Example 2: HR Staff Access Employee Documents Exclusively
For more complex scenarios using our custom groups:
<record id="rule_attachment_file_manager_regional" model="ir.rule">
<field name="name">File Manager: Regional Documents</field>
<field name="model_id" ref="base.model_ir_attachment"/>
<field name="groups" eval="[(4, ref('group_file_manager'))]"/>
<field name="domain_force">[
'|',
('create_uid', '=', user.id),
('company_id', 'in', company_ids)
]</field>
</record>
This limits the users of the HR function to viewing only attachments that are associated with records of employees, thus blocking them from having access to sales contracts or financial documents.
Advanced Security with Multiple Privileges
In cases involving more complex file security requirements, you can define additional privileges of the same category:
<!-- security/file_security.xml -->
<odoo>
<data>
<!-- Category remains the same -->
<record id="module_category_file_management" model="ir.module.category">
<field name="name">File Management</field>
</record>
<!-- Privilege 1: General File Access -->
<record id="privilege_file_access" model="res.groups.privilege">
<field name="name">File Access</field>
<field name="category_id" ref="module_category_file_management"/>
</record>
<record model="res.groups" id="group_file_viewer">
<field name="name">Viewer</field>
<field name="privilege_id" ref="privilege_file_access"/>
</record>
<record model="res.groups" id="group_file_manager">
<field name="name">Manager</field>
<field name="privilege_id" ref="privilege_file_access"/>
</record>
<!-- Privilege 2: Confidential Documents -->
<record id="privilege_confidential_docs" model="res.groups.privilege">
<field name="name">Confidential Documents</field>
<field name="category_id" ref="module_category_file_management"/>
</record>
<record model="res.groups" id="group_confidential_viewer">
<field name="name">View Only</field>
<field name="privilege_id" ref="privilege_confidential_docs"/>
</record>
<record model="res.groups" id="group_confidential_admin">
<field name="name">Full Access</field>
<field name="privilege_id" ref="privilege_confidential_admin"/>
</record>
</data>
</odoo>
This results in two distinct privilege rows at the User Settings level:
- File Access: Viewer/Manager options
- Confidential Documents: with View Only/Full Access options
Subsequently, every privilege may be assigned various access rights and record rules for its groups.
Field-Level Security for File Metadata
Sometimes you may want to obfuscate not only the files themselves, but individual fields in the attachment record. This can come in handy when trying to protect the metadata of the files, which may expose certain information.
Using your own definitions, employ the groups attribute with your own groups:
<field name="datas" groups="your_module.group_file_manager"/>
<field name="db_datas" invisible="1"/>
<field name="mimetype" groups="base.group_no_one"/>
<field name="checksum" invisible="1" groups="base.group_system"/>
This configuration:
- Hides the raw data file content (datas) from everyone except the file managers.
- Makes all data stored inside a database invisible
- Hides MIME types from common users (only technical details will be visible to the user)
- Limits checksum view access to system administrators
You can also shield file fields of your custom models
<field name="contract_document"
groups="your_module.group_file_manager"
widget="binary"
filename="contract_filename"/>
Portal and Public File Access: Special Considerations
With portal users and public file sharing, new security risks arise because they don't fit your organizational user structure.
Controlling Portal Access
Portal users (usually customers or vendors) have limited privileges, except when it comes to attachments, which need special treatment:
<record id="rule_attachment_portal" model="ir.rule">
<field name="name">Portal: Own Order Attachments</field>
<field name="model_id" ref="base.model_ir_attachment"/>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
<field name="domain_force">[
'|',
('res_model', '=', 'sale.order'),
('res_model', '=', 'account.move'),
('create_uid', '=', user.id)
]</field>
</record>
This ensures portal users only see attachments on their own sales orders and invoices.
Managing Public File Links
Odoo supports the option of making attachments “public,” which allows viewing them via direct link without login. This feature might be helpful for web content, whereas for confidential files, it’s a bad practice.
Best Practices For Public Attachments:
- Default to private: Attachments should never be designated as public unless intended as such.
- Audit public files regularly: Verify which attachments to a case, if any, are set to public by using:
self.env['ir.attachment'].search([('public', '=', True)])Use access tokens: For temporary sharing outside the organization, token-based access should be provided instead of making files publicly accessibleRestrict by file type: Allow only certain types of files (for example, product photos) to be shared publiclyTest Scenario 1: Restricted User Access
- Log in as a user who does not have the File Manager group
- Go to Settings > Technical > Attachments
- Try to access any of the restricted attachments below-should get "Access Denied"
- Try uploading a file - should be blocked
- Add user to File Viewer group—should now see permitted files
Test Scenario 2: Group Hierarchy
- Create a test user and assign only File Viewer group
- Try to edit attachments—should be blocked
- Upgrade user to File Manager group
- Retry edit operations—should now succeed
- Verify File Manager inherits File Viewer permissions
Test Scenario 3: Portal User Isolation
- Create Test User and grant only File Viewer role
- Attempt to edit attachments—should be blocked
- User upgrade to File manager group
- Retry the edit actions—should succeed
- Check File Manager inherits File Viewer permissions
Test Scenario 4: Privilege Structure in UI
- Go to Settings > Users & Companies > Users
- Click a user in the list and click the Access Rights tab
- Ensure the File Management category is listed
- Verify the File Access Control privilege is presented as a dropdown
- Verify Viewer and Manager options display correctly
Scenario 1: HR Resume Protection
Requirement: Only HR managers should access employee resumes.
Implementation:
<!-- Create HR-specific privilege -->
<record id="privilege_hr_documents" model="res.groups.privilege">
<field name="name">HR Documents</field>
<field name="category_id" ref="hr.module_category_hr"/>
</record>
<record model="res.groups" id="group_hr_doc_admin">
<field name="name">Document Administrator</field>
<field name="privilege_id" ref="privilege_hr_documents"/>
</record>
<!-- Record rule -->
<record id="rule_attachment_hr_resumes" model="ir.rule">
<field name="name">HR: Resume Access</field>
<field name="model_id" ref="base.model_ir_attachment"/>
<field name="groups" eval="[(4, ref('group_hr_doc_admin'))]"/>
<field name="domain_force">[
('res_model', '=', 'hr.employee'),
('name', 'ilike', 'resume')
]</field>
</record>
Benefits of Proper File Security
You get the following when the security for files is correctly implemented using the enhanced structure available in Odoo 19:
Data Protection: Sensitive documents remain available to personnel with access rights; data leakage and unauthorized disclosure are avoided.
Compliance: With our solution, meet regulatory requirements such as GDPR, HIPAA, or SOX demanding high control over access to documents and the records of the audit trails.
Operational Efficiency: Users see only relevant files without clutter, enhancing productivity without compromising security.
Better Organization: The new hierarchy with 3 layers (Category > Privilege > Group) provides better clarity in user settings and better manageability.
Audit Trail: Keep track of who accessed, modified, or downloaded sensitive documents with Odoo's built-in logging.
Customer Trust: Show customers and partners that their confidential information is secure with enterprise-grade security.
Scalability: The privilege layer reduces hardships when trying to add new levels of access without an entire restructure of the security framework.
Data File Security in Odoo 19 enhances Odoo’s complete security system by expanding it beyond database records to secured binary files, which are often the carriers of your most confidential data. The addition of a new 3-level system of security (Category > Privilege > Group) in Odoo 19 increases the control over data files.
With the ability to utilize the ir.attachment access rights related to the res.groups.privilege model using record rules and proper storage, you obtain multiple layers of protection that combine with one another seamlessly.
The important part of this is that file security is a part of Odoo security and a perfectly natural addition from the point of view we explained in our pillar blog post, but improved by a better structure. The definition of privileges determines levels of access; groups are technical solutions for it, while rules of records determine access privileges for specific files.
Remember that each time you upload files to your Odoo platform is an occasion when trust is placed in your organization by your employees and your clients. With the secure framework provided by Odoo 19’s new privilege-based structure, you are guaranteed that trust is always well-placed. Simply remember the rules mentioned above and follow them.
To read more about An Overview of Data Files Core Operations in Odoo 18, refer to our blog An Overview of Data Files Core Operations in Odoo 18.