Browse Source

Merge pull request #996 from LasLabs/bugfix/10.0/LABS-474-password_security_auth_totp_compatibility

[10.0][ADD][FIX] auth_totp and password_security compatibility
pull/1045/head
Dave Lasley 7 years ago
committed by GitHub
parent
commit
140f14148e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 60
      auth_totp_password_security/README.rst
  2. 5
      auth_totp_password_security/__init__.py
  3. 20
      auth_totp_password_security/__manifest__.py
  4. 5
      auth_totp_password_security/controllers/__init__.py
  5. 26
      auth_totp_password_security/controllers/main.py
  6. 5
      auth_totp_password_security/tests/__init__.py
  7. 77
      auth_totp_password_security/tests/test_main.py
  8. 3
      password_security/controllers/main.py
  9. 12
      password_security/tests/test_password_security_home.py

60
auth_totp_password_security/README.rst

@ -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.

5
auth_totp_password_security/__init__.py

@ -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

20
auth_totp_password_security/__manifest__.py

@ -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',
],
}

5
auth_totp_password_security/controllers/__init__.py

@ -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

26
auth_totp_password_security/controllers/main.py

@ -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

5
auth_totp_password_security/tests/__init__.py

@ -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

77
auth_totp_password_security/tests/test_main.py

@ -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)

3
password_security/controllers/main.py

@ -36,7 +36,8 @@ class PasswordSecurityHome(AuthSignupHome):
def web_login(self, *args, **kw):
ensure_db()
response = super(PasswordSecurityHome, self).web_login(*args, **kw)
if not request.httprequest.method == 'POST':
login_success = request.params.get('login_success', True)
if not request.httprequest.method == 'POST' or not login_success:
return response
uid = request.session.authenticate(
request.session.db,

12
password_security/tests/test_password_security_home.py

@ -114,6 +114,16 @@ class TestPasswordSecurityHome(TransactionCase):
assets['web_login'](), res,
)
def test_web_login_login_success_flag(self):
"""It should return result of super when login_success flag False"""
with self.mock_assets() as assets:
assets['request'].httprequest.method = 'POST'
assets['request'].params = {'login_success': False}
result = self.password_security_home.web_login()
expected = assets['web_login']()
self.assertEqual(result, expected)
def test_web_login_authenticate(self):
""" It should attempt authentication to obtain uid """
with self.mock_assets() as assets:
@ -229,6 +239,8 @@ class TestPasswordSecurityHome(TransactionCase):
main.AuthSignupHome, 'get_auth_signup_qcontext', spec=dict
) as qcontext:
assets['web_auth_signup'].side_effect = MockPassError
assets['request'].render.return_value = MockResponse()
res = self.password_security_home.web_auth_signup()
assets['request'].render.assert_called_once_with(
'auth_signup.signup', qcontext(),

Loading…
Cancel
Save