mirror of
https://github.com/jazzband/django-axes.git
synced 2026-03-16 22:30:23 +00:00
Make sure axes can handle weird user agents. Fixes #38
This commit is contained in:
parent
f502093a7d
commit
dacdb32d18
4 changed files with 82 additions and 25 deletions
|
|
@ -4,6 +4,7 @@ python:
|
|||
env:
|
||||
- PYTHONPATH=$PYTHONPATH:$PWD DJANGO_VERSION=1.4.5
|
||||
- PYTHONPATH=$PYTHONPATH:$PWD DJANGO_VERSION=1.5
|
||||
- PYTHONPATH=$PYTHONPATH:$PWD DJANGO_VERSION=1.5.1
|
||||
install:
|
||||
- pip install --use-mirrors Django==$DJANGO_VERSION
|
||||
script:
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ def get_user_attempts(request):
|
|||
username = request.POST.get('username', None)
|
||||
|
||||
if USE_USER_AGENT:
|
||||
ua = request.META.get('HTTP_USER_AGENT', '<unknown>')
|
||||
ua = request.META.get('HTTP_USER_AGENT', '<unknown>')[:255]
|
||||
attempts = AccessAttempt.objects.filter(
|
||||
user_agent=ua, ip_address=ip, username=username, trusted=True
|
||||
)
|
||||
|
|
@ -237,7 +237,7 @@ def watch_login(func):
|
|||
)
|
||||
|
||||
access_log = AccessLog.objects.create(
|
||||
user_agent=request.META.get('HTTP_USER_AGENT', '<unknown>'),
|
||||
user_agent=request.META.get('HTTP_USER_AGENT', '<unknown>')[:255],
|
||||
ip_address=get_ip(request),
|
||||
username=request.POST.get('username', None),
|
||||
http_accept=request.META.get('HTTP_ACCEPT', '<unknown>'),
|
||||
|
|
@ -368,7 +368,7 @@ def check_request(request, login_unsuccessful):
|
|||
|
||||
def create_new_failure_records(request, failures):
|
||||
ip = get_ip(request)
|
||||
ua = request.META.get('HTTP_USER_AGENT', '<unknown>')
|
||||
ua = request.META.get('HTTP_USER_AGENT', '<unknown>')[:255]
|
||||
username = request.POST.get('username', None)
|
||||
|
||||
params = {
|
||||
|
|
@ -397,7 +397,7 @@ def create_new_failure_records(request, failures):
|
|||
|
||||
def create_new_trusted_record(request):
|
||||
ip = get_ip(request)
|
||||
ua = request.META.get('HTTP_USER_AGENT', '<unknown>')
|
||||
ua = request.META.get('HTTP_USER_AGENT', '<unknown>')[:255]
|
||||
username = request.POST.get('username', None)
|
||||
|
||||
if not username:
|
||||
|
|
|
|||
|
|
@ -1,41 +1,72 @@
|
|||
from django.db import models
|
||||
#import signals
|
||||
FAILURES_DESC = 'Failed Logins'
|
||||
|
||||
#XXX TODO
|
||||
# set unique by user_agent, ip
|
||||
# make user agent, ip indexed fields
|
||||
|
||||
class CommonAccess(models.Model):
|
||||
user_agent = models.CharField(max_length=255)
|
||||
ip_address = models.IPAddressField('IP Address', null=True)
|
||||
username = models.CharField(max_length=255, null=True)
|
||||
user_agent = models.CharField(
|
||||
max_length=255,
|
||||
)
|
||||
|
||||
ip_address = models.IPAddressField(
|
||||
verbose_name='IP Address',
|
||||
null=True,
|
||||
)
|
||||
|
||||
username = models.CharField(
|
||||
max_length=255,
|
||||
null=True,
|
||||
)
|
||||
|
||||
# Once a user logs in from an ip, that combination is trusted and not
|
||||
# locked out in case of a distributed attack
|
||||
trusted = models.BooleanField(default=False)
|
||||
http_accept = models.CharField('HTTP Accept', max_length=1025)
|
||||
path_info = models.CharField('Path', max_length=255)
|
||||
attempt_time = models.DateTimeField(auto_now_add=True)
|
||||
trusted = models.BooleanField(
|
||||
default=False,
|
||||
)
|
||||
|
||||
http_accept = models.CharField(
|
||||
verbose_name='HTTP Accept',
|
||||
max_length=1025,
|
||||
)
|
||||
|
||||
path_info = models.CharField(
|
||||
verbose_name='Path',
|
||||
max_length=255,
|
||||
)
|
||||
|
||||
attempt_time = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
ordering = ['-attempt_time']
|
||||
|
||||
class AccessAttempt(CommonAccess):
|
||||
get_data = models.TextField('GET Data')
|
||||
post_data = models.TextField('POST Data')
|
||||
failures_since_start = models.PositiveIntegerField(FAILURES_DESC)
|
||||
|
||||
def __unicode__(self):
|
||||
return u'Attempted Access: %s' % self.attempt_time
|
||||
class AccessAttempt(CommonAccess):
|
||||
get_data = models.TextField(
|
||||
verbose_name='GET Data',
|
||||
)
|
||||
|
||||
post_data = models.TextField(
|
||||
verbose_name='POST Data',
|
||||
)
|
||||
|
||||
failures_since_start = models.PositiveIntegerField(
|
||||
verbose_name='Failed Logins',
|
||||
)
|
||||
|
||||
@property
|
||||
def failures(self):
|
||||
return self.failures_since_start
|
||||
|
||||
def __unicode__(self):
|
||||
return u'Attempted Access: %s' % self.attempt_time
|
||||
|
||||
|
||||
class AccessLog(CommonAccess):
|
||||
logout_time = models.DateTimeField(null=True, blank=True)
|
||||
logout_time = models.DateTimeField(
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
def __unicode__(self):
|
||||
return u'Access Log for %s @ %s' % (self.username, self.attempt_time)
|
||||
|
|
|
|||
|
|
@ -30,11 +30,11 @@ class AccessAttemptTest(TestCase):
|
|||
|
||||
return self._generate_random_string()
|
||||
|
||||
def _login(self, existing_username=False):
|
||||
def _login(self, existing_username=False, user_agent='test-browser'):
|
||||
response = self.client.post(reverse('admin:index'), {
|
||||
'username': self._random_username(existing_username),
|
||||
'password': self._generate_random_string(),
|
||||
})
|
||||
}, HTTP_USER_AGENT=user_agent)
|
||||
|
||||
return response
|
||||
|
||||
|
|
@ -101,3 +101,28 @@ class AccessAttemptTest(TestCase):
|
|||
'password': valid_username
|
||||
})
|
||||
self.assertNotIn(LOGIN_FORM_KEY, response.context)
|
||||
|
||||
def test_long_user_agent_valid(self):
|
||||
"""Tests if can handle a long user agent
|
||||
"""
|
||||
long_user_agent = 'ie6' * 1024
|
||||
valid_username = self._random_username(existing_username=True)
|
||||
response = self.client.post(reverse('admin:index'), {
|
||||
'username': valid_username,
|
||||
'password': valid_username
|
||||
}, HTTP_USER_AGENT=long_user_agent)
|
||||
self.assertNotIn(LOGIN_FORM_KEY, response.context)
|
||||
|
||||
def test_long_user_agent_not_valid(self):
|
||||
"""Tests if can handle a long user agent with failure
|
||||
"""
|
||||
long_user_agent = 'ie6' * 1024
|
||||
for i in range(0, FAILURE_LIMIT):
|
||||
response = self._login(
|
||||
existing_username=False,
|
||||
user_agent=long_user_agent,
|
||||
)
|
||||
self.assertContains(response, LOGIN_FORM_KEY)
|
||||
|
||||
response = self._login()
|
||||
self.assertContains(response, self.LOCKED_MESSAGE)
|
||||
|
|
|
|||
Loading…
Reference in a new issue