Prior to this fix, elements with background images were converted to images via the html2canvas library. This made them work in Outlook at the cost of several tradeoffs: - the process was slow and asynchronous - there could be no interactivity (links, buttons, etc.) in the converted element - responsive behavior was wonky: if only a slice of the image was shown when it was converted (due to background-size cover behavior), the rest was lost so if more width was needed in mobile, we would be zooming on that slice, making it sometimes irrelevant and pixelated This replaces all that with a conversion to VML, which is a vector format supported by Outlook. This conversion is done only for Outlook, which means that all other clients are getting the original background element again. There is a way to keep the background-size cover behavior in VML, using the "aspect" attribute with value "atleast" but this only works on v-fill elements and sadly putting the image on a v-fill element bugs in Windows Mail (which is the default mail client on Windows 10 and 11) and this client can't be singled out of mso conditionals. To get around this issue, since this is only for desktop clients, we assume the width of the screen to be large and mimick the cover behavior by cropping the image to the target size. This allows us to put the image on the v-image element and have proper rendering in Outlook and Windows Mail on desktop. Note: When retrieving the image by URL in Python in order to crop it, we need to ensure we have an absolute path. This is done - perhaps seemingly naively - by checking if the URL contains '//'. Here's the reasoning behind that choice. To check if a URL is absolute, we could use `urllib.parse.urlparse` and check if it has a scheme but that would lead to `www.odoo.com/path` being considered relative (and thus we'd add a host to the URL even though there's already one). Instead, we could check it it has a netloc but that would lead to the same issue since the documentation of `urlparse` says: > Following the syntax specifications in [RFC 1808](https://datatracker.ietf.org/doc/html/rfc1808.html), urlparse recognizes a netloc only if it is properly introduced by ‘//’. Still, it would be more technically correct since `//some/path` would be considered absolute (which it should be since it resolves to `<current_scheme>//some/path`). Base on that documentation, it seems that simply checking if the URL contains '//' is pretty much equivalent to checking if it has a scheme, with the double advantage that it's simpler and that it works for `//some/path` as well. However, note that it doesn't solve the issue of `www.odoo.com/path`. In summary, here are the results with the current method: ``` http://www.odoo.com/path -> http://www.google.com/path // OK some/path -> http://localhost:8069/some/path // OK /some/path -> http://localhost:8069/some/path // OK //some/path -> //some/path // OK www.odoo.com/path -> http://localhost:8069/www.google.com/path // WRONG ``` X-original-commit: 9561ba31917024825705c876442139405e7a7957 Part-of: odoo/odoo#124465
526 lines
22 KiB
Python
526 lines
22 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from markupsafe import Markup
|
|
|
|
from odoo.addons.mail.tests import common
|
|
from odoo.exceptions import AccessError
|
|
from odoo.tests import tagged, users
|
|
|
|
|
|
class TestMailRenderCommon(common.MailCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestMailRenderCommon, cls).setUpClass()
|
|
|
|
# activate multi language support
|
|
cls.env['res.lang']._activate_lang('fr_FR')
|
|
cls.user_admin.write({'lang': 'en_US'})
|
|
|
|
# test records
|
|
cls.render_object = cls.env['res.partner'].create({
|
|
'name': 'TestRecord',
|
|
'lang': 'en_US',
|
|
})
|
|
cls.render_object_fr = cls.env['res.partner'].create({
|
|
'name': 'Element de Test',
|
|
'lang': 'fr_FR',
|
|
})
|
|
|
|
# some jinja templates
|
|
cls.base_inline_template_bits = [
|
|
'<p>Hello</p>',
|
|
'<p>Hello {{ object.name }}</p>',
|
|
"""<p>
|
|
{{ '<span>English Speaker</span>' if object.lang == 'en_US' else '<span>Other Speaker</span>' }}
|
|
</p>""",
|
|
"""
|
|
<p>{{ 13 + 13 }}</p>
|
|
<h1>This is a test</h1>
|
|
""",
|
|
"""<b>Test</b>{{ '' if True else '<b>Code not executed</b>' }}""",
|
|
]
|
|
cls.base_inline_template_bits_fr = [
|
|
'<p>Bonjour</p>',
|
|
'<p>Bonjour {{ object.name }}</p>',
|
|
"""<p>
|
|
{{ '<span>Narrateur Anglais</span>' if object.lang == 'en_US' else '<span>Autre Narrateur</span>' }}
|
|
</p>"""
|
|
]
|
|
|
|
# some qweb templates, their views and their xml ids
|
|
cls.base_qweb_bits = [
|
|
'<p>Hello</p>',
|
|
'<p>Hello <t t-esc="object.name"/></p>',
|
|
"""<p>
|
|
<span t-if="object.lang == 'en_US'">English Speaker</span>
|
|
<span t-else="">Other Speaker</span>
|
|
</p>"""
|
|
]
|
|
cls.base_qweb_bits_fr = [
|
|
'<p>Bonjour</p>',
|
|
'<p>Bonjour <t t-esc="object.name"/></p>',
|
|
"""<p>
|
|
<span t-if="object.lang == 'en_US'">Narrateur Anglais</span>
|
|
<span t-else="">Autre Narrateur</span>
|
|
</p>"""
|
|
]
|
|
cls.base_qweb_templates = cls.env['ir.ui.view'].create([
|
|
{'name': 'TestRender%d' % index,
|
|
'type': 'qweb',
|
|
'arch': qweb_content,
|
|
} for index, qweb_content in enumerate(cls.base_qweb_bits)
|
|
])
|
|
cls.base_qweb_templates_data = cls.env['ir.model.data'].create([
|
|
{'name': template.name, 'module': 'mail',
|
|
'model': template._name, 'res_id': template.id,
|
|
} for template in cls.base_qweb_templates
|
|
])
|
|
cls.base_qweb_templates_xmlids = [
|
|
model_data.complete_name
|
|
for model_data in cls.base_qweb_templates_data
|
|
]
|
|
|
|
# render result
|
|
cls.base_rendered = [
|
|
'<p>Hello</p>',
|
|
'<p>Hello %s</p>' % cls.render_object.name,
|
|
"""<p>
|
|
<span>English Speaker</span>
|
|
</p>"""
|
|
]
|
|
cls.base_rendered_fr = [
|
|
'<p>Bonjour</p>',
|
|
'<p>Bonjour %s</p>' % cls.render_object_fr.name,
|
|
"""<p>
|
|
<span>Autre Narrateur</span>
|
|
</p>"""
|
|
]
|
|
cls.base_rendered_void = [
|
|
'<p>Hello</p>',
|
|
'<p>Hello </p>',
|
|
"""<p>
|
|
<span>English Speaker</span>
|
|
</p>"""
|
|
]
|
|
|
|
# link to mail template
|
|
cls.test_template = cls.env['mail.template'].create({
|
|
'name': 'Test Template',
|
|
'subject': cls.base_inline_template_bits[0],
|
|
'body_html': cls.base_qweb_bits[1],
|
|
'model_id': cls.env['ir.model']._get('res.partner').id,
|
|
'lang': '{{ object.lang }}'
|
|
})
|
|
|
|
# some translations
|
|
cls.test_template.with_context(lang='fr_FR').subject = cls.base_qweb_bits_fr[0]
|
|
cls.test_template.with_context(lang='fr_FR').body_html = cls.base_qweb_bits_fr[1]
|
|
|
|
cls.env['ir.model.data'].create({
|
|
'name': 'test_template_xmlid',
|
|
'module': 'mail',
|
|
'model': cls.test_template._name,
|
|
'res_id': cls.test_template.id,
|
|
})
|
|
|
|
# Enable group-based template management
|
|
cls.env['ir.config_parameter'].set_param('mail.restrict.template.rendering', True)
|
|
|
|
# User without the group "mail.group_mail_template_editor"
|
|
cls.user_rendering_restricted = common.mail_new_test_user(
|
|
cls.env, login='user_rendering_restricted',
|
|
groups='base.group_user',
|
|
company_id=cls.company_admin.id,
|
|
name='Code Template Restricted User',
|
|
notification_type='inbox',
|
|
signature='--\nErnest'
|
|
)
|
|
cls.user_rendering_restricted.groups_id -= cls.env.ref('mail.group_mail_template_editor')
|
|
cls.user_employee.groups_id += cls.env.ref('mail.group_mail_template_editor')
|
|
|
|
|
|
@tagged('mail_render')
|
|
class TestMailRender(TestMailRenderCommon):
|
|
|
|
@users('employee')
|
|
def test_evaluation_context(self):
|
|
""" Test evaluation context and various ways of tweaking it. """
|
|
partner = self.env['res.partner'].browse(self.render_object.ids)
|
|
MailRenderMixin = self.env['mail.render.mixin']
|
|
|
|
custom_ctx = {'custom_ctx': 'Custom Context Value'}
|
|
add_context = {
|
|
'custom_value': 'Custom Render Value'
|
|
}
|
|
srces = [
|
|
'<b>I am {{ user.name }}</b>',
|
|
'<span>Datetime is {{ format_datetime(datetime.datetime(2021, 6, 1), dt_format="MM - d - YYY") }}</span>',
|
|
'<span>Context {{ ctx.get("custom_ctx") }}, value {{ custom_value }}</span>',
|
|
]
|
|
results = [
|
|
'<b>I am %s</b>' % self.env.user.name,
|
|
'<span>Datetime is 06 - 1 - 2021</span>',
|
|
'<span>Context Custom Context Value, value Custom Render Value</span>'
|
|
]
|
|
for src, expected in zip(srces, results):
|
|
for engine in ['inline_template']:
|
|
result = MailRenderMixin.with_context(**custom_ctx)._render_template(
|
|
src, partner._name, partner.ids,
|
|
engine=engine, add_context=add_context
|
|
)[partner.id]
|
|
self.assertEqual(expected, result)
|
|
|
|
@users('employee')
|
|
def test_prepend_preview_inline_template_to_qweb(self):
|
|
body = 'body'
|
|
preview = 'foo{{"false" if 1 > 2 else "true"}}bar'
|
|
result = self.env['mail.render.mixin']._prepend_preview(Markup(body), preview)
|
|
self.assertEqual(result, '''<div style="display:none;font-size:1px;height:0px;width:0px;opacity:0;">
|
|
foo<t t-out=""false" if 1 > 2 else "true""/>bar
|
|
</div>body''')
|
|
|
|
@users('employee')
|
|
def test_render_field(self):
|
|
template = self.env['mail.template'].browse(self.test_template.ids)
|
|
partner = self.env['res.partner'].browse(self.render_object.ids)
|
|
for fname, expected in zip(['subject', 'body_html'], self.base_rendered):
|
|
rendered = template._render_field(
|
|
fname,
|
|
partner.ids,
|
|
compute_lang=True
|
|
)[partner.id]
|
|
self.assertEqual(rendered, expected)
|
|
|
|
@users('employee')
|
|
def test_render_field_lang(self):
|
|
""" Test translation in french """
|
|
template = self.env['mail.template'].browse(self.test_template.ids)
|
|
partner = self.env['res.partner'].browse(self.render_object_fr.ids)
|
|
for fname, expected in zip(['subject', 'body_html'], self.base_rendered_fr):
|
|
rendered = template._render_field(
|
|
fname,
|
|
partner.ids,
|
|
compute_lang=True
|
|
)[partner.id]
|
|
self.assertEqual(rendered, expected)
|
|
|
|
@users('employee')
|
|
def test_render_field_no_records(self):
|
|
""" Test rendering on void IDs, or a list with dummy / falsy ID """
|
|
template = self.test_template.with_env(self.env)
|
|
partner = self.render_object.with_env(self.env)
|
|
for res_ids in ([], (), [False], [''], [None], [False, partner.id]): # various corner cases
|
|
for fname, expected_obj, expected_void in zip(['subject', 'body_html'], self.base_rendered, self.base_rendered_void):
|
|
with self.subTest():
|
|
rendered_all = template._render_field(
|
|
fname,
|
|
res_ids,
|
|
compute_lang=True
|
|
)
|
|
if res_ids:
|
|
self.assertTrue(res_ids[0] in rendered_all,
|
|
f'Rendering: key {repr(res_ids[0])} is considered as valid and should have an entry')
|
|
self.assertEqual(rendered_all[res_ids[0]], expected_void)
|
|
if len(res_ids) == 2: # second is partner
|
|
self.assertTrue(res_ids[1] in rendered_all)
|
|
self.assertEqual(rendered_all[res_ids[1]], expected_obj)
|
|
if not res_ids:
|
|
self.assertFalse(rendered_all,
|
|
'Rendering: void input -> void output')
|
|
|
|
@users('employee')
|
|
def test_render_field_not_existing(self):
|
|
""" Test trying to render a not-existing field: raise a proper ValueError
|
|
instead of crashing / raising a KeyError """
|
|
template = self.env['mail.template'].browse(self.test_template.ids)
|
|
partner = self.env['res.partner'].browse(self.render_object_fr.ids)
|
|
with self.assertRaises(ValueError):
|
|
_rendered = template._render_field(
|
|
'not_existing',
|
|
partner.ids,
|
|
compute_lang=True
|
|
)[partner.id]
|
|
|
|
@users('employee')
|
|
def test_render_template_inline_template(self):
|
|
partner = self.env['res.partner'].browse(self.render_object.ids)
|
|
for source, expected in zip(self.base_inline_template_bits, self.base_rendered):
|
|
rendered = self.env['mail.render.mixin']._render_template(
|
|
source,
|
|
partner._name,
|
|
partner.ids,
|
|
engine='inline_template',
|
|
)[partner.id]
|
|
self.assertEqual(rendered, expected)
|
|
|
|
@users('employee')
|
|
def test_render_template_qweb(self):
|
|
partner = self.env['res.partner'].browse(self.render_object.ids)
|
|
for source, expected in zip(self.base_qweb_bits, self.base_rendered):
|
|
rendered = self.env['mail.render.mixin']._render_template(
|
|
source,
|
|
partner._name,
|
|
partner.ids,
|
|
engine='qweb',
|
|
)[partner.id]
|
|
self.assertEqual(rendered, expected)
|
|
|
|
@users('employee')
|
|
def test_render_template_qweb_view(self):
|
|
partner = self.env['res.partner'].browse(self.render_object.ids)
|
|
for source, expected in zip(self.base_qweb_templates_xmlids, self.base_rendered):
|
|
rendered = self.env['mail.render.mixin']._render_template(
|
|
source,
|
|
partner._name,
|
|
partner.ids,
|
|
engine='qweb_view',
|
|
)[partner.id]
|
|
self.assertEqual(rendered, expected)
|
|
|
|
@users('employee')
|
|
def test_render_template_various(self):
|
|
""" Test static rendering """
|
|
partner = self.env['res.partner'].browse(self.render_object.ids)
|
|
MailRenderMixin = self.env['mail.render.mixin']
|
|
|
|
# static string
|
|
src = 'This is a string'
|
|
expected = 'This is a string'
|
|
for engine in ['inline_template']:
|
|
result = MailRenderMixin._render_template(
|
|
src, partner._name, partner.ids, engine=engine,
|
|
)[partner.id]
|
|
self.assertEqual(expected, result)
|
|
|
|
# code string
|
|
src = 'This is a string with a number {{ 13+13 }}'
|
|
expected = 'This is a string with a number 26'
|
|
for engine in ['inline_template']:
|
|
result = MailRenderMixin._render_template(
|
|
src, partner._name, partner.ids, engine=engine,
|
|
)[partner.id]
|
|
self.assertEqual(expected, result)
|
|
|
|
# block string
|
|
src = "This is a string with a block {{ 'hidden' if False else 'displayed' }}"
|
|
expected = 'This is a string with a block displayed'
|
|
for engine in ['inline_template']:
|
|
result = MailRenderMixin._render_template(
|
|
src, partner._name, partner.ids, engine=engine,
|
|
)[partner.id]
|
|
self.assertEqual(expected, result)
|
|
|
|
# static xml
|
|
src = '<p class="text-muted"><span>This is a string</span></p>'
|
|
expected = '<p class="text-muted"><span>This is a string</span></p>'
|
|
for engine in ['inline_template', 'qweb']:
|
|
result = MailRenderMixin._render_template(
|
|
src, partner._name, partner.ids, engine=engine,
|
|
)[partner.id]
|
|
self.assertEqual(expected, result) # tde: checkme
|
|
|
|
# code xml
|
|
srces = [
|
|
'<p class="text-muted"><span>This is a string with a number {{ 13+13 }}</span></p>',
|
|
'<p class="text-muted"><span>This is a string with a number <t t-out="13+13"/></span></p>',
|
|
]
|
|
expected = '<p class="text-muted"><span>This is a string with a number 26</span></p>'
|
|
for engine, src in zip(['inline_template', 'qweb'], srces):
|
|
result = MailRenderMixin._render_template(
|
|
src, partner._name, partner.ids, engine=engine,
|
|
)[partner.id]
|
|
self.assertEqual(expected, str(result))
|
|
src = """<p>
|
|
<t t-set="line_statement_variable" t-value="3" />
|
|
<span>We have <t t-out="line_statement_variable" /> cookies in stock</span>
|
|
<span>We have <t t-set="block_variable" t-value="4" /><t t-out="block_variable" /> cookies in stock</span>
|
|
</p>"""
|
|
expected = """<p>
|
|
<span>We have 3 cookies in stock</span>
|
|
<span>We have 4 cookies in stock</span>
|
|
</p>"""
|
|
for engine in ['qweb']:
|
|
result = MailRenderMixin._render_template(
|
|
src, partner._name, partner.ids, engine=engine,
|
|
)[partner.id]
|
|
self.assertEqual(result, expected)
|
|
|
|
@users('employee')
|
|
def test_replace_local_links(self):
|
|
local_links_template_bits = [
|
|
'<a href="/web/path?a=a&b=b"/>',
|
|
'<img src="/web/path?a=a&b=b"/>',
|
|
'<v:fill src="/web/path?a=a&b=b"/>',
|
|
'<v:image src="/web/path?a=a&b=b"/>',
|
|
'<div style="background-image:url(/web/path?a=a&b=b);"/>',
|
|
'<div style="background-image:url(\'/web/path?a=a&b=b\');"/>',
|
|
'<div style="background-image:url("/web/path?a=a&b=b");"/>',
|
|
'<div background="/web/path?a=a&b=b"/>',
|
|
]
|
|
base_url = self.env['mail.render.mixin'].get_base_url()
|
|
rendered_local_links = [
|
|
'<a href="%s/web/path?a=a&b=b"/>' % base_url,
|
|
'<img src="%s/web/path?a=a&b=b"/>' % base_url,
|
|
'<v:fill src="%s/web/path?a=a&b=b"/>' % base_url,
|
|
'<v:image src="%s/web/path?a=a&b=b"/>' % base_url,
|
|
'<div style="background-image:url(%s/web/path?a=a&b=b);"/>' % base_url,
|
|
'<div style="background-image:url(\'%s/web/path?a=a&b=b\');"/>' % base_url,
|
|
'<div style="background-image:url("%s/web/path?a=a&b=b");"/>' % base_url,
|
|
'<div background="%s/web/path?a=a&b=b"/>' % base_url,
|
|
]
|
|
for source, expected in zip(local_links_template_bits, rendered_local_links):
|
|
rendered = self.env['mail.render.mixin']._replace_local_links(source)
|
|
self.assertEqual(rendered, expected)
|
|
|
|
|
|
@tagged('mail_render')
|
|
class TestMailRenderSecurity(TestMailRenderCommon):
|
|
""" Test security of rendering, based on qweb finding + restricted rendering
|
|
group usage. """
|
|
|
|
@users('employee')
|
|
def test_render_inline_template_impersonate(self):
|
|
""" Test that the use of SUDO do not change the current user. """
|
|
partner = self.env['res.partner'].browse(self.render_object.ids)
|
|
src = '{{ user.name }} - {{ object.name }}'
|
|
expected = '%s - %s' % (self.env.user.name, partner.name)
|
|
result = self.env['mail.render.mixin'].sudo()._render_template_inline_template(
|
|
src, partner._name, partner.ids
|
|
)[partner.id]
|
|
self.assertIn(expected, result)
|
|
|
|
@users('user_rendering_restricted')
|
|
def test_render_inline_template_restricted(self):
|
|
"""Test if we correctly detect static template."""
|
|
res_ids = self.env['res.partner'].search([], limit=1).ids
|
|
with self.assertRaises(AccessError, msg='Simple user should not be able to render dynamic code'):
|
|
self.env['mail.render.mixin']._render_template_inline_template(
|
|
self.base_inline_template_bits[3],
|
|
'res.partner',
|
|
res_ids
|
|
)
|
|
|
|
src = """<h1>This is a static template</h1>"""
|
|
result = self.env['mail.render.mixin']._render_template_inline_template(
|
|
src,
|
|
'res.partner',
|
|
res_ids
|
|
)[res_ids[0]]
|
|
self.assertEqual(src, str(result))
|
|
|
|
@users('user_rendering_restricted')
|
|
def test_render_inline_template_restricted_static(self):
|
|
"""Test that we render correctly static templates (without placeholders)."""
|
|
model = 'res.partner'
|
|
res_ids = self.env[model].search([], limit=1).ids
|
|
MailRenderMixin = self.env['mail.render.mixin']
|
|
|
|
result = MailRenderMixin._render_template_inline_template(
|
|
self.base_inline_template_bits[0],
|
|
model,
|
|
res_ids
|
|
)[res_ids[0]]
|
|
self.assertEqual(result, self.base_inline_template_bits[0])
|
|
|
|
@users('employee')
|
|
def test_render_inline_template_unrestricted(self):
|
|
""" Test if we correctly detect static template. """
|
|
res_ids = self.env['res.partner'].search([], limit=1).ids
|
|
result = self.env['mail.render.mixin']._render_template_inline_template(
|
|
self.base_inline_template_bits[3],
|
|
'res.partner',
|
|
res_ids
|
|
)[res_ids[0]]
|
|
self.assertIn('26', result, 'Template Editor should be able to render inline_template code')
|
|
|
|
@users('user_rendering_restricted')
|
|
def test_render_template_qweb_restricted(self):
|
|
model = 'res.partner'
|
|
res_ids = self.env[model].search([], limit=1).ids
|
|
partner = self.env[model].browse(res_ids)
|
|
|
|
src = """<h1>This is a static template</h1>"""
|
|
|
|
result = self.env['mail.render.mixin']._render_template_qweb(src, model, res_ids)[
|
|
partner.id]
|
|
self.assertEqual(src, str(result))
|
|
|
|
@users('user_rendering_restricted')
|
|
def test_security_function_call(self):
|
|
"""Test the case when the template call a custom function.
|
|
|
|
This function should not be called when the template is not rendered.
|
|
"""
|
|
model = 'res.partner'
|
|
res_ids = self.env[model].search([], limit=1).ids
|
|
partner = self.env[model].browse(res_ids)
|
|
MailRenderMixin = self.env['mail.render.mixin']
|
|
|
|
def cust_function():
|
|
# Can not use "MagicMock" in a Jinja sand-boxed environment
|
|
# so create our own function
|
|
cust_function.call = True
|
|
return 'return value'
|
|
|
|
cust_function.call = False
|
|
|
|
src = """<h1>This is a test</h1>
|
|
<p>{{ cust_function() }}</p>"""
|
|
expected = """<h1>This is a test</h1>
|
|
<p>return value</p>"""
|
|
context = {'cust_function': cust_function}
|
|
|
|
result = self.env['mail.render.mixin'].with_user(self.user_admin)._render_template_inline_template(
|
|
src, partner._name, partner.ids,
|
|
add_context=context
|
|
)[partner.id]
|
|
self.assertEqual(expected, result)
|
|
self.assertTrue(cust_function.call)
|
|
|
|
with self.assertRaises(AccessError, msg='Simple user should not be able to render dynamic code'):
|
|
MailRenderMixin._render_template_inline_template(src, model, res_ids, add_context=context)
|
|
|
|
@users('user_rendering_restricted')
|
|
def test_security_inline_template_restricted(self):
|
|
"""Test if we correctly detect condition block (which might contains code)."""
|
|
res_ids = self.env['res.partner'].search([], limit=1).ids
|
|
with self.assertRaises(AccessError, msg='Simple user should not be able to render dynamic code'):
|
|
self.env['mail.render.mixin']._render_template_inline_template(self.base_inline_template_bits[4], 'res.partner', res_ids)
|
|
|
|
@users('employee')
|
|
def test_security_inline_template_unrestricted(self):
|
|
"""Test if we correctly detect condition block (which might contains code)."""
|
|
res_ids = self.env['res.partner'].search([], limit=1).ids
|
|
result = self.env['mail.render.mixin']._render_template_inline_template(self.base_inline_template_bits[4], 'res.partner', res_ids)[res_ids[0]]
|
|
self.assertNotIn('Code not executed', result, 'The condition block did not work')
|
|
|
|
@users('user_rendering_restricted')
|
|
def test_security_qweb_template_restricted(self):
|
|
"""Test if we correctly detect condition block (which might contains code)."""
|
|
res_ids = self.env['res.partner'].search([], limit=1).ids
|
|
with self.assertRaises(AccessError, msg='Simple user should not be able to render qweb code'):
|
|
self.env['mail.render.mixin']._render_template_qweb(self.base_qweb_bits[1], 'res.partner', res_ids)
|
|
|
|
@users('user_rendering_restricted')
|
|
def test_security_qweb_template_restricted_cached(self):
|
|
"""Test if we correctly detect condition block (which might contains code)."""
|
|
res_ids = self.env['res.partner'].search([], limit=1).ids
|
|
|
|
# Render with the admin first to fill the cache
|
|
self.env['mail.render.mixin'].with_user(self.user_admin)._render_template_qweb(
|
|
self.base_qweb_bits[1], 'res.partner', res_ids)
|
|
|
|
# Check that it raise even when rendered previously by an admin
|
|
with self.assertRaises(AccessError, msg='Simple user should not be able to render qweb code'):
|
|
self.env['mail.render.mixin']._render_template_qweb(
|
|
self.base_qweb_bits[1], 'res.partner', res_ids)
|
|
|
|
@users('employee')
|
|
def test_security_qweb_template_unrestricted(self):
|
|
"""Test if we correctly detect condition block (which might contains code)."""
|
|
res_ids = self.env['res.partner'].search([], limit=1).ids
|
|
result = self.env['mail.render.mixin']._render_template_qweb(self.base_qweb_bits[1], 'res.partner', res_ids)[res_ids[0]]
|
|
self.assertNotIn('Code not executed', result, 'The condition block did not work')
|