Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
language: python
sudo: false
python:
- "2.7"

env:
- DJANGO="Django==1.7.11"
- DJANGO="Django==1.8.11"
- DJANGO="Django==1.9.4"

install: pip install $DJANGO

script: ./runtests.sh
6 changes: 4 additions & 2 deletions runtests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ cat > $SETTINGS <<EOF
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'test.db',
'NAME': ':memory:',
},
}

Expand Down Expand Up @@ -42,6 +42,8 @@ export PYTHONPATH=.
export DJANGO_SETTINGS_MODULE=settings

django-admin.py test session_csrf $@
return_code=$?

rm -f $SETTINGS*
rm -f test.db

exit $return_code
86 changes: 38 additions & 48 deletions session_csrf/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import django.test
from django import http
from django.conf.urls.defaults import patterns
from django.conf.urls import patterns
from django.contrib.auth import logout
from django.contrib.auth.middleware import AuthenticationMiddleware
from django.contrib.auth.models import User
Expand All @@ -11,8 +11,7 @@
from django.core import signals
from django.core.cache import cache
from django.core.handlers.wsgi import WSGIRequest
from django.db import close_connection
from django.template import context
from django.core.exceptions import ImproperlyConfigured

import mock

Expand Down Expand Up @@ -46,33 +45,33 @@ def login(self):
def test_csrftoken_unauthenticated(self):
# request.csrf_token is '' for anonymous users.
response = self.client.get('/', follow=True)
self.assertEqual(response._request.csrf_token, '')
self.assertEqual(response.wsgi_request.csrf_token, '')

def test_csrftoken_authenticated(self):
# request.csrf_token is a random non-empty string for authed users.
self.login()
response = self.client.get('/', follow=True)
# The CSRF token is a 32-character MD5 string.
self.assertEqual(len(response._request.csrf_token), 32)
self.assertEqual(len(response.wsgi_request.csrf_token), 32)

def test_csrftoken_new_session(self):
# The csrf_token is added to request.session the first time.
self.login()
response = self.client.get('/', follow=True)
# The CSRF token is a 32-character MD5 string.
token = response._request.session['csrf_token']
token = response.wsgi_request.session['csrf_token']
self.assertEqual(len(token), 32)
self.assertEqual(token, response._request.csrf_token)
self.assertEqual(token, response.wsgi_request.csrf_token)

def test_csrftoken_existing_session(self):
# The csrf_token in request.session is reused on subsequent requests.
self.login()
r1 = self.client.get('/', follow=True)
token = r1._request.session['csrf_token']
token = r1.wsgi_request.session['csrf_token']

r2 = self.client.get('/', follow=True)
self.assertEqual(r1._request.csrf_token, r2._request.csrf_token)
self.assertEqual(token, r2._request.csrf_token)
self.assertEqual(r1.wsgi_request.csrf_token, r2.wsgi_request.csrf_token)
self.assertEqual(token, r2.wsgi_request.csrf_token)


class TestCsrfMiddleware(django.test.TestCase):
Expand Down Expand Up @@ -160,7 +159,7 @@ def test_csrf_token_context_processor(self):
request.csrf_token = self.token
request.groups = []
ctx = {}
for processor in context.get_standard_processors():
for processor in get_context_processors():
ctx.update(processor(request))
self.assertEqual(ctx['csrf_token'], self.token)

Expand Down Expand Up @@ -209,7 +208,7 @@ def test_new_anon_token_on_request(self):
response = self.client.get('/anon')
# Get the key from the cookie and find the token in the cache.
key = response.cookies['anoncsrf'].value
self.assertEqual(response._request.csrf_token, cache.get(prep_key(key)))
self.assertEqual(response.wsgi_request.csrf_token, cache.get(prep_key(key)))

def test_existing_anon_cookie_on_request(self):
# We reuse an existing anon cookie key+token.
Expand All @@ -218,7 +217,7 @@ def test_existing_anon_cookie_on_request(self):
# Now check that subsequent requests use that cookie.
response = self.client.get('/anon')
self.assertEqual(response.cookies['anoncsrf'].value, key)
self.assertEqual(response._request.csrf_token, cache.get(prep_key(key)))
self.assertEqual(response.wsgi_request.csrf_token, cache.get(prep_key(key)))

