Skip to content

Commit

Permalink
Facelift, support for py3 and modern django version, wheel support. (#61
Browse files Browse the repository at this point in the history
)

* Facelift, support for py3 and modern django version, wheel support.
* Add support for Python 3 and PyPy
* Move to pytest for testing
* Add wheel build support
* Drops support for Django < 1.6, adds support for Django 1.6, 1.7 1.8 and 1.9

This adds a proper tox config and uses this in travis, moves to pytest
instead of nose and cleans up some old stuff.
  • Loading branch information
EnTeQuAk committed Apr 27, 2016
1 parent 684f1d5 commit e5dc7f6
Show file tree
Hide file tree
Showing 17 changed files with 506 additions and 332 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
*.egg-info
*.db
*.sw[po]
.cache
.tox
dist
33 changes: 26 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
sudo: false

language: python
env:
- DJANGO_VERSION=1.4.5
- DJANGO_VERSION=1.5.1

python:
- "2.6"
- "2.7"
install: pip install -q Django==${DJANGO_VERSION} --use-mirrors
script: ./run.sh test
- '2.7'
- '3.4'
- '3.5'
- pypy

env:
- DJANGO_VERSION=1.9.x
- DJANGO_VERSION=1.8.x
- DJANGO_VERSION=1.7.x
- DJANGO_VERSION=1.6.x

matrix:
exclude:
- python: '3.5'
env: DJANGO_VERSION=1.7.x
- python: '3.5'
env: DJANGO_VERSION=1.6.x

install:
- pip install tox

script:
- tox -e "$TRAVIS_PYTHON_VERSION-$DJANGO_VERSION"
11 changes: 11 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@
CHANGES
=======

v3.0
====

Currenty in development.

* Add support for Python 3 and PyPy
* Move to pytest for testing
* Add wheel build support
* Drops support for Django < 1.6, adds support for Django 1.6, 1.7, 1.8 and 1.9


v2.0.3
======

Expand Down
2 changes: 0 additions & 2 deletions csp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
VERSION = (2, 0, 3)
__version__ = '.'.join(map(str, VERSION))
3 changes: 0 additions & 3 deletions csp/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +0,0 @@
from csp.tests.test_decorators import DecoratorTests # noqa
from csp.tests.test_middleware import MiddlewareTests # noqa
from csp.tests.test_utils import UtilsTests # noqa
19 changes: 19 additions & 0 deletions csp/tests/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
CSP_REPORT_ONLY = False

DATABASES = {
'default': {
'NAME': 'test.db',
'ENGINE': 'django.db.backends.sqlite3',
}
}

INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'csp',
)

SECRET_KEY = 'csp-test-key'
105 changes: 56 additions & 49 deletions csp/tests/test_decorators.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,65 @@
from django.http import HttpResponse
from django.test import RequestFactory, TestCase
from django.test import RequestFactory
from django.test.utils import override_settings

from nose.tools import eq_

from csp.decorators import csp, csp_replace, csp_update, csp_exempt
from csp.middleware import CSPMiddleware


REQUEST = RequestFactory().get('/')
mw = CSPMiddleware()

class DecoratorTests(TestCase):
def test_csp_exempt(self):
@csp_exempt
def view(request):
return HttpResponse()
response = view(REQUEST)
assert response._csp_exempt

@override_settings(CSP_IMG_SRC=['foo.com'])
def test_csp_update(self):
@csp_update(IMG_SRC='bar.com')
def view(request):
return HttpResponse()
response = view(REQUEST)
eq_(response._csp_update, {'img-src': 'bar.com'})

@override_settings(CSP_IMG_SRC=['foo.com'])
def test_csp_replace(self):
@csp_replace(IMG_SRC='bar.com')
def view(request):
return HttpResponse()
response = view(REQUEST)
eq_(response._csp_replace, {'img-src': 'bar.com'})

def test_csp(self):
@csp(IMG_SRC=['foo.com'], FONT_SRC=['bar.com'])
def view(request):
return HttpResponse()
response = view(REQUEST)
eq_(response._csp_config,
{'img-src': ['foo.com'], 'font-src': ['bar.com']})
mw.process_response(REQUEST, response)
policy_list = sorted(response['Content-Security-Policy'].split("; "))
eq_(policy_list, ["font-src bar.com", "img-src foo.com"])

def test_csp_string_values(self):
# Test backwards compatibility where values were strings
@csp(IMG_SRC='foo.com', FONT_SRC='bar.com')
def view(request):
return HttpResponse()
response = view(REQUEST)
eq_(response._csp_config,
{'img-src': ['foo.com'], 'font-src': ['bar.com']})
mw.process_response(REQUEST, response)
policy_list = sorted(response['Content-Security-Policy'].split("; "))
eq_(policy_list, ["font-src bar.com", "img-src foo.com"])

def test_csp_exempt():
@csp_exempt
def view(request):
return HttpResponse()
response = view(REQUEST)
assert response._csp_exempt


@override_settings(CSP_IMG_SRC=['foo.com'])
def test_csp_update():
@csp_update(IMG_SRC='bar.com')
def view(request):
return HttpResponse()
response = view(REQUEST)
assert response._csp_update == {'img-src': 'bar.com'}


