[FIX] web, *: multi-db /web/session/authenticate

*: base_setup, hr_timesheet, mail, partner_autocomplete, web_tour

Start odoo without -d and with a --dbfilter that allows multiple
databases. Via JSON-RPC access the /web/session/authenticate route
providing a non-filtered database and valid credentials. Traceback,
`request.env` is None.

Since httpocalypse the initialization of the ORM (cursor, registry,
environment) is greedy. It means that the connection to the database is
established very early during the request routing or skip altogether in
case no dbname was known at that time. This contrast with prepocalypse
where the various ORM thingies were lazily setup the first time they
were accessed.

This changement has an important implication regarding authentication.

In prepocalypse, thanks to the lazy approache, a cursor/registry/env
would be setup on the database you just login upon using the
`request.env` for the first time. This was very nice in this regard but
had other problems.

Since httpocalypse such operation is no more possible. Devs must
initialize and use their own cursor/registry/env in case they
authenticate on another database than the one `request.cr` is (maybe)
connected to.

The `/web/session/authenticate` controller is an example of such case.
It crates its own cr/registry/environment after authentication. The
problem the controller uses `ir.http.session_info` and that not all
overrides were updated to use `self.env` (=the env created in the web
controller) instead of `request.env` (=the missing env of the request).

closes odoo/odoo#108063

