From 4e75c63e51e886eb58250fe9cf9a80c08bbbedea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Mon, 29 Jan 2024 13:18:30 +0100 Subject: [PATCH] Include context information in access error We include the date/time when the access error may be due to history and user's groups when it depends on it. Closes #12670 --- trytond/CHANGELOG | 1 + trytond/trytond/ir/message.xml | 6 ++++++ trytond/trytond/ir/model.py | 23 +++++++++++++++++++---- trytond/trytond/model/modelsql.py | 19 ++++++++++++++++++- trytond/trytond/wizard/wizard.py | 19 +++++++++++++++---- 5 files changed, 59 insertions(+), 9 deletions(-) diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG index 3165388392..59be57558a 100644 --- a/trytond/CHANGELOG +++ b/trytond/CHANGELOG @@ -1,3 +1,4 @@ +* Include context information in access error * Remove trytond-worker max tasks per child option for Python <= 3.10 * Do not copy readonly relational fields * Support list of single value in sortable_values tools diff --git a/trytond/trytond/ir/message.xml b/trytond/trytond/ir/message.xml index 1235e0dff9..64d87b19ee 100644 --- a/trytond/trytond/ir/message.xml +++ b/trytond/trytond/ir/message.xml @@ -224,6 +224,12 @@ this repository contains the full copyright notices and license terms. --> You are not allowed to delete records "%(ids)s" of "%(model)s" because of at lease one of those rules: %(rules)s + + At date/time: %(datetime)s + + + Within groups: %(groups)s + Invalid domain in schema "%(schema)s". diff --git a/trytond/trytond/ir/model.py b/trytond/trytond/ir/model.py index 97af35fa76..c71319997b 100644 --- a/trytond/trytond/ir/model.py +++ b/trytond/trytond/ir/model.py @@ -630,11 +630,18 @@ class ModelAccess(DeactivableMixin, ModelSQL, ModelView): or (raise_exception and not transaction.check_access)): return True + User = pool.get('res.user') + Group = pool.get('res.group') + access = cls.get_access([model_name])[model_name][mode] if not access and access is not None: if raise_exception: - raise AccessError(gettext( - 'ir.msg_access_rule_error', **Model.__names__())) + groups = Group.browse(User.get_groups()) + raise AccessError( + gettext('ir.msg_access_rule_error', **Model.__names__()), + gettext( + 'ir.msg_context_groups', + groups=', '.join(g.rec_name for g in groups))) else: return False return True @@ -821,6 +828,9 @@ class ModelFieldAccess(DeactivableMixin, ModelSQL, ModelView): return dict((x, True) for x in fields) return True + User = pool.get('res.user') + Group = pool.get('res.group') + accesses = dict((f, a[mode]) for f, a in cls.get_access([model_name])[model_name].items()) if access: @@ -828,9 +838,14 @@ class ModelFieldAccess(DeactivableMixin, ModelSQL, ModelView): for field in fields: if not accesses.get(field, True): if raise_exception: + groups = Group.browse(User.get_groups()) raise AccessError( - gettext('ir.msg_access_rule_field_error', - **Model.__names__(field))) + gettext( + 'ir.msg_access_rule_field_error', + **Model.__names__(field)), + gettext( + 'ir.msg_context_groups', + groups=', '.join(g.rec_name for g in groups))) else: return False return True diff --git a/trytond/trytond/model/modelsql.py b/trytond/trytond/model/modelsql.py index b4ec568dbe..f7a3f6480a 100644 --- a/trytond/trytond/model/modelsql.py +++ b/trytond/trytond/model/modelsql.py @@ -1549,8 +1549,14 @@ class ModelSQL(ModelStorage): @classmethod def __check_domain_rule(cls, ids, mode): pool = Pool() + Lang = pool.get('ir.lang') Rule = pool.get('ir.rule') Model = pool.get('ir.model') + try: + User = pool.get('res.user') + Group = pool.get('res.group') + except KeyError: + User = Group = None table = cls.__table__() transaction = Transaction() in_max = transaction.database.IN_MAX @@ -1628,7 +1634,18 @@ class ModelSQL(ModelStorage): else: msg = gettext( f'ir.msg_{mode}_error', ids=ids, model=model) - raise AccessError(msg) + + ctx_msg = [] + lang = Lang.get() + if cls._history and transaction.context.get('_datetime'): + ctx_msg.append(gettext('ir.msg_context_datetime', + datetime=lang.strftime( + transaction.context['_datetime']))) + if domain and User and Group: + groups = Group.browse(User.get_groups()) + ctx_msg.append(gettext('ir.msg_context_groups', + groups=', '.join(g.rec_name for g in groups))) + raise AccessError(msg, '\n'.join(ctx_msg)) @classmethod def __search_query(cls, domain, count, query, order): diff --git a/trytond/trytond/wizard/wizard.py b/trytond/trytond/wizard/wizard.py index e454f68d94..4413afabb4 100644 --- a/trytond/trytond/wizard/wizard.py +++ b/trytond/trytond/wizard/wizard.py @@ -251,6 +251,7 @@ class Wizard(URLMixin, PoolBase): ModelAccess = pool.get('ir.model.access') ActionWizard = pool.get('ir.action.wizard') User = pool.get('res.user') + Group = pool.get('res.group') context = Transaction().context if Transaction().user == 0: @@ -265,18 +266,28 @@ class Wizard(URLMixin, PoolBase): models = ActionWizard.get_models( cls.__name__, action_id=context.get('action_id')) if model and models and model not in models: - raise AccessError(gettext( + groups = Group.browse(User.get_groups()) + raise AccessError( + gettext( 'ir.msg_access_wizard_model_error', wizard=cls.__name__, - model=model)) + model=model), + gettext( + 'ir.msg_context_groups', + groups=', '.join(g.rec_name for g in groups))) groups = set(User.get_groups()) wizard_groups = ActionWizard.get_groups(cls.__name__, action_id=context.get('action_id')) if wizard_groups: if not groups & wizard_groups: - raise AccessError(gettext( + groups = Group.browse(User.get_groups()) + raise AccessError( + gettext( 'ir.msg_access_wizard_error', - name=cls.__name__)) + name=cls.__name__), + gettext( + 'ir.msg_context_groups', + groups=', '.join(g.rec_name for g in groups))) elif model and model != 'ir.ui.menu': if (not callable(getattr(Model, 'table_query', None)) or Model.write.__func__ != ModelSQL.write.__func__):