diff --git a/axes/conf.py b/axes/conf.py index 0de99b3..daf1c70 100644 --- a/axes/conf.py +++ b/axes/conf.py @@ -22,18 +22,18 @@ if settings.AXES_LOCKOUT_PARAMETERS is None: if getattr(settings, "AXES_LOCK_OUT_BY_USER_OR_IP", False): settings.AXES_LOCKOUT_PARAMETERS = ["username", "ip_address"] elif getattr(settings, "AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP", False): - settings.AXES_LOCKOUT_PARAMETERS = [("username", "ip_address")] + settings.AXES_LOCKOUT_PARAMETERS = [["username", "ip_address"]] else: settings.AXES_LOCKOUT_PARAMETERS = ["ip_address"] if getattr(settings, "AXES_USE_USER_AGENT", False): if isinstance(settings.AXES_LOCKOUT_PARAMETERS[0], str): - settings.AXES_LOCKOUT_PARAMETERS[0] = ( + settings.AXES_LOCKOUT_PARAMETERS[0] = [ settings.AXES_LOCKOUT_PARAMETERS[0], "user_agent", - ) + ] else: - settings.AXES_LOCKOUT_PARAMETERS[0] += ("user_agent",) + settings.AXES_LOCKOUT_PARAMETERS[0].append("user_agent") # lock out just for admin site settings.AXES_ONLY_ADMIN_SITE = getattr(settings, "AXES_ONLY_ADMIN_SITE", False) diff --git a/axes/handlers/cache.py b/axes/handlers/cache.py index 49574d2..a797f39 100644 --- a/axes/handlers/cache.py +++ b/axes/handlers/cache.py @@ -79,7 +79,7 @@ class AxesCacheHandler(AbstractAxesHandler, AxesBaseHandler): return username = get_client_username(request, credentials) - lockout_parameters = list(get_lockout_parameters(request, credentials)) + lockout_parameters = get_lockout_parameters(request, credentials) if lockout_parameters == ["username"] and username is None: log.warning( "AXES: Username is None and username is the only one lockout parameter, new record will NOT be created." diff --git a/axes/handlers/database.py b/axes/handlers/database.py index b3553bc..035cc60 100644 --- a/axes/handlers/database.py +++ b/axes/handlers/database.py @@ -165,7 +165,7 @@ class AxesDatabaseHandler(AbstractAxesHandler, AxesBaseHandler): return # 2. database query: Get or create access record with the new failure data - lockout_parameters = list(get_lockout_parameters(request, credentials)) + lockout_parameters = get_lockout_parameters(request, credentials) if lockout_parameters == ["username"] and username is None: log.warning( "AXES: Username is None and username is the only one lockout parameter, new record will NOT be created." diff --git a/axes/helpers.py b/axes/helpers.py index 1e2267b..ac73aef 100644 --- a/axes/helpers.py +++ b/axes/helpers.py @@ -1,4 +1,3 @@ -from collections.abc import Iterable from datetime import timedelta from hashlib import sha256 from logging import getLogger @@ -221,18 +220,18 @@ def get_client_http_accept(request: HttpRequest) -> str: def get_lockout_parameters( request_or_attempt: Union[HttpRequest, AccessBase], credentials: Optional[dict] = None, -) -> Iterable[Union[str, Iterable]]: +) -> List[Union[str, List[str]]]: if callable(settings.AXES_LOCKOUT_PARAMETERS): return settings.AXES_LOCKOUT_PARAMETERS( request_or_attempt, credentials - ) # pylint: disable=not-callable + ) - elif isinstance(settings.AXES_LOCKOUT_PARAMETERS, str): + if isinstance(settings.AXES_LOCKOUT_PARAMETERS, str): return import_string(settings.AXES_LOCKOUT_PARAMETERS)( request_or_attempt, credentials ) - elif isinstance(settings.AXES_LOCKOUT_PARAMETERS, Iterable): + if isinstance(settings.AXES_LOCKOUT_PARAMETERS, list): return settings.AXES_LOCKOUT_PARAMETERS raise TypeError( @@ -281,6 +280,7 @@ def get_client_parameters( f"{e} lockout parameter is not allowed. " f"Allowed parameters: {', '.join(parameters_dict.keys())}" ) + log.exception(error_msg) raise ValueError(error_msg) from e return filter_kwargs diff --git a/tests/test_attempts.py b/tests/test_attempts.py index accd9c9..04af617 100644 --- a/tests/test_attempts.py +++ b/tests/test_attempts.py @@ -130,24 +130,24 @@ class ResetResponseTestCase(AxesTestCase): reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 2) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_reset_user_and_ip(self): reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 5) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_reset_ip_user_and_ip(self): self.request.META["REMOTE_ADDR"] = self.IP_1 reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 3) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_reset_username_user_and_ip(self): self.request.GET["username"] = self.USERNAME_1 reset_request(self.request) self.assertEqual(AccessAttempt.objects.count(), 3) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_reset_ip_username_user_and_ip(self): self.request.GET["username"] = self.USERNAME_1 self.request.META["REMOTE_ADDR"] = self.IP_1 diff --git a/tests/test_handlers.py b/tests/test_handlers.py index 17f8be3..6d216e9 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -324,20 +324,20 @@ class AxesDatabaseHandlerTestCase(AxesHandlerBaseTestCase): ( 2, 1, - {"AXES_LOCKOUT_PARAMETERS": [("ip_address", "user_agent")]}, + {"AXES_LOCKOUT_PARAMETERS": [["ip_address", "user_agent"]]}, ["admin", "admin1"], ), (2, 1, {"AXES_LOCKOUT_PARAMETERS": ["username"]}, ["admin", "admin1"]), ( 2, 1, - {"AXES_LOCKOUT_PARAMETERS": [("username", "ip_address")]}, + {"AXES_LOCKOUT_PARAMETERS": [["username", "ip_address"]]}, ["admin", "admin1"], ), ( 1, 2, - {"AXES_LOCKOUT_PARAMETERS": [("username", "ip_address")]}, + {"AXES_LOCKOUT_PARAMETERS": [["username", "ip_address"]]}, ["admin", "admin"], ), ( @@ -454,7 +454,7 @@ class ResetAttemptsCacheHandlerTestCase(AxesHandlerBaseTestCase): self.check_failures(0, ip_address=self.IP_1) self.check_failures(2, ip_address=self.IP_2) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_handler_reset_attempts_ip_and_username(self): self.set_up_login_attempts() self.check_failures(1, username=self.USERNAME_1, ip_address=self.IP_1) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index c4f8c28..2efb59c 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -182,7 +182,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) @override_settings(AXES_VERBOSE=True) def test_verbose_user_ip_combo_client_details(self): username = "test@example.com" @@ -199,7 +199,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) @override_settings(AXES_VERBOSE=False) def test_non_verbose_user_ip_combo_client_details(self): username = "test@example.com" @@ -214,7 +214,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("ip_address", "user_agent")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["ip_address", "user_agent"]]) @override_settings(AXES_VERBOSE=True) def test_verbose_user_agent_client_details(self): username = "test@example.com" @@ -231,7 +231,7 @@ class ClientStringTestCase(AxesTestCase): self.assertEqual(expected, actual) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("ip_address", "user_agent")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["ip_address", "user_agent"]]) @override_settings(AXES_VERBOSE=False) def test_non_verbose_user_agent_client_details(self): username = "test@example.com" @@ -314,7 +314,7 @@ class ClientParametersTestCase(AxesTestCase): [{"ip_address": self.ip_address}], ) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_get_filter_kwargs_user_and_ip(self): self.assertEqual( get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), @@ -328,7 +328,7 @@ class ClientParametersTestCase(AxesTestCase): [{"username": self.username}, {"ip_address": self.ip_address}], ) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("ip_address", "user_agent")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["ip_address", "user_agent"]]) def test_get_filter_kwargs_ip_and_agent(self): self.assertEqual( get_client_parameters(self.username, self.ip_address, self.user_agent, self.request, self.credentials), @@ -336,7 +336,7 @@ class ClientParametersTestCase(AxesTestCase): ) @override_settings( - AXES_LOCKOUT_PARAMETERS=[("username", "ip_address", "user_agent")] + AXES_LOCKOUT_PARAMETERS=[["username", "ip_address", "user_agent"]] ) def test_get_filter_kwargs_user_ip_agent(self): self.assertEqual( diff --git a/tests/test_logging.py b/tests/test_logging.py index 11f0684..bbb6b3e 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -43,7 +43,7 @@ class AppsTestCase(AxesTestCase): AppConfig.initialize() log.info.assert_called_with(_BEGIN, _VERSION, "blocking by ip_address") - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_axes_config_log_user_ip(self, log): AppConfig.initialize() log.info.assert_called_with( diff --git a/tests/test_login.py b/tests/test_login.py index 07d594f..89aa105 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -182,7 +182,7 @@ class DatabaseLoginTestCase(AxesTestCase): self.assertContains(response, self.LOCKED_MESSAGE, status_code=self.BLOCKED) self.assertTrue(self.attempt_count()) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_combination_user_and_ip(self): """ Test login failure when lockout parameters is combination @@ -322,7 +322,7 @@ class DatabaseLoginTestCase(AxesTestCase): # Test for true and false positives when blocking by user and IP together. # Cache disabled. When LOCK_OUT_BY_COMBINATION_USER_AND_IP = True - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_blocks_when_same_user_same_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -331,7 +331,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.BLOCKED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_allows_when_same_user_diff_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -340,7 +340,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_allows_when_diff_user_same_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -349,7 +349,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_allows_when_diff_user_diff_ip_without_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -358,7 +358,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_with_empty_username_allows_other_users_without_cache( self, ): @@ -369,7 +369,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self.client.get(reverse("admin:login"), REMOTE_ADDR=self.IP_1) self.assertContains(response, self.LOGIN_FORM_KEY, status_code=200, html=True) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("ip_address", "user_agent")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["ip_address", "user_agent"]]) def test_lockout_by_user_still_allows_login_with_differnet_user_agent(self): # User with empty username is locked out with "test-browser" user agent. self._lockout_user_from_ip(username="username", ip_addr=self.IP_1, user_agent="test-browser") @@ -465,7 +465,7 @@ class DatabaseLoginTestCase(AxesTestCase): # Test for true and false positives when blocking by user and IP together. # With cache enabled. When LOCK_OUT_BY_COMBINATION_USER_AND_IP = True - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_blocks_when_same_user_same_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -474,7 +474,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.BLOCKED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_allows_when_same_user_diff_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -483,7 +483,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_1, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_allows_when_diff_user_same_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -492,7 +492,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_1) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_allows_when_diff_user_diff_ip_using_cache(self): # User 1 is locked out from IP 1. self._lockout_user1_from_ip1() @@ -502,7 +502,7 @@ class DatabaseLoginTestCase(AxesTestCase): self.assertEqual(response.status_code, self.ALLOWED) @override_settings( - AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")], AXES_FAILURE_LIMIT=2 + AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]], AXES_FAILURE_LIMIT=2 ) def test_lockout_by_user_and_ip_allows_when_diff_user_same_ip_using_cache_multiple_attempts( self, @@ -531,7 +531,7 @@ class DatabaseLoginTestCase(AxesTestCase): response = self._login(self.USER_2, self.VALID_PASSWORD, ip_addr=self.IP_2) self.assertEqual(response.status_code, self.ALLOWED) - @override_settings(AXES_LOCKOUT_PARAMETERS=[("username", "ip_address")]) + @override_settings(AXES_LOCKOUT_PARAMETERS=[["username", "ip_address"]]) def test_lockout_by_user_and_ip_with_empty_username_allows_other_users_using_cache( self, ):