أمان الوصول في أودو — ir.model.access.csv وقواعد السجلات (أودو 16–19)
مرجع شامل للمطوّر حول نظام الأمان ثنائي الطبقة في أودو: صلاحيات CRUD على مستوى النموذج عبر ir.model.access.csv والتصفية على مستوى السجل عبر ir.rule. موثق لأودو 18 و19.
يُطبّق أودو التحكم في الوصول على طبقتين مستقلتين. الطبقة الأولى — الوصول على مستوى النموذج — تحدد مجموعات المستخدمين القادرين على تنفيذ عمليات الإنشاء والقراءة والكتابة والحذف (CRUD) على نموذج ما ككل. الطبقة الثانية — قواعد السجلات — تُقيّد بشكل أكبر السجلات المحددة التي يمكن للمستخدم رؤيتها أو تعديلها ضمن النموذج. يجب تهيئة كلتا الطبقتين بشكل صحيح لوحدة آمنة. يُعدّ غياب التحكم في الوصول على نموذج مخصص أحد أكثر أسباب استثناءات `AccessError` وتسرب البيانات غير المقصود شيوعًا.
طبقتا الأمان
| الطبقة | الآلية | الملف / النموذج | التحكم في | التقييم |
|---|---|---|---|---|
| 1 — الوصول إلى النموذج | ir.model.access (ACL) | security/ir.model.access.csv | هل يمكن للمجموعة X تنفيذ CRUD على النموذج Y أصلًا؟ | يُفحص أولًا. إذا رُفض هنا، لا تُقيَّم قواعد السجلات أبدًا. |
| 2 — الوصول إلى السجل | ir.rule (قواعد السجلات) | security/*.xml (سجلات ir.rule) | أي سجلات محددة يمكن للمجموعة X قراءتها/كتابتها/إنشاؤها/حذفها؟ | يُفحص ثانيًا. يُطبَّق مرشح النطاق على الاستعلام. |
الجزء 1 — الوصول إلى النموذج: ir.model.access.csv
يجب أن يحتوي كل نموذج مخصص على سجل `ir.model.access` واحد على الأقل، وإلا يُرفض كل وصول إليه افتراضيًا. النهج القياسي هو تعريف هذه السجلات في ملف CSV في `security/ir.model.access.csv` والإعلان عنه في `__manifest__.py` تحت مفتاح `data`.
ir.model.access.csv — مرجع الأعمدة
| العمود | مطلوب | الصيغة / مثال | ملاحظات |
|---|---|---|---|
| id | نعم | access_my_model_user | معرّف خارجي لصف ACL هذا. يجب أن يكون فريدًا في الوحدة. الاتفاقية: access_<model>_<group_suffix>. |
| name | نعم | access_my_model_user | تسمية قابلة للقراءة من قِبل الإنسان. يمكن أن تطابق id. تظهر في واجهة حقوق الوصول. |
| model_id:id | نعم | model_my_module_my_model | المعرّف الخارجي للنموذج. الصيغة: model_ + اسم النموذج مع استبدال النقاط بشرطات سفلية. مثلًا: sale.order → model_sale_order. |
| group_id:id | لا | base.group_user | المعرّف الخارجي للمجموعة. اتركه فارغًا لمنح الوصول لجميع المستخدمين (بدون قيود مجموعة). القيم الشائعة: base.group_user (المستخدمون الداخليون)، base.group_system (المسؤول). |
| perm_read | نعم | 0 أو 1 | 1 = السماح بالقراءة. مطلوب لأي عرض قائمة أو نموذج للفتح. |
| perm_write | نعم | 0 أو 1 | 1 = السماح بالكتابة (تحرير السجلات الموجودة). |
| perm_create | نعم | 0 أو 1 | 1 = السماح بالإنشاء (سجلات جديدة). يمكن للمستخدم الذي لديه صلاحية الكتابة دون الإنشاء التحرير لكن ليس إضافة السجلات. |
| perm_unlink | نعم | 0 أو 1 | 1 = السماح بالحذف. اضبطه على 0 للمستخدمين العاديين؛ احتفظ به للمديرين. |
ir.model.access.csv — مثال كامل
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
# Internal users — read + write + create, no delete
access_project_task_user,access_project_task_user,model_project_task,base.group_user,1,1,1,0
# Managers — full CRUD
access_project_task_manager,access_project_task_manager,model_project_task,project.group_project_manager,1,1,1,1
# Portal users — read only
access_project_task_portal,access_project_task_portal,model_project_task,base.group_portal,1,0,0,0تعريف ملفات الأمان في __manifest__.py
{
'name': 'My Module',
'depends': ['base'],
'data': [
# Security files must come BEFORE views in the data list
# so that groups are defined before views reference them.
'security/groups.xml', # custom group definitions
'security/ir.model.access.csv', # model-level ACL
'security/record_rules.xml', # record rules (ir.rule)
# Views and other data come after:
'views/my_model_views.xml',
],
}الجزء 2 — المجموعات (res.groups)
المجموعات هي الحلقة الرابطة بين المستخدمين وحقوق الوصول. ينتمي المستخدم إلى مجموعة واحدة أو أكثر؛ وصفوف ACL وقواعد السجلات مرتبطة بمجموعات. يمكنك تعريف مجموعات مخصصة لوحدتك أو إعادة استخدام مجموعات أودو المضمّنة. يمكن للمجموعات أن ترث من مجموعات أخرى — يحصل المستخدم في مجموعة فرعية تلقائيًا على جميع صلاحيات وصول المجموعة الأم.
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Define a module-specific group category -->
<record id="module_category_my_module" model="ir.module.category">
<field name="name">My Module</field>
<field name="sequence">100</field>
</record>
<!-- User group — basic access -->
<record id="group_my_module_user" model="res.groups">
<field name="name">User</field>
<field name="category_id" ref="module_category_my_module"/>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
<!-- Manager group — inherits user group, adds extra rights -->
<record id="group_my_module_manager" model="res.groups">
<field name="name">Manager</field>
<field name="category_id" ref="module_category_my_module"/>
<field name="implied_ids" eval="[(4, ref('group_my_module_user'))]"/>
<field name="users" eval="[(4, ref('base.user_admin'))]"/>
</record>
</odoo>الجزء 3 — قواعد السجلات (ir.rule)
تضيف قواعد السجلات مرشح نطاق فوق ACL على مستوى النموذج. حتى لو كانت للمجموعة صلاحية القراءة على نموذج، يمكن لقاعدة سجل تقييدها لرؤية السجلات المطابقة لنطاق فقط — مثلًا السجلات التابعة لشركتهم الخاصة أو التي أنشأوها بأنفسهم. تأتي قواعد السجلات بنوعين: القواعد العالمية والقواعد الخاصة بالمجموعات.
| نوع القاعدة | حقل groups | المنطق مع قواعد أخرى | حالة الاستخدام |
|---|---|---|---|
| قاعدة عالمية | فارغ (بدون مجموعات) | AND — تُطبَّق دائمًا، تُدمج مع جميع القواعد الأخرى باستخدام AND | فرض قيد على جميع المستخدمين — مثلًا عزل الشركة في متعدد الشركات |
| قاعدة خاصة بمجموعة | مجموعة واحدة أو أكثر | OR — بين القواعد لنفس المجموعة؛ AND مع القواعد العالمية | منح وصول إضافي لمجموعة محددة — مثلًا المديرون يرون جميع السجلات |
مرجع حقل XML لـ ir.rule
| الحقل | النوع | مطلوب | الوصف |
|---|---|---|---|
| name | Char | نعم | تسمية قابلة للقراءة تظهر في واجهة قواعد السجلات. |
| model_id | Many2one → ir.model | نعم | النموذج الذي تنطبق عليه هذه القاعدة. الإشارة إليه بـ ref='<module>.<model_external_id>'. |
| domain_force | Char (سلسلة نطاق) | نعم | مرشح النطاق. السجلات المطابقة لهذا النطاق يمكن الوصول إليها؛ الأخرى غير مرئية للمجموعة المتأثرة. |
| groups | Many2many → res.groups | لا | المجموعات التي تنطبق عليها هذه القاعدة. اتركه فارغًا للقاعدة العالمية. |
| perm_read | Boolean | لا (افتراضي True) | تطبيق هذه القاعدة على عمليات القراءة. |
| perm_write | Boolean | لا (افتراضي True) | تطبيق هذه القاعدة على عمليات الكتابة. |
| perm_create | Boolean | لا (افتراضي True) | تطبيق هذه القاعدة على عمليات الإنشاء. |
| perm_unlink | Boolean | لا (افتراضي True) | تطبيق هذه القاعدة على عمليات الحذف. |
أنماط قواعد السجلات الشائعة
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Pattern 1: Own-records rule
Users can only read/write records they created.
Global rule — applies to all users. -->
<record id="rule_my_model_own_records" model="ir.rule">
<field name="name">My Model: Own Records Only</field>
<field name="model_id" ref="model_my_module_my_model"/>
<field name="domain_force">[('create_uid', '=', user.id)]</field>
<!-- No groups → global rule, applies to everyone -->
</record>
<!-- Pattern 2: Manager bypass
Managers can see ALL records (overrides the own-records rule above
for members of the manager group via OR logic). -->
<record id="rule_my_model_manager_all" model="ir.rule">
<field name="name">My Model: Manager Sees All</field>
<field name="model_id" ref="model_my_module_my_model"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('my_module.group_my_module_manager'))]"/>
</record>
<!-- Pattern 3: Multi-company isolation
Global rule — users can only see records of their own company.
Combining allowed_company_ids ensures multi-company switching works. -->
<record id="rule_my_model_company" model="ir.rule">
<field name="name">My Model: Company Isolation</field>
<field name="model_id" ref="model_my_module_my_model"/>
<field name="domain_force">
['|',
('company_id', '=', False),
('company_id', 'in', company_ids)]
</field>
<!-- Global rule — no groups field -->
</record>
</odoo>استكشاف الأخطاء
| الخطأ / الأعراض | السبب المحتمل | الحل |
|---|---|---|
| AccessError: ليس لديك الحق في الوصول إلى كائنات من نوع <model> | لا يوجد صف ir.model.access لهذا النموذج ومجموعة المستخدم | أضف صفًا إلى ir.model.access.csv للنموذج والمجموعة الصحيحة، ثم قم بترقية الوحدة. |
| قائمة السجلات فارغة أو لا يمكن فتح سجل معين — لا يظهر خطأ | نطاق domain_force لقاعدة سجل يُصفّي السجلات التي يتوقع المستخدم رؤيتها | في وضع المطوّر انتقل إلى الإعدادات → التقني → الأمان → قواعد السجلات وتحقق من القواعد على النموذج. اختبر النطاق في صدفة Python: self.env['my.model'].search([]). |
| External ID not found: <module>.group_xyz أثناء تثبيت الوحدة | ملف groups.xml مدرج بعد ir.model.access.csv في قائمة data في __manifest__.py | ضع groups.xml قبل ir.model.access.csv في قائمة data — يجب تحميل المجموعات قبل صفوف ACL التي تستند إليها. |
| المستخدم المسؤول يتجاوز قواعد السجلات — هذا متوقع وليس خطأ | يتجاوز المستخدم الفائق في أودو (المسؤول، uid=1) جميع قواعد السجلات حسب التصميم | لاختبار قواعد السجلات، استخدم مستخدمًا غير مسؤول. في اختبارات الوحدة، استخدم self.env['my.model'].with_user(regular_user).search([]) لمحاكاة السياق غير المسؤول. |
| نطاق [(1, '=', 1)] في قاعدة المدير لا يزال يحجب بعض السجلات | قاعدة عالمية تُطبّق منطق AND فوق قاعدة OR للمدير، مما يُضيّق النتائج | راجع جميع القواعد العالمية (بدون مجموعات مضبوطة) على النموذج — هذه تُطبّق AND دائمًا. إذا كانت القاعدة العالمية مقيّدة جدًا، حدّد نطاقها لمجموعات غير المديرين أو أزلها من النطاق الفعّال للمدير. |
ملاحظات الإصدار
| إصدار أودو | التغييرات الرئيسية المؤثرة على أمان الوصول |
|---|---|
| أودو 16 | لا توجد تغييرات كاسرة على ملف CSV لـ ACL أو ir.rule. المتغير company_ids متاح في domain_force لقواعد متعددة الشركات. إعادة تسمية `<list>` لا تؤثر على الأمان. |
| أودو 17 | لا توجد تغييرات كاسرة لواجهة API لـ ACL أو قواعد السجلات. يُشدّد أودو 17 التحقق من سلاسل domain_force — النطاقات غير الصحيحة التي كانت تفشل بصمت سابقًا قد تُثير الآن أخطاءً. |
| أودو 18 | لا توجد تغييرات كاسرة. صيغة ir.model.access.csv لم تتغير. واجهة API لقواعد السجلات لم تتغير. إضافة مجموعات base جديدة لتدفقات الموقع/البوابة — تحقق من وراثة المجموعات عند ترقية الوحدات بقواعد البوابة. |
| أودو 19 | لا توجد تغييرات كاسرة لـ ACL أو قواعد السجلات. تحسينات أداء ORM تُقلل من عبء تقييم domain_force على الجداول الكبيرة. |
تحتاج تدقيقًا أمنيًا أو تحكمًا مخصصًا في الوصول لوحدات أودو؟
يُصمّم مطورونا المعتمدون من أودو ويُدقّقون التحكم في الوصول للشركات السعودية — بما في ذلك البيانات المالية الحساسة لـ ZATCA وسجلات الموارد البشرية المتوافقة مع PDPL وعزل الشركات المتعددة عبر هياكل المجموعات.
الأسئلة الشائعة
ماذا يحدث إذا قمت بتثبيت وحدة بدون ملف ir.model.access.csv؟
هل يمكنني منح الوصول لجميع المستخدمين دون تعيين مجموعة محددة؟
ما الفرق بين perm_write وperm_create في ACL؟
كيف تتفاعل قواعد السجلات العالمية مع القواعد الخاصة بالمجموعات؟
كيف أكتب قاعدة سجل تسمح للمستخدمين برؤية سجلات شركتهم فقط في إعداد متعدد الشركات؟
هل يمكن لقواعد السجلات تقييد الحقول التي يراها المستخدم، وليس فقط السجلات؟

iWesabe Editorial Team
رؤى عملية حول Odoo ERP وامتثال ZATCA والعمليات الرقمية للشركات السعودية — بقلم فرق الاستشارات والمالية والهندسة في iWesabe.
مقالات ذات صلة
السياق والنطاق في أودو — مرجع المطوّر (أودو 16–19)
دليل عملي لنطاقات أودو (تصفية السجلات) والسياق (تمرير الحالة كأزواج مفتاح-قيمة) — كيف تعمل وأين تظهر وقواعد الصيغة من أودو 16 إلى أودو 19. موثق لأودو 18 و19.
كيفية إضافة الألوان إلى صفوف وخلايا عرض القائمة في أودو
استخدم سمات decoration-* لترميز الصفوف والخلايا بالألوان في عروض قائمة أودو بناءً على قيم الحقول — مع صيغة التعبيرات وجميع أنواع الزخرفة وملاحظة الترحيل لإعادة التسمية من tree إلى list. موثق لأودو 18 و19.
إهمال attrs في أودو: الانتقال إلى invisible وreadonly وrequired في أودو 16–19
تم إهمال قاموس attrs في أودو 16 وحذفه في أودو 17. يحدد هذا الدليل كل نمط attrs ومقابله من السمات المباشرة — مع كود قبل/بعد لحالات الاستخدام الأكثر شيوعًا. موثق لأودو 18 و19.