From 44c8746d8cd16fca91d3681f29478785dea0cbbd Mon Sep 17 00:00:00 2001 From: Vladimir Bolshakov Date: Mon, 1 Feb 2016 19:05:17 +0300 Subject: [PATCH 1/8] Templates settings as recommended from Django 1.8 --- defender/test_settings.py | 7 +++++++ defender/travis_settings.py | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/defender/test_settings.py b/defender/test_settings.py index 2ecba37..f776098 100644 --- a/defender/test_settings.py +++ b/defender/test_settings.py @@ -29,6 +29,13 @@ INSTALLED_APPS = [ 'defender', ] +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'APP_DIRS': True, + }, +] + SECRET_KEY = os.environ.get('SECRET_KEY', 'too-secret-for-test') LOGIN_REDIRECT_URL = '/admin' diff --git a/defender/travis_settings.py b/defender/travis_settings.py index 8987ea1..82bd694 100644 --- a/defender/travis_settings.py +++ b/defender/travis_settings.py @@ -29,6 +29,13 @@ INSTALLED_APPS = [ 'defender', ] +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'APP_DIRS': True, + }, +] + SECRET_KEY = os.environ.get('SECRET_KEY', 'too-secret-for-test') LOGIN_REDIRECT_URL = '/admin' From f4e0ddc03234ad7af768617642f3f88d4406948d Mon Sep 17 00:00:00 2001 From: Vladimir Bolshakov Date: Mon, 1 Feb 2016 19:06:38 +0300 Subject: [PATCH 2/8] assertEquals -> assertEqual --- defender/tests.py | 120 +++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/defender/tests.py b/defender/tests.py index c911e1e..28b69bc 100644 --- a/defender/tests.py +++ b/defender/tests.py @@ -108,7 +108,7 @@ class AccessAttemptTest(DefenderTestCase): def test_login_get(self): """ visit the login page """ response = self.client.get(ADMIN_LOGIN_URL) - self.assertEquals(response.status_code, 200) + self.assertEqual(response.status_code, 200) def test_failure_limit_by_ip_once(self): """ Tests the login lock by ip when trying to login @@ -247,7 +247,7 @@ class AccessAttemptTest(DefenderTestCase): request.session = SessionStore() request.META['HTTP_X_FORWARDED_FOR'] = '192.168.24.24' - self.assertEquals(utils.get_ip(request), '192.168.24.24') + self.assertEqual(utils.get_ip(request), '192.168.24.24') request_factory = RequestFactory() request = request_factory.get(ADMIN_LOGIN_URL) @@ -255,7 +255,7 @@ class AccessAttemptTest(DefenderTestCase): request.session = SessionStore() request.META['REMOTE_ADDR'] = '24.24.24.24' - self.assertEquals(utils.get_ip(request), '24.24.24.24') + self.assertEqual(utils.get_ip(request), '24.24.24.24') def test_get_ip(self): """ Tests if can handle a long user agent @@ -265,7 +265,7 @@ class AccessAttemptTest(DefenderTestCase): request.user = AnonymousUser() request.session = SessionStore() - self.assertEquals(utils.get_ip(request), '127.0.0.1') + self.assertEqual(utils.get_ip(request), '127.0.0.1') def test_long_user_agent_not_valid(self): """ Tests if can handle a long user agent with failure @@ -301,13 +301,13 @@ class AccessAttemptTest(DefenderTestCase): # So, we shouldn't have gotten a lock-out yet. # But we should get one now, check redirect make sure it is valid. response = self._login() - self.assertEquals(response.status_code, 302) - self.assertEquals(response['Location'], 'http://localhost/othe/login/') + self.assertEqual(response.status_code, 302) + self.assertEqual(response['Location'], 'http://localhost/othe/login/') # doing a get should also get locked out message response = self.client.get(ADMIN_LOGIN_URL) - self.assertEquals(response.status_code, 302) - self.assertEquals(response['Location'], 'http://localhost/othe/login/') + self.assertEqual(response.status_code, 302) + self.assertEqual(response['Location'], 'http://localhost/othe/login/') @patch('defender.config.LOCKOUT_URL', '/o/login/') def test_failed_login_redirect_to_URL_local(self): @@ -322,13 +322,13 @@ class AccessAttemptTest(DefenderTestCase): # So, we shouldn't have gotten a lock-out yet. # But we should get one now, check redirect make sure it is valid. response = self._login() - self.assertEquals(response.status_code, 302) - self.assertEquals(response['Location'], 'http://testserver/o/login/') + self.assertEqual(response.status_code, 302) + self.assertEqual(response['Location'], lockout_url) # doing a get should also get locked out message response = self.client.get(ADMIN_LOGIN_URL) - self.assertEquals(response.status_code, 302) - self.assertEquals(response['Location'], 'http://testserver/o/login/') + self.assertEqual(response.status_code, 302) + self.assertEqual(response['Location'], lockout_url) @patch('defender.config.LOCKOUT_TEMPLATE', 'defender/lockout.html') def test_failed_login_redirect_to_template(self): @@ -343,12 +343,12 @@ class AccessAttemptTest(DefenderTestCase): # So, we shouldn't have gotten a lock-out yet. # But we should get one now, check template make sure it is valid. response = self._login() - self.assertEquals(response.status_code, 200) + self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'defender/lockout.html') # doing a get should also get locked out message response = self.client.get(ADMIN_LOGIN_URL) - self.assertEquals(response.status_code, 200) + self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'defender/lockout.html') @patch('defender.config.COOLOFF_TIME', 0) @@ -372,81 +372,81 @@ class AccessAttemptTest(DefenderTestCase): response = self._login() self.assertContains(response, LOGIN_FORM_KEY) - self.assertEquals(AccessAttempt.objects.count(), 1) + self.assertEqual(AccessAttempt.objects.count(), 1) self.assertIsNotNone(str(AccessAttempt.objects.all()[0])) def test_is_valid_ip(self): """ Test the is_valid_ip() method """ - self.assertEquals(utils.is_valid_ip('192.168.0.1'), True) - self.assertEquals(utils.is_valid_ip('130.80.100.24'), True) - self.assertEquals(utils.is_valid_ip('8.8.8.8'), True) - self.assertEquals(utils.is_valid_ip('127.0.0.1'), True) - self.assertEquals(utils.is_valid_ip('fish'), False) - self.assertEquals(utils.is_valid_ip(None), False) - self.assertEquals(utils.is_valid_ip(''), False) - self.assertEquals(utils.is_valid_ip('0x41.0x41.0x41.0x41'), False) - self.assertEquals(utils.is_valid_ip('192.168.100.34.y'), False) - self.assertEquals( + self.assertEqual(utils.is_valid_ip('192.168.0.1'), True) + self.assertEqual(utils.is_valid_ip('130.80.100.24'), True) + self.assertEqual(utils.is_valid_ip('8.8.8.8'), True) + self.assertEqual(utils.is_valid_ip('127.0.0.1'), True) + self.assertEqual(utils.is_valid_ip('fish'), False) + self.assertEqual(utils.is_valid_ip(None), False) + self.assertEqual(utils.is_valid_ip(''), False) + self.assertEqual(utils.is_valid_ip('0x41.0x41.0x41.0x41'), False) + self.assertEqual(utils.is_valid_ip('192.168.100.34.y'), False) + self.assertEqual( utils.is_valid_ip('2001:0db8:85a3:0000:0000:8a2e:0370:7334'), True) - self.assertEquals( + self.assertEqual( utils.is_valid_ip('2001:db8:85a3:0:0:8a2e:370:7334'), True) - self.assertEquals( + self.assertEqual( utils.is_valid_ip('2001:db8:85a3::8a2e:370:7334'), True) - self.assertEquals( + self.assertEqual( utils.is_valid_ip('::ffff:192.0.2.128'), True) - self.assertEquals( + self.assertEqual( utils.is_valid_ip('::ffff:8.8.8.8'), True) def test_parse_redis_url(self): """ test the parse_redis_url method """ # full regular conf = parse_redis_url("redis://user:password@localhost2:1234/2") - self.assertEquals(conf.get('HOST'), 'localhost2') - self.assertEquals(conf.get('DB'), 2) - self.assertEquals(conf.get('PASSWORD'), 'password') - self.assertEquals(conf.get('PORT'), 1234) + self.assertEqual(conf.get('HOST'), 'localhost2') + self.assertEqual(conf.get('DB'), 2) + self.assertEqual(conf.get('PASSWORD'), 'password') + self.assertEqual(conf.get('PORT'), 1234) # full non local conf = parse_redis_url("redis://user:pass@www.localhost.com:1234/2") - self.assertEquals(conf.get('HOST'), 'www.localhost.com') - self.assertEquals(conf.get('DB'), 2) - self.assertEquals(conf.get('PASSWORD'), 'pass') - self.assertEquals(conf.get('PORT'), 1234) + self.assertEqual(conf.get('HOST'), 'www.localhost.com') + self.assertEqual(conf.get('DB'), 2) + self.assertEqual(conf.get('PASSWORD'), 'pass') + self.assertEqual(conf.get('PORT'), 1234) # no user name conf = parse_redis_url("redis://password@localhost2:1234/2") - self.assertEquals(conf.get('HOST'), 'localhost2') - self.assertEquals(conf.get('DB'), 2) - self.assertEquals(conf.get('PASSWORD'), None) - self.assertEquals(conf.get('PORT'), 1234) + self.assertEqual(conf.get('HOST'), 'localhost2') + self.assertEqual(conf.get('DB'), 2) + self.assertEqual(conf.get('PASSWORD'), None) + self.assertEqual(conf.get('PORT'), 1234) # no user name 2 with colon conf = parse_redis_url("redis://:password@localhost2:1234/2") - self.assertEquals(conf.get('HOST'), 'localhost2') - self.assertEquals(conf.get('DB'), 2) - self.assertEquals(conf.get('PASSWORD'), 'password') - self.assertEquals(conf.get('PORT'), 1234) + self.assertEqual(conf.get('HOST'), 'localhost2') + self.assertEqual(conf.get('DB'), 2) + self.assertEqual(conf.get('PASSWORD'), 'password') + self.assertEqual(conf.get('PORT'), 1234) # Empty conf = parse_redis_url(None) - self.assertEquals(conf.get('HOST'), 'localhost') - self.assertEquals(conf.get('DB'), 0) - self.assertEquals(conf.get('PASSWORD'), None) - self.assertEquals(conf.get('PORT'), 6379) + self.assertEqual(conf.get('HOST'), 'localhost') + self.assertEqual(conf.get('DB'), 0) + self.assertEqual(conf.get('PASSWORD'), None) + self.assertEqual(conf.get('PORT'), 6379) # no db conf = parse_redis_url("redis://:password@localhost2:1234") - self.assertEquals(conf.get('HOST'), 'localhost2') - self.assertEquals(conf.get('DB'), 0) - self.assertEquals(conf.get('PASSWORD'), 'password') - self.assertEquals(conf.get('PORT'), 1234) + self.assertEqual(conf.get('HOST'), 'localhost2') + self.assertEqual(conf.get('DB'), 0) + self.assertEqual(conf.get('PASSWORD'), 'password') + self.assertEqual(conf.get('PORT'), 1234) # no password conf = parse_redis_url("redis://localhost2:1234/0") - self.assertEquals(conf.get('HOST'), 'localhost2') - self.assertEquals(conf.get('DB'), 0) - self.assertEquals(conf.get('PASSWORD'), None) - self.assertEquals(conf.get('PORT'), 1234) + self.assertEqual(conf.get('HOST'), 'localhost2') + self.assertEqual(conf.get('DB'), 0) + self.assertEqual(conf.get('PASSWORD'), None) + self.assertEqual(conf.get('PORT'), 1234) def test_get_ip_address_from_request(self): req = HttpRequest() @@ -558,7 +558,7 @@ class AccessAttemptTest(DefenderTestCase): def test_use_celery(self): """ Check that use celery works""" - self.assertEquals(AccessAttempt.objects.count(), 0) + self.assertEqual(AccessAttempt.objects.count(), 0) for i in range(0, int(config.FAILURE_LIMIT)): response = self._login() @@ -570,8 +570,8 @@ class AccessAttemptTest(DefenderTestCase): response = self._login() self.assertContains(response, self.LOCKED_MESSAGE) - self.assertEquals(AccessAttempt.objects.count(), - config.FAILURE_LIMIT+1) + self.assertEqual(AccessAttempt.objects.count(), + config.FAILURE_LIMIT + 1) self.assertIsNotNone(str(AccessAttempt.objects.all()[0])) @patch('defender.config.LOCKOUT_BY_IP_USERNAME', True) From 948877c1566f6b529e82d8bd77a9567dbc8c6d3b Mon Sep 17 00:00:00 2001 From: Vladimir Bolshakov Date: Mon, 1 Feb 2016 19:07:16 +0300 Subject: [PATCH 3/8] Fix formatting. --- defender/tests.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/defender/tests.py b/defender/tests.py index 28b69bc..93971da 100644 --- a/defender/tests.py +++ b/defender/tests.py @@ -18,7 +18,6 @@ from .connection import parse_redis_url, get_redis_connection from .models import AccessAttempt from .test import DefenderTestCase, DefenderTransactionTestCase - # Django >= 1.7 compatibility try: LOGIN_FORM_KEY = '
Date: Mon, 1 Feb 2016 19:08:54 +0300 Subject: [PATCH 4/8] Fix testing of failed login redirect to URL for Django 1.9. Location header in redirect can be relative URL from Django 1.9. --- defender/tests.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/defender/tests.py b/defender/tests.py index 93971da..27e8e3c 100644 --- a/defender/tests.py +++ b/defender/tests.py @@ -1,9 +1,11 @@ import random import string import time +from distutils.version import StrictVersion from mock import patch +from django import get_version from django.contrib.auth.models import User from django.contrib.auth.models import AnonymousUser from django.contrib.sessions.backends.db import SessionStore @@ -27,6 +29,7 @@ except NoReverseMatch: ADMIN_LOGIN_URL = reverse('admin:index') LOGIN_FORM_KEY = 'this_is_the_login_form' +DJANGO_VERSION = StrictVersion(get_version()) VALID_USERNAME = VALID_PASSWORD = 'valid' @@ -318,6 +321,13 @@ class AccessAttemptTest(DefenderTestCase): # Check if we are in the same login page self.assertContains(response, LOGIN_FORM_KEY) + # RFC 7231 allows relative URIs in Location header. + # Django from version 1.9 is support this: + # https://docs.djangoproject.com/en/1.9/releases/1.9/#http-redirects-no-longer-forced-to-absolute-uris + lockout_url = 'http://testserver/o/login/' + if DJANGO_VERSION >= StrictVersion('1.9'): + lockout_url = '/o/login/' + # So, we shouldn't have gotten a lock-out yet. # But we should get one now, check redirect make sure it is valid. response = self._login() From 23a690395c8db57fb5712c08e26f21175d0c7666 Mon Sep 17 00:00:00 2001 From: Vladimir Bolshakov Date: Mon, 1 Feb 2016 19:09:28 +0300 Subject: [PATCH 5/8] Use render shortcut instead of render_to_response. --- defender/utils.py | 6 ++---- defender/views.py | 7 ++----- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/defender/utils.py b/defender/utils.py index 0b1b1d5..2183368 100644 --- a/defender/utils.py +++ b/defender/utils.py @@ -2,8 +2,7 @@ import logging from django.http import HttpResponse from django.http import HttpResponseRedirect -from django.shortcuts import render_to_response -from django.template import RequestContext +from django.shortcuts import render from django.core.validators import validate_ipv46_address from django.core.exceptions import ValidationError @@ -251,8 +250,7 @@ def lockout_response(request): 'cooloff_time_minutes': config.COOLOFF_TIME / 60, 'failure_limit': config.FAILURE_LIMIT, } - return render_to_response(config.LOCKOUT_TEMPLATE, context, - context_instance=RequestContext(request)) + return render(request, config.LOCKOUT_TEMPLATE, context) if config.LOCKOUT_URL: return HttpResponseRedirect(config.LOCKOUT_URL) diff --git a/defender/views.py b/defender/views.py index 898a1f2..ba46abc 100644 --- a/defender/views.py +++ b/defender/views.py @@ -1,5 +1,4 @@ -from django.shortcuts import render_to_response -from django.template import RequestContext +from django.shortcuts import render from django.http import HttpResponseRedirect from django.core.urlresolvers import reverse from django.contrib.admin.views.decorators import staff_member_required @@ -16,9 +15,7 @@ def block_view(request): context = {'blocked_ip_list': blocked_ip_list, 'blocked_username_list': blocked_username_list} - return render_to_response( - 'defender/admin/blocks.html', - context, context_instance=RequestContext(request)) + return render(request, 'defender/admin/blocks.html', context) @staff_member_required From 2ce4e16979e095f1885f35cd767b0625dcd424c6 Mon Sep 17 00:00:00 2001 From: Vladimir Bolshakov Date: Mon, 1 Feb 2016 19:09:59 +0300 Subject: [PATCH 6/8] Use url method instead of patterns in test URLs setup. --- defender/test_urls.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/defender/test_urls.py b/defender/test_urls.py index 88ad7e7..d9b2e14 100644 --- a/defender/test_urls.py +++ b/defender/test_urls.py @@ -1,7 +1,6 @@ -from django.conf.urls import patterns, include +from django.conf.urls import url, include from django.contrib import admin -urlpatterns = patterns( - '', - (r'^admin/', include(admin.site.urls)), -) +urlpatterns = [ + url(r'^admin/', include(admin.site.urls)), +] From 4b8852204d40676d51fc47c82d81a99542264ea0 Mon Sep 17 00:00:00 2001 From: Vladimir Bolshakov Date: Mon, 1 Feb 2016 19:10:28 +0300 Subject: [PATCH 7/8] Django 1.9 is supported in installation requirements. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5bc3162..7b906a0 100644 --- a/setup.py +++ b/setup.py @@ -69,7 +69,7 @@ setup(name='django-defender', include_package_data=True, packages=get_packages('defender'), package_data=get_package_data('defender'), - install_requires=['Django>=1.6,<1.9', 'redis==2.10.3', + install_requires=['Django>=1.6,<1.10', 'redis==2.10.3', 'hiredis==0.2.0', 'mockredispy==2.9.0.11'], tests_require=['mock', 'mockredispy', 'coverage', 'celery'], ) From 16e4e4d9c60c761afceaf56bd1b8ca74fc09d0df Mon Sep 17 00:00:00 2001 From: Vladimir Bolshakov Date: Mon, 1 Feb 2016 19:42:14 +0300 Subject: [PATCH 8/8] Exclude Python 3.3 and Django 1.9 from Travis matrix, because of Django 1.8 is the last version with Python 3.3 support. https://docs.djangoproject.com/en/1.9/releases/1.9/#python-compatibility --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1fe83f3..3478943 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,6 +35,8 @@ matrix: env: DJANGO=Django==1.8.8 - python: "2.6" env: DJANGO=Django==1.9.1 + - python: "3.3" + env: DJANGO=Django==1.9.1 - python: "3.5" env: DJANGO=Django==1.6.11 - python: "3.5"