Skip to content

Commit

Permalink
Merge PR #722 into 14.0
Browse files Browse the repository at this point in the history
Signed-off-by thomaspaulb
  • Loading branch information
OCA-git-bot committed Nov 13, 2024
2 parents c6a4462 + 79f2454 commit cfb79e7
Show file tree
Hide file tree
Showing 28 changed files with 1,445 additions and 0 deletions.
108 changes: 108 additions & 0 deletions impersonate_login/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
=================
Impersonate Login
=================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:1fca331cbc5f2dcb804e5612e5669a9ab4998d80f22d46d6683266580f9ca40f
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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/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
:target: https://github.com/OCA/server-auth/tree/14.0/impersonate_login
:alt: OCA/server-auth
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-auth-14-0/server-auth-14-0-impersonate_login
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-auth&target_branch=14.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

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".

To ensure that any abuse of this feature will not go unnoticed, the
following measures are in place:

- 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.
-

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**

.. contents::
:local:

Usage
=====

The impersonating user must belong to group "Impersonate Users".
- 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" toi mpersonate another user.
- On the top-right corner, the button "Back to Original User" is displayed in case the current user is being impersonated.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-auth/issues>`_.
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
`feedback <https://github.com/OCA/server-auth/issues/new?body=module:%20impersonate_login%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
~~~~~~~

* Akretion

Contributors
~~~~~~~~~~~~

