From d8fecf4c1cd52e33eb66e20dfa0ac64e2e0324f2 Mon Sep 17 00:00:00 2001 From: "Xavier BOL (xbo)" Date: Mon, 17 Oct 2022 14:01:43 +0000 Subject: [PATCH] [FIX] project: manage true and false domain for project sharing Before this commit, when the domain used in `read_group` and `search` to check if the portal user can access to the fields used in the domain does not take into account the `TRUE_LEAF` (that is, `(1, '=', 1)`) and `FALSE_LEAF` (that is, `(0, '=', 1)`). In other words, when the `TRUE_LEAF` or `FALSE_LEAF` is in the domain, an access error is raised saying the field name `1` or `0` cannot be accessible by the portal user. This commit allows to add `TRUE_LEAF` and `FALSE_LEAF` in the domain to avoid having an access error because of that. close #103214 closes odoo/odoo#103465 X-original-commit: 953033e1ab20a01918dae7ca495654cddef14af3 Signed-off-by: Laurent Stukkens (ltu) Signed-off-by: Xavier --- addons/project/models/project.py | 6 +-- addons/project/tests/test_project_sharing.py | 53 ++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/addons/project/models/project.py b/addons/project/models/project.py index 21e55212fc6..473a73f670e 100644 --- a/addons/project/models/project.py +++ b/addons/project/models/project.py @@ -1884,13 +1884,13 @@ class Task(models.Model): # only take field name when having ':' e.g 'date_deadline:week' => 'date_deadline' fields_list += [f.split(':')[0] for f in fields_groupby] if domain: - fields_list += [term[0].split('.')[0] for term in domain if isinstance(term, (tuple, list))] + fields_list += [term[0].split('.')[0] for term in domain if isinstance(term, (tuple, list)) and term not in [expression.TRUE_LEAF, expression.FALSE_LEAF]] self._ensure_fields_are_accessible(fields_list) return super(Task, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy) @api.model def _search(self, args, offset=0, limit=None, order=None, count=False, access_rights_uid=None): - fields_list = {term[0] for term in args if isinstance(term, (tuple, list))} + fields_list = {term[0] for term in args if isinstance(term, (tuple, list)) and term not in [expression.TRUE_LEAF, expression.FALSE_LEAF]} self._ensure_fields_are_accessible(fields_list) return super(Task, self)._search(args, offset=offset, limit=limit, order=order, count=count, access_rights_uid=access_rights_uid) @@ -1902,7 +1902,7 @@ class Task(models.Model): return super(Task, self).mapped(func) def filtered_domain(self, domain): - fields_list = [term[0] for term in domain if isinstance(term, (tuple, list))] + fields_list = [term[0] for term in domain if isinstance(term, (tuple, list)) and term not in [expression.TRUE_LEAF, expression.FALSE_LEAF]] self._ensure_fields_are_accessible(fields_list) return super(Task, self).filtered_domain(domain) diff --git a/addons/project/tests/test_project_sharing.py b/addons/project/tests/test_project_sharing.py index 64e4541428a..c163957af4d 100644 --- a/addons/project/tests/test_project_sharing.py +++ b/addons/project/tests/test_project_sharing.py @@ -2,6 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import Command +from odoo.osv import expression from odoo.exceptions import AccessError from odoo.tests import tagged from odoo.tests.common import Form @@ -246,3 +247,55 @@ class TestProjectSharing(TestProjectSharingCommon): ], }) self.task_portal.with_user(self.user_portal).write({'stage_id': self.project_portal.type_ids[-1].id}) + + def test_orm_method_with_true_false_domain(self): + """ Test orm method overriden in project for project sharing works with TRUE_LEAF/FALSE_LEAF + + Test Case + ========= + 1) Share a project in edit mode for portal user + 2) Search the portal task contained in the project shared by using a domain with TRUE_LEAF + 3) Check the task is found with the `search` method + 4) filter the task with `TRUE_DOMAIN` and check if the task is always returned by `filtered_domain` method + 5) filter the task with `FALSE_DOMAIN` and check if no task is returned by `filtered_domain` method + 6) Search the task with `FALSE_LEAF` and check no task is found with `search` method + 7) Call `read_group` method with `TRUE_LEAF` in the domain and check if the task is found + 8) Call `read_group` method with `FALSE_LEAF` in the domain and check if no task is found + """ + domain = [('id', '=', self.task_portal.id)] + self.project_portal.write({ + 'collaborator_ids': [Command.create({ + 'partner_id': self.user_portal.partner_id.id, + })], + }) + task = self.env['project.task'].with_user(self.user_portal).search( + expression.AND([ + expression.TRUE_DOMAIN, + domain, + ]) + ) + self.assertTrue(task, 'The task should be found.') + self.assertEqual(task, task.filtered_domain(expression.TRUE_DOMAIN), 'The task found should be kept since the domain is truly') + self.assertFalse(task.filtered_domain(expression.FALSE_DOMAIN), 'The task should not be found since the domain is falsy') + task = self.env['project.task'].with_user(self.user_portal).search( + expression.AND([ + expression.FALSE_DOMAIN, + domain, + ]), + ) + self.assertFalse(task, 'No task should be found since the domain contained a falsy tuple.') + + task_read_group = self.env['project.task'].read_group( + expression.AND([expression.TRUE_DOMAIN, domain]), + ['id'], + [], + ) + self.assertEqual(task_read_group[0]['__count'], 1, 'The task should be found with the read_group method containing a truly tuple.') + self.assertEqual(task_read_group[0]['id'], self.task_portal.id, 'The task should be found with the read_group method containing a truly tuple.') + + task_read_group = self.env['project.task'].read_group( + expression.AND([expression.FALSE_DOMAIN, domain]), + ['id'], + [], + ) + self.assertFalse(task_read_group[0]['__count'], 'No result should found with the read_group since the domain is falsy.')