diff --git a/.gitignore b/.gitignore index 8aa07c2..d796ad2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.pyc .coverage htmlcov/ +venv +.python-version diff --git a/.travis.yml b/.travis.yml index 82bb89f..a066dc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,71 +1,47 @@ language: python +cache: pip + python: - - "2.6" - "2.7" - - "3.4" - - "3.5" - "3.6" -install: - - pip install coveralls coverage - - pip install $DJANGO -script: - - coverage run runtests.py - - coverage report -m -after_script: - - coverage combine - - coveralls + - "3.7" + +sudo: false + env: - - DJANGO="Django>=1.4,<1.5" - - DJANGO="Django>=1.5,<1.6" - - DJANGO="Django>=1.6,<1.7" - - DJANGO="Django>=1.7,<1.8" - DJANGO="Django>=1.8,<1.9" - DJANGO="Django>=1.9,<1.10" - DJANGO="Django>=1.10,<1.11" - DJANGO="Django>=1.11,<2.0" + - DJANGO="Django>=2.0,<2.1" + - DJANGO="Django==2.2.12" - DJANGO="https://github.com/django/django/archive/master.tar.gz" + matrix: + fast_finish: true exclude: - # Python 2.6 support has been dropped in Django 1.7 - - python: "2.6" - env: DJANGO="Django>=1.7,<1.8" - - python: "2.6" - env: DJANGO="Django>=1.8,<1.9" - - python: "2.6" - env: DJANGO="Django>=1.9,<1.10" - - python: "2.6" - env: DJANGO="Django>=1.10,<1.11" - - python: "2.6" - env: DJANGO="Django>=1.11,<2.0" - - python: "2.6" - env: DJANGO="https://github.com/django/django/archive/master.tar.gz" + - python: "2.7" + env: DJANGO="Django>=2.0,<2.1" + - python: "2.7" + env: DJANGO="Django==2.2.12" - python: "2.7" env: DJANGO="https://github.com/django/django/archive/master.tar.gz" - - python: "3.4" - env: DJANGO="Django>=1.4,<1.5" - python: "3.4" env: DJANGO="https://github.com/django/django/archive/master.tar.gz" - - python: "3.5" - env: DJANGO="Django>=1.4,<1.5" - - python: "3.5" - env: DJANGO="Django>=1.5,<1.6" - - python: "3.5" - env: DJANGO="Django>=1.6,<1.7" - - python: "3.5" - env: DJANGO="Django>=1.7,<1.8" - python: "3.6" - env: DJANGO="Django>=1.4,<1.5" - - python: "3.6" - env: DJANGO="Django>=1.5,<1.6" - - python: "3.6" - env: DJANGO="Django>=1.6,<1.7" - - python: "3.6" - env: DJANGO="Django>=1.7,<1.8" - - python: "3.6" - env: DJANGO="Django>=1.8,<1.9" - - python: "3.6" - env: DJANGO="Django>=1.9,<1.10" - - python: "3.6" - env: DJANGO="Django>=1.10,<1.11" + env: DJANGO="Django>=1.8,<1.11" + allow_failures: - env: DJANGO="https://github.com/django/django/archive/master.tar.gz" + +install: + - pip install coveralls coverage + - pip install $DJANGO + +script: + - coverage run runtests.py + - coverage report -m + +after_script: + - coverage combine + - coveralls diff --git a/requirements.txt b/requirements.txt index 03490d0..4ebc8ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ -Django>=1.4 coverage diff --git a/runtests.py b/runtests.py index 3e56770..b566ecd 100755 --- a/runtests.py +++ b/runtests.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +import logging import sys import django @@ -6,15 +7,20 @@ import django from django.conf import settings from django.core.management import execute_from_command_line +from tos.compat import get_middleware_settings_key + + +middleware_settings_key = get_middleware_settings_key() + if not settings.configured: - settings.configure( - DATABASES={ + django_settings = { + 'DATABASES': { 'default': { 'ENGINE': 'django.db.backends.sqlite3', } }, - INSTALLED_APPS=[ + 'INSTALLED_APPS': [ 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', @@ -23,14 +29,7 @@ if not settings.configured: 'tos', 'tos.tests' ], - MIDDLEWARE_CLASSES=[ - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - ], - TEMPLATES=[ + 'TEMPLATES': [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], @@ -45,10 +44,10 @@ if not settings.configured: }, }, ], - ROOT_URLCONF='tos.tests.test_urls', - LOGIN_URL='/login/', - SITE_ID='1', - CACHES = { + 'ROOT_URLCONF': 'tos.tests.test_urls', + 'LOGIN_URL': '/login/', + 'SITE_ID': '1', + 'CACHES': { 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', }, @@ -56,11 +55,34 @@ if not settings.configured: 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', } }, - TOS_CACHE_NAME = 'tos' - ) + 'TOS_CACHE_NAME': 'tos' + } + + if django.VERSION >= (1, 10, 0): + django_settings[middleware_settings_key] = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + ] + else: + django_settings[middleware_settings_key] = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + ] + + settings.configure(**django_settings) -import logging logging.basicConfig( level = logging.DEBUG, format = '%(asctime)s %(levelname)s %(message)s', diff --git a/tos/__init__.py b/tos/__init__.py index b1cd446..2baceb4 100644 --- a/tos/__init__.py +++ b/tos/__init__.py @@ -1,3 +1,3 @@ default_app_config = 'tos.apps.TOSConfig' -VERSION = (0, 7, 2) +VERSION = (0, 8, 0) diff --git a/tos/apps.py b/tos/apps.py index b797327..4491b41 100644 --- a/tos/apps.py +++ b/tos/apps.py @@ -4,7 +4,11 @@ from django.db.models.signals import pre_save from .signal_handlers import invalidate_cached_agreements -MIDDLEWARES = getattr(settings, 'MIDDLEWARE_CLASSES', []) +from .compat import get_middleware_settings_key + + +middleware_settings_key = get_middleware_settings_key() +MIDDLEWARES = getattr(settings, middleware_settings_key, []) class TOSConfig(AppConfig): diff --git a/tos/compat.py b/tos/compat.py index bfb690e..1d9b160 100644 --- a/tos/compat.py +++ b/tos/compat.py @@ -1,5 +1,6 @@ import django from django.conf import settings +from django.utils import deprecation def patterns(mod, *urls): @@ -50,7 +51,26 @@ def get_cache(cache_name): return get_cache(cache_name) +def get_middleware_mixin(): + if django.VERSION >= (1, 10, 0): + return deprecation.MiddlewareMixin + else: + return object + + +def get_middleware_settings_key(): + if django.VERSION >= (1, 10, 0): + return 'MIDDLEWARE' + else: + return 'MIDDLEWARE_CLASSES' + + if django.VERSION < (1, 5): from django.templatetags.future import url else: from django.template.defaulttags import url + +if django.VERSION >= (1, 10, 0): + from django.urls import reverse +else: + from django.core.urlresolvers import reverse diff --git a/tos/middleware.py b/tos/middleware.py index fbadb82..a10bea1 100644 --- a/tos/middleware.py +++ b/tos/middleware.py @@ -1,22 +1,22 @@ -from django import VERSION as DJANGO_VERSION from django.conf import settings -from django.contrib.auth import REDIRECT_FIELD_NAME, SESSION_KEY as session_key -from django.core.urlresolvers import reverse +from django.contrib.auth import SESSION_KEY as session_key +from django.contrib.auth import REDIRECT_FIELD_NAME from django.http import HttpResponseRedirect -from django.utils import deprecation from django.utils.cache import add_never_cache_headers -from .compat import get_cache +from .compat import get_cache, get_middleware_mixin, reverse from .models import UserAgreement + cache = get_cache(getattr(settings, 'TOS_CACHE_NAME', 'default')) tos_check_url = reverse('tos_check_tos') +middleware_mixin = get_middleware_mixin() - -class UserAgreementMiddleware(deprecation.MiddlewareMixin if DJANGO_VERSION >= (1, 10, 0) else object): +class UserAgreementMiddleware(middleware_mixin): """ Some middleware to check if users have agreed to the latest TOS """ + def process_request(self, request): # Don't get in the way of any mutating requests if request.method != 'GET': diff --git a/tos/models.py b/tos/models.py index 1174b7a..9bd33f5 100644 --- a/tos/models.py +++ b/tos/models.py @@ -68,8 +68,8 @@ class TermsOfService(BaseModel): class UserAgreement(BaseModel): - terms_of_service = models.ForeignKey(TermsOfService, related_name='terms') - user = models.ForeignKey(get_fk_user_model(), related_name='user_agreement') + terms_of_service = models.ForeignKey(TermsOfService, related_name='terms', on_delete=models.CASCADE) + user = models.ForeignKey(get_fk_user_model(), related_name='user_agreement', on_delete=models.CASCADE) def __unicode__(self): return u'%s agreed to TOS: %s' % (self.user.username, diff --git a/tos/tests/test_middleware.py b/tos/tests/test_middleware.py index fb4c601..7979ddb 100644 --- a/tos/tests/test_middleware.py +++ b/tos/tests/test_middleware.py @@ -1,19 +1,21 @@ from django.conf import settings from django.contrib.auth import REDIRECT_FIELD_NAME -from django.core.urlresolvers import reverse from django.test import TestCase -from django.test.utils import override_settings +from django.test.utils import modify_settings -from tos.compat import get_cache, get_runtime_user_model +from tos.compat import get_cache, get_runtime_user_model, reverse from tos.middleware import UserAgreementMiddleware from tos.models import TermsOfService, UserAgreement from tos.signal_handlers import invalidate_cached_agreements -@override_settings( - MIDDLEWARE_CLASSES=settings.MIDDLEWARE_CLASSES + [ - 'tos.middleware.UserAgreementMiddleware', - ] +@modify_settings( + MIDDLEWARE_CLASSES={ + 'append': 'tos.middleware.UserAgreementMiddleware', + }, + MIDDLEWARE={ + 'append': 'tos.middleware.UserAgreementMiddleware', + }, ) class TestMiddleware(TestCase): @@ -86,10 +88,13 @@ class TestMiddleware(TestCase): self.assertEqual(response.url.replace('http://testserver', ''), str(reverse('index'))) -@override_settings( - MIDDLEWARE_CLASSES=settings.MIDDLEWARE_CLASSES + [ - 'tos.middleware.UserAgreementMiddleware', - ] +@modify_settings( + MIDDLEWARE_CLASSES={ + 'append': 'tos.middleware.UserAgreementMiddleware', + }, + MIDDLEWARE={ + 'append': 'tos.middleware.UserAgreementMiddleware', + }, ) class BumpCoverage(TestCase): diff --git a/tos/tests/test_views.py b/tos/tests/test_views.py index 82caa86..1813885 100644 --- a/tos/tests/test_views.py +++ b/tos/tests/test_views.py @@ -1,8 +1,7 @@ from django.conf import settings -from django.core.urlresolvers import reverse from django.test import TestCase -from tos.compat import get_runtime_user_model +from tos.compat import get_runtime_user_model, reverse from tos.models import TermsOfService, UserAgreement, has_user_agreed_latest_tos @@ -80,14 +79,14 @@ class TestViews(TestCase): self.assertTrue(has_user_agreed_latest_tos(self.user1)) response = self.client.post(self.login_url, dict(username='user1', - password='user1pass')) + password='user1pass')) self.assertEqual(302, response.status_code) def test_redirect_security(self): """ redirect to outside url not allowed, should redirect to login url""" response = self.client.post(self.login_url, dict(username='user1', - password='user1pass', next='http://example.com')) + password='user1pass', next='http://example.com')) self.assertEqual(302, response.status_code) self.assertIn(settings.LOGIN_REDIRECT_URL, response._headers['location'][1])