diff --git a/.travis.yml b/.travis.yml index a14a66f..3110220 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ env: - DJANGO=1.8 - DJANGO=1.9 - DJANGO=1.10 + - DJANGO=1.11 services: - redis-server @@ -38,10 +39,14 @@ matrix: env: DJANGO=1.9 - python: "2.6" env: DJANGO=1.10 + - python: "2.6" + env: DJANGO=1.11 - python: "3.3" env: DJANGO=1.9 - python: "3.3" env: DJANGO=1.10 + - python: "3.3" + env: DJANGO=1.11 - python: "3.5" env: DJANGO=1.6 - python: "3.5" diff --git a/README.md b/README.md index 2ab9f2f..103c1da 100644 --- a/README.md +++ b/README.md @@ -147,18 +147,11 @@ This started out as a fork of django-axes, and is using as much of their code as possible, and removing the parts not needed, and speeding up the lookups to improve the login. -Django 1.11 -============ -Due to a change with Django 1.11 where they deprecated the `django.contrib.auth.views.login` in favor -of `django.contrib.auth.views.LoginView` https://docs.djangoproject.com/en/1.11/topics/auth/default/#django.contrib.auth.views.LoginView - -This is currently breaking the defender middleware (FailedLoginMiddleware) we have setup, and needs to be changed to support -the LoginView Class Based View before it will work with Django 1.11. If you know up to fix it, please submit a PR. requirements ============ -- django: 1.6.x, 1.7.x, 1.8.x, 1.9.x, 1.10.x +- django: 1.6.x, 1.7.x, 1.8.x, 1.9.x, 1.10.x, 1.11.x - redis - python: 2.6.x, 2.7.x, 3.3.x, 3.4.x, 3.5.x, 3.6.x, PyPy diff --git a/defender/decorators.py b/defender/decorators.py index 5bdf5cd..b90d47c 100644 --- a/defender/decorators.py +++ b/defender/decorators.py @@ -16,14 +16,6 @@ def watch_login(func): # call the login function 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 - if request.method == 'POST': # see if the login was successful login_unsuccessful = ( diff --git a/defender/middleware.py b/defender/middleware.py index 7d40116..9e7c0cf 100644 --- a/defender/middleware.py +++ b/defender/middleware.py @@ -1,11 +1,26 @@ from django.contrib.auth import views as auth_views +from django.utils.decorators import method_decorator from .decorators import watch_login class FailedLoginMiddleware(object): + patched = False + def __init__(self, *args, **kwargs): super(FailedLoginMiddleware, self).__init__(*args, **kwargs) - # watch the auth login - auth_views.login = watch_login(auth_views.login) + # Watch the auth login. + # Monkey-patch only once - otherwise we would be recording + # failed attempts multiple times! + if not FailedLoginMiddleware.patched: + # Django 1.11 turned the `login` function view into the + # `LoginView` class-based view + try: + from django.contrib.auth.views import LoginView + watch_login_method = method_decorator(watch_login) + LoginView.dispatch = watch_login_method(LoginView.dispatch) + except ImportError: # Django < 1.11 + auth_views.login = watch_login(auth_views.login) + + FailedLoginMiddleware.patched = True