diff --git a/auth_admin/__init__.py b/auth_admin/__init__.py new file mode 100755 index 00000000..cec04a5b --- /dev/null +++ b/auth_admin/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +from . import controllers +from . import models +from . import wizard diff --git a/auth_admin/__manifest__.py b/auth_admin/__manifest__.py new file mode 100755 index 00000000..d2f49137 --- /dev/null +++ b/auth_admin/__manifest__.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +{ + 'name': 'Auth Admin', + 'author': 'Hibou Corp. ', + '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', + ], +} diff --git a/auth_admin/controllers/__init__.py b/auth_admin/controllers/__init__.py new file mode 100755 index 00000000..757b12a1 --- /dev/null +++ b/auth_admin/controllers/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import main diff --git a/auth_admin/controllers/main.py b/auth_admin/controllers/main.py new file mode 100755 index 00000000..13b2ec14 --- /dev/null +++ b/auth_admin/controllers/main.py @@ -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) diff --git a/auth_admin/models/__init__.py b/auth_admin/models/__init__.py new file mode 100755 index 00000000..741ed460 --- /dev/null +++ b/auth_admin/models/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import res_users diff --git a/auth_admin/models/res_users.py b/auth_admin/models/res_users.py new file mode 100755 index 00000000..e3d0ffea --- /dev/null +++ b/auth_admin/models/res_users.py @@ -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) diff --git a/auth_admin/views/res_users.xml b/auth_admin/views/res_users.xml new file mode 100755 index 00000000..b818c870 --- /dev/null +++ b/auth_admin/views/res_users.xml @@ -0,0 +1,14 @@ + + + + auth_admin.res.users.tree + res.users + + + + +