diff --git a/defender/config.py b/defender/config.py index 6e045c5..1d11ceb 100644 --- a/defender/config.py +++ b/defender/config.py @@ -72,13 +72,13 @@ try: cooloff_times[index] = int(cooloff_time) if not len(cooloff_times): - raise TypeError() + raise TypeError() # pragma: no cover LOCKOUT_COOLOFF_TIMES = cooloff_times except (TypeError, ValueError): raise Exception("DEFENDER_LOCKOUT_COOLOFF_TIME needs to be an integer or list of integers having at least one element") except ValueError: - raise Exception("DEFENDER_LOCKOUT_COOLOFF_TIME needs to be an integer or list of integers having at least one element") + raise Exception("DEFENDER_LOCKOUT_COOLOFF_TIME needs to be an integer or list of integers having at least one element") # pragma: no cover except ValueError: # pragma: no cover raise Exception("DEFENDER_COOLOFF_TIME needs to be an integer") # pragma: no cover diff --git a/defender/tests.py b/defender/tests.py index 8ee952a..7d56529 100644 --- a/defender/tests.py +++ b/defender/tests.py @@ -13,6 +13,8 @@ from django.test.client import RequestFactory from redis.client import Redis from django.urls import reverse +from defender.data import get_approx_account_lockouts_from_login_attempts + from . import utils from . import config from .signals import ( @@ -950,20 +952,12 @@ class AccessAttemptTest(DefenderTestCase): self.assertRaises(Exception) @patch("defender.config.LOCKOUT_COOLOFF_TIMES", [3, 6]) - @patch("defender.config.IP_FAILURE_LIMIT", 3) + @patch("defender.config.FAILURE_LIMIT", 3) def test_lockout_cooloff_correctly_scales_with_ip_when_set(self): self.test_ip_failure_limit() - self.assertTrue(AccessAttempt.objects.filter( - Q(attempt_time__gte=datetime.now() - timedelta(hours=config.ACCESS_ATTEMPT_EXPIRATION)) & - Q(ip_address="127.0.0.1") - ).count() >= 3) self.assertEqual(utils.get_lockout_cooloff_time(ip_address="127.0.0.1"), 3) utils.reset_failed_attempts(ip_address="127.0.0.1") self.test_ip_failure_limit() - self.assertTrue(AccessAttempt.objects.filter( - Q(attempt_time__gte=datetime.now() - timedelta(hours=config.ACCESS_ATTEMPT_EXPIRATION)) & - Q(ip_address="127.0.0.1") - ).count() >= 6) self.assertEqual(utils.get_lockout_cooloff_time(ip_address="127.0.0.1"), 6) time.sleep(config.LOCKOUT_COOLOFF_TIMES[1]) if config.MOCK_REDIS: @@ -972,20 +966,12 @@ class AccessAttemptTest(DefenderTestCase): self.test_valid_login() @patch("defender.config.LOCKOUT_COOLOFF_TIMES", [3, 6]) - @patch("defender.config.USERNAME_FAILURE_LIMIT", 3) + @patch("defender.config.FAILURE_LIMIT", 3) def test_lockout_cooloff_correctly_scales_with_username_when_set(self): self.test_username_failure_limit() - self.assertTrue(AccessAttempt.objects.filter( - Q(attempt_time__gte=datetime.now() - timedelta(hours=config.ACCESS_ATTEMPT_EXPIRATION)) & - Q(username=VALID_USERNAME) - ).count() >= 3) self.assertEqual(utils.get_lockout_cooloff_time(username=VALID_USERNAME), 3) utils.reset_failed_attempts(username=VALID_USERNAME) self.test_username_failure_limit() - self.assertTrue(AccessAttempt.objects.filter( - Q(attempt_time__gte=datetime.now() - timedelta(hours=config.ACCESS_ATTEMPT_EXPIRATION)) & - Q(username=VALID_USERNAME) - ).count() >= 6) self.assertEqual(utils.get_lockout_cooloff_time(username=VALID_USERNAME), 6) time.sleep(config.LOCKOUT_COOLOFF_TIMES[1]) if config.MOCK_REDIS: @@ -993,6 +979,18 @@ class AccessAttemptTest(DefenderTestCase): get_redis_connection().do_expire() # pragma: no cover self.test_valid_login() + @patch("defender.config.STORE_ACCESS_ATTEMPTS", False) + def test_get_approx_account_lockouts_from_login_attempts_auto_return_zero_pt1(self): + self.assertEqual(get_approx_account_lockouts_from_login_attempts(ip_address="127.0.0.1"), 0) + + def test_get_approx_account_lockouts_from_login_attempts_auto_return_zero_pt2(self): + self.assertEqual(get_approx_account_lockouts_from_login_attempts(), 0) + + @patch("defender.config.DISABLE_IP_LOCKOUT", True) + def test_get_approx_account_lockouts_from_login_attempts_auto_return_zero_pt1(self): + with self.assertRaises(Exception): + get_approx_account_lockouts_from_login_attempts(ip_address="127.0.0.1") + class SignalTest(DefenderTestCase): """ Test that signals are properly sent when blocking usernames and IPs. diff --git a/defender/utils.py b/defender/utils.py index f3fe15c..fb8ec3a 100644 --- a/defender/utils.py +++ b/defender/utils.py @@ -206,6 +206,8 @@ def get_user_attempts(request, get_username=get_username_from_request, username= return max(ip_count, username_count) def get_lockout_cooloff_time(ip_address=None, username=None): + if not config.LOCKOUT_COOLOFF_TIMES: + return 0 index = max(0, min( len(config.LOCKOUT_COOLOFF_TIMES) - 1, get_approx_account_lockouts_from_login_attempts(ip_address, username) - 1