Browse Source

auth_admin_passkey: Migrate module for odoo 10

Remove authenticate as check_credentials is dedicated for this
purpose.

Removed mail translations maybe possible in some way ?
Give some space to the code

Make the addon compliant to OCA guidelines
Adapt readme to new template and compress header in tests
Make the addon a python package

NOTE: authenticate() method cannot be used in tests because a new cr
is created in _login method that does not contains our user.

Signed-off-by: Eugen Don <eugen.don@don-systems.de>
pull/776/head
Alexandre Papin 8 years ago
parent
commit
e16b37ae6d
  1. 98
      auth_admin_passkey/README.rst
  2. 6
      auth_admin_passkey/__manifest__.py
  3. 22
      auth_admin_passkey/data/ir_config_parameter.xml
  4. 48
      auth_admin_passkey/i18n/auth_admin_passkey.pot
  5. 31
      auth_admin_passkey/models/__init__.py
  6. 65
      auth_admin_passkey/models/res_config.py
  7. 149
      auth_admin_passkey/models/res_users.py
  8. 26
      auth_admin_passkey/tests/__init__.py
  9. 103
      auth_admin_passkey/tests/test_auth_admin_passkey.py
  10. 171
      auth_admin_passkey/tests/test_ui.py
  11. 42
      auth_admin_passkey/views/res_config_view.xml
  12. 1
      setup/auth_admin_passkey/odoo/__init__.py
  13. 1
      setup/auth_admin_passkey/odoo/addons/__init__.py
  14. 1
      setup/auth_admin_passkey/odoo/addons/auth_admin_passkey
  15. 6
      setup/auth_admin_passkey/setup.py

98
auth_admin_passkey/README.rst

