Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions impersonate_login/README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

=================
Impersonate Login
=================
Expand All @@ -17,7 +13,7 @@ Impersonate Login
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github
Expand Down Expand Up @@ -64,12 +60,23 @@ 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.

Each user also has an *Impersonation* setting, on the *Access Rights*
tab of the user form (administrators only):

- *Default*: follow the general setting above;
- *Allowed*: the user can always be impersonated, even with the
*Administration: Settings* rights;
- *Forbidden*: the user can never be impersonated.

Usage
=====

1. In the menu that is displayed when clicking on the user avatar on the
top right corner, or in the res.users list, click "Switch Login" to
impersonate another user.
impersonate another user. Users that cannot be impersonated (set to
*Forbidden*, or set to *Default* with the *Administration: Settings*
rights when the general setting restricts them) are not listed, and
the button is not shown on their row.
2. On the top-right corner, the button "Back to Original User" is
displayed in case the current user is being impersonated.

Expand Down
6 changes: 6 additions & 0 deletions impersonate_login/models/res_config_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ class ResConfigSettings(models.TransientModel):
),
default=False,
)

def set_values(self):
res = super().set_values()
# Whether a user can be impersonated depends on this setting.
self.env["res.users"].invalidate_model(["impersonate_allowed"])
return res
106 changes: 92 additions & 14 deletions impersonate_login/models/res_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,93 @@
class Users(models.Model):
_inherit = "res.users"

impersonate_mode = fields.Selection(
selection=[
("default", "Default"),
("allowed", "Allowed"),
("forbidden", "Forbidden"),
],
string="Impersonation",
default="default",
required=True,
groups="base.group_system",
help="Default: follow the general setting (when 'Restrict Impersonation of "
"Administration: Settings Users' is enabled, users with that access right "
"cannot be impersonated).\n"
"Allowed: this user can always be impersonated, even with that access right.\n"
"Forbidden: this user can never be impersonated.",
)

impersonate_allowed = fields.Boolean(
compute="_compute_impersonate_allowed",
search="_search_impersonate_allowed",
string="Can Be Impersonated",
help="Whether the current user may switch to this user: never oneself, "
"a user set to Forbidden, nor (when the general setting restricts "
"them) a user with the 'Administration: Settings' access right.",
)

@api.depends("impersonate_mode", "groups_id")
@api.depends_context("uid")
def _compute_impersonate_allowed(self):
for user in self:
user.impersonate_allowed = (
user.id != self.env.uid and not user._get_impersonation_refusal()
)

def _search_impersonate_allowed(self, operator, value):
"""Same rule as _get_impersonation_refusal(), as a SQL domain."""
if operator not in ("=", "!=") or not isinstance(value, bool):
raise UserError(self.env._("Operation not supported"))
domain = [
("id", "!=", self.env.uid),
("impersonate_mode", "!=", "forbidden"),
]
config_restrict = (
self.env["ir.config_parameter"]
.sudo()
.get_param("impersonate_login.restrict_impersonate_admin_settings")
)
if config_restrict:
domain += [
"|",
("impersonate_mode", "=", "allowed"),
("groups_id", "not in", self.env.ref("base.group_system").ids),
]
# impersonate_mode is readable by administrators only: the subquery is
# built as superuser, its result only says who may be switched to.
query = self.sudo().with_context(active_test=False)._search(domain)
allowed = (operator == "=") == value
return [("id", "in" if allowed else "not in", query)]

def _get_impersonation_refusal(self):
"""Why nobody may impersonate this user (a message), or False."""
self.ensure_one()
mode = self.sudo().impersonate_mode
if mode == "forbidden":
return self.env._("This user cannot be impersonated.")
if mode == "allowed":
return False
config_restrict = (
self.env["ir.config_parameter"]
.sudo()
.get_param("impersonate_login.restrict_impersonate_admin_settings")
)
if config_restrict and self.env.ref("base.group_system") in (
self.sudo().groups_id
):
return self.env._(
"You cannot impersonate users with"
" 'Administration: Settings' access rights."
)
return False

def _check_can_be_impersonated(self):
"""Raise if nobody may impersonate this user."""
refusal = self._get_impersonation_refusal()
if refusal:
raise UserError(refusal)

def _get_partner_name(self, user_id):
return self.env["res.users"].browse(user_id).partner_id.name

