Skip to content

Commit ce4f98a

Browse files
committed
Merge PR #960 into 19.0
Signed-off-by Kev-Roche
2 parents 957944c + 4f30684 commit ce4f98a

11 files changed

Lines changed: 159 additions & 8 deletions

File tree

impersonate_login/README.rst

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ following measures are in place:
4444
- Mails and messages are sent from the original user.
4545
- Impersonated logins are logged and can be consulted through the
4646
Settings -> Technical menu.
47-
-
48-
49-
There is an alternative module to allow logins as another user
50-
(auth_admin_passkey), but it does not support these security mechanisms.
47+
- You can optionally forbid impersonation of users with "Administration:
48+
Settings" rights by enabling the related option in the settings. There
49+
is an alternative module to allow logins as another user
50+
(auth_admin_passkey), but it does not support these security
51+
mechanisms.
5152

5253
**Table of contents**
5354

@@ -59,6 +60,10 @@ Configuration
5960

6061
The impersonating user must belong to group "Impersonate Users".
6162

63+
If you want to prevent impersonation of users with the *Administration:
64+
Settings* rights, enable the *Restrict Impersonation of "Administration:
65+
Settings" Users* option in the settings.
66+
6267
Usage
6368
=====
6469

impersonate_login/__manifest__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"data": [
2121
"security/group.xml",
2222
"security/ir.model.access.csv",
23+
"views/res_config_settings.xml",
2324
"views/res_users.xml",
2425
"views/impersonate_log.xml",
2526
],

impersonate_login/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
from . import mail_message
55
from . import impersonate_log
66
from . import model
7+
from . import res_config_settings

impersonate_login/models/model.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,17 @@
99
class BaseModel(models.AbstractModel):
1010
_inherit = "base"
1111

