[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
This commit is contained in:
william-andre
2023-07-20 11:49:05 +02:00
committed by qdp-odoo
parent b405bc41fb
commit ba5df07223
4 changed files with 6 additions and 14 deletions
+1 -11
View File
@@ -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):
+2 -2
View File
@@ -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'):
+1 -1
View File
@@ -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'):
+2
View File
@@ -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)