test_reauthentication.py 4.2 KB

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