Browse Source
[ADD] auth_totp_password_security: Compatibility
[ADD] auth_totp_password_security: Compatibility
* Overload mfa_login_post action introduced by auth_totp so that password expiration check from password_security still happens during an MFA login * Add unit tests for new logicpull/996/head
Oleg Bulkin
7 years ago
7 changed files with 198 additions and 0 deletions
-
60auth_totp_password_security/README.rst
-
5auth_totp_password_security/__init__.py
-
20auth_totp_password_security/__manifest__.py
-
5auth_totp_password_security/controllers/__init__.py
-
26auth_totp_password_security/controllers/main.py
-
5auth_totp_password_security/tests/__init__.py
-
77auth_totp_password_security/tests/test_main.py
@ -0,0 +1,60 @@ |
|||
.. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg |
|||
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html |
|||
:alt: License: LGPL-3 |
|||
|
|||
======================================= |
|||
MFA and Password Security Compatibility |
|||
======================================= |
|||
|
|||
This is a glue module with extra logic needed for full compatibility between |
|||
the ``auth_totp`` and ``password_security`` modules. |
|||
|
|||
Installation |
|||
============ |
|||
|
|||
There is no need to install this module directly as this should happen |
|||
automatically if both of the parent modules are installed. |
|||
|
|||
Usage |
|||
===== |
|||
|
|||
.. 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 |
|||
|
|||
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 welcome feedback. |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Images |
|||
------ |
|||
|
|||
* Odoo Community Association: |
|||
`Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_. |
|||
|
|||
Contributors |
|||
------------ |
|||
|
|||
* Oleg Bulkin <obulkin@laslabs.com> |
|||
|
|||
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. |
@ -0,0 +1,5 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) |
|||
|
|||
from . import controllers |
@ -0,0 +1,20 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) |
|||
|
|||
{ |
|||
'name': 'MFA and Password Security Compatibility', |
|||
'summary': 'auth_totp and password_security compatibility', |
|||
'version': '10.0.1.0.0', |
|||
'category': 'Hidden', |
|||
'website': 'https://github.com/OCA/server-tools', |
|||
'author': 'LasLabs, Odoo Community Association (OCA)', |
|||
'license': 'LGPL-3', |
|||
'application': False, |
|||
'installable': True, |
|||
'auto_install': True, |
|||
'depends': [ |
|||
'auth_totp', |
|||
'password_security', |
|||
], |
|||
} |
@ -0,0 +1,5 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) |
|||
|
|||
from . import main |
@ -0,0 +1,26 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) |
|||
|
|||
from odoo.http import redirect_with_hash, request, route |
|||
from odoo.addons.auth_totp.controllers.main import AuthTotp |
|||
|
|||
|
|||
class AuthTotpPasswordSecurity(AuthTotp): |
|||
@route() |
|||
def mfa_login_post(self, *args, **kwargs): |
|||
"""Overload to check password expiration after MFA login""" |
|||
super_object = super(AuthTotpPasswordSecurity, self) |
|||
response = super_object.mfa_login_post(*args, **kwargs) |
|||
|
|||
if not request.params.get('login_success'): |
|||
return response |
|||
|
|||
user = request.env['res.users'].sudo().browse(request.uid) |
|||
if user._password_has_expired(): |
|||
user.action_expire_password() |
|||
request.session.logout(keep_db=True) |
|||
request.params['login_success'] = False |
|||
return redirect_with_hash(user.partner_id.signup_url) |
|||
|
|||
return response |
@ -0,0 +1,5 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) |
|||
|
|||
from . import test_main |
@ -0,0 +1,77 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2017 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) |
|||
|
|||
from datetime import datetime |
|||
from mock import patch |
|||
from odoo.fields import Datetime |
|||
from odoo.tests.common import TransactionCase |
|||
from ..controllers.main import AuthTotpPasswordSecurity |
|||
|
|||
CONTROLLER_PATH = 'odoo.addons.auth_totp_password_security.controllers.main' |
|||
MODEL_PATH = 'odoo.addons.password_security.models.res_users.ResUsers' |
|||
|
|||
|
|||
@patch(CONTROLLER_PATH + '.AuthTotp.mfa_login_post') |
|||
class TestAuthTotpPasswordSecurity(TransactionCase): |
|||
|
|||
def setUp(self): |
|||
super(TestAuthTotpPasswordSecurity, self).setUp() |
|||
|
|||
self.test_controller = AuthTotpPasswordSecurity() |
|||
|
|||
self.test_user = self.env.ref('base.user_root') |
|||
self.test_user.company_id.password_expiration = 1 |
|||
pass_date = datetime(year=2016, month=1, day=1) |
|||
self.test_user.password_write_date = Datetime.to_string(pass_date) |
|||
|
|||
request_patcher = patch(CONTROLLER_PATH + '.request') |
|||
self.addCleanup(request_patcher.stop) |
|||
self.request_mock = request_patcher.start() |
|||
self.request_mock.params = {'login_success': True} |
|||
self.request_mock.uid = self.test_user.id |
|||
self.request_mock.env = self.env |
|||
|
|||
# Needed when tests are run with no prior requests |
|||
base_request_patcher = patch('odoo.http.request') |
|||
self.addCleanup(base_request_patcher.stop) |
|||
base_request_patcher.start() |
|||
|
|||
def test_mfa_login_post_no_mfa_login(self, super_mock): |
|||
"""Should return result of super if MFA login not complete""" |
|||
test_response = 'Test Response' |
|||
super_mock.return_value = test_response |
|||
self.request_mock.params = {} |
|||
result = self.test_controller.mfa_login_post().get_data() |
|||
|
|||
self.assertEqual(result, test_response) |
|||
|
|||
def test_mfa_login_post_pass_not_expired(self, super_mock): |
|||
"""Should return result of super if user's password not expired""" |
|||
test_response = 'Test Response' |
|||
super_mock.return_value = test_response |
|||
self.test_user.password_write_date = Datetime.to_string(datetime.now()) |
|||
result = self.test_controller.mfa_login_post().get_data() |
|||
|
|||
self.assertEqual(result, test_response) |
|||
|
|||
@patch(MODEL_PATH + '.action_expire_password') |
|||
def test_mfa_login_post_expired_helper(self, helper_mock, super_mock): |
|||
"""Should correctly call helper if user's password is expired""" |
|||
self.test_controller.mfa_login_post() |
|||
|
|||
helper_mock.assert_called_once_with() |
|||
|
|||
def test_mfa_login_post_expired_log_out(self, super_mock): |
|||
"""Should log out user and update params if password is expired""" |
|||
self.test_controller.mfa_login_post() |
|||
|
|||
self.request_mock.session.logout.assert_called_once_with(keep_db=True) |
|||
self.assertFalse(self.request_mock.params['login_success']) |
|||
|
|||
def test_mfa_login_post_expired_redirect(self, super_mock): |
|||
"""Should return correct redirect if password is expired""" |
|||
result = self.test_controller.mfa_login_post().get_data() |
|||
|
|||
expected = self.test_user.partner_id.signup_url |
|||
self.assertIn(expected, result) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue