diff --git a/admin_honeypot/compat.py b/admin_honeypot/compat.py new file mode 100644 index 0000000..da770ae --- /dev/null +++ b/admin_honeypot/compat.py @@ -0,0 +1,15 @@ +""" +Compatibility layer for various django and python versions imports +""" + +try: + from django.urls import reverse +except ImportError: # For Django version less than 2.0 + from django.core.urlresolvers import reverse # noqa + +try: + # Python 2.7 + from urllib import quote_plus +except ImportError: + # Python 3+ + from urllib.parse import quote_plus # noqa diff --git a/admin_honeypot/listeners.py b/admin_honeypot/listeners.py index 1b266dc..3dd5956 100644 --- a/admin_honeypot/listeners.py +++ b/admin_honeypot/listeners.py @@ -1,7 +1,8 @@ +from admin_honeypot.compat import reverse from admin_honeypot.signals import honeypot + from django.conf import settings from django.core.mail import mail_admins -from django.core.urlresolvers import reverse from django.template.loader import render_to_string @@ -17,5 +18,6 @@ def notify_admins(instance, request, **kwargs): message = render_to_string('admin_honeypot/email_message.txt', context).strip() mail_admins(subject=subject, message=message) + if getattr(settings, 'ADMIN_HONEYPOT_EMAIL_ADMINS', True): honeypot.connect(notify_admins) diff --git a/admin_honeypot/models.py b/admin_honeypot/models.py index c0a7e6a..2018952 100644 --- a/admin_honeypot/models.py +++ b/admin_honeypot/models.py @@ -1,6 +1,6 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ -from admin_honeypot import listeners +from admin_honeypot import listeners # noqa class LoginAttempt(models.Model): diff --git a/admin_honeypot/urls.py b/admin_honeypot/urls.py index 4ac0c34..e52ed44 100644 --- a/admin_honeypot/urls.py +++ b/admin_honeypot/urls.py @@ -1,6 +1,8 @@ from admin_honeypot import views from django.conf.urls import url +app_name = 'admin_honeypot' + urlpatterns = [ url(r'^login/$', views.AdminHoneypot.as_view(), name='login'), url(r'^.*$', views.AdminHoneypot.as_view(), name='index'), diff --git a/admin_honeypot/views.py b/admin_honeypot/views.py index a42a289..0d1e7c5 100755 --- a/admin_honeypot/views.py +++ b/admin_honeypot/views.py @@ -1,10 +1,10 @@ import django +from admin_honeypot.compat import reverse from admin_honeypot.forms import HoneypotLoginForm from admin_honeypot.models import LoginAttempt from admin_honeypot.signals import honeypot from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth.views import redirect_to_login -from django.core.urlresolvers import reverse from django.shortcuts import redirect from django.utils.translation import ugettext as _ from django.views import generic diff --git a/setup.py b/setup.py index 51b5aff..dd16fb2 100644 --- a/setup.py +++ b/setup.py @@ -15,6 +15,10 @@ classifiers=[ 'Development Status :: 5 - Production/Stable', 'Framework :: Django', + 'Framework :: Django :: 1.8', + 'Framework :: Django :: 1.10', + 'Framework :: Django :: 1.11', + 'Framework :: Django :: 2.0', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Natural Language :: English', @@ -26,8 +30,9 @@ 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', - ], + ], keywords='django admin honeypot trap', maintainer='Derek Payton', maintainer_email='derek.payton@gmail.com', @@ -37,4 +42,4 @@ include_package_data=True, packages=find_packages(), zip_safe=False, - ) +) diff --git a/tests/settings.py b/tests/settings.py index 507b8e9..378fd15 100755 --- a/tests/settings.py +++ b/tests/settings.py @@ -38,12 +38,18 @@ 'admin_honeypot', ) -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', ) +TEMPLATES = [ + { + 'APP_DIRS': True, + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + }, +] ADMIN_HONEYPOT_EMAIL_ADMINS = True diff --git a/tests/test_suite.py b/tests/test_suite.py index b0b7311..4df2ec3 100755 --- a/tests/test_suite.py +++ b/tests/test_suite.py @@ -1,17 +1,13 @@ -import django -import pytest +import re +from admin_honeypot.compat import reverse, quote_plus from admin_honeypot.models import LoginAttempt from django.conf import settings from django.core import mail -from django.core.urlresolvers import reverse from django.test import TestCase -try: - # Python 2.7 - from urllib import quote_plus -except ImportError: - # Python 3+ - from urllib.parse import quote_plus + +CSRF_TOKEN_REGEX = re.compile(r"(name='csrfmiddlewaretoken' value='\w+')") + class AdminHoneypotTest(TestCase): maxDiff = None @@ -35,12 +31,13 @@ def honeypot_url(self): def test_same_content(self): """ The honeypot should be an exact replica of the admin login page, - with the exception of where the form submits to and the CSS to - hide the user tools. + with the exception of where the form submits to, the CSS to + hide the user tools and the CSRF token """ admin_html = self.client.get(self.admin_url, follow=True).content.decode('utf-8') - honeypot_html = (self.client.get(self.honeypot_url, follow=True).content.decode('utf-8') + honeypot_html = ( + self.client.get(self.honeypot_url, follow=True).content.decode('utf-8') # /admin/login/ -> /secret/login/ .replace(self.honeypot_login_url, self.admin_login_url) @@ -51,6 +48,10 @@ def test_same_content(self): .replace(quote_plus(self.honeypot_url), quote_plus(self.admin_url)) ) + # Remove CSRF token + admin_html = CSRF_TOKEN_REGEX.sub('', admin_html) + honeypot_html = CSRF_TOKEN_REGEX.sub('', honeypot_html) + self.assertEqual(honeypot_html, admin_html) def test_create_login_attempt(self): diff --git a/tests/urls.py b/tests/urls.py index 5dba699..c7bea5a 100755 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,13 +1,18 @@ -try: - from django.conf.urls import patterns, include, url -except ImportError: # django < 1.4 - from django.conf.urls.defaults import patterns, include, url +import django +from django.conf.urls import include, url # Uncomment the next two lines to enable the admin: from django.contrib import admin admin.autodiscover() -urlpatterns = patterns('', - url(r'^admin/', include('admin_honeypot.urls', namespace='admin_honeypot')), - url(r'^secret/', include(admin.site.urls)), -) +if django.VERSION < (2, 0): + urlpatterns = [ + url(r'^admin/', include('admin_honeypot.urls', namespace='admin_honeypot')), + url(r'^secret/', include(admin.site.urls)), + ] +else: + from django.urls import path + urlpatterns = [ + path('admin/', include('admin_honeypot.urls', namespace='admin_honeypot')), + path('secret/', admin.site.urls), + ] diff --git a/tox.ini b/tox.ini index b45257c..356bdd3 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,8 @@ [tox] envlist = django18_py27, django18_py33, django18_py34, django18_py35 - django19_py27, django19_py34, django19_py35 + django19_py27, django19_py34, django19_py35, + django20_py34, django20_py35, django20_py36, [testenv] commands = py.test tests/ @@ -22,7 +23,7 @@ commands = py.test tests/ --cov admin_honeypot --cov-config .coveragerc --cov-report term-missing --pep8 admin_honeypot coveralls deps = - Django>=1.9,<1.10 + Django>=1.9,<=2.0 coveralls pytest-cov pytest-pep8 @@ -75,3 +76,21 @@ basepython = python3.5 deps = Django>=1.9,<1.10 {[testenv]deps} + +[testenv:django20_py34] +basepython = python3.4 +deps = + Django==2.0.2 + {[testenv]deps} + +[testenv:django20_py35] +basepython = python3.5 +deps = + Django==2.0.2 + {[testenv]deps} + +[testenv:django20_py36] +basepython = python3.6 +deps = + Django==2.0.2 + {[testenv]deps} \ No newline at end of file