@override_settings(CSP_IMG_SRC=['foo.com'])
def test_csp_replace():
@csp_replace(IMG_SRC='bar.com')
def view(request):
return HttpResponse()
response = view(REQUEST)
assert response._csp_replace == {'img-src': 'bar.com'}


def test_csp():
@csp(IMG_SRC=['foo.com'], FONT_SRC=['bar.com'])
def view(request):
return HttpResponse()
response = view(REQUEST)
assert response._csp_config == {
'img-src': ['foo.com'], 'font-src': ['bar.com']
}

mw.process_response(REQUEST, response)
policy_list = sorted(response['Content-Security-Policy'].split("; "))
assert policy_list == ["font-src bar.com", "img-src foo.com"]


def test_csp_string_values():
# Test backwards compatibility where values were strings
@csp(IMG_SRC='foo.com', FONT_SRC='bar.com')
def view(request):
return HttpResponse()
response = view(REQUEST)
assert response._csp_config == {
'img-src': ['foo.com'], 'font-src': ['bar.com']
}

mw.process_response(REQUEST, response)
policy_list = sorted(response['Content-Security-Policy'].split("; "))
assert policy_list == ["font-src bar.com", "img-src foo.com"]
139 changes: 72 additions & 67 deletions csp/tests/test_middleware.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
from django.http import HttpResponse, HttpResponseServerError
from django.test import RequestFactory, TestCase
from django.test import RequestFactory
from django.test.utils import override_settings

from nose.tools import eq_

from csp.middleware import CSPMiddleware


Expand All @@ -12,67 +10,74 @@
rf = RequestFactory()


class MiddlewareTests(TestCase):
def test_add_header(self):
request = rf.get('/')
response = HttpResponse()
mw.process_response(request, response)
assert HEADER in response

def test_exempt(self):
request = rf.get('/')
response = HttpResponse()
response._csp_exempt = True
mw.process_response(request, response)
assert HEADER not in response

@override_settings(CSP_EXCLUDE_URL_PREFIXES=('/inlines-r-us'))
def text_exclude(self):
request = rf.get('/inlines-r-us/foo')
response = HttpResponse()
mw.process_response(request, response)
assert HEADER not in response

@override_settings(CSP_REPORT_ONLY=True)
def test_report_only(self):
request = rf.get('/')
response = HttpResponse()
mw.process_response(request, response)
assert HEADER not in response
assert HEADER + '-Report-Only' in response

def test_dont_replace(self):
request = rf.get('/')
response = HttpResponse()
response[HEADER] = 'default-src example.com'
mw.process_response(request, response)
eq_(response[HEADER], 'default-src example.com')

def test_use_config(self):
request = rf.get('/')
response = HttpResponse()
response._csp_config = {'default-src': ['example.com']}
mw.process_response(request, response)
eq_(response[HEADER], 'default-src example.com')

def test_use_update(self):
request = rf.get('/')
response = HttpResponse()
response._csp_update = {'default-src': ['example.com']}
mw.process_response(request, response)
eq_(response[HEADER], "default-src 'self' example.com")

@override_settings(CSP_IMG_SRC=['foo.com'])
def test_use_replace(self):
request = rf.get('/')
response = HttpResponse()
response._csp_replace = {'img-src': ['bar.com']}
mw.process_response(request, response)
eq_(response[HEADER], "default-src 'self'; img-src bar.com")

@override_settings(DEBUG=True)
def test_debug_exempt(self):
request = rf.get('/')
response = HttpResponseServerError()
mw.process_response(request, response)
assert HEADER not in response
def test_add_header():
request = rf.get('/')
response = HttpResponse()
mw.process_response(request, response)
assert HEADER in response


def test_exempt():
request = rf.get('/')
response = HttpResponse()
response._csp_exempt = True
mw.process_response(request, response)
assert HEADER not in response


@override_settings(CSP_EXCLUDE_URL_PREFIXES=('/inlines-r-us'))
def text_exclude():
request = rf.get('/inlines-r-us/foo')
response = HttpResponse()
mw.process_response(request, response)
assert HEADER not in response


@override_settings(CSP_REPORT_ONLY=True)
def test_report_only():
request = rf.get('/')
response = HttpResponse()
mw.process_response(request, response)
assert HEADER not in response
assert HEADER + '-Report-Only' in response


def test_dont_replace():
request = rf.get('/')
response = HttpResponse()
response[HEADER] = 'default-src example.com'
mw.process_response(request, response)
assert response[HEADER] == 'default-src example.com'


def test_use_config():
request = rf.get('/')
response = HttpResponse()
response._csp_config = {'default-src': ['example.com']}
mw.process_response(request, response)
assert response[HEADER] == 'default-src example.com'


def test_use_update():
request = rf.get('/')
response = HttpResponse()
response._csp_update = {'default-src': ['example.com']}
mw.process_response(request, response)
assert response[HEADER] == "default-src 'self' example.com"


@override_settings(CSP_IMG_SRC=['foo.com'])
def test_use_replace():
request = rf.get('/')
response = HttpResponse()
response._csp_replace = {'img-src': ['bar.com']}
mw.process_response(request, response)
assert response[HEADER] == "default-src 'self'; img-src bar.com"


@override_settings(DEBUG=True)
def test_debug_exempt():
request = rf.get('/')
response = HttpResponseServerError()
mw.process_response(request, response)
assert HEADER not in response
Loading

0 comments on commit e5dc7f6

Please sign in to comment.