From 024588dc20922680b1d08141d40b6e20c9708af4 Mon Sep 17 00:00:00 2001 From: Kev-Roche Date: Wed, 18 Mar 2026 11:32:40 +0100 Subject: [PATCH 1/2] [18.0][FIX] impersonate_login access issues --- impersonate_login/models/model.py | 11 ++++++ .../tests/test_impersonate_login.py | 39 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/impersonate_login/models/model.py b/impersonate_login/models/model.py index 62803ea9d2..b803614545 100644 --- a/impersonate_login/models/model.py +++ b/impersonate_login/models/model.py @@ -9,8 +9,17 @@ class BaseModel(models.AbstractModel): _inherit = "base" + def _keep_real_user_on_create_write(self): + # Avoid overriding the create_uid and write_uid + # when the model is abstract or transient + if self._abstract or self._transient: + return True + return False + def _prepare_create_values(self, vals_list): result_vals_list = super()._prepare_create_values(vals_list) + if self._keep_real_user_on_create_write(): + return result_vals_list if ( request and request.session.get("impersonate_from_uid") @@ -23,6 +32,8 @@ def _prepare_create_values(self, vals_list): def write(self, vals): """Overwrite the write_uid with the impersonating user""" res = super().write(vals) + if self._keep_real_user_on_create_write(): + return res if ( request and request.session.get("impersonate_from_uid") diff --git a/impersonate_login/tests/test_impersonate_login.py b/impersonate_login/tests/test_impersonate_login.py index fe18c0024c..ac71803481 100644 --- a/impersonate_login/tests/test_impersonate_login.py +++ b/impersonate_login/tests/test_impersonate_login.py @@ -297,3 +297,42 @@ def test_04_write_uid(self): self.assertEqual(contact.id, contact_id) self.assertEqual(contact.ref, "abc") self.assertEqual(contact.write_uid, self.admin_user) + + def test_05_create_uid_on_transient_model(self): + """Check the create_uid of records created + during an impersonated session on a transient model""" + # Login as admin + self.authenticate(user="admin", password="admin") + + # Impersonate demo user and create a wizard record + self._impersonate_user(self.demo_user) + + response = self.url_open( + "/web/dataset/call_kw/mail.followers.edit/web_save", + data=json.dumps( + { + "params": { + "model": "mail.followers.edit", + "method": "web_save", + "args": [ + [], + { + "res_model": "res.partner", + "message": "Hello", + }, + {}, + ], + "kwargs": {}, + }, + } + ), + headers={"Content-Type": "application/json"}, + ) + self.assertEqual(response.status_code, 200) + data = response.json() + result = data["result"] + settings_id = result[0]["id"] + + wizard = self.env["mail.followers.edit"].browse(settings_id) + self.assertIn("Hello", wizard.message) + self.assertEqual(wizard.create_uid, self.demo_user) From 4f30684e2346280ac5d0d305ae3a4c428f848bae Mon Sep 17 00:00:00 2001 From: Pablo Castelo Date: Thu, 20 Nov 2025 17:14:11 +0100 Subject: [PATCH 2/2] [IMP] impersonate_login: restrict impersonate admins --- impersonate_login/README.rst | 13 ++++--- impersonate_login/__manifest__.py | 1 + impersonate_login/models/__init__.py | 1 + .../models/res_config_settings.py | 15 ++++++++ impersonate_login/models/res_users.py | 14 ++++++++ impersonate_login/readme/CONFIGURE.md | 4 +++ impersonate_login/readme/DESCRIPTION.md | 3 +- .../static/description/index.html | 11 ++++-- .../tests/test_impersonate_login.py | 35 +++++++++++++++++++ .../views/res_config_settings.xml | 20 +++++++++++ 10 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 impersonate_login/models/res_config_settings.py create mode 100644 impersonate_login/views/res_config_settings.xml diff --git a/impersonate_login/README.rst b/impersonate_login/README.rst index 0c495cb23a..a09c478141 100644 --- a/impersonate_login/README.rst +++ b/impersonate_login/README.rst @@ -44,10 +44,11 @@ following measures are in place: - Mails and messages are sent from the original user. - Impersonated logins are logged and can be consulted through the Settings -> Technical menu. -- - -There is an alternative module to allow logins as another user -(auth_admin_passkey), but it does not support these security mechanisms. +- You can optionally forbid impersonation of users with "Administration: + Settings" rights by enabling the related option in the settings. There + is an alternative module to allow logins as another user + (auth_admin_passkey), but it does not support these security + mechanisms. **Table of contents** @@ -59,6 +60,10 @@ Configuration The impersonating user must belong to group "Impersonate Users". +If you want to prevent impersonation of users with the *Administration: +Settings* rights, enable the *Restrict Impersonation of "Administration: +Settings" Users* option in the settings. + Usage ===== diff --git a/impersonate_login/__manifest__.py b/impersonate_login/__manifest__.py index 7e70327f53..4128a100f2 100644 --- a/impersonate_login/__manifest__.py +++ b/impersonate_login/__manifest__.py @@ -20,6 +20,7 @@ "data": [ "security/group.xml", "security/ir.model.access.csv", + "views/res_config_settings.xml", "views/res_users.xml", "views/impersonate_log.xml", ], diff --git a/impersonate_login/models/__init__.py b/impersonate_login/models/__init__.py index debb66e9c1..d483c409d4 100644 --- a/impersonate_login/models/__init__.py +++ b/impersonate_login/models/__init__.py @@ -4,3 +4,4 @@ from . import mail_message from . import impersonate_log from . import model +from . import res_config_settings diff --git a/impersonate_login/models/res_config_settings.py b/impersonate_login/models/res_config_settings.py new file mode 100644 index 0000000000..04fbf9571f --- /dev/null +++ b/impersonate_login/models/res_config_settings.py @@ -0,0 +1,15 @@ +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + restrict_impersonate_admin_settings = fields.Boolean( + string="Restrict Impersonation of 'Administration: Settings' Users", + config_parameter="impersonate_login.restrict_impersonate_admin_settings", + help=( + "If enabled, users with the 'Administration: Settings' access right" + " cannot be impersonated." + ), + default=False, + ) diff --git a/impersonate_login/models/res_users.py b/impersonate_login/models/res_users.py index 2b2ea96984..ed0ccca8a9 100644 --- a/impersonate_login/models/res_users.py +++ b/impersonate_login/models/res_users.py @@ -24,6 +24,20 @@ def _is_impersonate_user(self): def impersonate_login(self): if request: + config_restrict = ( + self.env["ir.config_parameter"] + .sudo() + .get_param("impersonate_login.restrict_impersonate_admin_settings") + ) + if config_restrict: + admin_settings_group = self.env.ref("base.group_system") + if admin_settings_group in self.group_ids: + raise UserError( + self.env._( + "You cannot impersonate users with" + " 'Administration: Settings' access rights." + ) + ) if request.session.get("impersonate_from_uid"): if self.id == request.session.get("impersonate_from_uid"): return self.back_to_origin_login() diff --git a/impersonate_login/readme/CONFIGURE.md b/impersonate_login/readme/CONFIGURE.md index 33ebb6904a..649fb31c76 100644 --- a/impersonate_login/readme/CONFIGURE.md +++ b/impersonate_login/readme/CONFIGURE.md @@ -1 +1,5 @@ The impersonating user must belong to group "Impersonate Users". + +If you want to prevent impersonation of users with the *Administration: Settings* +rights, enable the *Restrict Impersonation of "Administration: Settings" Users* +option in the settings. diff --git a/impersonate_login/readme/DESCRIPTION.md b/impersonate_login/readme/DESCRIPTION.md index cee4996cfb..757ab10e2b 100644 --- a/impersonate_login/readme/DESCRIPTION.md +++ b/impersonate_login/readme/DESCRIPTION.md @@ -6,6 +6,7 @@ To ensure that any abuse of this feature will not go unnoticed, the following me * In the chatter, it is displayed who is the user that is logged as another user. * Mails and messages are sent from the original user. * Impersonated logins are logged and can be consulted through the Settings -> Technical menu. -* +* You can optionally forbid impersonation of users with "Administration: Settings" + rights by enabling the related option in the settings. There is an alternative module to allow logins as another user (auth_admin_passkey), but it does not support these security mechanisms. diff --git a/impersonate_login/static/description/index.html b/impersonate_login/static/description/index.html index f659e67e1a..b4033662cd 100644 --- a/impersonate_login/static/description/index.html +++ b/impersonate_login/static/description/index.html @@ -386,10 +386,12 @@