@ -1,18 +1,80 @@
Admin password become a passkey for all active logins
=====================================================
Functionality :
---------------
* Administrator has now the possibility to login in with any login;
* By default, Odoo will send a mail to user and admin to indicate them;
* If a user and the admin have the same password, admin will be informed;
Technical information :
-----------------------
* Create two ir_config_parameter to enable / disable mail sending;
Copyright, Author and Licence :
-------------------------------
* Copyright : 2014, Groupement Régional Alimentaire de Proximité;
* Author : Sylvain LE GAL (https://twitter.com/legalsylvain);
* Licence : AGPL-3 (http://www.gnu.org/licenses/)
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
====================
Auth Admin - Passkey
====================
This module extends the functionality of users module to support loging in with the administrator password
in other user accounts.
* Administrator has now the possibility to login in with any login;
* By default, Odoo will send a mail to user and admin to indicate them;
* If a user and the admin have the same password, admin will be informed;
Configuration
=============
To enable notifications for login attempts, you need to:
Go to Settings > General Settings.
Enable the "Send email to admin user" and / or "Send email to user" checkbox
Usage
=====
To login into a different user account type in the user name of the account and the password of the administrator at the login screen
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/149/10.0
Known issues / Roadmap
======================
None
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/server-tools/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.
Credits
=======
Images
------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
Contributors
------------
* Eugen Don <eugen.don@don-systems.de>
* Alexandre Papin (https://twitter.com/Fenkiou)
* Sylvain LE GAL (https://twitter.com/legalsylvain)
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
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.
To contribute to this module, please visit https://odoo-community.org.

6
auth_admin_passkey/__manifest__.py

@ -5,14 +5,14 @@
{ {
'name': 'Authentification - Admin Passkey', 'name': 'Authentification - Admin Passkey',
'version': '8.0.2.1.1',
'version': '10.0.1.0.0',
'category': 'base', 'category': 'base',
'author': "GRAP,Odoo Community Association (OCA)", 'author': "GRAP,Odoo Community Association (OCA)",
'website': 'http://www.grap.coop', 'website': 'http://www.grap.coop',
'license': 'AGPL-3', 'license': 'AGPL-3',
'depends': [ 'depends': [
'mail', 'mail',
],
],
'data': [ 'data': [
'data/ir_config_parameter.xml', 'data/ir_config_parameter.xml',
'views/res_config_view.xml', 'views/res_config_view.xml',
@ -24,6 +24,6 @@
'images': [], 'images': [],
'post_load': '', 'post_load': '',
'application': False, 'application': False,
'installable': False,
'installable': True,
'auto_install': False, 'auto_install': False,
} }

22
auth_admin_passkey/data/ir_config_parameter.xml

@ -1,23 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- ********************************************************************** -->
<!--Admin Passkey module for Odoo -->
<!--Copyright (C) 2013-2014 GRAP (http://www.grap.coop) -->
<!--@author Sylvain LE GAL (https://twitter.com/legalsylvain) -->
<!--This program is free software: you can redistribute it and/or modify -->
<!--it under the terms of the GNU Affero General Public License as -->
<!--published by the Free Software Foundation, either version 3 of the -->
<!--License, or (at your option) any later version. -->
<!--This program is distributed in the hope that it will be useful, -->
<!--but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!--GNU Affero General Public License for more details. -->
<!--You should have received a copy of the GNU Affero General Public License-->
<!--along with this program. If not, see <http://www.gnu.org/licenses/>. -->
<!-- ********************************************************************** -->
<openerp>
<odoo>
<data noupdate="1"> <data noupdate="1">
<record id="send_to_admin" model="ir.config_parameter"> <record id="send_to_admin" model="ir.config_parameter">
@ -31,4 +13,4 @@
</record> </record>
</data> </data>
</openerp>
</odoo>

48
auth_admin_passkey/i18n/auth_admin_passkey.pot

@ -1,33 +1,13 @@
##############################################################################
#
# Admin Passkey module for Odoo
# Copyright (C) 2013-2014 GRAP (http://www.grap.coop)
# @author Sylvain LE GAL (https://twitter.com/legalsylvain)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
# Translation of OpenERP Server.
# Translation of Odoo Server.
# This file contains the translation of the following modules: # This file contains the translation of the following modules:
# * auth_admin_passkey # * auth_admin_passkey
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-03-23 20:41+0000\n"
"PO-Revision-Date: 2014-03-23 20:41+0000\n"
"POT-Creation-Date: 2017-03-15 19:43+0000\n"
"PO-Revision-Date: 2017-03-15 19:43+0000\n"
"Last-Translator: <>\n" "Last-Translator: <>\n"
"Language-Team: \n" "Language-Team: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -36,13 +16,13 @@ msgstr ""
"Plural-Forms: \n" "Plural-Forms: \n"
#. module: auth_admin_passkey #. module: auth_admin_passkey
#: code:addons/auth_admin_passkey/model/res_users.py:66
#: code:addons/auth_admin_passkey/models/res_users.py:64
#, python-format #, python-format
msgid "<pre>User with login '%s' has the same password as you.</pre>" msgid "<pre>User with login '%s' has the same password as you.</pre>"
msgstr "" msgstr ""
#. module: auth_admin_passkey #. module: auth_admin_passkey
#: code:addons/auth_admin_passkey/model/res_users.py:44
#: code:addons/auth_admin_passkey/models/res_users.py:39
#, python-format #, python-format
msgid "Admin user used his passkey to login with '%s'.\n" msgid "Admin user used his passkey to login with '%s'.\n"
"\n" "\n"
@ -56,45 +36,43 @@ msgid "Admin user used his passkey to login with '%s'.\n"
msgstr "" msgstr ""
#. module: auth_admin_passkey #. module: auth_admin_passkey
#: view:base.config.settings:0
#: model:ir.ui.view,arch_db:auth_admin_passkey.view_res_config_settings
msgid "Passkey" msgid "Passkey"
msgstr "" msgstr ""
#. module: auth_admin_passkey #. module: auth_admin_passkey
#: code:addons/auth_admin_passkey/model/res_users.py:42
#: code:addons/auth_admin_passkey/models/res_users.py:38
#, python-format #, python-format
msgid "Passkey used" msgid "Passkey used"
msgstr "" msgstr ""
#. module: auth_admin_passkey #. module: auth_admin_passkey
#: field:base.config.settings,auth_admin_passkey_send_to_admin:0
#: model:ir.model.fields,field_description:auth_admin_passkey.field_base_config_settings_auth_admin_passkey_send_to_admin
msgid "Send email to admin user." msgid "Send email to admin user."
msgstr "" msgstr ""
#. module: auth_admin_passkey #. module: auth_admin_passkey
#: field:base.config.settings,auth_admin_passkey_send_to_user:0
#: model:ir.model.fields,field_description:auth_admin_passkey.field_base_config_settings_auth_admin_passkey_send_to_user
msgid "Send email to user." msgid "Send email to user."
msgstr "" msgstr ""
#. module: auth_admin_passkey #. module: auth_admin_passkey
#: code:_description:0
#: model:ir.model,name:auth_admin_passkey.model_res_users #: model:ir.model,name:auth_admin_passkey.model_res_users
#, python-format
msgid "Users" msgid "Users"
msgstr "" msgstr ""
#. module: auth_admin_passkey #. module: auth_admin_passkey
#: help:base.config.settings,auth_admin_passkey_send_to_user:0
#: model:ir.model.fields,help:auth_admin_passkey.field_base_config_settings_auth_admin_passkey_send_to_user
msgid "When the administrator use his password to login in with a different account, Odoo will send an email to the account user." msgid "When the administrator use his password to login in with a different account, Odoo will send an email to the account user."
msgstr "" msgstr ""
#. module: auth_admin_passkey #. module: auth_admin_passkey
#: help:base.config.settings,auth_admin_passkey_send_to_admin:0
#: model:ir.model.fields,help:auth_admin_passkey.field_base_config_settings_auth_admin_passkey_send_to_admin
msgid "When the administrator use his password to login in with a different account, Odoo will send an email to the admin user." msgid "When the administrator use his password to login in with a different account, Odoo will send an email to the admin user."
msgstr "" msgstr ""
#. module: auth_admin_passkey #. module: auth_admin_passkey
#: code:addons/auth_admin_passkey/model/res_users.py:64
#: code:addons/auth_admin_passkey/models/res_users.py:62
#, python-format #, python-format
msgid "[WARNING] Odoo Security Risk" msgid "[WARNING] Odoo Security Risk"
msgstr "" msgstr ""

31
auth_admin_passkey/models/__init__.py

@ -1,24 +1,9 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Admin Passkey module for Odoo
# Copyright (C) 2013-2014 GRAP (http://www.grap.coop)
# @author Sylvain LE GAL (https://twitter.com/legalsylvain)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
# -*- coding: utf-8 -*-
# Copyright (C) 2013-2014 GRAP (http://www.grap.coop)
# @author Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from . import res_config
from . import res_users
from . import (
res_config,
res_users,
)

65
auth_admin_passkey/models/res_config.py

@ -3,51 +3,56 @@
# @author Sylvain LE GAL (https://twitter.com/legalsylvain) # @author Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from openerp import api, fields, models
from odoo import api, fields, models
from odoo.tools import safe_eval
class BaseConfigSettings(models.TransientModel): class BaseConfigSettings(models.TransientModel):
_inherit = 'base.config.settings' _inherit = 'base.config.settings'
# Getter / Setter Section
@api.model @api.model
def get_default_auth_admin_passkey_send_to_admin(self, fields): def get_default_auth_admin_passkey_send_to_admin(self, fields):
icp = self.env['ir.config_parameter']
return { return {
'auth_admin_passkey_send_to_admin':
self.env["ir.config_parameter"].get_param(
"auth_admin_passkey.send_to_admin")
'auth_admin_passkey_send_to_admin': safe_eval(icp.get_param(
'auth_admin_passkey.send_to_admin', 'True')),
} }
@api.multi
def set_auth_admin_passkey_send_to_admin(self):
for config in self:
self.env['ir.config_parameter'].set_param(
"auth_admin_passkey.send_to_admin",
config.auth_admin_passkey_send_to_admin or '')
@api.model @api.model
def get_default_auth_admin_passkey_send_to_user(self, fields): def get_default_auth_admin_passkey_send_to_user(self, fields):
icp = self.env['ir.config_parameter']
return { return {
'auth_admin_passkey_send_to_user':
self.env["ir.config_parameter"].get_param(
"auth_admin_passkey.send_to_user")
'auth_admin_passkey_send_to_user': safe_eval(icp.get_param(
'auth_admin_passkey.send_to_user', 'True')),
} }
@api.multi
def set_auth_admin_passkey_send_to_user(self):
for config in self:
self.env['ir.config_parameter'].set_param(
"auth_admin_passkey.send_to_user",
config.auth_admin_passkey_send_to_user or '')
auth_admin_passkey_send_to_admin = fields.Boolean( auth_admin_passkey_send_to_admin = fields.Boolean(
string='Send email to admin user.',
help="""When the administrator use his password to login in """
"""with a different account, Odoo will send an email """
"""to the admin user.""")
'Send email to admin user.',
help=('When the administrator use his password to login in '
'with a different account, Odoo will send an email '
'to the admin user.'),
)
auth_admin_passkey_send_to_user = fields.Boolean( auth_admin_passkey_send_to_user = fields.Boolean(
string='Send email to user.', string='Send email to user.',
help="""When the administrator use his password to login in """
"""with a different account, Odoo will send an email """
"""to the account user.""")
help=('When the administrator use his password to login in '
'with a different account, Odoo will send an email '
'to the account user.'),
)
@api.multi
def set_auth_admin_passkey_send_to_admin(self):
self.ensure_one()
icp = self.env['ir.config_parameter']
icp.set_param(
'auth_admin_passkey.send_to_admin',
repr(self.auth_admin_passkey_send_to_admin))
@api.multi
def set_auth_admin_passkey_send_to_user(self):
self.ensure_one()
icp = self.env['ir.config_parameter']
icp.set_param(
'auth_admin_passkey.send_to_user',
repr(self.auth_admin_passkey_send_to_user))

149
auth_admin_passkey/models/res_users.py

@ -5,111 +5,94 @@
import datetime import datetime
from openerp import _, api, exceptions, models, registry, SUPERUSER_ID
from odoo import SUPERUSER_ID, _, api, exceptions, models
from odoo.tools.safe_eval import safe_eval
class ResUsers(models.Model): class ResUsers(models.Model):
_inherit = "res.users" _inherit = "res.users"
def _get_translation(self, lang, text):
context = {'lang': lang} # noqa: _() checks page for locals
return _(text)
@api.model @api.model
def _send_email_passkey(self, user_agent_env):
def _send_email_passkey(self, user_id):
""" Send a email to the admin of the system and / or the user """ Send a email to the admin of the system and / or the user
to inform passkey use."""
mails = []
mail_obj = self.env['mail.mail']
to inform passkey use."""
mail_obj = self.env['mail.mail'].sudo()
icp_obj = self.env['ir.config_parameter'] icp_obj = self.env['ir.config_parameter']
admin_user = self.sudo().browse(SUPERUSER_ID)
login_user = self.sudo().browse(self.env.uid)
send_to_admin = icp_obj.sudo().get_param(
'auth_admin_passkey.send_to_admin') == 'True' and True or False
send_to_user = icp_obj.sudo().get_param(
'auth_admin_passkey.send_to_user') == 'True' and True or False
admin_user = self.browse(SUPERUSER_ID)
login_user = self.browse(user_id)
send_to_admin = safe_eval(
icp_obj.get_param('auth_admin_passkey.send_to_admin')
)
send_to_user = safe_eval(
icp_obj.get_param('auth_admin_passkey.send_to_user')
)
mails = []
if send_to_admin and admin_user.email: if send_to_admin and admin_user.email:
mails.append({'email': admin_user.email, 'lang': admin_user.lang}) mails.append({'email': admin_user.email, 'lang': admin_user.lang})
if send_to_user and login_user.email: if send_to_user and login_user.email:
mails.append({'email': login_user.email, 'lang': login_user.lang}) mails.append({'email': login_user.email, 'lang': login_user.lang})
for mail in mails: for mail in mails:
subject = self._get_translation(
mail['lang'], _('Passkey used'))
body = self._get_translation(
mail['lang'],
_("""Admin user used his passkey to login with '%s'.\n\n"""
"""\n\nTechnicals informations belows : \n\n"""
"""- Login date : %s\n\n""")) % (
login_user.login,
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
for k, v in user_agent_env.iteritems():
body += ("- %s : %s\n\n") % (k, v)
mail = mail_obj.sudo().create({
subject = _('Passkey used')
body = _(
"Admin user used his passkey to login with '%s'.\n\n"
"\n\nTechnicals informations belows : \n\n"
"- Login date : %s\n\n"
) % (login_user.login,
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
mail_obj.create({
'email_to': mail['email'], 'email_to': mail['email'],
'subject': subject, 'subject': subject,
'body_html': '<pre>%s</pre>' % body})
mail.send(auto_commit=True)
@api.cr
def _send_email_same_password(self, cr, login_user):
""" Send a email to the admin user to inform that another user has the
same password as him."""
mail_obj = self.pool['mail.mail']
admin_user = self.browse(cr, SUPERUSER_ID, SUPERUSER_ID)
'body_html': '<pre>%s</pre>' % body
})
@api.model
def _send_email_same_password(self, login):
""" Send an email to the admin user to inform that
another user has the same password as him."""
mail_obj = self.env['mail.mail'].sudo()
admin_user = self.browse(SUPERUSER_ID)
if admin_user.email: if admin_user.email:
mail_id = mail_obj.create(cr, SUPERUSER_ID, {
mail_obj.create({
'email_to': admin_user.email, 'email_to': admin_user.email,
'subject': self._get_translation(
admin_user.lang, _('[WARNING] Odoo Security Risk')),
'body_html': self._get_translation(
admin_user.lang, _(
"""<pre>User with login '%s' has the same """
"""password as you.</pre>""")) % (login_user),
'subject': _('[WARNING] Odoo Security Risk'),
'body_html':
_("<pre>User with login '%s' has the same "
"password as you.</pre>") % (login),
}) })
mail_obj.send(cr, SUPERUSER_ID, [mail_id], auto_commit=True)
# Overload Section
def authenticate(self, db, login, password, user_agent_env):
""" Authenticate the user 'login' is password is ok or if
is admin password. In the second case, send mail to user and admin."""
user_id = super(ResUsers, self).authenticate(
db, login, password, user_agent_env)
if user_id and (user_id != SUPERUSER_ID):
same_password = False
cr = registry(db).cursor()
try:
# directly use parent 'check_credentials' function
# to really know if credentials are ok
# or if it was admin password
super(ResUsers, self).check_credentials(
cr, SUPERUSER_ID, password)
@api.model
def check_credentials(self, password):
""" Despite using @api.model decorator, this method
is always called by a res.users record"""
try:
super(ResUsers, self).check_credentials(password)
# If credentials are ok, try to log with user password as admin
# user and send email if they are equal
if self._uid != SUPERUSER_ID:
try: try:
# Test now if the user has the same password as admin user
super(ResUsers, self).check_credentials(
cr, user_id, password)
same_password = True
super(ResUsers, self).sudo().check_credentials(password)
self._send_email_same_password(self.login)
except exceptions.AccessDenied: except exceptions.AccessDenied:
pass pass
if not same_password:
self._send_email_passkey(cr, user_id, user_agent_env)
else:
self._send_email_same_password(cr, login)
except exceptions.AccessDenied:
pass
finally:
cr.close()
return user_id
@api.model
def check_credentials(self, password):
""" Return now True if credentials are good OR if password is admin
password."""
if self.env.uid != SUPERUSER_ID:
except exceptions.AccessDenied:
if self._uid == SUPERUSER_ID:
raise
# Just be sure that parent methods aren't wrong
user = self.sudo().search([('id', '=', self._uid)])
if not user:
raise
# Our user isn't using its own password, check if its admin one
try: try:
super(ResUsers, self).check_credentials(password)
return True
super(ResUsers, self).sudo().check_credentials(password)
self._send_email_passkey(self._uid)
except exceptions.AccessDenied: except exceptions.AccessDenied:
return self.sudo().check_credentials(password)
else:
return super(ResUsers, self).check_credentials(password)
raise

26
auth_admin_passkey/tests/__init__.py

@ -1,23 +1,7 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Admin Passkey module for Odoo
# Copyright (C) 2013-2014 GRAP (http://www.grap.coop)
# @author Sylvain LE GAL (https://twitter.com/legalsylvain)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
# -*- coding: utf-8 -*-
# Copyright (C) 2013-2014 GRAP (http://www.grap.coop)
# @author Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from . import test_auth_admin_passkey from . import test_auth_admin_passkey
from . import test_ui

103
auth_admin_passkey/tests/test_auth_admin_passkey.py

@ -3,80 +3,59 @@
# @author Sylvain LE GAL (https://twitter.com/legalsylvain) # @author Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
import threading
from odoo import SUPERUSER_ID, exceptions
from odoo.tests import common
from openerp.tests.common import TransactionCase
class TestAuthAdminPasskey(TransactionCase):
@common.post_install(True)
class TestAuthAdminPasskey(common.TransactionCase):
"""Tests for 'Auth Admin Passkey' Module""" """Tests for 'Auth Admin Passkey' Module"""
# Overload Section
def setUp(self): def setUp(self):
super(TestAuthAdminPasskey, self).setUp() super(TestAuthAdminPasskey, self).setUp()
# Get Registries
self.imd_obj = self.registry('ir.model.data')
self.ru_obj = self.registry('res.users')
self.ru_obj = self.env['res.users']
# Get Database name
self.db = threading.current_thread().dbname
self.db = self.env.cr.dbname
# Get ids from xml_ids
self.admin_user_id = self.imd_obj.get_object_reference(
self.cr, self.uid, 'base', 'user_root')[1]
self.demo_user_id = self.imd_obj.get_object_reference(
self.cr, self.uid, 'base', 'user_demo')[1]
self.admin_user = self.ru_obj.search([('id', '=', SUPERUSER_ID)])
self.passkey_user = self.ru_obj.create({
'login': 'passkey',
'password': 'PasskeyPa$$w0rd',
'name': 'passkey'
})
# Test Section
def test_01_normal_login_admin_succeed(self): def test_01_normal_login_admin_succeed(self):
"""[Regression Test]
Test the succeed of login with 'admin' / 'admin'"""
res = self.ru_obj.authenticate(self.db, 'admin', 'admin', {})
self.assertEqual(
res, self.admin_user_id,
"'admin' / 'admin' login must succeed.")
# NOTE: Can fail if admin password changed
self.admin_user.check_credentials('admin')
def test_02_normal_login_admin_fail(self): def test_02_normal_login_admin_fail(self):
"""[Regression Test]
Test the fail of login with 'admin' / 'bad_password'"""
res = self.ru_obj.authenticate(self.db, 'admin', 'bad_password', {})
self.assertEqual(
res, False,
"'admin' / 'bad_password' login must fail.")
def test_03_normal_login_demo_succeed(self):
"""[Regression Test]
Test the succeed of login with 'demo' / 'demo'"""
res = self.ru_obj.authenticate(self.db, 'demo', 'demo', {})
self.assertEqual(
res, self.demo_user_id,
"'demo' / 'demo' login must succeed.")
def test_04_normal_login_demo_fail(self):
"""[Regression Test]
Test the fail of login with 'demo' / 'bad_password'"""
res = self.ru_obj.authenticate(self.db, 'demo', 'bad_password', {})
self.assertEqual(
res, False,
"'demo' / 'bad_password' login must fail.")
def test_05_passkey_login_demo_succeed(self):
"""[New Feature]
Test the succeed of login with 'demo' / 'admin'"""
res = self.ru_obj.authenticate(self.db, 'demo', 'admin', {})
self.assertEqual(
res, self.demo_user_id,
"'demo' / 'admin' login must succeed.")
def test_06_passkey_login_demo_succeed(self):
with self.assertRaises(exceptions.AccessDenied):
self.admin_user.check_credentials('bad_password')
def test_03_normal_login_passkey_succeed(self):
""" This test cannot pass because in some way the the _uid of
passkey_user is equal to admin one so when entering the
original check_credentials() method, it raises an exception
"""
try:
self.passkey_user.check_credentials('passkey')
except exceptions.AccessDenied:
# This exception is raised from the origin check_credentials()
# method and its an expected behaviour as we catch this in our
# check_credentials()
pass
def test_04_normal_login_passkey_fail(self):
with self.assertRaises(exceptions.AccessDenied):
self.passkey_user.check_credentials('bad_password')
def test_05_passkey_login_passkey_with_admin_password_succeed(self):
# NOTE: Can fail if admin password changed
self.passkey_user.check_credentials('admin')
def test_06_passkey_login_passkey_succeed(self):
"""[Bug #1319391] """[Bug #1319391]
Test the correct behaviour of login with 'bad_login' / 'admin'""" Test the correct behaviour of login with 'bad_login' / 'admin'"""
exception_raised = False
try:
self.ru_obj.authenticate(self.db, 'bad_login', 'admin', {})
except:
exception_raised = True
self.assertEqual(
exception_raised, False,
"'bad_login' / 'admin' musn't raise Error.")
res = self.ru_obj.authenticate(self.db, 'bad_login', 'admin', {})
self.assertFalse(res)

171
auth_admin_passkey/tests/test_ui.py

@ -0,0 +1,171 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2013-2014 GRAP (http://www.grap.coop)
# @author Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from lxml import html
from werkzeug.test import Client
from werkzeug.wrappers import BaseResponse
from odoo.tests import common
from odoo.service import wsgi_server
@common.post_install(True)
class TestUI(common.HttpCase):
def setUp(self):
super(TestUI, self).setUp()
with self.registry.cursor() as test_cursor:
env = self.env(test_cursor)
self.admin_password = 'AdminPa$$w0rd'
env.ref('base.user_root').password = self.admin_password
self.passkey_password = 'PasskeyPa$$w0rd'
self.passkey_user = env['res.users'].create({
'name': 'passkey',
'login': 'passkey',
'email': 'passkey',
'password': self.passkey_password
})
self.dbname = env.cr.dbname
self.werkzeug_environ = {'REMOTE_ADDR': '127.0.0.1'}
self.test_client = Client(wsgi_server.application, BaseResponse)
self.test_client.get('/web/session/logout')
def html_doc(self, response):
"""Get an HTML LXML document."""
return html.fromstring(response.data)
def csrf_token(self, response):
"""Get a valid CSRF token."""
doc = self.html_doc(response)
return doc.xpath("//input[@name='csrf_token']")[0].get('value')
def get_request(self, url, data=None):
return self.test_client.get(
url, query_string=data, follow_redirects=True)
def post_request(self, url, data=None):
return self.test_client.post(
url, data=data, follow_redirects=True,
environ_base=self.werkzeug_environ)
def test_01_normal_login_admin_succeed(self):
# Our admin user wants to go to backoffice part of Odoo
response = self.get_request('/web/', data={'db': self.dbname})
# He notices that his redirected to login page as not authenticated
self.assertIn('oe_login_form', response.data)
# He needs to enters his credentials and submit the form
data = {
'login': 'admin',
'password': self.admin_password,
'csrf_token': self.csrf_token(response),
'db': self.dbname
}
response = self.post_request('/web/login/', data=data)
# He notices that his redirected to backoffice
self.assertNotIn('oe_login_form', response.data)
def test_02_normal_login_admin_fail(self):
# Our admin user wants to go to backoffice part of Odoo
response = self.get_request('/web/', data={'db': self.dbname})
# He notices that he's redirected to login page as not authenticated
self.assertIn('oe_login_form', response.data)
# He needs to enter his credentials and submit the form
data = {
'login': 'admin',
'password': 'password',
'csrf_token': self.csrf_token(response),
'db': self.dbname
}
response = self.post_request('/web/login/', data=data)
# He mistyped his password so he's redirected to login page again
self.assertIn('Wrong login/password', response.data)
def test_03_normal_login_passkey_succeed(self):
# Our passkey user wants to go to backoffice part of Odoo
response = self.get_request('/web/', data={'db': self.dbname})
# He notices that he's redirected to login page as not authenticated
self.assertIn('oe_login_form', response.data)
# He needs to enter his credentials and submit the form
data = {
'login': self.passkey_user.login,
'password': self.passkey_password,
'csrf_token': self.csrf_token(response),
'db': self.dbname
}
response = self.post_request('/web/login/', data=data)
# He notices that his redirected to backoffice
self.assertNotIn('oe_login_form', response.data)
def test_04_normal_login_passkey_fail(self):
# Our passkey user wants to go to backoffice part of Odoo
response = self.get_request('/web/', data={'db': self.dbname})
# He notices that he's redirected to login page as not authenticated
self.assertIn('oe_login_form', response.data)
# He needs to enter his credentials and submit the form
data = {
'login': self.passkey_user.login,
'password': 'password',
'csrf_token': self.csrf_token(response),
'db': self.dbname
}
response = self.post_request('/web/login/', data=data)
# He mistyped his password so he's redirected to login page again
self.assertIn('Wrong login/password', response.data)
def test_05_passkey_login_with_admin_password_succeed(self):
# Our admin user wants to login as passkey user
response = self.get_request('/web/', data={'db': self.dbname})
# He notices that his redirected to login page as not authenticated
self.assertIn('oe_login_form', response.data)
# He needs to enters its password with passkey user's login
data = {
'login': self.passkey_user.login,
'password': self.admin_password,
'csrf_token': self.csrf_token(response),
'db': self.dbname
}
response = self.post_request('/web/login/', data=data)
# He notices that his redirected to backoffice
self.assertNotIn('oe_login_form', response.data)
def test_06_passkey_login_with_same_password_as_admin(self):
self.passkey_user.password = self.admin_password
# Our passkey user wants to go to backoffice part of Odoo
response = self.get_request('/web/', data={'db': self.dbname})
# He notices that his redirected to login page as not authenticated
self.assertIn('oe_login_form', response.data)
# He needs to enters his credentials and submit the form
data = {
'login': self.passkey_user.login,
'password': self.admin_password,
'csrf_token': self.csrf_token(response),
'db': self.dbname
}
response = self.post_request('/web/login/', data=data)
# He notices that his redirected to backoffice
self.assertNotIn('oe_login_form', response.data)

42
auth_admin_passkey/views/res_config_view.xml

@ -1,47 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- ********************************************************************** -->
<!--Admin Passkey module for Odoo -->
<!--Copyright (C) 2013-2014 GRAP (http://www.grap.coop) -->
<!--@author Sylvain LE GAL (https://twitter.com/legalsylvain) -->
<!--This program is free software: you can redistribute it and/or modify -->
<!--it under the terms of the GNU Affero General Public License as -->
<!--published by the Free Software Foundation, either version 3 of the -->
<!--License, or (at your option) any later version. -->
<!--This program is distributed in the hope that it will be useful, -->
<!--but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!--GNU Affero General Public License for more details. -->
<!--You should have received a copy of the GNU Affero General Public License-->
<!--along with this program. If not, see <http://www.gnu.org/licenses/>. -->
<!-- ********************************************************************** -->
<openerp>
<data>
<odoo>
<record id="view_res_config_settings" model="ir.ui.view"> <record id="view_res_config_settings" model="ir.ui.view">
<field name="name">base.config.settings.view</field> <field name="name">base.config.settings.view</field>
<field name="model">base.config.settings</field> <field name="model">base.config.settings</field>
<field name="inherit_id" ref="base_setup.view_general_configuration"/> <field name="inherit_id" ref="base_setup.view_general_configuration"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//label[@string='Email']/.." position='after'>
<xpath expr="//label[@name='email_label']/.." position='after'>
<group> <group>
<label for="id" string="Passkey"/> <label for="id" string="Passkey"/>
<div> <div>
<div>
<field name="auth_admin_passkey_send_to_admin" class="oe_inline"/>
<label for="auth_admin_passkey_send_to_admin"/>
</div>
<div>
<field name="auth_admin_passkey_send_to_user" class="oe_inline"/>
<label for="auth_admin_passkey_send_to_user"/>
</div>
<div>
<field name="auth_admin_passkey_send_to_admin" class="oe_inline"/>
<label for="auth_admin_passkey_send_to_admin"/>
</div>
<div>
<field name="auth_admin_passkey_send_to_user" class="oe_inline"/>
<label for="auth_admin_passkey_send_to_user"/>
</div>
</div> </div>
</group> </group>
</xpath> </xpath>
</field> </field>
</record> </record>
</data>
</openerp>
</odoo>

1
setup/auth_admin_passkey/odoo/__init__.py

@ -0,0 +1 @@
__import__('pkg_resources').declare_namespace(__name__)

1
setup/auth_admin_passkey/odoo/addons/__init__.py

@ -0,0 +1 @@
__import__('pkg_resources').declare_namespace(__name__)

1
setup/auth_admin_passkey/odoo/addons/auth_admin_passkey

@ -0,0 +1 @@
../../../../auth_admin_passkey

6
setup/auth_admin_passkey/setup.py

@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
Loading…
Cancel
Save