Skip to content
Closed
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
21 changes: 21 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
language: python
sudo: false
python:
- "2.6"
- "2.7"

env:
- DJANGO="Django==1.4.22"
- DJANGO="Django==1.7.10"
- DJANGO="Django==1.8.4"

matrix:
exclude:
- python: "2.6"
env: DJANGO="Django==1.8.4"
- python: "2.6"
env: DJANGO="Django==1.7.10"

install: pip install $DJANGO

script: ./runtests.sh
19 changes: 16 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ applied before your views are imported.
Differences from Django
-----------------------

``django-session-csrf`` does not assign CSRF tokens to anonymous users because
we don't want to support a session for every anonymous user. Instead, views
that need anonymous forms can be decorated with ``@anonymous_csrf``::
By default ``django-session-csrf`` does not assign CSRF tokens to anonymous
users because we don't want to support a session for every anonymous user.
Instead, views that need anonymous forms can be decorated with
``@anonymous_csrf``::

from session_csrf import anonymous_csrf

Expand Down Expand Up @@ -105,6 +106,18 @@ the following setting:

Default: False

Alternatively, you can make ``django-session-csrf`` act exactly as Django does
with the following setting:

``ANON_AS_LOGGED_IN``
set the CSRF token for anonymous users in their session

Default: ``False``

If ``ANON_AS_LOGGEDIN`` is set, the ``anonymous_csrf`` and
``anonymous_csrf_exempt`` will do nothing to the view they decorate and issue a
warning.


Why do I want this?
-------------------
Expand Down
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
17 changes: 15 additions & 2 deletions session_csrf/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""CSRF protection without cookies."""
import functools
import hashlib
import warnings

from django.conf import settings
from django.core.cache import cache
Expand All @@ -12,6 +13,7 @@
ANON_COOKIE = getattr(settings, 'ANON_COOKIE', 'anoncsrf')
ANON_TIMEOUT = getattr(settings, 'ANON_TIMEOUT', 60 * 60 * 2) # 2 hours.
ANON_ALWAYS = getattr(settings, 'ANON_ALWAYS', False)
ANON_AS_LOGGED_IN = getattr(settings, 'ANON_AS_LOGGED_IN', False)
PREFIX = 'sessioncsrf:'


Expand All @@ -32,6 +34,7 @@ def prep_key(key):
prefixed = PREFIX + key
return hashlib.md5(prefixed).hexdigest()


class CsrfMiddleware(object):

# csrf_processing_done prevents checking CSRF more than once. That could
Expand All @@ -50,7 +53,7 @@ def process_request(self, request):
"""
if hasattr(request, 'csrf_token'):
return
if request.user.is_authenticated():
if request.user.is_authenticated() or ANON_AS_LOGGED_IN:
if 'csrf_token' not in request.session:
token = django_csrf._get_new_csrf_key()
request.csrf_token = request.session['csrf_token'] = token
Expand Down Expand Up @@ -81,7 +84,7 @@ def process_view(self, request, view_func, args, kwargs):
return

if (getattr(view_func, 'anonymous_csrf_exempt', False)
and not request.user.is_authenticated()):
and not (request.user.is_authenticated() or ANON_AS_LOGGED_IN)):
return

# Bail if this is a safe method.
Expand Down Expand Up @@ -123,6 +126,11 @@ def process_response(self, request, response):

def anonymous_csrf(f):
"""Decorator that assigns a CSRF token to an anonymous user."""
if ANON_AS_LOGGED_IN:
# this is pointless, we should warn the user
warnings.warn("You have set ANON_AS_LOGGED_IN to True, anontmous_csrf decorator will do nothing")
return f

@functools.wraps(f)
def wrapper(request, *args, **kw):
use_anon_cookie = not (request.user.is_authenticated() or ANON_ALWAYS)
Expand All @@ -147,6 +155,11 @@ def wrapper(request, *args, **kw):

def anonymous_csrf_exempt(f):
"""Like @csrf_exempt but only for anonymous requests."""
if ANON_AS_LOGGED_IN:
# this is pointless, we should warn the user
warnings.warn("You have set ANON_AS_LOGGED_IN to True, anontmous_csrf decorator will do nothing")
return f

f.anonymous_csrf_exempt = True
return f

Expand Down
Loading