Files
odoo_source/addons/web/models/ir_http.py
T
Aaron Bohy da257e9d57 [IMP] web: save a has_group rpc at webclient startup
...when voip addon is installed.

We want to avoid predictable rpcs that are systematically done at
webclient startup. In particular, the voip service needs to know if
the user has group "base.group_user", which produces a rpc when
the service is started.

To avoid this, this commit adds the information in the page
directly. As this information is also useful at other places
(even though it's not at startup), we directly add it to the
groupCache of the user service (and we do the same for the
"base.group_system" group, which we also receive in the page).

closes odoo/odoo#139477

Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
2023-10-24 13:55:26 +00:00

203 lines
8.8 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
import hashlib
import json
import odoo
from odoo import api, models
from odoo.http import request, DEFAULT_MAX_CONTENT_LENGTH
from odoo.tools import ormcache, ustr
from odoo.tools.misc import str2bool
"""
Debug mode is stored in session and should always be a string.
It can be activated with an URL query string `debug=<mode>` where mode
is either:
- 'tests' to load tests assets
- 'assets' to load assets non minified
- any other truthy value to enable simple debug mode (to show some
technical feature, to show complete traceback in frontend error..)
- any falsy value to disable debug mode
You can use any truthy/falsy value from `str2bool` (eg: 'on', 'f'..)
Multiple debug modes can be activated simultaneously, separated with a
comma (eg: 'tests, assets').
"""
ALLOWED_DEBUG_MODES = ['', '1', 'assets', 'tests', 'disable-t-cache']
class Http(models.AbstractModel):
_inherit = 'ir.http'
bots = ["bot", "crawl", "slurp", "spider", "curl", "wget", "facebookexternalhit", "whatsapp", "trendsmapresolver", "pinterest", "instagram"]
@classmethod
def is_a_bot(cls):
user_agent = request.httprequest.user_agent.string.lower()
# We don't use regexp and ustr voluntarily
# timeit has been done to check the optimum method
return any(bot in user_agent for bot in cls.bots)
@classmethod
def _handle_debug(cls):
debug = request.httprequest.args.get('debug')
if debug is not None:
request.session.debug = ','.join(
mode if mode in ALLOWED_DEBUG_MODES
else '1' if str2bool(mode, mode)
else ''
for mode in (debug or '').split(',')
)
@classmethod
def _pre_dispatch(cls, rule, args):
super()._pre_dispatch(rule, args)
cls._handle_debug()
@classmethod
def _post_logout(cls):
super()._post_logout()
request.future_response.set_cookie('cids', max_age=0)
def webclient_rendering_context(self):
return {
'menu_data': request.env['ir.ui.menu'].load_menus(request.session.debug),
'session_info': self.session_info(),
}
def session_info(self):
user = self.env.user
session_uid = request.session.uid
version_info = odoo.service.common.exp_version()
if session_uid:
user_context = dict(self.env['res.users'].context_get())
if user_context != request.session.context:
request.session.context = user_context
else:
user_context = {}
IrConfigSudo = self.env['ir.config_parameter'].sudo()
max_file_upload_size = int(IrConfigSudo.get_param(
'web.max_file_upload_size',
default=DEFAULT_MAX_CONTENT_LENGTH,
))
mods = odoo.conf.server_wide_modules or []
if request.db:
mods = list(request.registry._init_modules) + mods
is_internal_user = user.has_group('base.group_user')
session_info = {
"uid": session_uid,
"is_system": user._is_system() if session_uid else False,
"is_admin": user._is_admin() if session_uid else False,
"is_internal_user": is_internal_user,
"user_context": user_context,
"db": self.env.cr.dbname,
"user_settings": self.env['res.users.settings']._find_or_create_for_user(user)._res_users_settings_format(),
"server_version": version_info.get('server_version'),
"server_version_info": version_info.get('server_version_info'),
"support_url": "https://www.odoo.com/buy",
"name": user.name,
"username": user.login,
"partner_display_name": user.partner_id.display_name,
"partner_id": user.partner_id.id if session_uid and user.partner_id else None,
"web.base.url": IrConfigSudo.get_param('web.base.url', default=''),
"active_ids_limit": int(IrConfigSudo.get_param('web.active_ids_limit', default='20000')),
'profile_session': request.session.profile_session,
'profile_collectors': request.session.profile_collectors,
'profile_params': request.session.profile_params,
"max_file_upload_size": max_file_upload_size,
"home_action_id": user.action_id.id,
"cache_hashes": {
"translations": self.env['ir.http'].sudo().get_web_translations_hash(
mods, request.session.context['lang']
) if session_uid else None,
},
"currencies": self.sudo().get_currencies(),
'bundle_params': {
'lang': request.session.context['lang'],
},
}
if request.session.debug:
session_info['bundle_params']['debug'] = request.session.debug
if is_internal_user:
# the following is only useful in the context of a webclient bootstrapping
# 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 = 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({
"load_menus": hashlib.sha512(menu_json_utf8).hexdigest()[:64], # sha512/256
})
# We need sudo since a user may not have access to ancestor companies
disallowed_ancestor_companies_sudo = user.company_ids.sudo().parent_ids - user.company_ids
all_companies_in_hierarchy_sudo = disallowed_ancestor_companies_sudo + user.company_ids
session_info.update({
# current_company should be default_company
"user_companies": {
'current_company': user.company_id.id,
'allowed_companies': {
comp.id: {
'id': comp.id,
'name': comp.name,
'sequence': comp.sequence,
'child_ids': (comp.child_ids & user.company_ids).ids,
'parent_id': comp.parent_id.id,
} for comp in user.company_ids
},
'disallowed_ancestor_companies': {
comp.id: {
'id': comp.id,
'name': comp.name,
'sequence': comp.sequence,
'child_ids': (comp.child_ids & all_companies_in_hierarchy_sudo).ids,
'parent_id': comp.parent_id.id,
} for comp in disallowed_ancestor_companies_sudo
},
},
"show_effect": True,
"display_switch_company_menu": user.has_group('base.group_multi_company') and len(user.company_ids) > 1,
})
return session_info
@api.model
def get_frontend_session_info(self):
user = self.env.user
session_uid = request.session.uid
session_info = {
'is_admin': user._is_admin() if session_uid else False,
'is_system': user._is_system() if session_uid else False,
'is_website_user': user._is_public() if session_uid else False,
'user_id': user.id if session_uid else False,
'is_frontend': True,
'profile_session': request.session.profile_session,
'profile_collectors': request.session.profile_collectors,
'profile_params': request.session.profile_params,
'show_effect': bool(request.env['ir.config_parameter'].sudo().get_param('base_setup.show_effect')),
'currencies': self.get_currencies(),
'bundle_params': {
'lang': request.session.context['lang'],
},
}
if request.session.debug:
session_info['bundle_params']['debug'] = request.session.debug
if session_uid:
version_info = odoo.service.common.exp_version()
session_info.update({
'server_version': version_info.get('server_version'),
'server_version_info': version_info.get('server_version_info')
})
return session_info
@ormcache()
def get_currencies(self):
Currency = self.env['res.currency']
currencies = Currency.search_fetch([], ['symbol', 'position', 'decimal_places'])
return {
c.id: {'symbol': c.symbol, 'position': c.position, 'digits': [69, c.decimal_places]}
for c in currencies
}