mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
Merge branch 'mig/18.0/auth_admin' into '18.0'
WIP: mig/18.0/auth_admin into 18.0 See merge request hibou-io/hibou-odoo/suite!1722
This commit is contained in:
3
auth_admin/__init__.py
Executable file
3
auth_admin/__init__.py
Executable file
@@ -0,0 +1,3 @@
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import wizard
|
||||
31
auth_admin/__manifest__.py
Executable file
31
auth_admin/__manifest__.py
Executable file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
'name': 'Auth Admin',
|
||||
'author': 'Hibou Corp.',
|
||||
'category': 'Hidden',
|
||||
'version': '18.0.1.0.0',
|
||||
'description':
|
||||
"""
|
||||
Login as other user
|
||||
===================
|
||||
|
||||
Provides a way for an authenticated user, with certain permissions, to login as a different user.
|
||||
Can also create a URL that logs in as that user.
|
||||
|
||||
Out of the box, only allows you to generate a login for an 'External User', e.g. portal users.
|
||||
|
||||
*2017-11-15* New button to generate the login on the Portal User Wizard (Action on Contact)
|
||||
|
||||
Added the option to copy the Force Login URL by simply clicking the Copy widget in the Portal User Wizard.
|
||||
|
||||
""",
|
||||
'depends': [
|
||||
'base',
|
||||
'website',
|
||||
'portal',
|
||||
],
|
||||
'auto_install': False,
|
||||
'data': [
|
||||
'views/res_users.xml',
|
||||
'wizard/portal_wizard_views.xml',
|
||||
],
|
||||
}
|
||||
2
auth_admin/controllers/__init__.py
Executable file
2
auth_admin/controllers/__init__.py
Executable file
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import main
|
||||
42
auth_admin/controllers/main.py
Executable file
42
auth_admin/controllers/main.py
Executable file
@@ -0,0 +1,42 @@
|
||||
from odoo import http, exceptions
|
||||
from ..models.res_users import check_admin_auth_login
|
||||
|
||||
from logging import getLogger
|
||||
_logger = getLogger(__name__)
|
||||
|
||||
|
||||
class AuthAdmin(http.Controller):
|
||||
|
||||
@http.route(['/auth_admin'], type='http', auth='public', website=True)
|
||||
def index(self, *args, **post):
|
||||
u = post.get('u')
|
||||
e = post.get('e')
|
||||
o = post.get('o')
|
||||
h = post.get('h')
|
||||
|
||||
if not all([u, e, o, h]):
|
||||
exceptions.Warning('Invalid Request')
|
||||
|
||||
u = str(u)
|
||||
e = str(e)
|
||||
o = str(o)
|
||||
h = str(h)
|
||||
|
||||
try:
|
||||
user = check_admin_auth_login(http.request.env, u, e, o, h)
|
||||
|
||||
# this is mostly like session finalize() as we skip MFA
|
||||
env = http.request.env(user=user)
|
||||
user_context = dict(env['res.users'].context_get())
|
||||
|
||||
http.request.session.should_rotate = True
|
||||
http.request.session.update({
|
||||
'login': user.login,
|
||||
'uid': user.id,
|
||||
'context': user_context,
|
||||
'session_token': env.user._compute_session_token(http.request.session.sid),
|
||||
})
|
||||
|
||||
return http.request.redirect('/my/home')
|
||||
except (exceptions.Warning, ) as e:
|
||||
return http.Response(e.message, status=400)
|
||||
52
auth_admin/i18n/es.po
Normal file
52
auth_admin/i18n/es.po
Normal file
@@ -0,0 +1,52 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * auth_admin
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 15.0+e\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-10-11 22:01+0000\n"
|
||||
"PO-Revision-Date: 2021-10-11 22:01+0000\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: auth_admin
|
||||
#: model:ir.model.fields,field_description:auth_admin.field_portal_wizard_user__force_login_url
|
||||
msgid "Force Login URL"
|
||||
msgstr "Forzar URL de Login"
|
||||
|
||||
#. module: auth_admin
|
||||
#: model_terms:ir.ui.view,arch_db:auth_admin.auth_admin_view_users_tree
|
||||
msgid "Generate Login"
|
||||
msgstr "Generar Login"
|
||||
|
||||
#. module: auth_admin
|
||||
#: model_terms:ir.ui.view,arch_db:auth_admin.portal_wizard
|
||||
msgid "Generate Login URL"
|
||||
msgstr "Generar URL de Login"
|
||||
|
||||
#. module: auth_admin
|
||||
#: model:ir.model,name:auth_admin.model_portal_wizard
|
||||
msgid "Grant Portal Access"
|
||||
msgstr "Otorgar Acceso al Portal "
|
||||
|
||||
#. module: auth_admin
|
||||
#: code:addons/auth_admin/wizard/portal_wizard.py:0
|
||||
#, python-format
|
||||
msgid "Portal Access Management"
|
||||
msgstr "Gestionar Acceso al Portal"
|
||||
|
||||
#. module: auth_admin
|
||||
#: model:ir.model,name:auth_admin.model_portal_wizard_user
|
||||
msgid "Portal User Config"
|
||||
msgstr "Configuración del Usuario de Portal"
|
||||
|
||||
#. module: auth_admin
|
||||
#: model:ir.model,name:auth_admin.model_res_users
|
||||
msgid "Users"
|
||||
msgstr "Usuarios"
|
||||
1
auth_admin/models/__init__.py
Executable file
1
auth_admin/models/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import res_users
|
||||
92
auth_admin/models/res_users.py
Executable file
92
auth_admin/models/res_users.py
Executable file
@@ -0,0 +1,92 @@
|
||||
from odoo import models, api, exceptions
|
||||
from odoo.http import request
|
||||
from datetime import datetime
|
||||
from time import mktime
|
||||
import hmac
|
||||
from hashlib import sha256
|
||||
|
||||
from logging import getLogger
|
||||
_logger = getLogger(__name__)
|
||||
|
||||
|
||||
def admin_auth_generate_login(env, user):
|
||||
"""
|
||||
Generates a URL to allow the current user to login as the portal user.
|
||||
|
||||
:param env: Odoo environment
|
||||
:param user: `res.users` in
|
||||
:return:
|
||||
"""
|
||||
if not env['res.partner'].has_access('write'):
|
||||
return None
|
||||
u = str(user.id)
|
||||
now = datetime.utcnow()
|
||||
fifteen = int(mktime(now.timetuple())) + (15 * 60)
|
||||
e = str(fifteen)
|
||||
o = str(env.uid)
|
||||
|
||||
config = env['ir.config_parameter'].sudo()
|
||||
key = str(config.search([('key', '=', 'database.secret')], limit=1).value)
|
||||
h = hmac.new(key.encode(), (u + e + o).encode(), sha256)
|
||||
|
||||
base_url = str(config.search([('key', '=', 'web.base.url')], limit=1).value)
|
||||
|
||||
_logger.warning('login url for user id: ' + u + ' original user id: ' + o)
|
||||
|
||||
return base_url + '/auth_admin?u=' + u + '&e=' + e + '&o=' + o + '&h=' + h.hexdigest()
|
||||
|
||||
|
||||
def check_admin_auth_login(env, u_user_id, e_expires, o_org_user_id, hash_):
|
||||
"""
|
||||
Checks that the parameters are valid and that the user exists.
|
||||
|
||||
:param env: Odoo environment
|
||||
:param u_user_id: Desired user id to login as.
|
||||
:param e_expires: Expiration timestamp
|
||||
:param o_org_user_id: Original user id.
|
||||
:param hash_: HMAC generated hash
|
||||
:return: `res.users`
|
||||
"""
|
||||
|
||||
now = datetime.utcnow()
|
||||
now = int(mktime(now.timetuple()))
|
||||
fifteen = now + (15 * 60)
|
||||
|
||||
config = env['ir.config_parameter'].sudo()
|
||||
key = str(config.search([('key', '=', 'database.secret')], limit=1).value)
|
||||
|
||||
myh = hmac.new(key.encode(), str(str(u_user_id) + str(e_expires) + str(o_org_user_id)).encode(), sha256)
|
||||
|
||||
if not hmac.compare_digest(hash_, myh.hexdigest()):
|
||||
raise exceptions.AccessDenied('Invalid Request')
|
||||
|
||||
if not (now <= int(e_expires) <= fifteen):
|
||||
raise exceptions.AccessDenied('Expired')
|
||||
|
||||
user = env['res.users'].sudo().search([('id', '=', int(u_user_id))], limit=1)
|
||||
if not user.id:
|
||||
raise exceptions.AccessDenied('Invalid User')
|
||||
return user
|
||||
|
||||
|
||||
class ResUsers(models.Model):
|
||||
_inherit = 'res.users'
|
||||
|
||||
def admin_auth_generate_login(self):
|
||||
self.ensure_one()
|
||||
|
||||
login_url = admin_auth_generate_login(self.env, self)
|
||||
if login_url:
|
||||
raise exceptions.UserError(login_url)
|
||||
|
||||
return False
|
||||
|
||||
def _check_credentials(self, password, env):
|
||||
try:
|
||||
return super(ResUsers, self)._check_credentials(password, env)
|
||||
except exceptions.AccessDenied:
|
||||
if request and hasattr(request, 'session') and request.session.get('auth_admin'):
|
||||
_logger.warning('_check_credentials for user id: ' + \
|
||||
str(request.session.uid) + ' original user id: ' + str(request.session.auth_admin))
|
||||
else:
|
||||
raise
|
||||
16
auth_admin/views/res_users.xml
Executable file
16
auth_admin/views/res_users.xml
Executable file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="auth_admin_view_users_tree" model="ir.ui.view">
|
||||
<field name="name">auth_admin.res.users.tree</field>
|
||||
<field name="model">res.users</field>
|
||||
<field name="inherit_id" ref="base.view_users_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//list" position="inside">
|
||||
<field name="share" invisible="1"/>
|
||||
<button string="Generate Login" type="object"
|
||||
name="admin_auth_generate_login"
|
||||
invisible="not share"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
1
auth_admin/wizard/__init__.py
Executable file
1
auth_admin/wizard/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import portal_wizard
|
||||
39
auth_admin/wizard/portal_wizard.py
Executable file
39
auth_admin/wizard/portal_wizard.py
Executable file
@@ -0,0 +1,39 @@
|
||||
from odoo import api, fields, models, _
|
||||
from ..models.res_users import admin_auth_generate_login
|
||||
|
||||
|
||||
class PortalWizard(models.TransientModel):
|
||||
_inherit = 'portal.wizard'
|
||||
|
||||
def admin_auth_generate_login(self):
|
||||
self.ensure_one()
|
||||
self.user_ids.admin_auth_generate_login()
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": self._name,
|
||||
"views": [[False, "form"]],
|
||||
"res_id": self.id,
|
||||
"target": "new",
|
||||
}
|
||||
|
||||
|
||||
class PortalWizardUser(models.TransientModel):
|
||||
_inherit = 'portal.wizard.user'
|
||||
|
||||
force_login_url = fields.Char(string='Force Login URL')
|
||||
|
||||
def admin_auth_generate_login(self):
|
||||
ir_model_access = self.env['ir.model.access']
|
||||
for row in self.filtered(lambda r: r.is_portal):
|
||||
user = row.partner_id.user_ids[0] if row.partner_id.user_ids else None
|
||||
if ir_model_access.check('res.partner', mode='unlink') and user:
|
||||
row.force_login_url = admin_auth_generate_login(self.env, user)
|
||||
self.filtered(lambda r: not r.is_portal).update({'force_login_url': ''})
|
||||
return {
|
||||
'name': _('Portal Access Management'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'portal.wizard',
|
||||
'res_id': self.wizard_id.id,
|
||||
'target': 'new',
|
||||
}
|
||||
18
auth_admin/wizard/portal_wizard_views.xml
Executable file
18
auth_admin/wizard/portal_wizard_views.xml
Executable file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="portal_wizard" model="ir.ui.view">
|
||||
<field name="name">Portal Access Management - Auth Admin</field>
|
||||
<field name="model">portal.wizard</field>
|
||||
<field name="inherit_id" ref="portal.wizard_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='is_portal']" position="after">
|
||||
<field name="force_login_url" widget="CopyClipboardChar"/>
|
||||
</xpath>
|
||||
<xpath expr="//list/button[last()]" position="after">
|
||||
<button string="Generate Login URL" type="object" name="admin_auth_generate_login" class="btn-primary" />
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user