- Kévin Roche <kevin.roche@akretion.com>
- [360ERP](https://www.360erp.com):
- Andrea Stirpe

Maintainers
~~~~~~~~~~~

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-Kev-Roche| image:: https://github.com/Kev-Roche.png?size=40px
:target: https://github.com/Kev-Roche
:alt: Kev-Roche

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-Kev-Roche|

This module is part of the `OCA/server-auth <https://github.com/OCA/server-auth/tree/14.0/impersonate_login>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 2 additions & 0 deletions impersonate_login/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from .hooks import pre_init_hook
31 changes: 31 additions & 0 deletions impersonate_login/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2024 Akretion (https://www.akretion.com).
# @author Kévin Roche <kevin.roche@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Impersonate Login",
"summary": "tools",
"version": "14.0.1.0.0",
"category": "Tools",
"website": "https://github.com/OCA/server-auth",
"author": "Akretion, Odoo Community Association (OCA)",
"maintainers": ["Kev-Roche"],
"license": "AGPL-3",
"application": False,
"installable": True,
"depends": [
"web",
"mail",
],
"data": [
"views/assets.xml",
"views/res_users.xml",
"views/impersonate_log.xml",
"security/group.xml",
"security/ir.model.access.csv",
],
"qweb": [
"static/src/xml/user_menu.xml",
],
"pre_init_hook": "pre_init_hook",
}
19 changes: 19 additions & 0 deletions impersonate_login/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2024 360ERP (<https://www.360erp.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging


def pre_init_hook(cr):
"""
Pre-create the impersonated_author_id column in the mail_message table
to prevent the ORM from invoking its compute method on a large volume
of existing mail messages.
"""
logger = logging.getLogger(__name__)
logger.info("Add mail_message.impersonated_author_id column if not exists")
cr.execute(
"ALTER TABLE mail_message "
"ADD COLUMN IF NOT EXISTS "
"impersonated_author_id INTEGER"
)
6 changes: 6 additions & 0 deletions impersonate_login/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from . import res_users
from . import ir_http
from . import mail_thread
from . import mail_message
from . import impersonate_log
from . import model
26 changes: 26 additions & 0 deletions impersonate_login/models/impersonate_log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright (C) 2024 Akretion (<http://www.akretion.com>).
# @author Kévin Roche <kevin.roche@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).


from odoo import fields, models


class ImpersonateLog(models.Model):
_name = "impersonate.log"
_description = "Impersonate Logs"

user_id = fields.Many2one(
comodel_name="res.users",
string="User",
)
impersonated_partner_id = fields.Many2one(
comodel_name="res.partner",
string="Logged as",
)
date_start = fields.Datetime(
string="Start Date",
)
date_end = fields.Datetime(
string="End Date",
)
20 changes: 20 additions & 0 deletions impersonate_login/models/ir_http.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright (C) 2024 Akretion (<http://www.akretion.com>).
# @author Kévin Roche <kevin.roche@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import models
from odoo.http import request


class Http(models.AbstractModel):
_inherit = "ir.http"

def session_info(self):
session_info = super().session_info()
session_info.update(
{
"is_impersonate_user": request.env.user._is_impersonate_user(),
"impersonate_from_uid": request.session.impersonate_from_uid,
}
)
return session_info
79 changes: 79 additions & 0 deletions impersonate_login/models/mail_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Copyright (C) 2024 Akretion (<http://www.akretion.com>).
# @author Kévin Roche <kevin.roche@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import _, api, fields, models
from odoo.http import request
from odoo.tools import html_escape


class Message(models.Model):
_inherit = "mail.message"

impersonated_author_id = fields.Many2one(
comodel_name="res.partner",
compute="_compute_impersonated_author_id",
store=True,
)

body = fields.Html(
compute="_compute_message_body",
inverse="_inverse_message_body",
store=True,
readonly=False,
)

@api.depends("author_id")
def _compute_impersonated_author_id(self):
for rec in self:
if request and request.session.impersonate_from_uid:
rec.impersonated_author_id = (
self.env["res.users"]
.browse(request.session.impersonate_from_uid)
.partner_id.id
)
else:
rec.impersonated_author_id = False

@api.depends("author_id", "impersonated_author_id")
def _compute_message_body(self):
for rec in self:
additional_info = ""
if (
request
and request.session.impersonate_from_uid
and rec.impersonated_author_id
):
current_partner = (
self.env["res.users"].browse(request.session.uid).partner_id
)
additional_info = _("Logged in as {}").format(
html_escape(current_partner.name)
)
if rec.body and additional_info:
rec.body = f"<b>{additional_info}</b><br/>{rec.body}"
else:
rec.body = rec.body

def _inverse_message_body(self):
for rec in self:
additional_info = ""
if (
request
and request.session.impersonate_from_uid
and rec.impersonated_author_id
):
current_partner = (
self.env["res.users"].browse(request.session.uid).partner_id
)
additional_info = _("Logged in as {}").format(
html_escape(current_partner.name)
)
if additional_info:
start_with = f"<b>{additional_info}</b><br/>"
if rec.body and rec.body.startswith(start_with):
rec.body = rec.body
else:
rec.body = f"{start_with}{rec.body}"
else:
rec.body = rec.body
30 changes: 30 additions & 0 deletions impersonate_login/models/mail_thread.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright (C) 2024 Akretion (<http://www.akretion.com>).
# @author Kévin Roche <kevin.roche@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import models
from odoo.http import request


class MailThread(models.AbstractModel):
_inherit = "mail.thread"

def _message_compute_author(
self, author_id=None, email_from=None, raise_exception=True
):
if request and request.session.impersonate_from_uid:
author = self.env["res.users"].browse(request.session.uid).partner_id
if author_id == author.id or author_id is None:
impersonate_from_author = (
self.env["res.users"]
.browse(request.session.impersonate_from_uid)
.partner_id
)
email = impersonate_from_author.email_formatted
return impersonate_from_author.id, email

return super()._message_compute_author(
author_id,
email_from,
raise_exception,
)
44 changes: 44 additions & 0 deletions impersonate_login/models/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright (C) 2024 Akretion (<http://www.akretion.com>).
# @author Kévin Roche <kevin.roche@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from psycopg2.extensions import AsIs

from odoo import api, models
from odoo.http import request


class BaseModel(models.AbstractModel):
_inherit = "base"

@api.model
def _create(self, data_list):
res = super()._create(data_list)
if (
request
and request.session.impersonate_from_uid
and "create_uid" in self._fields
):
self.env.cr.execute(
"""
UPDATE %(table)s
SET create_uid = %(impersonator_id)s
WHERE id IN %(record_ids)s
""",
{
"table": AsIs(self._table),
"impersonator_id": request.session.impersonate_from_uid,
"record_ids": tuple(rec.id for rec in res),
},
)
return res

def write(self, vals):
res = super().write(vals)
if (
request
and request.session.impersonate_from_uid
and "write_uid" in self._fields
):
self._fields["write_uid"].write(self, request.session.impersonate_from_uid)
return res
Loading

0 comments on commit cfb79e7

Please sign in to comment.