# -*- coding: utf-8 -*- from odoo import models from odoo.http import request import logging _logger = logging.getLogger(__name__) class IrHttp(models.AbstractModel): _inherit = 'ir.http' @classmethod def _pre_dispatch(cls, rule, args): """ Sanitize allowed_company_ids in the request session, request environment, and request parameters contexts BEFORE the environment is fully used. This prevents AccessError from environments.py when any of these contexts contain company IDs that are not in the user's authorized list. """ try: if request.env.uid: # Use sudo() to query the user's companies safely, bypassing company restrictions. user_cids = set(request.env.user.sudo()._get_company_ids()) default_cid = request.env.user.sudo().company_id.id def sanitize_context_cids(context, user_cids, default_cid): if not isinstance(context, dict) or 'allowed_company_ids' not in context: return False cids = context['allowed_company_ids'] if not isinstance(cids, list): return False valid_cids = [c for c in cids if c in user_cids] if not valid_cids: valid_cids = [default_cid] if valid_cids != cids: _logger.warning("Sanitized allowed_company_ids in context from %s to %s for user %s", cids, valid_cids, request.env.uid) context['allowed_company_ids'] = valid_cids return True return False # Sanitize session context if getattr(request, 'session', None) and getattr(request.session, 'context', None): sanitize_context_cids(request.session.context, user_cids, default_cid) # Sanitize request.env.context env_context = dict(request.env.context) if sanitize_context_cids(env_context, user_cids, default_cid): request.update_context(**env_context) # Sanitize request params root context if isinstance(getattr(request, 'params', None), dict): root_context = request.params.get('context') if isinstance(root_context, dict): sanitize_context_cids(root_context, user_cids, default_cid) # Sanitize request params kwargs context kwargs = request.params.get('kwargs') if isinstance(kwargs, dict): kwargs_context = kwargs.get('context') if isinstance(kwargs_context, dict): sanitize_context_cids(kwargs_context, user_cids, default_cid) except Exception as e: _logger.warning("IrHttp: could not sanitize company context in _pre_dispatch: %s", e, exc_info=True) return super()._pre_dispatch(rule, args) @classmethod def _dispatch(cls, endpoint): """ Sanitize allowed_company_ids in the request parameters (request.params) context AFTER they have been populated by the JSON-RPC dispatcher but BEFORE the controller/model method is executed. """ try: if request.env.uid: # Use sudo() to query the user's companies safely, bypassing company restrictions. user_cids = set(request.env.user.sudo()._get_company_ids()) default_cid = request.env.user.sudo().company_id.id def sanitize_context_cids(context, user_cids, default_cid): if not isinstance(context, dict) or 'allowed_company_ids' not in context: return False cids = context['allowed_company_ids'] if not isinstance(cids, list): return False valid_cids = [c for c in cids if c in user_cids] if not valid_cids: valid_cids = [default_cid] if valid_cids != cids: _logger.warning("Sanitized allowed_company_ids in _dispatch from %s to %s for user %s", cids, valid_cids, request.env.uid) context['allowed_company_ids'] = valid_cids return True return False # Sanitize request params root context if isinstance(getattr(request, 'params', None), dict): root_context = request.params.get('context') if isinstance(root_context, dict): sanitize_context_cids(root_context, user_cids, default_cid) # Sanitize request params kwargs context kwargs = request.params.get('kwargs') if isinstance(kwargs, dict): kwargs_context = kwargs.get('context') if isinstance(kwargs_context, dict): sanitize_context_cids(kwargs_context, user_cids, default_cid) except Exception as e: _logger.warning("IrHttp: could not sanitize company context in _dispatch: %s", e, exc_info=True) return super()._dispatch(endpoint)