You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
12 KiB
281 lines
12 KiB
# -*- coding: utf-8 -*-
|
|
# Copyright 2016 LasLabs Inc.
|
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
|
|
|
import mock
|
|
|
|
from contextlib import contextmanager
|
|
|
|
from odoo.tests.common import TransactionCase
|
|
from odoo.http import Response
|
|
|
|
from ..controllers import main
|
|
|
|
|
|
IMPORT = 'odoo.addons.password_security.controllers.main'
|
|
|
|
|
|
class EndTestException(Exception):
|
|
""" It allows for isolation of resources by raise """
|
|
|
|
|
|
class MockResponse(object):
|
|
def __new__(cls):
|
|
return mock.Mock(spec=Response)
|
|
|
|
|
|
class MockPassError(main.PassError):
|
|
def __init__(self):
|
|
super(MockPassError, self).__init__('Message')
|
|
|
|
|
|
class TestPasswordSecurityHome(TransactionCase):
|
|
|
|
def setUp(self):
|
|
super(TestPasswordSecurityHome, self).setUp()
|
|
self.PasswordSecurityHome = main.PasswordSecurityHome
|
|
self.password_security_home = self.PasswordSecurityHome()
|
|
self.passwd = 'I am a password!'
|
|
self.qcontext = {
|
|
'password': self.passwd,
|
|
}
|
|
|
|
@contextmanager
|
|
def mock_assets(self):
|
|
""" It mocks and returns assets used by this controller """
|
|
methods = ['do_signup', 'web_login', 'web_auth_signup',
|
|
'web_auth_reset_password',
|
|
]
|
|
with mock.patch.multiple(
|
|
main.AuthSignupHome, **{m: mock.DEFAULT for m in methods}
|
|
) as _super:
|
|
mocks = {}
|
|
for method in methods:
|
|
mocks[method] = _super[method]
|
|
mocks[method].return_value = MockResponse()
|
|
with mock.patch('%s.request' % IMPORT) as request:
|
|
with mock.patch('%s.ensure_db' % IMPORT) as ensure:
|
|
with mock.patch('%s.http' % IMPORT) as http:
|
|
http.redirect_with_hash.return_value = \
|
|
MockResponse()
|
|
mocks.update({
|
|
'request': request,
|
|
'ensure_db': ensure,
|
|
'http': http,
|
|
})
|
|
yield mocks
|
|
|
|
def test_do_signup_check(self):
|
|
""" It should check password on user """
|
|
with self.mock_assets() as assets:
|
|
check_password = assets['request'].env.user._check_password
|
|
check_password.side_effect = EndTestException
|
|
with self.assertRaises(EndTestException):
|
|
self.password_security_home.do_signup(self.qcontext)
|
|
check_password.assert_called_once_with(
|
|
self.passwd,
|
|
)
|
|
|
|
def test_do_signup_return(self):
|
|
""" It should return result of super """
|
|
with self.mock_assets() as assets:
|
|
res = self.password_security_home.do_signup(self.qcontext)
|
|
self.assertEqual(assets['do_signup'](), res)
|
|
|
|
def test_web_login_ensure_db(self):
|
|
""" It should verify available db """
|
|
with self.mock_assets() as assets:
|
|
assets['ensure_db'].side_effect = EndTestException
|
|
with self.assertRaises(EndTestException):
|
|
self.password_security_home.web_login()
|
|
|
|
def test_web_login_super(self):
|
|
""" It should call superclass w/ proper args """
|
|
expect_list = [1, 2, 3]
|
|
expect_dict = {'test1': 'good1', 'test2': 'good2'}
|
|
with self.mock_assets() as assets:
|
|
assets['web_login'].side_effect = EndTestException
|
|
with self.assertRaises(EndTestException):
|
|
self.password_security_home.web_login(
|
|
*expect_list, **expect_dict
|
|
)
|
|
assets['web_login'].assert_called_once_with(
|
|
*expect_list, **expect_dict
|
|
)
|
|
|
|
def test_web_login_no_post(self):
|
|
""" It should return immediate result of super when not POST """
|
|
with self.mock_assets() as assets:
|
|
assets['request'].httprequest.method = 'GET'
|
|
assets['request'].session.authenticate.side_effect = \
|
|
EndTestException
|
|
res = self.password_security_home.web_login()
|
|
self.assertEqual(
|
|
assets['web_login'](), res,
|
|
)
|
|
|
|
def test_web_login_authenticate(self):
|
|
""" It should attempt authentication to obtain uid """
|
|
with self.mock_assets() as assets:
|
|
assets['request'].httprequest.method = 'POST'
|
|
authenticate = assets['request'].session.authenticate
|
|
request = assets['request']
|
|
authenticate.side_effect = EndTestException
|
|
with self.assertRaises(EndTestException):
|
|
self.password_security_home.web_login()
|
|
authenticate.assert_called_once_with(
|
|
request.session.db,
|
|
request.params['login'],
|
|
request.params['password'],
|
|
)
|
|
|
|
def test_web_login_authenticate_fail(self):
|
|
""" It should return super result if failed auth """
|
|
with self.mock_assets() as assets:
|
|
authenticate = assets['request'].session.authenticate
|
|
request = assets['request']
|
|
request.httprequest.method = 'POST'
|
|
request.env['res.users'].sudo.side_effect = EndTestException
|
|
authenticate.return_value = False
|
|
res = self.password_security_home.web_login()
|
|
self.assertEqual(
|
|
assets['web_login'](), res,
|
|
)
|
|
|
|
def test_web_login_get_user(self):
|
|
""" It should get the proper user as sudo """
|
|
with self.mock_assets() as assets:
|
|
request = assets['request']
|
|
request.httprequest.method = 'POST'
|
|
sudo = request.env['res.users'].sudo()
|
|
sudo.browse.side_effect = EndTestException
|
|
with self.assertRaises(EndTestException):
|
|
self.password_security_home.web_login()
|
|
sudo.browse.assert_called_once_with(
|
|
request.uid
|
|
)
|
|
|
|
def test_web_login_valid_pass(self):
|
|
""" It should return parent result if pass isn't expired """
|
|
with self.mock_assets() as assets:
|
|
request = assets['request']
|
|
request.httprequest.method = 'POST'
|
|
user = request.env['res.users'].sudo().browse()
|
|
user.action_expire_password.side_effect = EndTestException
|
|
user._password_has_expired.return_value = False
|
|
res = self.password_security_home.web_login()
|
|
self.assertEqual(
|
|
assets['web_login'](), res,
|
|
)
|
|
|
|
def test_web_login_expire_pass(self):
|
|
""" It should expire password if necessary """
|
|
with self.mock_assets() as assets:
|
|
request = assets['request']
|
|
request.httprequest.method = 'POST'
|
|
user = request.env['res.users'].sudo().browse()
|
|
user.action_expire_password.side_effect = EndTestException
|
|
user._password_has_expired.return_value = True
|
|
with self.assertRaises(EndTestException):
|
|
self.password_security_home.web_login()
|
|
|
|
def test_web_login_log_out_if_expired(self):
|
|
"""It should log out user if password expired"""
|
|
with self.mock_assets() as assets:
|
|
request = assets['request']
|
|
request.httprequest.method = 'POST'
|
|
user = request.env['res.users'].sudo().browse()
|
|
user._password_has_expired.return_value = True
|
|
self.password_security_home.web_login()
|
|
|
|
logout_mock = request.session.logout
|
|
logout_mock.assert_called_once_with(keep_db=True)
|
|
|
|
def test_web_login_redirect(self):
|
|
""" It should redirect w/ hash to reset after expiration """
|
|
with self.mock_assets() as assets:
|
|
request = assets['request']
|
|
request.httprequest.method = 'POST'
|
|
user = request.env['res.users'].sudo().browse()
|
|
user._password_has_expired.return_value = True
|
|
res = self.password_security_home.web_login()
|
|
self.assertEqual(
|
|
assets['http'].redirect_with_hash(), res,
|
|
)
|
|
|
|
def test_web_auth_signup_valid(self):
|
|
""" It should return super if no errors """
|
|
with self.mock_assets() as assets:
|
|
res = self.password_security_home.web_auth_signup()
|
|
self.assertEqual(
|
|
assets['web_auth_signup'](), res,
|
|
)
|
|
|
|
def test_web_auth_signup_invalid_qcontext(self):
|
|
""" It should catch PassError and get signup qcontext """
|
|
with self.mock_assets() as assets:
|
|
with mock.patch.object(
|
|
main.AuthSignupHome, 'get_auth_signup_qcontext',
|
|
) as qcontext:
|
|
assets['web_auth_signup'].side_effect = MockPassError
|
|
qcontext.side_effect = EndTestException
|
|
with self.assertRaises(EndTestException):
|
|
self.password_security_home.web_auth_signup()
|
|
|
|
def test_web_auth_signup_invalid_render(self):
|
|
""" It should render & return signup form on invalid """
|
|
with self.mock_assets() as assets:
|
|
with mock.patch.object(
|
|
main.AuthSignupHome, 'get_auth_signup_qcontext', spec=dict
|
|
) as qcontext:
|
|
assets['web_auth_signup'].side_effect = MockPassError
|
|
res = self.password_security_home.web_auth_signup()
|
|
assets['request'].render.assert_called_once_with(
|
|
'auth_signup.signup', qcontext(),
|
|
)
|
|
self.assertEqual(
|
|
assets['request'].render(), res,
|
|
)
|
|
|
|
def test_web_auth_reset_password_fail_login(self):
|
|
""" It should raise from failed _validate_pass_reset by login """
|
|
with self.mock_assets() as assets:
|
|
with mock.patch.object(
|
|
main.AuthSignupHome, 'get_auth_signup_qcontext', spec=dict
|
|
) as qcontext:
|
|
qcontext['login'] = 'login'
|
|
search = assets['request'].env.sudo().search
|
|
assets['request'].httprequest.method = 'POST'
|
|
user = mock.MagicMock()
|
|
user._validate_pass_reset.side_effect = MockPassError
|
|
search.return_value = user
|
|
with self.assertRaises(MockPassError):
|
|
self.password_security_home.web_auth_reset_password()
|
|
|
|
def test_web_auth_reset_password_fail_email(self):
|
|
""" It should raise from failed _validate_pass_reset by email """
|
|
with self.mock_assets() as assets:
|
|
with mock.patch.object(
|
|
main.AuthSignupHome, 'get_auth_signup_qcontext', spec=dict
|
|
) as qcontext:
|
|
qcontext['login'] = 'login'
|
|
search = assets['request'].env.sudo().search
|
|
assets['request'].httprequest.method = 'POST'
|
|
user = mock.MagicMock()
|
|
user._validate_pass_reset.side_effect = MockPassError
|
|
search.side_effect = [[], user]
|
|
with self.assertRaises(MockPassError):
|
|
self.password_security_home.web_auth_reset_password()
|
|
|
|
def test_web_auth_reset_password_success(self):
|
|
""" It should return parent response on no validate errors """
|
|
with self.mock_assets() as assets:
|
|
with mock.patch.object(
|
|
main.AuthSignupHome, 'get_auth_signup_qcontext', spec=dict
|
|
) as qcontext:
|
|
qcontext['login'] = 'login'
|
|
assets['request'].httprequest.method = 'POST'
|
|
res = self.password_security_home.web_auth_reset_password()
|
|
self.assertEqual(
|
|
assets['web_auth_reset_password'](), res,
|
|
)
|