Repository: jazzband/django-axes

Issue:
Lockout response status code.
Is [403](https://tools.ietf.org/html/rfc2616#section-10.4.4) status code with reason in body is more suited for lockout response?



Discussion:

Resolving patch:
diff --git a/axes/decorators.py b/axes/decorators.py
--- a/axes/decorators.py
+++ b/axes/decorators.py
@@ -103,7 +103,7 @@ def is_valid_public_ip(ip_address):
     if not is_valid_ip(ip_address):
         return False
     PRIVATE_IPS_PREFIX = (
-        '10.', 
+        '10.',
         '172.16.', '172.17.', '172.18.', '172.19.', '172.20.', '172.21.', '172.22.',
         '172.23.', '172.24.', '172.25.', '172.26.', '172.27.', '172.28.', '172.29.',
         '172.30.', '172.31.',
@@ -358,7 +358,7 @@ def lockout_response(request):
         }
         template = get_template(LOCKOUT_TEMPLATE)
         content = template.render(context, request)
-        return HttpResponse(content)
+        return HttpResponse(content, status=403)
 
     LOCKOUT_URL = get_lockout_url()
     if LOCKOUT_URL:
@@ -366,10 +366,11 @@ def lockout_response(request):
 
     if COOLOFF_TIME:
         return HttpResponse("Account locked: too many login attempts.  "
-                            "Please try again later.")
+                            "Please try again later.", status=403)
     else:
         return HttpResponse("Account locked: too many login attempts.  "
-                            "Contact an admin to unlock your account.")
+                            "Contact an admin to unlock your account.",
+                            status=403)
 
 
 def is_already_locked(request):


Test patch:
diff --git a/axes/tests.py b/axes/tests.py
--- a/axes/tests.py
+++ b/axes/tests.py
@@ -78,7 +78,7 @@ def test_failure_limit_once(self):
         # So, we shouldn't have gotten a lock-out yet.
         # But we should get one now
         response = self._login()
-        self.assertContains(response, self.LOCKED_MESSAGE)
+        self.assertContains(response, self.LOCKED_MESSAGE, status_code=403)
 
     def test_failure_limit_many(self):
         """Tests the login lock trying to login a lot of times more
@@ -93,7 +93,7 @@ def test_failure_limit_many(self):
         # We should get a locked message each time we try again
         for i in range(0, random.randrange(1, FAILURE_LIMIT)):
             response = self._login()
-            self.assertContains(response, self.LOCKED_MESSAGE)
+            self.assertContains(response, self.LOCKED_MESSAGE, status_code=403)
 
     def test_valid_login(self):
         """Tests a valid login for a real username
@@ -145,7 +145,7 @@ def test_long_user_agent_not_valid(self):
         for i in range(0, FAILURE_LIMIT + 1):
             response = self._login(user_agent=long_user_agent)
 
-        self.assertContains(response, self.LOCKED_MESSAGE)
+        self.assertContains(response, self.LOCKED_MESSAGE, status_code=403)
 
     def test_reset_ip(self):
         """Tests if can reset an ip address
@@ -208,7 +208,7 @@ def test_lockout_by_combination_user_and_ip(self):
         # So, we shouldn't have gotten a lock-out yet.
         # But we should get one now
         response = self._login(is_valid_username=True, is_valid_password=False)
-        self.assertContains(response, self.LOCKED_MESSAGE)
+        self.assertContains(response, self.LOCKED_MESSAGE, status_code=403)
 
     def test_log_data_truncated(self):
         """Tests that query2str properly truncates data to the max_length (default 1024)