From 4174b330f780df676a17fc41d4d30391ee3809e8 Mon Sep 17 00:00:00 2001 From: Julien Castiaux Date: Fri, 2 Sep 2022 11:23:28 +0000 Subject: [PATCH] [FIX] http_routing: /r russian vs /r link tracker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Install website_links, create a link e.g. to http://example.com, install the russian language and translate the default website. Access the short link you created before-hand, 404 website page not found. Accessing a website starting with /r is ambiguous, is /r the link-tracker controller or is /r a russian lang alias (nearest lang algorithm)? The controller should be prioritary to the lang alias. This restore the behavior as it was before the httpocalyse. closes odoo/odoo#99555 X-original-commit: e8a1b4c0cdffb38e48ca980205a61c75c564dee8 Signed-off-by: Jérémy Kersten Signed-off-by: Julien Castiaux --- addons/http_routing/models/ir_http.py | 11 +-- addons/website_links/tests/__init__.py | 1 + addons/website_links/tests/test_controller.py | 67 +++++++++++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 addons/website_links/tests/test_controller.py diff --git a/addons/http_routing/models/ir_http.py b/addons/http_routing/models/ir_http.py index ebe37919fee..d75608f1205 100644 --- a/addons/http_routing/models/ir_http.py +++ b/addons/http_routing/models/ir_http.py @@ -402,16 +402,19 @@ class IrHttp(models.AbstractModel): return super()._match(path) # See /1, match a non website endpoint - with contextlib.suppress(NotFound): + try: rule, args = super()._match(path) routing = rule.endpoint.routing request.is_frontend = routing.get('website', False) request.is_frontend_multilang = request.is_frontend and routing.get('multilang', routing['type'] == 'http') if not request.is_frontend: return rule, args - - _, url_lang_str, *rest = path.split('/', 2) - path_no_lang = '/' + (rest[0] if rest else '') + except NotFound: + _, url_lang_str, *rest = path.split('/', 2) + path_no_lang = '/' + (rest[0] if rest else '') + else: + url_lang_str = '' + path_no_lang = path allow_redirect = request.httprequest.method != 'POST' # There is no user on the environment yet but the following code diff --git a/addons/website_links/tests/__init__.py b/addons/website_links/tests/__init__.py index 1e046233e6c..6ec97095797 100644 --- a/addons/website_links/tests/__init__.py +++ b/addons/website_links/tests/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- +from . import test_controller from . import test_ui diff --git a/addons/website_links/tests/test_controller.py b/addons/website_links/tests/test_controller.py new file mode 100644 index 00000000000..2e055cea56f --- /dev/null +++ b/addons/website_links/tests/test_controller.py @@ -0,0 +1,67 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.tests import tagged +from odoo.tests.common import HttpCase + + +@tagged('post_install', '-at_install') +class TestWebsiteLinksRussian(HttpCase): + """ + The /r URL prefix is considered as an alias to /ru by the "nearest + lang" algorithm of our http router (http_routing match). This test + suite makes sure that there the link-tracker "/r" controller is not + affected by any (wrong) /ru redirection. + """ + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.link = cls.env['link.tracker'].create({ + 'url': cls.base_url() + '/web/health', # no-op route + }) + + # Courtesy of website/tests/test_lang_url.py + website = cls.env.ref('website.default_website') + lang_en = cls.env.ref('base.lang_en') + lang_ru = cls.env['res.lang']._activate_lang('ru_RU') + website.language_ids = lang_en + lang_ru + website.default_lang_id = lang_en + + def test0_direct_link_tracker(self): + res = self.url_open(f'/r/{self.link.code}', allow_redirects=False) + res.raise_for_status() + self.assertEqual(res.status_code, 301, "Should be link-tracking redirection") + self.assertEqual(res.headers.get('Location'), self.link.url, + "Should not be redirected to /ru") + + def test1_russian_link_tracker(self): + res = self.url_open(f'/r/r/{self.link.code}', allow_redirects=False) + res.raise_for_status() + self.assertEqual(res.status_code, 301, "Should be a lang alias redirection") + self.assertEqual(res.headers.get('Location'), f'{self.base_url()}/ru/r/{self.link.code}', + "Should be redirected to /ru as r is an alias for ru (russian)") + + res = self.url_open(res.headers['Location'], allow_redirects=False) + res.raise_for_status() + self.assertEqual(res.status_code, 301, "Should be a link-tracking redirection") + self.assertEqual(res.headers.get('Location'), self.link.url, + "Should not be redirected to /ru") + + def test2_russian_page(self): + # This generate a new unused link + no_link_code = self.env['link.tracker.code']._get_random_code_strings()[0] + + view = self.env['ir.ui.view'].create({ + 'name': 'Base', + 'type': 'qweb', + 'arch': '
Welcome to this webpage!
', + 'key': 'test.base_view', + }) + self.env['website.page'].create({ + 'url': f'/{no_link_code}', + 'view_id': view.id, + 'website_published': True, + }) + + res = self.url_open(f'/r/{no_link_code}', allow_redirects=False) + self.assertEqual(res.status_code, 404, "No link tracker exists for the requested code")