diff options
| author | Victor Feyens <vfe@odoo.com> | 2019-10-11 16:14:19 +0000 |
|---|---|---|
| committer | Victor Feyens <vfe@odoo.com> | 2019-10-24 17:12:16 +0000 |
| commit | f76c5a8bbd9a4f639c59b7f602b165c8d38ff360 (patch) | |
| tree | 4dfe9c504dd3d4233c303fb53424c74e875edac0 | |
| parent | ebac65bebf709481561a18a02ed77eba80e73ede (diff) | |
[IMP] ORM: do not allow invalid allowed_company_ids context key.
Since https://github.com/odoo/odoo/commit/a5b6f31cf28e5381e1c85f66730bcdb55998e643,
the current companies of the user are saved in the context as "allowed_company_ids"
context key.
In case of invalid context content, the api was computing the intersection
between the context content and the user company(ies) and falling back on user
company_id(s) when catching an error.
A sanity check was done, but no feedback was given to the user,
saying that the context change was falsy.
This commits changes this behavior to :
* raise an AccessError when trying to access self.env.company(ies) when
invalid or unauthorized companies are defined in the context.
* take sudo mode into consideration, allowing inter-company impacts,
even when current user doesn't have access to a given company,
if the code is done in a sudoed environment.
Co-Authored-By: Raphael Collet <rco@odoo.com>
| -rw-r--r-- | odoo/addons/base/i18n/base.pot | 6 | ||||
| -rw-r--r-- | odoo/api.py | 87 |
2 files changed, 69 insertions, 24 deletions
diff --git a/odoo/addons/base/i18n/base.pot b/odoo/addons/base/i18n/base.pot index 0454f19cb0a..769dc898f55 100644 --- a/odoo/addons/base/i18n/base.pot +++ b/odoo/addons/base/i18n/base.pot @@ -21502,6 +21502,12 @@ msgid "" msgstr "" #. module: base +#: code:addons/api.py:0 +#, python-format +msgid "Access to unauthorized or invalid companies." +msgstr "" + +#. module: base #: code:addons/base/models/ir_rule.py:0 #, python-format msgid "" diff --git a/odoo/api.py b/odoo/api.py index d3766ea3f3e..931dc5ab149 100644 --- a/odoo/api.py +++ b/odoo/api.py @@ -51,6 +51,7 @@ from decorator import decorate, decorator from werkzeug.local import Local, release_local import odoo +from .tools.translate import _ from odoo.tools import frozendict, classproperty, lazy_property, StackMap from odoo.exceptions import CacheMiss @@ -541,35 +542,73 @@ class Environment(Mapping): @lazy_property def company(self): - """ return the company in which the user is logged in (as an instance) """ - company_ids = self.context.get('allowed_company_ids', False) + """Return the current company (as an instance). + + If not specified in the context ('allowed_company_ids'), + fallback on current user main company. + + :raise AccessError: invalid or unauthorized 'allowed_company_ids' context key content. + :return: current company (default=`self.user.company_id`) + :rtype: res.company + + .. warning:: + + No sanity checks applied in sudo mode ! + When in sudo mode, a user can access any company, + even if not in his allowed companies. + + This allows to trigger inter-company modifications, + even if the current user doesn't have access to + the targeted company. + """ + company_ids = self.context.get('allowed_company_ids', []) if company_ids: - company_id = int(company_ids[0]) - if company_id in self.user.company_ids.ids: - return self['res.company'].browse(company_id) + if not self.su: + user_company_ids = self.user.company_ids.ids + if any(cid not in user_company_ids for cid in company_ids): + raise AccessError(_("Access to unauthorized or invalid companies.")) + return self['res.company'].browse(company_ids[0]) return self.user.company_id @lazy_property def companies(self): - """ return a recordset of the enabled companies by the user """ - try: # In case the user tries to bidouille the url (eg: cids=1,foo,bar) - allowed_company_ids = self.context.get('allowed_company_ids') - # Prevent the user to enable companies for which he doesn't have any access - users_company_ids = self.user.company_ids.ids - allowed_company_ids = [company_id for company_id in allowed_company_ids if company_id in users_company_ids] - except Exception: - # By setting the default companies to all user companies instead of the main one - # we save a lot of potential trouble in all "out of context" calls, such as - # /mail/redirect or /web/image, etc. And it is not unsafe because the user does - # have access to these other companies. The risk of exposing foreign records - # (wrt to the context) is low because all normal RPCs will have a proper - # allowed_company_ids. - # Examples: - # - when printing a report for several records from several companies - # - when accessing to a record from the notification email template - # - when loading an binary image on a template - allowed_company_ids = self.user.company_ids.ids - return self['res.company'].browse(allowed_company_ids) + """Return a recordset of the enabled companies by the user. + + If not specified in the context('allowed_company_ids'), + fallback on current user companies. + + :raise AccessError: invalid or unauthorized 'allowed_company_ids' context key content. + :return: current companies (default=`self.user.company_ids`) + :rtype: res.company + + .. warning:: + + No sanity checks applied in sudo mode ! + When in sudo mode, a user can access any company, + even if not in his allowed companies. + + This allows to trigger inter-company modifications, + even if the current user doesn't have access to + the targeted company. + """ + company_ids = self.context.get('allowed_company_ids', []) + if company_ids: + if not self.su: + user_company_ids = self.user.company_ids.ids + if any(cid not in user_company_ids for cid in company_ids): + raise AccessError(_("Access to unauthorized or invalid companies.")) + return self['res.company'].browse(company_ids) + # By setting the default companies to all user companies instead of the main one + # we save a lot of potential trouble in all "out of context" calls, such as + # /mail/redirect or /web/image, etc. And it is not unsafe because the user does + # have access to these other companies. The risk of exposing foreign records + # (wrt to the context) is low because all normal RPCs will have a proper + # allowed_company_ids. + # Examples: + # - when printing a report for several records from several companies + # - when accessing to a record from the notification email template + # - when loading an binary image on a template + return self.user.company_ids @property def lang(self): |
