diff --git a/addons/project/tests/__init__.py b/addons/project/tests/__init__.py index 6f85611241d..8aea3e56d27 100644 --- a/addons/project/tests/__init__.py +++ b/addons/project/tests/__init__.py @@ -7,6 +7,7 @@ from . import test_project_config from . import test_project_flow from . import test_project_recurrence from . import test_project_sharing +from . import test_project_sharing_portal_access from . import test_project_sharing_ui from . import test_project_subtasks from . import test_project_ui diff --git a/addons/project/tests/test_project_sharing.py b/addons/project/tests/test_project_sharing.py index 0a4828b4a96..5846fc77f0e 100644 --- a/addons/project/tests/test_project_sharing.py +++ b/addons/project/tests/test_project_sharing.py @@ -51,10 +51,12 @@ class TestProjectSharingCommon(TestProjectCommon): 'project_id': cls.project_portal.id, }) + cls.project_sharing_form_view_xml_id = 'project.project_sharing_project_task_view_form' + def get_project_sharing_form_view(self, record, with_user=None): return Form( record.with_user(with_user or self.env.user), - view="project.project_sharing_project_task_view_form" + view=self.project_sharing_form_view_xml_id ) @tagged('project_sharing') diff --git a/addons/project/tests/test_project_sharing_portal_access.py b/addons/project/tests/test_project_sharing_portal_access.py new file mode 100644 index 00000000000..f7cfed90e29 --- /dev/null +++ b/addons/project/tests/test_project_sharing_portal_access.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from collections import OrderedDict +from odoo import Command +from odoo.exceptions import AccessError +from odoo.tests import tagged + +from .test_project_sharing import TestProjectSharingCommon + + +@tagged('post_install', '-at_install') +class TestProjectSharingPortalAccess(TestProjectSharingCommon): + + @classmethod + def setUpClass(cls): + super().setUpClass() + project_share_wizard = cls.env['project.share.wizard'].create({ + 'access_mode': 'edit', + 'res_model': 'project.project', + 'res_id': cls.project_portal.id, + 'partner_ids': [ + Command.link(cls.partner_portal.id), + ], + }) + project_share_wizard.action_send_mail() + + Task = cls.env['project.task'] + cls.read_protected_fields_task = OrderedDict([ + (k, v) + for k, v in Task._fields.items() + if k in Task.SELF_READABLE_FIELDS + ]) + cls.write_protected_fields_task = OrderedDict([ + (k, v) + for k, v in Task._fields.items() + if k in Task.SELF_WRITABLE_FIELDS + ]) + cls.readonly_protected_fields_task = OrderedDict([ + (k, v) + for k, v in Task._fields.items() + if k in Task.SELF_READABLE_FIELDS and k not in Task.SELF_WRITABLE_FIELDS + ]) + cls.other_fields_task = OrderedDict([ + (k, v) + for k, v in Task._fields.items() + if k not in Task.SELF_READABLE_FIELDS + ]) + + def test_readonly_fields(self): + """ The fields are not writeable should not be editable by the portal user. """ + view_infos = self.task_portal.fields_view_get(view_id=self.env.ref(self.project_sharing_form_view_xml_id).id) + project_task_fields = { + field_name + for field_name, field_attrs in view_infos['fields'].items() + if field_name not in self.write_protected_fields_task + } + with self.get_project_sharing_form_view(self.task_portal, self.user_portal) as form: + for field in project_task_fields: + with self.assertRaises(AssertionError, msg="Field '%s' should be readonly in the project sharing form view "): + form.__setattr__(field, 'coucou') + + def test_read_task_with_portal_user(self): + self.task_portal.with_user(self.user_portal).read(self.read_protected_fields_task) + + with self.assertRaises(AccessError): + self.task_portal.with_user(self.user_portal).read(self.other_fields_task) + + def test_write_with_portal_user(self): + for field in self.readonly_protected_fields_task: + with self.assertRaises(AccessError): + self.task_portal.with_user(self.user_portal).write({field: 'dummy'}) + + for field in self.other_fields_task: + with self.assertRaises(AccessError): + self.task_portal.with_user(self.user_portal).write({field: 'dummy'})