Files
odoo_source/odoo/addons/test_http/tests/test_session.py
T
Julien Castiaux ca5ca24bc6 [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>
2023-03-28 10:30:02 +02:00

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)