mirror of
https://github.com/jazzband/django-axes.git
synced 2026-03-16 22:30:23 +00:00
Merge pull request #125 from AFioca/master
Fix #_get_user_attempts to include username when filtering AccessAtte…
This commit is contained in:
commit
697147b46f
2 changed files with 36 additions and 11 deletions
|
|
@ -56,7 +56,8 @@ BEHIND_REVERSE_PROXY_WITH_DIRECT_ACCESS = getattr(settings, 'AXES_BEHIND_REVERSE
|
|||
REVERSE_PROXY_HEADER = getattr(settings, 'AXES_REVERSE_PROXY_HEADER', 'HTTP_X_FORWARDED_FOR')
|
||||
|
||||
# lock out user from particular IP based on combination USER+IP
|
||||
LOCK_OUT_BY_COMBINATION_USER_AND_IP = getattr(settings, 'AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP', False)
|
||||
def should_lock_out_by_combination_user_and_ip():
|
||||
return getattr(settings, 'AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP', False)
|
||||
|
||||
COOLOFF_TIME = getattr(settings, 'AXES_COOLOFF_TIME', None)
|
||||
if (isinstance(COOLOFF_TIME, int) or isinstance(COOLOFF_TIME, float) ):
|
||||
|
|
@ -238,10 +239,12 @@ def _get_user_attempts(request):
|
|||
ip_address=ip, username=username, trusted=True
|
||||
)
|
||||
|
||||
if not attempts and not LOCK_OUT_BY_COMBINATION_USER_AND_IP:
|
||||
if not attempts:
|
||||
params = {'ip_address': ip, 'trusted': False}
|
||||
if USE_USER_AGENT:
|
||||
params['user_agent'] = ua
|
||||
if should_lock_out_by_combination_user_and_ip():
|
||||
params['username'] = username
|
||||
|
||||
attempts = AccessAttempt.objects.filter(**params)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import string
|
|||
import time
|
||||
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import NoReverseMatch
|
||||
from django.core.urlresolvers import reverse
|
||||
|
|
@ -17,12 +18,13 @@ from axes.utils import reset
|
|||
class AccessAttemptTest(TestCase):
|
||||
"""Test case using custom settings for testing
|
||||
"""
|
||||
VALID_USERNAME = 'valid-username'
|
||||
VALID_PASSWORD = 'valid-password'
|
||||
LOCKED_MESSAGE = 'Account locked: too many login attempts.'
|
||||
LOGIN_FORM_KEY = '<input type="submit" value="Log in" />'
|
||||
|
||||
def _login(self, is_valid=False, user_agent='test-browser'):
|
||||
"""Login a user. A valid credential is used when is_valid is True,
|
||||
def _login(self, is_valid_username=False, is_valid_password=False, user_agent='test-browser'):
|
||||
"""Login a user. A valid credential is used when is_valid_username is True,
|
||||
otherwise it will use a random string to make a failed login.
|
||||
"""
|
||||
try:
|
||||
|
|
@ -30,17 +32,22 @@ class AccessAttemptTest(TestCase):
|
|||
except NoReverseMatch:
|
||||
admin_login = reverse('admin:index')
|
||||
|
||||
if is_valid:
|
||||
if is_valid_username:
|
||||
# Use a valid username
|
||||
username = self.user.username
|
||||
username = self.VALID_USERNAME
|
||||
else:
|
||||
# Generate a wrong random username
|
||||
chars = string.ascii_uppercase + string.digits
|
||||
username = ''.join(random.choice(chars) for x in range(10))
|
||||
|
||||
if is_valid_password:
|
||||
password = self.VALID_PASSWORD
|
||||
else:
|
||||
password = 'invalid-password'
|
||||
|
||||
response = self.client.post(admin_login, {
|
||||
'username': username,
|
||||
'password': self.VALID_PASSWORD,
|
||||
'password': password,
|
||||
'this_is_the_login_form': 1,
|
||||
}, HTTP_USER_AGENT=user_agent)
|
||||
|
||||
|
|
@ -50,7 +57,7 @@ class AccessAttemptTest(TestCase):
|
|||
"""Create a valid user for login
|
||||
"""
|
||||
self.user = User.objects.create_superuser(
|
||||
username='valid-username',
|
||||
username=self.VALID_USERNAME,
|
||||
email='test@example.com',
|
||||
password=self.VALID_PASSWORD,
|
||||
)
|
||||
|
|
@ -87,13 +94,13 @@ class AccessAttemptTest(TestCase):
|
|||
def test_valid_login(self):
|
||||
"""Tests a valid login for a real username
|
||||
"""
|
||||
response = self._login(is_valid=True)
|
||||
response = self._login(is_valid_username=True, is_valid_password=True)
|
||||
self.assertNotContains(response, self.LOGIN_FORM_KEY, status_code=302)
|
||||
|
||||
def test_valid_logout(self):
|
||||
"""Tests a valid logout and make sure the logout_time is updated
|
||||
"""
|
||||
response = self._login(is_valid=True)
|
||||
response = self._login(is_valid_username=True, is_valid_password=True)
|
||||
self.assertEquals(AccessLog.objects.latest('id').logout_time, None)
|
||||
|
||||
response = self.client.get(reverse('admin:logout'))
|
||||
|
|
@ -124,7 +131,7 @@ class AccessAttemptTest(TestCase):
|
|||
"""Tests if can handle a long user agent
|
||||
"""
|
||||
long_user_agent = 'ie6' * 1024
|
||||
response = self._login(is_valid=True, user_agent=long_user_agent)
|
||||
response = self._login(is_valid_username=True, is_valid_password=True, user_agent=long_user_agent)
|
||||
self.assertNotContains(response, self.LOGIN_FORM_KEY, status_code=302)
|
||||
|
||||
def test_long_user_agent_not_valid(self):
|
||||
|
|
@ -183,3 +190,18 @@ class AccessAttemptTest(TestCase):
|
|||
# Make another lockout
|
||||
self.test_failure_limit_once()
|
||||
self.assertEquals(scope.signal_received, 2)
|
||||
|
||||
@override_settings(AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP=True)
|
||||
def test_lockout_by_combination_user_and_ip(self):
|
||||
"""Tests the login lock with a valid username and invalid password
|
||||
when AXES_LOCK_OUT_BY_COMBINATION_USER_AND_IP is True
|
||||
"""
|
||||
for i in range(1, FAILURE_LIMIT): # test until one try before the limit
|
||||
response = self._login(is_valid_username=True, is_valid_password=False)
|
||||
# Check if we are in the same login page
|
||||
self.assertContains(response, self.LOGIN_FORM_KEY)
|
||||
|
||||
# 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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue