fixed issues #15 #35 and #36, added way to clean up access attempt table, as well as an option to not login attempts. Also speed up django admin pages

This commit is contained in:
Ken Cochrane 2015-03-20 10:09:39 -04:00
parent 231db4a633
commit 47d6a71825
6 changed files with 68 additions and 13 deletions

View file

@ -8,8 +8,8 @@ python:
- "pypy"
env:
- DJANGO=Django==1.6.10
- DJANGO=Django==1.7.5
- DJANGO=Django==1.6.11
- DJANGO=Django==1.7.7
services:
- redis-server
@ -28,7 +28,7 @@ script:
matrix:
exclude:
- python: "2.6"
env: DJANGO=Django==1.7.5
env: DJANGO=Django==1.7.7
after_success:
- coveralls --verbose

View file

@ -22,6 +22,16 @@ Sites using Defender:
Versions
========
- 0.3
- Added management command ``cleanup_django_axes`` to clean up access
attempt table.
- Added ``DEFENDER_STORE_ACCESS_ATTEMPTS`` config to say if you want to
store attempts to DB or not.
- Added ``DEFENDER_ACCESS_ATTEMPT_EXPIRATION`` config to specify how long
to store the access attempt records in the db, before the management command
cleans them up.
- changed the Django admin page to remove some filters which were making the
page load slow with lots of login attempts in the database.
- 0.2.2 - bug fix add missing files to pypi package
- 0.2.1 - bug fix
- 0.2 - security fix for XFF headers
@ -49,6 +59,8 @@ Features
- number of incorrect attempts before block
- 95% code coverage
- full documentation
- Ability to store login attempts to the database
- Management command to clean up login attempts database table
- admin pages
- list of blocked usernames and ip's
- ability to unblock people
@ -229,6 +241,29 @@ urlpatterns = patterns(
)
```
Management Commands:
--------------------
``cleanup_django_defender``:
If you have a website with a lot of traffic, the AccessAttempts table will get
full pretty quickly. If you don't need to keep the data for auditing purposes
there is a management command to help you keep it clean.
It will look at your ``DEFENDER_ACCESS_ATTEMPT_EXPIRATION`` setting to determine
which records will be deleted. Default if not specified, is 24 hours.
```bash
$ python manage.py cleanup_django_defender
```
You can set this up as a daily or weekly cron job to keep the table size down.
```bash
# run at 12:24 AM every morning.
24 0 * * * /usr/bin/python manage.py cleanup_django_defender >> /var/log/django_defender_cleanup.log
```
Admin Pages:
------------
@ -285,9 +320,17 @@ locked out.
* ``DEFENDER_REDIS_URL``: String: the redis url for defender.
[Default: ``redis://localhost:6379/0``]
(Example with password: ``redis://:mypassword@localhost:6379/0``)
* ``DEFENDER_STORE_ACCESS_ATTEMPTS``: Boolean: If you want to store the login
attempt to the database, set to True. If False, it is not saved
[Default: ``True``]
* ``DEFENDER_USE_CELERY``: Boolean: If you want to use Celery to store the login
attempt to the database, set to True. If False, it is saved inline.
[Default: ``False``]
* ``DEFENDER_ACCESS_ATTEMPT_EXPIRATION``: Int: Length of time in hours for how
long to keep the access attempt records in the database before the management
command cleans them up.
[Default: 24]
Running Tests
=============

View file

@ -13,17 +13,12 @@ class AccessAttemptAdmin(admin.ModelAdmin):
)
list_filter = [
'attempt_time',
'ip_address',
'username',
'path_info',
]
search_fields = [
'ip_address',
'username',
'user_agent',
'path_info',
]
date_hierarchy = 'attempt_time'

View file

@ -33,7 +33,8 @@ try:
# how long to wait before the bad login attempt gets forgotten. in seconds.
COOLOFF_TIME = int(get_setting('DEFENDER_COOLOFF_TIME', 300)) # seconds
except ValueError: # pragma: no cover
raise Exception('COOLOFF_TIME needs to be an integer') # pragma: no cover
raise Exception(
'DEFENDER_COOLOFF_TIME needs to be an integer') # pragma: no cover
LOCKOUT_TEMPLATE = get_setting('DEFENDER_LOCKOUT_TEMPLATE')
@ -45,5 +46,16 @@ USERNAME_FORM_FIELD = get_setting('DEFENDER_USERNAME_FORM_FIELD', 'username')
LOCKOUT_URL = get_setting('DEFENDER_LOCKOUT_URL')
USE_CELERY = get_setting('DEFENDER_USE_CELERY', False)
STORE_ACCESS_ATTEMPTS = get_setting('DEFENDER_STORE_ACCESS_ATTEMPTS', True)
# Used by the management command to decide how long to keep access attempt
# recods. Number is # of hours.
try:
ACCESS_ATTEMPT_EXPIRATION = int(get_setting(
'DEFENDER_ACCESS_ATTEMPT_EXPIRATION', 24))
except ValueError: # pragma: no cover
raise Exception(
'DEFENDER_ACCESS_ATTEMPT_EXPIRATION'
' needs to be an integer') # pragma: no cover

View file

@ -275,6 +275,11 @@ def check_request(request, login_unsuccessful):
def add_login_attempt_to_db(request, login_valid):
""" Create a record for the login attempt If using celery call celery
task, if not, call the method normally """
if not config.STORE_ACCESS_ATTEMPTS:
# If we don't want to store in the database, then don't proceed.
return
user_agent = request.META.get('HTTP_USER_AGENT', '<unknown>')[:255]
ip_address = get_ip(request)
username = request.POST.get(config.USERNAME_FORM_FIELD, None)

View file

@ -9,7 +9,7 @@ except ImportError:
from distutils.core import setup
version = '0.2.2'
version = '0.3'
def get_packages(package):
@ -44,7 +44,7 @@ setup(name='django-defender',
long_description="redis based Django app based on speed, that locks out"
"users after too many failed login attempts.",
classifiers=[
'Development Status :: 4 - Beta',
'Development Status :: 5 - Production/Stable',
'Framework :: Django',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
@ -69,6 +69,6 @@ setup(name='django-defender',
packages=get_packages('defender'),
package_data=get_package_data('defender'),
install_requires=['Django>=1.6,<1.8', 'redis==2.10.3',
'hiredis==0.1.4', 'mockredispy==2.9.0.10'],
'hiredis==0.1.6', 'mockredispy==2.9.0.10'],
tests_require=['mock', 'mockredispy', 'coverage', 'celery'],
)