From 7e32eacd97532195fe799ec144b7c6fea12601e1 Mon Sep 17 00:00:00 2001 From: Ken Cochrane Date: Sat, 3 Jan 2015 10:34:19 -0500 Subject: [PATCH] moved a config out of middleware into config and also updated README --- README.md | 31 +++++++++++++++---------------- defender/config.py | 4 ++++ defender/middleware.py | 10 ++-------- defender/tests.py | 11 ++++++----- 4 files changed, 27 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 80713cd..e90781a 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,7 @@ the goal is to do those things very well, and have full unit tests with docs. Build status ------------ -[![Build Status](https://travis-ci.org/kencochrane/django-defender.svg)](https://travis-ci.org/kencochrane/django-defender) - -[![Coverage Status](https://img.shields.io/coveralls/kencochrane/django-defender.svg)](https://coveralls.io/r/kencochrane/django-defender) +[![Build Status](https://travis-ci.org/kencochrane/django-defender.svg)](https://travis-ci.org/kencochrane/django-defender) [![Coverage Status](https://img.shields.io/coveralls/kencochrane/django-defender.svg)](https://coveralls.io/r/kencochrane/django-defender) Goals for 0.1 ============= @@ -105,10 +103,10 @@ destination. Cache backend: ============== -- IP_attempts (count, TTL) +- ip_attempts (count, TTL) - username_attempts (count, TTL) -- ip_blocks (list) # how to expire when in a list? -- username_blocks (list) # how to expire item in the list? +- ip_blocks (list) +- username_blocks (list) cache keys: ----------- @@ -192,29 +190,30 @@ Customizing Defender You have a couple options available to you to customize ``django-defender`` a bit. These should be defined in your ``settings.py`` file. -* ``DEFENDER_LOGIN_FAILURE_LIMIT``: The number of login attempts allowed before a +* ``DEFENDER_LOGIN_FAILURE_LIMIT``: Int: The number of login attempts allowed before a record is created for the failed logins. Default: ``3`` -* ``DEFENDER_USE_USER_AGENT``: If ``True``, lock out / log based on an IP address +* ``DEFENDER_USE_USER_AGENT``: Boolean: If ``True``, lock out / log based on an IP address AND a user agent. This means requests from different user agents but from the same IP are treated differently. Default: ``False`` -* ``DEFENDER_COOLOFF_TIME``: If set, defines a period of inactivity after which +* ``DEFENDER_COOLOFF_TIME``: Int: If set, defines a period of inactivity after which old failed login attempts will be forgotten. An integer, will be interpreted as a number of seconds. If ``0``, the locks will not expire. Default: ``300`` -* ``DEFENDER_LOCKOUT_TEMPLATE``: If set, specifies a template to render when a +* ``DEFENDER_LOCKOUT_TEMPLATE``: String: If set, specifies a template to render when a user is locked out. Template receives cooloff_time and failure_limit as context variables. Default: ``None`` -* ``DEFENDER_USERNAME_FORM_FIELD``: the name of the form field that contains your +* ``DEFENDER_USERNAME_FORM_FIELD``: String: the name of the form field that contains your users usernames. Default: ``username`` -* ``DEFENDER_REVERSE_PROXY_HEADER``: the name of the http header with your +* ``DEFENDER_REVERSE_PROXY_HEADER``: String: the name of the http header with your reverse proxy IP address Default: ``HTTP_X_FORWARDED_FOR`` -* ``DEFENDER_CACHE_PREFIX``: The cache prefix for your defender keys. +* ``DEFENDER_CACHE_PREFIX``: String: The cache prefix for your defender keys. Default: ``defender`` -* ``DEFENDER_LOCKOUT_URL``: The URL you want to redirect to if someone is +* ``DEFENDER_LOCKOUT_URL``: String: The URL you want to redirect to if someone is locked out. -* ``DEFENDER_REDIS_URL``: the redis url for defender. +* ``DEFENDER_REDIS_URL``: String: the redis url for defender. Default: ``redis://localhost:6379/0`` (Example with password: ``redis://:mypassword@localhost:6379/0``) - +* ``DEFENDER_PROTECTED_LOGINS``: Tuple: Used by ``ViewDecoratorMiddleware`` to decide +which login urls need protecting. Default: ``('/accounts/login/',)`` Running Tests ============= diff --git a/defender/config.py b/defender/config.py index 09ca006..d489cac 100644 --- a/defender/config.py +++ b/defender/config.py @@ -46,3 +46,7 @@ ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. " USERNAME_FORM_FIELD = get_setting('DEFENDER_USERNAME_FORM_FIELD', 'username') LOCKOUT_URL = get_setting('DEFENDER_LOCKOUT_URL') + + +PROTECTED_LOGINS = get_setting('DEFENDER_PROTECTED_LOGINS', + ('/accounts/login/',)) diff --git a/defender/middleware.py b/defender/middleware.py index 3ef5986..f2a89f9 100644 --- a/defender/middleware.py +++ b/defender/middleware.py @@ -1,7 +1,7 @@ -from django.conf import settings from django.contrib.auth import views as auth_views from .decorators import watch_login +from .config import PROTECTED_LOGINS class FailedLoginMiddleware(object): @@ -22,14 +22,8 @@ class ViewDecoratorMiddleware(object): `defender.middleware.FailedLoginMiddleware` and before the django flatpages middleware. """ - watched_logins = getattr( - settings, 'DEFENDER_PROTECTED_LOGINS', ( - '/accounts/login/', - ) - ) def process_view(self, request, view_func, view_args, view_kwargs): - if request.path in self.watched_logins: + if request.path in PROTECTED_LOGINS: return watch_login(view_func)(request, *view_args, **view_kwargs) - return None diff --git a/defender/tests.py b/defender/tests.py index 01d4151..5329dbf 100644 --- a/defender/tests.py +++ b/defender/tests.py @@ -444,15 +444,16 @@ class AccessAttemptTest(TestCase): from .admin import AccessAttemptAdmin AccessAttemptAdmin - @patch('defender.middleware.ViewDecoratorMiddleware.watched_logins', - (ADMIN_LOGIN_URL, )) + @patch('defender.config.PROTECTED_LOGINS', (ADMIN_LOGIN_URL, )) def test_decorator_middleware(self): # because watch_login is called twice in this test (once by the # middleware and once by the decorator) we have half as many attempts # before getting locked out. - # FIXME: I tried making sure every request in only processed once but - # there seems to be an issue with django reusing request objects. - for i in range(0, int(config.FAILURE_LIMIT / 2)): + # this is getting called twice, once for each decorator, not sure how + # to dynamically remove one of the middlewares during a test so we + # divide the failure limit by 2. + + for i in range(0, int(config.FAILURE_LIMIT)): response = self._login() # Check if we are in the same login page self.assertContains(response, LOGIN_FORM_KEY)