test_authentication.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. from datetime import datetime, timedelta
  2. import pytest
  3. from freezegun import freeze_time
  4. from pytz import UTC
  5. from flaskbb.auth.services import authentication as auth
  6. from flaskbb.core.auth.authentication import StopAuthentication
  7. pytestmark = pytest.mark.usefixtures('default_settings')
  8. class TestBlockTooManyFailedLogins(object):
  9. provider = auth.BlockTooManyFailedLogins(
  10. auth.
  11. FailedLoginConfiguration(limit=1, lockout_window=timedelta(hours=1))
  12. )
  13. @freeze_time(datetime(2018, 1, 1, 13, 30))
  14. def test_raises_StopAuthentication_if_user_is_at_limit_and_inside_window(
  15. self, Fred
  16. ):
  17. Fred.last_failed_login = datetime(2018, 1, 1, 14, tzinfo=UTC)
  18. Fred.login_attempts = 1
  19. with pytest.raises(StopAuthentication) as excinfo:
  20. self.provider.authenticate(Fred.email, 'not considered')
  21. assert 'too many failed login attempts' in excinfo.value.reason
  22. @freeze_time(datetime(2018, 1, 1, 14))
  23. def test_doesnt_raise_if_user_is_at_limit_but_outside_window(self, Fred):
  24. Fred.last_failed_login = datetime(2018, 1, 1, 12, tzinfo=UTC)
  25. Fred.login_attempts = 1
  26. self.provider.authenticate(Fred.email, 'not considered')
  27. def test_doesnt_raise_if_user_is_below_limit_but_inside_window(self, Fred):
  28. Fred.last_failed_login = datetime(2018, 1, 1, 12, tzinfo=UTC)
  29. Fred.login_attempts = 0
  30. self.provider.authenticate(Fred.email, 'not considered')
  31. def test_handles_if_user_has_no_failed_login_attempts(self, Fred):
  32. Fred.login_attempts = 0
  33. Fred.last_failed_login = None
  34. self.provider.authenticate(Fred.email, 'not considered')
  35. def test_handles_if_user_doesnt_exist(self, Fred):
  36. self.provider.authenticate('completely@made.up', 'not considered')
  37. class TestDefaultFlaskBBAuthProvider(object):
  38. provider = auth.DefaultFlaskBBAuthProvider()
  39. def test_returns_None_if_user_doesnt_exist(self, Fred):
  40. result = self.provider.authenticate('completely@made.up', 'lolnope')
  41. assert result is None
  42. def test_returns_None_if_password_doesnt_match(self, Fred):
  43. result = self.provider.authenticate(Fred.email, 'stillnotit')
  44. assert result is None
  45. def test_returns_user_if_identifer_and_password_match(self, Fred):
  46. result = self.provider.authenticate(Fred.email, 'fred')
  47. assert result.username == Fred.username
  48. class TestMarkFailedLoginAttempt(object):
  49. handler = auth.MarkFailedLogin()
  50. @freeze_time(datetime(2018, 1, 1, 12))
  51. def test_increments_users_failed_logins_and_sets_last_fail_date(
  52. self, Fred
  53. ):
  54. Fred.login_attempts = 0
  55. Fred.last_failed_login = datetime.min.replace(tzinfo=UTC)
  56. self.handler.handle_authentication_failure(Fred.email)
  57. assert Fred.login_attempts == 1
  58. assert Fred.last_failed_login == datetime.now(UTC)
  59. def test_handles_if_user_doesnt_exist(self, Fred):
  60. self.handler.handle_authentication_failure('completely@made.up')
  61. class TestClearFailedLogins(object):
  62. handler = auth.ClearFailedLogins()
  63. def test_clears_failed_logins_attempts(self, Fred):
  64. Fred.login_attempts = 1000
  65. self.handler.handle_post_auth(Fred)
  66. assert Fred.login_attempts == 0
  67. class TestBlockUnactivatedUser(object):
  68. handler = auth.BlockUnactivatedUser()
  69. def test_raises_StopAuthentication_if_user_is_unactivated(
  70. self, unactivated_user
  71. ):
  72. with pytest.raises(StopAuthentication) as excinfo:
  73. self.handler.handle_post_auth(unactivated_user)
  74. assert 'In order to use your account' in excinfo.value.reason