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.

293 lines
12 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2016 LasLabs Inc.
  3. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
  4. import mock
  5. from contextlib import contextmanager
  6. from openerp.tests.common import TransactionCase
  7. from openerp.http import Response
  8. from ..controllers import main
  9. IMPORT = 'openerp.addons.password_security.controllers.main'
  10. class EndTestException(Exception):
  11. """ It allows for isolation of resources by raise """
  12. class MockResponse(object):
  13. def __new__(cls):
  14. return mock.Mock(spec=Response)
  15. class MockPassError(main.PassError):
  16. def __init__(self):
  17. super(MockPassError, self).__init__('Message')
  18. class TestPasswordSecurityHome(TransactionCase):
  19. def setUp(self):
  20. super(TestPasswordSecurityHome, self).setUp()
  21. self.PasswordSecurityHome = main.PasswordSecurityHome
  22. self.password_security_home = self.PasswordSecurityHome()
  23. self.passwd = 'I am a password!'
  24. self.qcontext = {
  25. 'password': self.passwd,
  26. }
  27. @contextmanager
  28. def mock_assets(self):
  29. """ It mocks and returns assets used by this controller """
  30. methods = ['do_signup', 'web_login', 'web_auth_signup',
  31. 'web_auth_reset_password',
  32. ]
  33. with mock.patch.multiple(
  34. main.AuthSignupHome, **{m: mock.DEFAULT for m in methods}
  35. ) as _super:
  36. mocks = {}
  37. for method in methods:
  38. mocks[method] = _super[method]
  39. mocks[method].return_value = MockResponse()
  40. with mock.patch('%s.request' % IMPORT) as request:
  41. with mock.patch('%s.ensure_db' % IMPORT) as ensure:
  42. with mock.patch('%s.http' % IMPORT) as http:
  43. http.redirect_with_hash.return_value = \
  44. MockResponse()
  45. mocks.update({
  46. 'request': request,
  47. 'ensure_db': ensure,
  48. 'http': http,
  49. })
  50. yield mocks
  51. def test_do_signup_check(self):
  52. """ It should check password on user """
  53. with self.mock_assets() as assets:
  54. check_password = assets['request'].env.user.check_password
  55. check_password.side_effect = EndTestException
  56. with self.assertRaises(EndTestException):
  57. self.password_security_home.do_signup(self.qcontext)
  58. check_password.assert_called_once_with(
  59. self.passwd,
  60. )
  61. def test_do_signup_return(self):
  62. """ It should return result of super """
  63. with self.mock_assets() as assets:
  64. res = self.password_security_home.do_signup(self.qcontext)
  65. self.assertEqual(assets['do_signup'](), res)
  66. def test_web_login_ensure_db(self):
  67. """ It should verify available db """
  68. with self.mock_assets() as assets:
  69. assets['ensure_db'].side_effect = EndTestException
  70. with self.assertRaises(EndTestException):
  71. self.password_security_home.web_login()
  72. def test_web_login_super(self):
  73. """ It should call superclass w/ proper args """
  74. expect_list = [1, 2, 3]
  75. expect_dict = {'test1': 'good1', 'test2': 'good2'}
  76. with self.mock_assets() as assets:
  77. assets['web_login'].side_effect = EndTestException
  78. with self.assertRaises(EndTestException):
  79. self.password_security_home.web_login(
  80. *expect_list, **expect_dict
  81. )
  82. assets['web_login'].assert_called_once_with(
  83. *expect_list, **expect_dict
  84. )
  85. def test_web_login_no_post(self):
  86. """ It should return immediate result of super when not POST """
  87. with self.mock_assets() as assets:
  88. assets['request'].httprequest.method = 'GET'
  89. assets['request'].session.authenticate.side_effect = \
  90. EndTestException
  91. res = self.password_security_home.web_login()
  92. self.assertEqual(
  93. assets['web_login'](), res,
  94. )
  95. def test_web_login_login_success_flag(self):
  96. """It should return result of super when login_success flag False"""
  97. with self.mock_assets() as assets:
  98. assets['request'].httprequest.method = 'POST'
  99. assets['request'].params = {'login_success': False}
  100. result = self.password_security_home.web_login()
  101. expected = assets['web_login']()
  102. self.assertEqual(result, expected)
  103. def test_web_login_authenticate(self):
  104. """ It should attempt authentication to obtain uid """
  105. with self.mock_assets() as assets:
  106. assets['request'].httprequest.method = 'POST'
  107. authenticate = assets['request'].session.authenticate
  108. request = assets['request']
  109. authenticate.side_effect = EndTestException
  110. with self.assertRaises(EndTestException):
  111. self.password_security_home.web_login()
  112. authenticate.assert_called_once_with(
  113. request.session.db,
  114. request.params['login'],
  115. request.params['password'],
  116. )
  117. def test_web_login_authenticate_fail(self):
  118. """ It should return super result if failed auth """
  119. with self.mock_assets() as assets:
  120. authenticate = assets['request'].session.authenticate
  121. request = assets['request']
  122. request.httprequest.method = 'POST'
  123. request.env['res.users'].sudo.side_effect = EndTestException
  124. authenticate.return_value = False
  125. res = self.password_security_home.web_login()
  126. self.assertEqual(
  127. assets['web_login'](), res,
  128. )
  129. def test_web_login_get_user(self):
  130. """ It should get the proper user as sudo """
  131. with self.mock_assets() as assets:
  132. request = assets['request']
  133. request.httprequest.method = 'POST'
  134. sudo = request.env['res.users'].sudo()
  135. sudo.browse.side_effect = EndTestException
  136. with self.assertRaises(EndTestException):
  137. self.password_security_home.web_login()
  138. sudo.browse.assert_called_once_with(
  139. request.uid
  140. )
  141. def test_web_login_valid_pass(self):
  142. """ It should return parent result if pass isn't expired """
  143. with self.mock_assets() as assets:
  144. request = assets['request']
  145. request.httprequest.method = 'POST'
  146. user = request.env['res.users'].sudo().browse()
  147. user.action_expire_password.side_effect = EndTestException
  148. user._password_has_expired.return_value = False
  149. res = self.password_security_home.web_login()
  150. self.assertEqual(
  151. assets['web_login'](), res,
  152. )
  153. def test_web_login_expire_pass(self):
  154. """ It should expire password if necessary """
  155. with self.mock_assets() as assets:
  156. request = assets['request']
  157. request.httprequest.method = 'POST'
  158. user = request.env['res.users'].sudo().browse()
  159. user.action_expire_password.side_effect = EndTestException
  160. user._password_has_expired.return_value = True
  161. with self.assertRaises(EndTestException):
  162. self.password_security_home.web_login()
  163. def test_web_login_log_out_if_expired(self):
  164. """It should log out user if password expired"""
  165. with self.mock_assets() as assets:
  166. request = assets['request']
  167. request.httprequest.method = 'POST'
  168. user = request.env['res.users'].sudo().browse()
  169. user._password_has_expired.return_value = True
  170. self.password_security_home.web_login()
  171. logout_mock = request.session.logout
  172. logout_mock.assert_called_once_with(keep_db=True)
  173. def test_web_login_redirect(self):
  174. """ It should redirect w/ hash to reset after expiration """
  175. with self.mock_assets() as assets:
  176. request = assets['request']
  177. request.httprequest.method = 'POST'
  178. user = request.env['res.users'].sudo().browse()
  179. user._password_has_expired.return_value = True
  180. res = self.password_security_home.web_login()
  181. self.assertEqual(
  182. assets['http'].redirect_with_hash(), res,
  183. )
  184. def test_web_auth_signup_valid(self):
  185. """ It should return super if no errors """
  186. with self.mock_assets() as assets:
  187. res = self.password_security_home.web_auth_signup()
  188. self.assertEqual(
  189. assets['web_auth_signup'](), res,
  190. )
  191. def test_web_auth_signup_invalid_qcontext(self):
  192. """ It should catch PassError and get signup qcontext """
  193. with self.mock_assets() as assets:
  194. with mock.patch.object(
  195. main.AuthSignupHome, 'get_auth_signup_qcontext',
  196. ) as qcontext:
  197. assets['web_auth_signup'].side_effect = MockPassError
  198. qcontext.side_effect = EndTestException
  199. with self.assertRaises(EndTestException):
  200. self.password_security_home.web_auth_signup()
  201. def test_web_auth_signup_invalid_render(self):
  202. """ It should render & return signup form on invalid """
  203. with self.mock_assets() as assets:
  204. with mock.patch.object(
  205. main.AuthSignupHome, 'get_auth_signup_qcontext', spec=dict
  206. ) as qcontext:
  207. assets['web_auth_signup'].side_effect = MockPassError
  208. assets['request'].render.return_value = MockResponse()
  209. res = self.password_security_home.web_auth_signup()
  210. assets['request'].render.assert_called_once_with(
  211. 'auth_signup.signup', qcontext(),
  212. )
  213. self.assertEqual(
  214. assets['request'].render(), res,
  215. )
  216. def test_web_auth_reset_password_fail_login(self):
  217. """ It should raise from failed _validate_pass_reset by login """
  218. with self.mock_assets() as assets:
  219. with mock.patch.object(
  220. main.AuthSignupHome, 'get_auth_signup_qcontext', spec=dict
  221. ) as qcontext:
  222. qcontext['login'] = 'login'
  223. search = assets['request'].env.sudo().search
  224. assets['request'].httprequest.method = 'POST'
  225. user = mock.MagicMock()
  226. user._validate_pass_reset.side_effect = MockPassError
  227. search.return_value = user
  228. with self.assertRaises(MockPassError):
  229. self.password_security_home.web_auth_reset_password()
  230. def test_web_auth_reset_password_fail_email(self):
  231. """ It should raise from failed _validate_pass_reset by email """
  232. with self.mock_assets() as assets:
  233. with mock.patch.object(
  234. main.AuthSignupHome, 'get_auth_signup_qcontext', spec=dict
  235. ) as qcontext:
  236. qcontext['login'] = 'login'
  237. search = assets['request'].env.sudo().search
  238. assets['request'].httprequest.method = 'POST'
  239. user = mock.MagicMock()
  240. user._validate_pass_reset.side_effect = MockPassError
  241. search.side_effect = [[], user]
  242. with self.assertRaises(MockPassError):
  243. self.password_security_home.web_auth_reset_password()
  244. def test_web_auth_reset_password_success(self):
  245. """ It should return parent response on no validate errors """
  246. with self.mock_assets() as assets:
  247. with mock.patch.object(
  248. main.AuthSignupHome, 'get_auth_signup_qcontext', spec=dict
  249. ) as qcontext:
  250. qcontext['login'] = 'login'
  251. assets['request'].httprequest.method = 'POST'
  252. res = self.password_security_home.web_auth_reset_password()
  253. self.assertEqual(
  254. assets['web_auth_reset_password'](), res,
  255. )