Expand All @@ -24,20 +111,8 @@ 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.groups_id:
raise UserError(
self.env._(
"You cannot impersonate users with"
" 'Administration: Settings' access rights."
)
)
if self.id != request.session.impersonate_from_uid:
self._check_can_be_impersonated()
if request.session.impersonate_from_uid:
if self.id == request.session.impersonate_from_uid:
return self.back_to_origin_login()
Expand Down Expand Up @@ -93,9 +168,12 @@ def action_impersonate_login(self):
"base.action_res_users"
)
action["views"] = [[self.env.ref("base.view_users_tree").id, "list"]]
# Only the users that can be switched to: the others would only
# show a button that ends in an error.
action["domain"] = [
("id", "!=", self.env.user.id),
("share", "=", False),
("impersonate_allowed", "=", True),
]
action["target"] = "new"
return action
Expand Down
8 changes: 8 additions & 0 deletions impersonate_login/readme/CONFIGURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,11 @@ 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.

Each user also has an *Impersonation* setting, on the *Access Rights* tab of the
user form (administrators only):

- *Default*: follow the general setting above;
- *Allowed*: the user can always be impersonated, even with the
*Administration: Settings* rights;
- *Forbidden*: the user can never be impersonated.
3 changes: 3 additions & 0 deletions impersonate_login/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
1. In the menu that is displayed when clicking on the user avatar on the top right corner,
or in the res.users list, click "Switch Login" to impersonate another user.
Users that cannot be impersonated (set to *Forbidden*, or set to *Default* with
the *Administration: Settings* rights when the general setting restricts them)
are not listed, and the button is not shown on their row.
2. On the top-right corner, the button "Back to Original User" is displayed in case the current
user is being impersonated.
41 changes: 23 additions & 18 deletions impersonate_login/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<title>Impersonate Login</title>
<style type="text/css">

/*
Expand Down Expand Up @@ -360,21 +360,16 @@
</style>
</head>
<body>
<div class="document">
<div class="document" id="impersonate-login">
<h1 class="title">Impersonate Login</h1>


<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="impersonate-login">
<h1>Impersonate Login</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:70269856627bdab7531c30ba6e74a4f87bc2e09894e2e067195acd79c06933a5
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/server-auth/tree/18.0/impersonate_login"><img alt="OCA/server-auth" src="https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/server-auth-18-0/server-auth-18-0-impersonate_login"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/server-auth&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/server-auth/tree/18.0/impersonate_login"><img alt="OCA/server-auth" src="https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/server-auth-18-0/server-auth-18-0-impersonate_login"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/server-auth&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module allows one user (for example, a member of the support team)
to log in as another user. The impersonation session can be exited by
clicking on the button “Back to Original User”.</p>
Expand Down Expand Up @@ -407,40 +402,51 @@ <h1>Impersonate Login</h1>
</ul>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
<p>The impersonating user must belong to group “Impersonate Users”.</p>
<p>If you want to prevent impersonation of users with the <em>Administration:
Settings</em> rights, enable the <em>Restrict Impersonation of “Administration:
Settings” Users</em> option in the settings.</p>
<p>Each user also has an <em>Impersonation</em> setting, on the <em>Access Rights</em>
tab of the user form (administrators only):</p>
<ul class="simple">
<li><em>Default</em>: follow the general setting above;</li>
<li><em>Allowed</em>: the user can always be impersonated, even with the
<em>Administration: Settings</em> rights;</li>
<li><em>Forbidden</em>: the user can never be impersonated.</li>
</ul>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<ol class="arabic simple">
<li>In the menu that is displayed when clicking on the user avatar on the
top right corner, or in the res.users list, click “Switch Login” to
impersonate another user.</li>
impersonate another user. Users that cannot be impersonated (set to
<em>Forbidden</em>, or set to <em>Default</em> with the <em>Administration: Settings</em>
rights when the general setting restricts them) are not listed, and
the button is not shown on their row.</li>
<li>On the top-right corner, the button “Back to Original User” is
displayed in case the current user is being impersonated.</li>
</ol>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h2>
<h1><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/server-auth/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/server-auth/issues/new?body=module:%20impersonate_login%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-4">Credits</a></h2>
<h1><a class="toc-backref" href="#toc-entry-4">Credits</a></h1>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-5">Authors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-5">Authors</a></h2>
<ul class="simple">
<li>Akretion</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-6">Contributors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
<ul class="simple">
<li>Kévin Roche &lt;<a class="reference external" href="mailto:kevin.roche&#64;akretion.com">kevin.roche&#64;akretion.com</a>&gt;</li>
<li><a class="reference external" href="https://www.360erp.com">360ERP</a>:<ul>
Expand All @@ -450,7 +456,7 @@ <h3><a class="toc-backref" href="#toc-entry-6">Contributors</a></h3>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h3>
<h2><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
Expand All @@ -465,6 +471,5 @@ <h3><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h3>
</div>
</div>
</div>
</div>
</body>
</html>
Loading
Loading