Impersonate Login

  • Mails and messages are sent from the original user.
  • Impersonated logins are logged and can be consulted through the Settings -> Technical menu.
  • -
  • +
  • You can optionally forbid impersonation of users with “Administration: +Settings” rights by enabling the related option in the settings. There +is an alternative module to allow logins as another user +(auth_admin_passkey), but it does not support these security +mechanisms.
  • -

    There is an alternative module to allow logins as another user -(auth_admin_passkey), but it does not support these security mechanisms.

    Table of contents

      @@ -407,6 +409,9 @@

      Impersonate Login

      Configuration

      The impersonating user must belong to group “Impersonate Users”.

      +

      If you want to prevent impersonation of users with the Administration: +Settings rights, enable the Restrict Impersonation of “Administration: +Settings” Users option in the settings.

      Usage

      diff --git a/impersonate_login/tests/test_impersonate_login.py b/impersonate_login/tests/test_impersonate_login.py index ac71803481..c99dff0423 100644 --- a/impersonate_login/tests/test_impersonate_login.py +++ b/impersonate_login/tests/test_impersonate_login.py @@ -336,3 +336,38 @@ def test_05_create_uid_on_transient_model(self): wizard = self.env["mail.followers.edit"].browse(settings_id) self.assertIn("Hello", wizard.message) self.assertEqual(wizard.create_uid, self.demo_user) + + def test_06_limit_access_to_admin(self): + """ + Test restriction on impersonating admin users + with 'Administration: Settings' access rights. + """ + config_settings = self.env["res.config.settings"].create( + {"restrict_impersonate_admin_settings": True} + ) + config_settings.execute() + + config_restrict = ( + self.env["ir.config_parameter"] + .sudo() + .get_param("impersonate_login.restrict_impersonate_admin_settings") + ) + self.assertTrue(config_restrict) + + admin_settings_group = self.env.ref("base.group_system") + self.admin_user.group_ids += admin_settings_group + + self.authenticate(user=self.demo_login, password=self.demo_password) + self.assertEqual(self.session.uid, self.demo_user.id) + + self.demo_user.group_ids += self.env.ref( + "impersonate_login.group_impersonate_login" + ) + + with mute_logger("odoo.http"): + data = self._impersonate_user(self.admin_user) + self.assertEqual( + data["error"]["data"]["message"], + "You cannot impersonate users with " + "'Administration: Settings' access rights.", + ) diff --git a/impersonate_login/views/res_config_settings.xml b/impersonate_login/views/res_config_settings.xml new file mode 100644 index 0000000000..3768afdb73 --- /dev/null +++ b/impersonate_login/views/res_config_settings.xml @@ -0,0 +1,20 @@ + + + res.config.settings.impersonate + res.config.settings + + + + + + + + + + + +