This commit is contained in:
Jared Kipe
2018-05-27 13:00:30 -07:00
parent c24c3711ce
commit b75528d731
10 changed files with 230 additions and 0 deletions

4
auth_admin/__init__.py Executable file
View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
from . import controllers
from . import models
from . import wizard

30
auth_admin/__manifest__.py Executable file
View File

@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
{
'name': 'Auth Admin',
'author': 'Hibou Corp. <hello@hibou.io>',
'category': 'Hidden',
'version': '11.0.0.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)
""",
'depends': [
'base',
'website',
'portal',
],
'auto_install': False,
'data': [
'views/res_users.xml',
'wizard/portal_wizard_views.xml',
],
}

View File

@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import main

41
auth_admin/controllers/main.py Executable file
View File

@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
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)
http.request.session.uid = user.id
http.request.session.login = user.login
http.request.session.password = ''
http.request.session.auth_admin = int(o)
http.request.uid = user.id
uid = http.request.session.authenticate(http.request.session.db, user.login, 'x')
if uid is not False:
http.request.params['login_success'] = True
return http.redirect_with_hash('/my/home')
return http.local_redirect('/my/home')
except (exceptions.Warning, ) as e:
return http.Response(e.message, status=400)

2
auth_admin/models/__init__.py Executable file
View File

@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import res_users

90
auth_admin/models/res_users.py Executable file
View File

@@ -0,0 +1,90 @@
# -*- coding: utf-8 -*-
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'].check_access_rights('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.warn('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.Warning('Invalid Request')
if not (now <= int(e_expires) <= fifteen):
raise exceptions.Warning('Expired')
user = env['res.users'].sudo().search([('id', '=', int(u_user_id))], limit=1)
if not user.id:
raise exceptions.Warning('Invalid User')
return user
class ResUsers(models.Model):
_inherit = 'res.users'
@api.multi
def admin_auth_generate_login(self):
self.ensure_one()
login_url = admin_auth_generate_login(self.env, self)
if login_url:
raise exceptions.Warning(login_url)
return False
@api.model
def check_credentials(self, password):
if request and hasattr(request, 'session') and request.session.get('auth_admin'):
_logger.warn('check_credentials for user id: ' + str(request.session.uid) + ' original user id: ' + str(request.session.auth_admin))
return True
return super(ResUsers, self).check_credentials(password)

14
auth_admin/views/res_users.xml Executable file
View File

@@ -0,0 +1,14 @@
<?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="//tree" position="inside">
<field name="share" invisible="1"/>
<button string="Generate Login" type="object" name="admin_auth_generate_login" attrs="{'invisible': [('share', '=', False)]}"/>
</xpath>
</field>
</record>
</odoo>

2
auth_admin/wizard/__init__.py Executable file
View File

@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import portal_wizard

View File

@@ -0,0 +1,27 @@
from odoo import api, fields, models
from ..models.res_users import admin_auth_generate_login
class PortalWizard(models.TransientModel):
_inherit = 'portal.wizard'
@api.multi
def admin_auth_generate_login(self):
self.ensure_one()
self.user_ids.admin_auth_generate_login()
return {'type': 'ir.actions.do_nothing'}
class PortalWizardUser(models.TransientModel):
_inherit = 'portal.wizard.user'
force_login_url = fields.Char(string='Force Login URL')
@api.multi
def admin_auth_generate_login(self):
ir_model_access = self.env['ir.model.access']
for row in self:
row.force_login_url = ''
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 row.in_portal and user:
row.force_login_url = admin_auth_generate_login(self.env, user)

View 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='in_portal']" position="after">
<field name="force_login_url" readonly="1"/>
</xpath>
<xpath expr="//button[last()]" position="after">
<button string="Generate Login URL" type="object" name="admin_auth_generate_login" class="btn-default" />
</xpath>
</field>
</record>
</odoo>