[FIX] core: check browser lang is installed

A visitor could visit a web page having a lang in its context that is
not installed in the databased he is connected to. The problem is that
visitors are not logged-in thus it is not possible to determine their
lang via their `res.users` preferences. The lang used instead is the
lang set in the `Accept-Language` header of the incoming request, that
header is set by various browsers in accordance to the user system
preferences or browser settings.

The browser lang (`Request.best`) is only parsed according to the
`babel` database, it is a lang syntactically speaking but not necessary
a lang that is installed in the database.

At the moment the browser lang is set in the context (inside of
`Request._get_dbname_and_session`), it is not possible to verify it is
installed in the database as no connection to any database as been
established yet. Instead the lang is validated inside of
`ir.http._pre_dispatch` which is the method responsible to prepare/fix
various stuff on the request/session/context.

In regard to 93b684d3c7, we prefer to fallback on English, hence the
modification in `get_lang`.

closes odoo/odoo#116683

Reference-to: 93b684d3c7 ([FIX] base: lang should fallback on english instead of arab)
X-original-commit: 743e97667e44cdaab6db334d807cf3d409cea621
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
This commit is contained in:
Julien Castiaux
2023-03-28 10:30:02 +02:00
parent 5d0bdd6969
commit ca5ca24bc6
4 changed files with 55 additions and 3 deletions
+6 -1
View File
@@ -29,7 +29,7 @@ from odoo.exceptions import AccessDenied, AccessError, MissingError
from odoo.http import request, Response, ROUTING_KEYS, Stream
from odoo.modules.registry import Registry
from odoo.service import security
from odoo.tools import consteq, submap
from odoo.tools import get_lang, submap
from odoo.tools.translate import code_translations
from odoo.modules.module import get_resource_path, get_module_path
@@ -144,6 +144,11 @@ class IrHttp(models.AbstractModel):
if isinstance(val, models.BaseModel) and isinstance(val._uid, RequestUID):
args[key] = val.with_user(request.env.uid)
# verify the default language set in the context is valid,
# otherwise fallback on the company lang, english or the first
# lang installed
request.update_context(lang=get_lang(request.env)._get_cached('code'))
@classmethod
def _dispatch(cls, endpoint):
result = endpoint(**request.params)
+4
View File
@@ -67,6 +67,10 @@ class TestHttp(http.Controller):
def echo_http_csrf(self, **kwargs):
return str(kwargs)
@http.route('/test_http/echo-http-context-lang', type='http', auth='public', methods=['GET'], csrf=False)
def echo_http_context_lang(self, **kwargs):
return request.env.context.get('lang', '')
@http.route('/test_http/echo-json', type='json', auth='none', methods=['POST'], csrf=False)
def echo_json(self, **kwargs):
return kwargs
@@ -88,3 +88,46 @@ class TestHttpSession(TestHttpBase):
res = self.multidb_url_open('/test_http/greeting-user')
res.raise_for_status()
self.assertEqual(res.status_code, 200, "Should not be redirected to /web/login")
def test_session5_default_lang(self):
self.env['res.lang']._activate_lang('en_US') # default lang
lang_fr = self.env['res.lang']._activate_lang('fr_FR')
with self.subTest(case='no preferred lang'):
res = self.url_open('/test_http/echo-http-context-lang')
self.assertEqual(res.text, 'en_US')
with self.subTest(case='fr preferred and fr_FR enabled'):
res = self.url_open('/test_http/echo-http-context-lang', headers={
'Accept-Language': 'fr',
})
self.assertEqual(res.text, 'fr_FR')
with self.subTest(case='fr preferred but fr_FR disabled'):
lang_fr.active = False
res = self.url_open('/test_http/echo-http-context-lang', headers={
'Accept-Language': 'fr',
})
self.assertEqual(res.text, 'en_US')
def test_session6_saved_lang(self):
session = self.authenticate('demo', 'demo')
self.env['res.lang']._activate_lang('en_US') # default lang
lang_fr = self.env['res.lang']._activate_lang('fr_FR')
with self.subTest(case='no saved lang'):
res = self.url_open('/test_http/echo-http-context-lang')
self.assertEqual(res.text, 'en_US')
with self.subTest(case='fr saved and fr_FR enabled'):
session.context['lang'] = 'fr_FR'
odoo.http.root.session_store.save(session)
res = self.url_open('/test_http/echo-http-context-lang')
self.assertEqual(res.text, 'fr_FR')
with self.subTest(case='fr saved but fr_FR disabled'):
session['lang'] = 'fr_FR'
odoo.http.root.session_store.save(session)
lang_fr.active = False
res = self.url_open('/test_http/echo-http-context-lang')
self.assertEqual(res.text, 'en_US')
+2 -2
View File
@@ -1312,14 +1312,14 @@ def get_lang(env, lang_code=False):
"""
Retrieve the first lang object installed, by checking the parameter lang_code,
the context and then the company. If no lang is installed from those variables,
fallback on the first lang installed in the system.
fallback on english or on the first lang installed in the system.
:param env:
:param str lang_code: the locale (i.e. en_US)
:return res.lang: the first lang found that is installed on the system.
"""
langs = [code for code, _ in env['res.lang'].get_installed()]
lang = langs[0]
lang = 'en_US' if 'en_US' in langs else langs[0]
if lang_code and lang_code in langs:
lang = lang_code
elif env.context.get('lang') in langs: