diff --git a/addons/mail/i18n/mail.pot b/addons/mail/i18n/mail.pot index 13a684723f1..caa67ad0252 100644 --- a/addons/mail/i18n/mail.pot +++ b/addons/mail/i18n/mail.pot @@ -4856,6 +4856,7 @@ msgstr "" #. module: mail #: code:addons/mail/models/mail_render_mixin.py:0 #: code:addons/mail/models/mail_render_mixin.py:0 +#: code:addons/mail/models/mail_render_mixin.py:0 #, python-format msgid "Only users belonging to the \"%s\" group can modify dynamic templates." msgstr "" diff --git a/addons/mail/models/mail_render_mixin.py b/addons/mail/models/mail_render_mixin.py index a43df861a14..8e8f2e3b171 100644 --- a/addons/mail/models/mail_render_mixin.py +++ b/addons/mail/models/mail_render_mixin.py @@ -127,6 +127,23 @@ class MailRenderMixin(models.AbstractModel): # allow specifying rendering options directly from field when using the render mixin return name in ['render_engine', 'render_options'] or super()._valid_field_parameter(field, name) + @api.model_create_multi + def create(self, values_list): + record = super().create(values_list) + if self._unrestricted_rendering: + # If the rendering is unrestricted (e.g. mail.template), + # check the user is part of the mail editor group to create a new template if the template is dynamic + record._check_access_right_dynamic_template() + return record + + def write(self, vals): + super().write(vals) + if self._unrestricted_rendering: + # If the rendering is unrestricted (e.g. mail.template), + # check the user is part of the mail editor group to modify a template if the template is dynamic + self._check_access_right_dynamic_template() + return True + # ------------------------------------------------------------ # TOOLS # ------------------------------------------------------------ @@ -214,6 +231,48 @@ class MailRenderMixin(models.AbstractModel): """).format(preview_markup) return tools.prepend_html_content(html, html_preview) return html + + # ------------------------------------------------------------ + # SECURITY + # ------------------------------------------------------------ + + def _is_dynamic(self): + for template in self: + for fname, field in template._fields.items(): + engine = getattr(field, 'render_engine', 'inline_template') + if engine in ('qweb', 'qweb_view'): + if self._is_dynamic_template_qweb(template[fname]): + return True + else: + if self._is_dynamic_template_inline_template(template[fname]): + return True + return False + + @api.model + def _is_dynamic_template_qweb(self, template_src): + if template_src: + try: + node = html.fragment_fromstring(template_src, create_parent='div') + self.env["ir.qweb"].with_context(raise_on_code=True)._compile(node) + except QWebException as e: + if isinstance(e.__cause__, PermissionError): + return True + raise + return False + + @api.model + def _is_dynamic_template_inline_template(self, template_txt): + if template_txt: + template_instructions = parse_inline_template(str(template_txt)) + if len(template_instructions) > 1 or template_instructions[0][1]: + return True + return False + + def _check_access_right_dynamic_template(self): + if not self.env.su and not self.env.user.has_group('mail.group_mail_template_editor') and self._is_dynamic(): + group = self.env.ref('mail.group_mail_template_editor') + raise AccessError(_('Only users belonging to the "%s" group can modify dynamic templates.', group.name)) + # ------------------------------------------------------------ # RENDERING # ------------------------------------------------------------ diff --git a/addons/mail/security/ir.model.access.csv b/addons/mail/security/ir.model.access.csv index 33fefed4c3d..5158c30bae2 100644 --- a/addons/mail/security/ir.model.access.csv +++ b/addons/mail/security/ir.model.access.csv @@ -31,7 +31,7 @@ access_mail_tracking_value_portal,mail.tracking.value.portal,model_mail_tracking access_mail_tracking_value_user,mail.tracking.value.user,model_mail_tracking_value,base.group_user,0,0,0,0 access_mail_tracking_value_system,mail.tracking.value.system,model_mail_tracking_value,base.group_system,1,1,1,1 access_publisher_warranty_contract_all,publisher.warranty.contract.all,model_publisher_warranty_contract,,1,1,1,1 -access_mail_template,mail.template,model_mail_template,base.group_user,1,0,0,0 +access_mail_template,mail.template,model_mail_template,base.group_user,1,1,1,1 access_mail_template_editor,mail.template_editor,model_mail_template,mail.group_mail_template_editor,1,1,1,1 access_mail_template_system,mail.template_system,model_mail_template,base.group_system,1,1,1,1 access_mail_shortcode,mail.shortcode,model_mail_shortcode,base.group_user,1,1,1,1 diff --git a/addons/mail/security/mail_security.xml b/addons/mail/security/mail_security.xml index 31a7b11a9db..1c5fd4a30eb 100644 --- a/addons/mail/security/mail_security.xml +++ b/addons/mail/security/mail_security.xml @@ -65,6 +65,28 @@ + + Employees can only change their own templates + + [('create_uid', '=', user.id)] + + + + + + + + + Mail Template Editors - Edit All Templates + + [(1, '=', 1)] + + + + + + + Administrators can access all User Settings. diff --git a/addons/mail/tests/test_mail_template.py b/addons/mail/tests/test_mail_template.py index 73345107aef..3bc2412a90b 100644 --- a/addons/mail/tests/test_mail_template.py +++ b/addons/mail/tests/test_mail_template.py @@ -57,9 +57,25 @@ class TestMailTemplate(MailCommon): mail_template.with_user(self.user_admin).name = 'New name' self.assertEqual(mail_template.name, 'New name') - # Standard employee can not + # Standard employee can create and edit non-dynamic templates + employee_template = self.env['mail.template'].with_user(self.user_employee).create({'body_html': '

foo

'}) + + employee_template.with_user(self.user_employee).body_html = '

bar

' + + employee_template = self.env['mail.template'].with_user(self.user_employee).create({'email_to': 'foo@bar.com'}) + + employee_template.with_user(self.user_employee).email_to = 'bar@foo.com' + + # Standard employee cannot create and edit templates with dynamic qweb with self.assertRaises(AccessError): - self.env['mail.template'].with_user(self.user_employee).create({}) + self.env['mail.template'].with_user(self.user_employee).create({'body_html': '

'}) with self.assertRaises(AccessError): - mail_template.with_user(self.user_employee).name = 'Test write' + mail_template.with_user(self.user_employee).body_html = '

' + + # Standard employee cannot create and edit templates with dynamic inline fields + with self.assertRaises(AccessError): + self.env['mail.template'].with_user(self.user_employee).create({'email_to': '{{ object.partner_id.email }}'}) + + with self.assertRaises(AccessError): + mail_template.with_user(self.user_employee).email_to = '{{ object.partner_id.email }}' diff --git a/addons/mail/wizard/mail_compose_message_views.xml b/addons/mail/wizard/mail_compose_message_views.xml index 105078b69f0..9c72234780c 100644 --- a/addons/mail/wizard/mail_compose_message_views.xml +++ b/addons/mail/wizard/mail_compose_message_views.xml @@ -66,8 +66,9 @@