12+
def _keep_real_user_on_create_write(self):
13+
# Avoid overriding the create_uid and write_uid
14+
# when the model is abstract or transient
15+
if self._abstract or self._transient:
16+
return True
17+
return False
18+
1219
def _prepare_create_values(self, vals_list):
1320
result_vals_list = super()._prepare_create_values(vals_list)
21+
if self._keep_real_user_on_create_write():
22+
return result_vals_list
1423
if (
1524
request
1625
and request.session.get("impersonate_from_uid")
@@ -23,6 +32,8 @@ def _prepare_create_values(self, vals_list):
2332
def write(self, vals):
2433
"""Overwrite the write_uid with the impersonating user"""
2534
res = super().write(vals)
35+
if self._keep_real_user_on_create_write():
36+
return res
2637
if (
2738
request
2839
and request.session.get("impersonate_from_uid")
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from odoo import fields, models
2+
3+
4+
class ResConfigSettings(models.TransientModel):
5+
_inherit = "res.config.settings"
6+
7+
restrict_impersonate_admin_settings = fields.Boolean(
8+
string="Restrict Impersonation of 'Administration: Settings' Users",
9+
config_parameter="impersonate_login.restrict_impersonate_admin_settings",
10+
help=(
11+
"If enabled, users with the 'Administration: Settings' access right"
12+
" cannot be impersonated."
13+
),
14+
default=False,
15+
)

impersonate_login/models/res_users.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,20 @@ def _is_impersonate_user(self):
2424

2525
def impersonate_login(self):
2626
if request:
27+
config_restrict = (
28+
self.env["ir.config_parameter"]
29+
.sudo()
30+
.get_param("impersonate_login.restrict_impersonate_admin_settings")
31+
)
32+
if config_restrict:
33+
admin_settings_group = self.env.ref("base.group_system")
34+
if admin_settings_group in self.group_ids:
35+
raise UserError(
36+
self.env._(
37+
"You cannot impersonate users with"
38+
" 'Administration: Settings' access rights."
39+
)
40+
)
2741
if request.session.get("impersonate_from_uid"):
2842
if self.id == request.session.get("impersonate_from_uid"):
2943
return self.back_to_origin_login()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
The impersonating user must belong to group "Impersonate Users".
2+
3+
If you want to prevent impersonation of users with the *Administration: Settings*
4+
rights, enable the *Restrict Impersonation of "Administration: Settings" Users*
5+
option in the settings.

impersonate_login/readme/DESCRIPTION.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ To ensure that any abuse of this feature will not go unnoticed, the following me
66
* In the chatter, it is displayed who is the user that is logged as another user.
77
* Mails and messages are sent from the original user.
88
* Impersonated logins are logged and can be consulted through the Settings -> Technical menu.
9-
*
9+
* You can optionally forbid impersonation of users with "Administration: Settings"
10+
rights by enabling the related option in the settings.
1011
There is an alternative module to allow logins as another user (auth_admin_passkey),
1112
but it does not support these security mechanisms.

impersonate_login/static/description/index.html

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -386,10 +386,12 @@ <h1>Impersonate Login</h1>
386386
<li>Mails and messages are sent from the original user.</li>
387387
<li>Impersonated logins are logged and can be consulted through the
388388
Settings -&gt; Technical menu.</li>
389-
<li></li>
389+
<li>You can optionally forbid impersonation of users with “Administration:
390+
Settings” rights by enabling the related option in the settings. There
391+
is an alternative module to allow logins as another user
392+
(auth_admin_passkey), but it does not support these security
393+
mechanisms.</li>
390394
</ul>
391-
<p>There is an alternative module to allow logins as another user
392-
(auth_admin_passkey), but it does not support these security mechanisms.</p>
393395
<p><strong>Table of contents</strong></p>
394396
<div class="contents local topic" id="contents">
395397
<ul class="simple">
@@ -407,6 +409,9 @@ <h1>Impersonate Login</h1>
407409
<div class="section" id="configuration">
408410
<h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
409411
<p>The impersonating user must belong to group “Impersonate Users”.</p>
412+
<p>If you want to prevent impersonation of users with the <em>Administration:
413+
Settings</em> rights, enable the <em>Restrict Impersonation of “Administration:
414+
Settings” Users</em> option in the settings.</p>
410415
</div>
411416
<div class="section" id="usage">
412417
<h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>

impersonate_login/tests/test_impersonate_login.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,77 @@ def test_04_write_uid(self):
297297
self.assertEqual(contact.id, contact_id)
298298
self.assertEqual(contact.ref, "abc")
299299
self.assertEqual(contact.write_uid, self.admin_user)
300+
301+
def test_05_create_uid_on_transient_model(self):
302+
"""Check the create_uid of records created
303+
during an impersonated session on a transient model"""
304+
# Login as admin
305+
self.authenticate(user="admin", password="admin")
306+
307+
# Impersonate demo user and create a wizard record
308+
self._impersonate_user(self.demo_user)
309+
310+
response = self.url_open(
311+
"/web/dataset/call_kw/mail.followers.edit/web_save",
312+
data=json.dumps(
313+
{
314+
"params": {
315+
"model": "mail.followers.edit",
316+
"method": "web_save",
317+
"args": [
318+
[],
319+
{
320+
"res_model": "res.partner",
321+
"message": "Hello",
322+
},
323+
{},
324+
],
325+
"kwargs": {},
326+
},
327+
}
328+
),
329+
headers={"Content-Type": "application/json"},
330+
)
331+
self.assertEqual(response.status_code, 200)
332+
data = response.json()
333+
result = data["result"]
334+
settings_id = result[0]["id"]
335+
336+
wizard = self.env["mail.followers.edit"].browse(settings_id)
337+
self.assertIn("Hello", wizard.message)
338+
self.assertEqual(wizard.create_uid, self.demo_user)
339+
340+
def test_06_limit_access_to_admin(self):
341+
"""
342+
Test restriction on impersonating admin users
343+
with 'Administration: Settings' access rights.
344+
"""
345+
config_settings = self.env["res.config.settings"].create(
346+
{"restrict_impersonate_admin_settings": True}
347+
)
348+
config_settings.execute()
349+
350+
config_restrict = (
351+
self.env["ir.config_parameter"]
352+
.sudo()
353+
.get_param("impersonate_login.restrict_impersonate_admin_settings")
354+
)
355+
self.assertTrue(config_restrict)
356+
357+
admin_settings_group = self.env.ref("base.group_system")
358+
self.admin_user.group_ids += admin_settings_group
359+
360+
self.authenticate(user=self.demo_login, password=self.demo_password)
361+
self.assertEqual(self.session.uid, self.demo_user.id)
362+
363+
self.demo_user.group_ids += self.env.ref(
364+
"impersonate_login.group_impersonate_login"
365+
)
366+
367+
with mute_logger("odoo.http"):
368+
data = self._impersonate_user(self.admin_user)
369+
self.assertEqual(
370+
data["error"]["data"]["message"],
371+
"You cannot impersonate users with "
372+
"'Administration: Settings' access rights.",
373+
)

0 commit comments

Comments
 (0)