X-original-commit: 7b9bd9d37731fae724dc5d91da656dab70aa9ad4
Related: odoo/enterprise#35012
Signed-off-by: Julien Castiaux <juc@odoo.com>
This commit is contained in:
Julien Castiaux
2022-12-15 15:45:07 +01:00
parent 8d3940eab9
commit 5502313853
10 changed files with 53 additions and 20 deletions
+2 -3
View File
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
from odoo.http import request
class IrHttp(models.AbstractModel):
@@ -9,6 +8,6 @@ class IrHttp(models.AbstractModel):
def session_info(self):
result = super(IrHttp, self).session_info()
if request.env.user._is_internal():
result['show_effect'] = bool(request.env['ir.config_parameter'].sudo().get_param('base_setup.show_effect'))
if self.env.user._is_internal():
result['show_effect'] = bool(self.env['ir.config_parameter'].sudo().get_param('base_setup.show_effect'))
return result
+2 -2
View File
@@ -9,10 +9,10 @@ class IrHttp(models.AbstractModel):
def session_info(self):
result = super().session_info()
result['dbuuid'] = request.env['ir.config_parameter'].sudo().get_param('database.uuid')
result['dbuuid'] = self.env['ir.config_parameter'].sudo().get_param('database.uuid')
return result
def get_frontend_session_info(self):
result = super().get_frontend_session_info()
result['dbuuid'] = request.env['ir.config_parameter'].sudo().get_param('database.uuid')
result['dbuuid'] = self.env['ir.config_parameter'].sudo().get_param('database.uuid')
return result
+3 -4
View File
@@ -2,7 +2,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models
from odoo.http import request
class Http(models.AbstractModel):
@@ -13,8 +12,8 @@ class Http(models.AbstractModel):
widget to apply, depending on the current company.
"""
result = super(Http, self).session_info()
if request.env.user._is_internal():
company_ids = request.env.user.company_ids
if self.env.user._is_internal():
company_ids = self.env.user.company_ids
for company in company_ids:
result["user_companies"]["allowed_companies"][company.id].update({
@@ -30,7 +29,7 @@ class Http(models.AbstractModel):
@api.model
def get_timesheet_uoms(self):
company_ids = request.env.user.company_ids
company_ids = self.env.user.company_ids
uom_ids = company_ids.mapped('timesheet_encode_uom_id') | \
company_ids.mapped('project_time_mode_id')
return {
+2 -2
View File
@@ -9,7 +9,7 @@ class IrHttp(models.AbstractModel):
_inherit = 'ir.http'
def session_info(self):
user = request.env.user
user = self.env.user
result = super(IrHttp, self).session_info()
if self.env.user._is_internal():
result['notification_type'] = user.notification_type
@@ -18,7 +18,7 @@ class IrHttp(models.AbstractModel):
user_context = {'lang': guest.lang}
mods = odoo.conf.server_wide_modules or []
lang = user_context.get("lang")
translation_hash = request.env['ir.http'].sudo().get_web_translations_hash(mods, lang)
translation_hash = self.env['ir.http'].sudo().get_web_translations_hash(mods, lang)
result['cache_hashes']['translations'] = translation_hash
result.update({
'name': guest.name,
@@ -2,7 +2,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
from odoo.http import request
class Http(models.AbstractModel):
@@ -12,5 +11,5 @@ class Http(models.AbstractModel):
""" Add information about iap enrich to perform """
session_info = super(Http, self).session_info()
if session_info.get('is_admin'):
session_info['iap_company_enrich'] = not request.env.user.company_id.iap_enrich_auto_done
session_info['iap_company_enrich'] = not self.env.user.company_id.iap_enrich_auto_done
return session_info
+8
View File
@@ -40,6 +40,14 @@ class Session(http.Controller):
registry = odoo.modules.registry.Registry(db)
with registry.cursor() as cr:
env = odoo.api.Environment(cr, request.session.uid, request.session.context)
if not request.db and not request.session.is_explicit:
# request._save_session would not update the session_token
# as it lacks an environment, rotating the session myself
http.root.session_store.rotate(request.session, env)
request.future_response.set_cookie(
'session_id', request.session.sid,
max_age=http.SESSION_LIFETIME, httponly=True
)
return env['ir.http'].session_info()
@http.route('/web/session/get_lang_list', type='json', auth="none")
+5 -5
View File
@@ -65,7 +65,7 @@ class Http(models.AbstractModel):
}
def session_info(self):
user = request.env.user
user = self.env.user
session_uid = request.session.uid
version_info = odoo.service.common.exp_version()
@@ -89,7 +89,7 @@ class Http(models.AbstractModel):
"is_system": user._is_system() if session_uid else False,
"is_admin": user._is_admin() if session_uid else False,
"user_context": user_context,
"db": request.db,
"db": self.env.cr.dbname,
"server_version": version_info.get('server_version'),
"server_version_info": version_info.get('server_version_info'),
"support_url": "https://www.odoo.com/buy",
@@ -106,7 +106,7 @@ class Http(models.AbstractModel):
"max_file_upload_size": max_file_upload_size,
"home_action_id": user.action_id.id,
"cache_hashes": {
"translations": request.env['ir.http'].sudo().get_web_translations_hash(
"translations": self.env['ir.http'].sudo().get_web_translations_hash(
mods, request.session.context['lang']
) if session_uid else None,
},
@@ -122,7 +122,7 @@ class Http(models.AbstractModel):
# but is still included in some other calls (e.g. '/web/session/authenticate')
# to avoid access errors and unnecessary information, it is only included for users
# with access to the backend ('internal'-type users)
menus = request.env['ir.ui.menu'].load_menus(request.session.debug)
menus = self.env['ir.ui.menu'].load_menus(request.session.debug)
ordered_menus = {str(k): v for k, v in menus.items()}
menu_json_utf8 = json.dumps(ordered_menus, default=ustr, sort_keys=True).encode()
session_info['cache_hashes'].update({
@@ -174,6 +174,6 @@ class Http(models.AbstractModel):
return session_info
def get_currencies(self):
Currency = request.env['res.currency']
Currency = self.env['res.currency']
currencies = Currency.search([]).read(['symbol', 'position', 'decimal_places'])
return {c['id']: {'symbol': c['symbol'], 'position': c['position'], 'digits': [69,c['decimal_places']]} for c in currencies}
+1 -2
View File
@@ -2,7 +2,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
from odoo.http import request
class Http(models.AbstractModel):
@@ -12,6 +11,6 @@ class Http(models.AbstractModel):
result = super().session_info()
if result['is_admin']:
demo_modules_count = self.env['ir.module.module'].sudo().search_count([('demo', '=', True)])
result['web_tours'] = request.env['web_tour.tour'].get_consumed_tours()
result['web_tours'] = self.env['web_tour.tour'].get_consumed_tours()
result['tour_disable'] = demo_modules_count > 0
return result
@@ -1,9 +1,11 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import json
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
@@ -91,3 +93,29 @@ class TestHttpSession(TestHttpBase):
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")
+1
View File
@@ -857,6 +857,7 @@ class FilesystemSessionStore(sessions.FilesystemSessionStore):
session.sid = self.generate_key()
if session.uid and env:
session.session_token = security.compute_session_token(session, env)
session.should_rotate = False
self.save(session)
def vacuum(self):