From ba5df07223897dc8369f35da56222ebce5c1cdce Mon Sep 17 00:00:00 2001 From: william-andre Date: Mon, 26 Jun 2023 12:13:06 +0000 Subject: [PATCH] [FIX] base: do not depend on `active_test` to evaluate `parent_of` Let's assume that * Company S is a sub company of it's parent company P * Company S has access to all the accounts and taxes of company P * Some taxes are archived, but used Because of the needed access rules, there will be a `parent_of` on the record rules of accounts and taxes. If we consider that we should consider the context key `active_test` to add a implicit `('active', '=', True)` clause in the domain when evaluating `parent_of` and `child_of` clauses, an access error will be raised instead of hiding the archived records, even when simply trying to read an archived record. The archive feature and the security rules should be independent; if a security rules needs to depend on the fact that a record is archived, it should be explicit in the domain and not rely on side effects of the implementation of `parent_of`/`child_of` Part-of: odoo/odoo#125642 --- odoo/addons/base/models/res_partner.py | 12 +----------- odoo/addons/test_http/tests/test_static.py | 4 ++-- odoo/models.py | 2 +- odoo/osv/expression.py | 2 ++ 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/odoo/addons/base/models/res_partner.py b/odoo/addons/base/models/res_partner.py index 14e79096c8c..993ffef8120 100644 --- a/odoo/addons/base/models/res_partner.py +++ b/odoo/addons/base/models/res_partner.py @@ -196,7 +196,7 @@ class Partner(models.Model): title = fields.Many2one('res.partner.title') parent_id = fields.Many2one('res.partner', string='Related Company', index=True) parent_name = fields.Char(related='parent_id.name', readonly=True, string='Parent name') - child_ids = fields.One2many('res.partner', 'parent_id', string='Contact', domain=[('active', '=', True)]) # force "active_test" domain to bypass _search() override + child_ids = fields.One2many('res.partner', 'parent_id', string='Contact', domain=[('active', '=', True)]) ref = fields.Char(string='Reference', index=True) lang = fields.Selection(_lang_get, string='Language', help="All the emails and documents sent to this contact will be translated in this language.") @@ -890,16 +890,6 @@ class Partner(models.Model): partner = self.create(create_values) return partner.id, partner.display_name - @api.model - def _search(self, domain, offset=0, limit=None, order=None, access_rights_uid=None): - """ Override search() to always show inactive children when searching via ``child_of`` operator. The ORM will - always call search() with a simple domain of the form [('parent_id', 'in', [ids])]. """ - # a special ``domain`` is set on the ``child_ids`` o2m to bypass this logic, as it uses similar domain expressions - if len(domain) == 1 and len(domain[0]) == 3 and domain[0][:2] == ('parent_id', 'in') \ - and domain[0][2] != [False]: - self = self.with_context(active_test=False) - return super()._search(domain, offset, limit, order, access_rights_uid) - @api.model @api.returns('self', lambda value: value.id) def find_or_create(self, email, assert_valid_email=False): diff --git a/odoo/addons/test_http/tests/test_static.py b/odoo/addons/test_http/tests/test_static.py index ee5021f761a..f32032564a8 100644 --- a/odoo/addons/test_http/tests/test_static.py +++ b/odoo/addons/test_http/tests/test_static.py @@ -265,10 +265,10 @@ class TestHttpStatic(TestHttpStaticCommon): ) def test_static16_public_access_rights(self): - public_user = self.env.ref('base.public_user') + default_user = self.env.ref('base.default_user') with self.subTest('model access rights'): - res = self.url_open(f'/web/content/res.users/{public_user.id}/image_128') + res = self.url_open(f'/web/content/res.users/{default_user.id}/image_128') self.assertEqual(res.status_code, 404) with self.subTest('attachment + field access rights'): diff --git a/odoo/models.py b/odoo/models.py index c70abcd5f73..0fd0baffb60 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -5713,7 +5713,7 @@ class BaseModel(metaclass=MetaModel): else: (key, comparator, value) = leaf if comparator in ('child_of', 'parent_of'): - stack.append(set(self.search([('id', 'in', self.ids), leaf], order='id')._ids)) + stack.append(set(self.with_context(active_test=False).search([('id', 'in', self.ids), leaf], order='id')._ids)) continue if key.endswith('.id'): diff --git a/odoo/osv/expression.py b/odoo/osv/expression.py index a9a6745ac04..0f7220184d1 100644 --- a/odoo/osv/expression.py +++ b/odoo/osv/expression.py @@ -868,6 +868,7 @@ class expression(object): (when available), or as an expanded [(left,in,child_ids)] """ if not ids: return [FALSE_LEAF] + left_model = left_model.with_context(active_test=False) if left_model._parent_store: domain = OR([ [('parent_path', '=like', rec.parent_path + '%')] @@ -896,6 +897,7 @@ class expression(object): (when available), or as an expanded [(left,in,parent_ids)] """ if not ids: return [FALSE_LEAF] + left_model = left_model.with_context(active_test=False) if left_model._parent_store: parent_ids = [ int(label)