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 to93b684d3c7, 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>
209 lines
7.9 KiB
Python
209 lines
7.9 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import datetime
|
|
import json
|
|
import pytz
|
|
from urllib.parse import urlparse
|
|
from unittest.mock import patch
|
|
|
|
import odoo
|
|
from odoo.tests.common import get_db_name
|
|
from odoo.tools import mute_logger
|
|
from .test_common import TestHttpBase
|
|
|
|
|
|
GEOIP_ODOO_FARM_2 = {
|
|
'city': 'Ramillies',
|
|
'country_code': 'BE',
|
|
'country_name': 'Belgium',
|
|
'latitude': 50.6314,
|
|
'longitude': 4.8573,
|
|
'region': 'WAL',
|
|
'time_zone': 'Europe/Brussels'
|
|
}
|
|
|
|
|
|
class TestHttpSession(TestHttpBase):
|
|
|
|
@mute_logger('odoo.http') # greeting_none called ignoring args {'debug'}
|
|
def test_session0_debug_mode(self):
|
|
session = self.authenticate(None, None)
|
|
self.assertEqual(session.debug, '')
|
|
self.db_url_open('/test_http/greeting').raise_for_status()
|
|
self.assertEqual(session.debug, '')
|
|
self.db_url_open('/test_http/greeting?debug=1').raise_for_status()
|
|
self.assertEqual(session.debug, '1')
|
|
self.db_url_open('/test_http/greeting').raise_for_status()
|
|
self.assertEqual(session.debug, '1')
|
|
self.db_url_open('/test_http/greeting?debug=').raise_for_status()
|
|
self.assertEqual(session.debug, '')
|
|
|
|
def test_session1_default_session(self):
|
|
# The default session should not be saved on the filestore.
|
|
with patch.object(odoo.http.root.session_store, 'save') as mock_save:
|
|
res = self.db_url_open('/test_http/geoip')
|
|
res.raise_for_status()
|
|
try:
|
|
mock_save.assert_not_called()
|
|
except AssertionError as exc:
|
|
msg = f'save() was called with args: {mock_save.call_args}'
|
|
raise AssertionError(msg) from exc
|
|
|
|
def test_session3_logout_15_0_geoip(self):
|
|
session = self.authenticate(None, None)
|
|
session['db'] = 'idontexist'
|
|
session['geoip'] = {} # Until saas-15.2 geoip was directly stored in the session
|
|
odoo.http.root.session_store.save(session)
|
|
|
|
with self.assertLogs('odoo.http', level='WARNING') as (_, warnings):
|
|
res = self.multidb_url_open('/test_http/ensure_db', dblist=['db1', 'db2'])
|
|
|
|
self.assertEqual(warnings, [
|
|
"WARNING:odoo.http:Logged into database 'idontexist', but dbfilter rejects it; logging session out.",
|
|
])
|
|
self.assertFalse(session['db'])
|
|
self.assertEqual(res.status_code, 303)
|
|
self.assertEqual(urlparse(res.headers['Location']).path, '/web/database/selector')
|
|
|
|
def test_session4_web_authenticate_multidb(self):
|
|
self.db_list = [get_db_name(), 'another_database']
|
|
|
|
payload = json.dumps({
|
|
'jsonrpc': '2.0',
|
|
'id': None,
|
|
'method': 'call',
|
|
'params': {
|
|
'db': get_db_name(),
|
|
'login': 'admin',
|
|
'password': 'admin',
|
|
}
|
|
})
|
|
|
|
res = self.multidb_url_open(
|
|
'/web/session/authenticate', data=payload, headers={
|
|
'Content-Type': 'application/json',
|
|
}
|
|
)
|
|
res.raise_for_status()
|
|
self.assertEqual(res.status_code, 200)
|
|
|
|
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')
|
|
|
|
def test_session7_serializable(self):
|
|
"""Tests setting a non-serializable value to the session is prevented
|
|
The test ensures the warning/exception is raised at the moment the attribute is set,
|
|
and not simply when the session is being saved in the session store.
|
|
"""
|
|
session = self.authenticate(None, None)
|
|
self.assertFalse(session.foo)
|
|
|
|
# Values allowed
|
|
for value in [
|
|
123,
|
|
12.3,
|
|
'foo',
|
|
(1, 2, 3, 4),
|
|
[1, 2, 3, 4],
|
|
set(),
|
|
{'1234'},
|
|
datetime.datetime.now(),
|
|
datetime.date.today(),
|
|
datetime.time(1, 33, 7),
|
|
pytz.timezone('UTC'),
|
|
pytz.timezone('Europe/Brussels'),
|
|
]:
|
|
session.foo = value
|
|
self.assertEqual(session.foo, value)
|
|
session.pop('foo')
|
|
self.assertFalse(session.foo)
|
|
session['foo'] = value
|
|
self.assertEqual(session.foo, value)
|
|
session.pop('foo')
|
|
|
|
# Values forbidden by odoo, raising a warning
|
|
for value in [
|
|
str,
|
|
int,
|
|
float,
|
|
bool,
|
|
range,
|
|
"foo".startswith,
|
|
datetime.datetime.strftime,
|
|
]:
|
|
with self.assertLogs(level="WARNING"):
|
|
session['foo'] = value
|
|
self.assertFalse(session.foo)
|
|
with self.assertLogs(level="WARNING"):
|
|
session.foo = value
|
|
self.assertFalse(session.foo)
|
|
with self.assertLogs(level="WARNING"):
|
|
# testing you cannot set a non-serializable value at the creation of the session
|
|
# e.g. in the __init__ of the session class
|
|
self.assertFalse(odoo.http.root.session_store.session_class({'foo': value}, 1234).foo)
|
|
with self.assertRaises(TypeError):
|
|
dict.update(session, foo=value)
|
|
self.assertFalse(session.foo)
|
|
|
|
# Values forbidden by pickle, raising an exception
|
|
for value in [
|
|
lambda: 'bar',
|
|
]:
|
|
with self.assertRaises(AttributeError):
|
|
session['foo'] = value
|
|
self.assertFalse(session.foo)
|
|
with self.assertRaises(AttributeError):
|
|
session.foo = value
|
|
self.assertFalse(session.foo)
|
|
with self.assertRaises(AttributeError):
|
|
# testing you cannot set a non-serializable value at the creation of the session
|
|
# e.g. in the __init__ of the session class
|
|
self.assertFalse(odoo.http.root.session_store.session_class({'foo': value}, 1234).foo)
|
|
with self.assertRaises(TypeError):
|
|
dict.update(session, foo=value)
|
|
self.assertFalse(session.foo)
|