From 28ed4eddea82c84b8c4511f5f1ac2fc5ae719861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 17 Oct 2022 12:10:49 +0000 Subject: [PATCH] [FIX] portal: check model is valid when entering mail redirect controller When mail_action_view() calls this function, the model may not be a valid one or even be missing. So we check if the model exists before checking its type. We also correctly add kwargs parameters propagation. Add tests. Task-3025143 X-original-commit: b5d9f42ddc2dfde21714544add72023004069815 Part-of: odoo/odoo#103378 Co-authored-by: Krzysztof Magusiak --- addons/portal/controllers/mail.py | 6 +- addons/test_mail_full/tests/test_portal.py | 86 ++++++++++++++++++---- 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/addons/portal/controllers/mail.py b/addons/portal/controllers/mail.py index da6ce1404cf..c11b12f7b0e 100644 --- a/addons/portal/controllers/mail.py +++ b/addons/portal/controllers/mail.py @@ -238,6 +238,10 @@ class MailController(mail.MailController): If so, those two parameters are used to authentify the recipient in the chatter, if any. :return: """ + # no model / res_id, meaning no possible record -> direct skip to super + if not model or not res_id or model not in request.env: + return super(MailController, cls)._redirect_to_record(model, res_id, access_token=access_token, **kwargs) + if issubclass(type(request.env[model]), request.env.registry['portal.mixin']): uid = request.session.uid or request.env.ref('base.public_user').id record_sudo = request.env[model].sudo().browse(res_id).exists() @@ -257,4 +261,4 @@ class MailController(mail.MailController): url_params.update([("pid", pid), ("hash", hash)]) url = url.replace(query=urls.url_encode(url_params)).to_url() return request.redirect(url) - return super(MailController, cls)._redirect_to_record(model, res_id, access_token=access_token) + return super(MailController, cls)._redirect_to_record(model, res_id, access_token=access_token, **kwargs) diff --git a/addons/test_mail_full/tests/test_portal.py b/addons/test_mail_full/tests/test_portal.py index 7a725ca3a9d..1a83d25d343 100644 --- a/addons/test_mail_full/tests/test_portal.py +++ b/addons/test_mail_full/tests/test_portal.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from werkzeug.urls import url_parse, url_decode + import json from odoo import http @@ -23,6 +25,56 @@ class TestPortal(HttpCase, TestMailFullCommon, TestSMSRecipients): self.record_portal._portal_ensure_token() + +@tagged('-at_install', 'post_install', 'portal') +class TestPortalControllers(TestPortal): + + def test_redirect_to_records(self): + """ Test redirection of portal-enabled records """ + # Test Case 0: as anonymous, cannot access, redirect to web/login + response = self.url_open('/mail/view?model=%s&res_id=%s' % ( + self.record_portal._name, + self.record_portal.id), timeout=15) + + path = url_parse(response.url).path + self.assertEqual(path, '/web/login') + + # Test Case 1: as admin, can access record + self.authenticate(self.user_admin.login, self.user_admin.login) + response = self.url_open('/mail/view?model=%s&res_id=%s' % ( + self.record_portal._name, + self.record_portal.id), timeout=15) + + self.assertEqual(response.status_code, 200) + + fragment = url_parse(response.url).fragment + params = url_decode(fragment) + self.assertEqual(params['cids'], '%s' % self.user_admin.company_id.id) + self.assertEqual(params['id'], '%s' % self.record_portal.id) + self.assertEqual(params['model'], self.record_portal._name) + + def test_redirect_to_records_norecord(self): + """ Check specific use case of missing model, should directly redirect + to login page. """ + for model, res_id in [ + (False, self.record_portal.id), + ('', self.record_portal.id), + (self.record_portal._name, False), + (self.record_portal._name, ''), + (False, False), + ('wrong.model', self.record_portal.id), + (self.record_portal._name, -4), + ]: + response = self.url_open( + '/mail/view?model=%s&res_id=%s' % (model, res_id), + timeout=15 + ) + path = url_parse(response.url).path + self.assertEqual( + path, '/web/login', + 'Failed with %s - %s' % (model, res_id) + ) + def test_portal_message_fetch(self): """Test retrieving chatter messages through the portal controller""" self.authenticate(None, None) @@ -65,21 +117,6 @@ class TestPortal(HttpCase, TestMailFullCommon, TestSMSRecipients): # Empty messages should be ignored self.assertEqual(get_chatter_message_count(), 5) - @users('employee') - def test_portal_mixin(self): - """ Test internals of portal mixin """ - customer = self.partner_1.with_env(self.env) - record_portal = self.env['mail.test.portal'].create({ - 'partner_id': customer.id, - 'name': 'Test Portal Record', - }) - - self.assertFalse(record_portal.access_token) - self.assertEqual(record_portal.access_url, '/my/test_portal/%s' % record_portal.id) - - record_portal._portal_ensure_token() - self.assertTrue(record_portal.access_token) - def test_portal_share_comment(self): """ Test posting through portal controller allowing to use a hash to post wihtout access rights. """ @@ -106,3 +143,22 @@ class TestPortal(HttpCase, TestMailFullCommon, TestSMSRecipients): self.assertIn('Test', message.body) self.assertEqual(message.author_id, self.partner_2) + + +@tagged('portal') +class TestPortalMixin(TestPortal): + + @users('employee') + def test_portal_mixin(self): + """ Test internals of portal mixin """ + customer = self.partner_1.with_env(self.env) + record_portal = self.env['mail.test.portal'].create({ + 'partner_id': customer.id, + 'name': 'Test Portal Record', + }) + + self.assertFalse(record_portal.access_token) + self.assertEqual(record_portal.access_url, '/my/test_portal/%s' % record_portal.id) + + record_portal._portal_ensure_token() + self.assertTrue(record_portal.access_token)