test_reauthentication.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. from datetime import datetime
  2. import pytest
  3. from flaskbb.auth.services import reauthentication as reauth
  4. from flaskbb.core.auth.authentication import (PostReauthenticateHandler,
  5. ReauthenticateFailureHandler,
  6. ReauthenticateProvider,
  7. StopAuthentication)
  8. from flaskbb.plugins import spec
  9. from freezegun import freeze_time
  10. from pluggy import HookimplMarker
  11. from pytz import UTC
  12. pytestmark = pytest.mark.usefixtures('default_settings')
  13. def test_default_reauth_returns_true_if_secret_matches_user(Fred):
  14. service = reauth.DefaultFlaskBBReauthProvider()
  15. assert service.reauthenticate(Fred, 'fred')
  16. def test_clears_failed_logins_attempts(Fred):
  17. service = reauth.ClearFailedLoginsOnReauth()
  18. Fred.login_attempts = 1000
  19. service.handle_post_reauth(Fred)
  20. assert Fred.login_attempts == 0
  21. @freeze_time(datetime(2018, 1, 1, 13, 30))
  22. def test_marks_failed_login_attempt(Fred):
  23. service = reauth.MarkFailedReauth()
  24. Fred.login_attempts = 1
  25. Fred.last_failed_login = datetime.min.replace(tzinfo=UTC)
  26. service.handle_reauth_failure(Fred)
  27. assert Fred.login_attempts == 2
  28. assert Fred.last_failed_login == datetime(2018, 1, 1, 13, 30, tzinfo=UTC)
  29. class TestPluginAuthenticationManager(object):
  30. def raises_stop_authentication_if_user_isnt_reauthenticated(
  31. self, plugin_manager, mocker, database, Fred
  32. ):
  33. service = self._get_auth_manager(plugin_manager, database)
  34. reauth = mocker.MagicMock(spec=ReauthenticateProvider)
  35. plugin_manager.register(self.impls(reauth=reauth))
  36. with pytest.raises(StopAuthentication) as excinfo:
  37. service.reauthenticate(Fred, 'nope')
  38. reauth.assert_called_once_with(user=Fred, secret='nope')
  39. assert excinfo.value.reason == "Wrong password."
  40. def test_runs_failed_hooks_when_stopauthentication_is_raised(
  41. self, plugin_manager, mocker, database, Fred
  42. ):
  43. service = self._get_auth_manager(plugin_manager, database)
  44. failure = mocker.MagicMock(spec=ReauthenticateFailureHandler)
  45. plugin_manager.register(self.impls(failure=failure))
  46. with pytest.raises(StopAuthentication):
  47. service.reauthenticate(Fred, 'nope')
  48. failure.assert_called_once_with(user=Fred)
  49. def test_runs_post_reauth_handler_if_user_authenticates(
  50. self, plugin_manager, mocker, Fred, database
  51. ):
  52. service = self._get_auth_manager(plugin_manager, database)
  53. reauth = mocker.MagicMock(
  54. spec=ReauthenticateProvider, return_value=Fred
  55. )
  56. post_reauth = mocker.MagicMock(spec=PostReauthenticateHandler)
  57. plugin_manager.register(
  58. self.impls(reauth=reauth, post_reauth=post_reauth)
  59. )
  60. service.reauthenticate(Fred, 'fred')
  61. reauth.assert_called_once_with(user=Fred, secret='fred')
  62. post_reauth.assert_called_once_with(user=Fred)
  63. def test_reraises_if_session_commit_fails(
  64. self, mocker, plugin_manager, Fred
  65. ):
  66. class NotAnActualException(Exception):
  67. pass
  68. db = mocker.Mock()
  69. db.session.commit.side_effect = NotAnActualException
  70. service = self._get_auth_manager(plugin_manager, db)
  71. with pytest.raises(NotAnActualException):
  72. service.reauthenticate('doesnt exist', 'nope')
  73. db.session.rollback.assert_called_once_with()
  74. def _get_auth_manager(self, plugin_manager, db):
  75. plugin_manager.add_hookspecs(spec)
  76. return reauth.PluginReauthenticationManager(
  77. plugin_manager, session=db.session
  78. )
  79. @staticmethod
  80. def impls(reauth=None, post_reauth=None, failure=None):
  81. impl = HookimplMarker('flaskbb')
  82. class Impls:
  83. if reauth is not None:
  84. @impl
  85. def flaskbb_reauth_attempt(self, user, secret):
  86. return reauth(user=user, secret=secret)
  87. if post_reauth is not None:
  88. @impl
  89. def flaskbb_post_reauth(self, user):
  90. post_reauth(user=user)
  91. if failure is not None:
  92. @impl
  93. def flaskbb_reauth_failed(self, user):
  94. failure(user=user)
  95. return Impls()