Fixed a problem with what seems to be several levels of recursion causing many AccessAttempt objects to be created even though only one failed login attempt took place (for example). It seems to be working anyway...

--HG--
extra : convert_revision : svn%3A6515f4ec-ab5a-11dd-8fd9-859366ca643a/trunk%403
This commit is contained in:
wheaties.box 2008-12-06 09:50:52 +00:00
parent 404f02b85c
commit 8dd2141605
2 changed files with 64 additions and 8 deletions

View file

@ -1,4 +1,30 @@
VERSION = (0, 1, 0, 'pre')
VERSION = (0, 1, 1, 'alpha')
def get_version():
return '%s.%s.%s-%s' % VERSION
return '%s.%s.%s-%s' % VERSION
try:
from django.conf import settings
import logging, os
LOGFILE = os.path.join(settings.DIRNAME, 'axes.log')
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S',
filename=LOGFILE,
filemode='w')
fileLog = logging.FileHandler(LOGFILE, 'w')
fileLog.setLevel(logging.DEBUG)
# set a format which is simpler for console use
formatter = logging.Formatter('%(asctime)s %(name)-12s: %(levelname)-8s %(message)s')
# tell the handler to use this format
fileLog.setFormatter(formatter)
# add the handler to the root logger
logging.getLogger('').addHandler(fileLog)
except:
# if we have any problems, we most likely don't have a settings module loaded
pass

View file

@ -1,5 +1,7 @@
from axes.models import AccessAttempt
from django.conf import settings
from axes.models import AccessAttempt
import axes
import logging
# see if the user has overridden the failure limit
if hasattr(settings, 'LOGIN_FAILURE_LIMIT'):
@ -16,21 +18,38 @@ else:
def query2str(items):
return '\n'.join(['%s=%s' % (k, v) for k,v in items])
log = logging.getLogger('axes.watch_login')
log.info('BEGIN LOG')
log.info('Using django-axes ' + axes.get_version())
def watch_login(func, failures):
"""
Used to decorate the django.contrib.admin.site.login method.
"""
def new(*args, **kwargs):
request = args[0]
def decorated_login(request, *args, **kwargs):
# share some useful information
if func.__name__ != 'decorated_login':
log.info('Calling decorated function: %s' % func)
if args: log.info('args: %s' % args)
if kwargs: log.info('kwargs: %s' % kwargs)
# call the login function
response = func(*args, **kwargs)
response = func(request, *args, **kwargs)
if func.__name__ == 'decorated_login':
# if we're dealing with this function itself, don't bother checking
# for invalid login attempts. I suppose there's a bunch of
# recursion going on here that used to cause one failed login
# attempt to generate 10+ failed access attempt records (with 3
# failed attempts each supposedly)
return response
# only check when there's been an HTTP POST
if request.method == 'POST':
# see if the login was successful
if not response.has_header('location') and response.status_code != 302:
if response and not response.has_header('location') and response.status_code != 302:
log.debug('Failure dict (begin): %s' % failures)
ip = request.META.get('REMOTE_ADDR', '')
ua = request.META.get('HTTP_USER_AGENT', '<unknown>')
@ -39,15 +58,23 @@ def watch_login(func, failures):
# make sure we have an item for this key
try:
failures[key]
log.debug('Key %s exists' % key)
except KeyError:
log.debug('Creating key %s' % key)
failures[key] = 0
# add a failed attempt for this user
failures[key] += 1
log.info('Adding a failure for %s; %i failure(s)' % (key, failures[key]))
#log.debug('Request: %s' % request)
# if we reach or surpass the failure limit, create an
# AccessAttempt record
if failures[key] >= FAILURE_LIMIT:
log.info('=================================')
log.info('Creating access attempt record...')
log.info('=================================')
attempt = AccessAttempt.objects.create(
user_agent=ua,
ip_address=ip,
@ -61,5 +88,8 @@ def watch_login(func, failures):
if FAILURE_RESET:
del(failures[key])
log.debug('Failure dict (end): %s' % failures)
log.info('-' * 79)
return response
return new
return decorated_login