def test_new_anon_token_on_response(self):
# The anon cookie is sent and we vary on Cookie.
Expand All @@ -244,12 +243,12 @@ def test_anon_csrf_logout(self):

def test_existing_anon_cookie_not_in_cache(self):
response = self.client.get('/anon')
self.assertEqual(len(response._request.csrf_token), 32)
self.assertEqual(len(response.wsgi_request.csrf_token), 32)

# Clear cache and make sure we still get a token
cache.clear()
response = self.client.get('/anon')
self.assertEqual(len(response._request.csrf_token), 32)
self.assertEqual(len(response.wsgi_request.csrf_token), 32)

def test_anonymous_csrf_exempt(self):
response = self.client.post('/no-anon-csrf')
Expand Down Expand Up @@ -283,7 +282,7 @@ def test_csrftoken_unauthenticated(self):
# when ANON_ALWAYS is enabled.
response = self.client.get('/', follow=True)
# The CSRF token is a 32-character MD5 string.
self.assertEqual(len(response._request.csrf_token), 32)
self.assertEqual(len(response.wsgi_request.csrf_token), 32)

def test_authenticated_request(self):
# Nothing special happens, nothing breaks.
Expand All @@ -307,7 +306,7 @@ def test_new_anon_token_on_request(self):
response = self.client.get('/')
# Get the key from the cookie and find the token in the cache.
key = response.cookies['anoncsrf'].value
self.assertEqual(response._request.csrf_token, cache.get(prep_key(key)))
self.assertEqual(response.wsgi_request.csrf_token, cache.get(prep_key(key)))

def test_existing_anon_cookie_on_request(self):
# We reuse an existing anon cookie key+token.
Expand All @@ -317,7 +316,7 @@ def test_existing_anon_cookie_on_request(self):
# Now check that subsequent requests use that cookie.
response = self.client.get('/')
self.assertEqual(response.cookies['anoncsrf'].value, key)
self.assertEqual(response._request.csrf_token, cache.get(prep_key(key)))
self.assertEqual(response.wsgi_request.csrf_token, cache.get(prep_key(key)))
self.assertEqual(response['Vary'], 'Cookie')

def test_anon_csrf_logout(self):
Expand All @@ -328,12 +327,12 @@ def test_anon_csrf_logout(self):

def test_existing_anon_cookie_not_in_cache(self):
response = self.client.get('/')
self.assertEqual(len(response._request.csrf_token), 32)
self.assertEqual(len(response.wsgi_request.csrf_token), 32)

# Clear cache and make sure we still get a token
cache.clear()
response = self.client.get('/')
self.assertEqual(len(response._request.csrf_token), 32)
self.assertEqual(len(response.wsgi_request.csrf_token), 32)

def test_massive_anon_cookie(self):
# if the key + PREFIX + setting prefix is greater than 250
Expand All @@ -352,33 +351,24 @@ def test_surprising_characters(self):
self.assertEqual(warner.call_count, 0)


class ClientHandler(django.test.client.ClientHandler):
"""
Handler that stores the real request object on the response.

Almost all the code comes from the parent class.
"""
def get_context_processors():
"""Get context processors in a way that works for Django 1.7 and 1.8+"""
try:
# 1.7
from django.template.context import get_standard_processors
return get_standard_processors()
except ImportError:
# 1.8+
try:
from django.template.engine import Engine
engine = Engine.get_default()
except ImproperlyConfigured:
return []
return engine.template_context_processors

def __call__(self, environ):
# Set up middleware if needed. We couldn't do this earlier, because
# settings weren't available.
if self._request_middleware is None:
self.load_middleware()

signals.request_started.send(sender=self.__class__)
try:
request = WSGIRequest(environ)
# sneaky little hack so that we can easily get round
# CsrfViewMiddleware. This makes life easier, and is probably
# required for backwards compatibility with external tests against
# admin views.
request._dont_enforce_csrf_checks = not self.enforce_csrf_checks
response = self.get_response(request)
finally:
signals.request_finished.disconnect(close_connection)
signals.request_finished.send(sender=self.__class__)
signals.request_finished.connect(close_connection)

# Store the request object.
response._request = request
return response
# for 1.7 support
class ClientHandler(django.test.client.ClientHandler):
@property
def wsgi_request_middleware(self):
return self._request_middleware