Skip to content
Browse files

Add template tags and key_tuple for manual usage

  • Loading branch information...
1 parent a44beb7 commit a2c81566835c7643dfa49ebb7344661cdd97e35b @edcrewe committed Nov 4, 2012
View
61 cookieless/middleware.py
@@ -1,15 +1,14 @@
#-*- coding:utf-8 -*-import time
import re, pdb, time
-from urlparse import urlparse
from django.conf import settings
from django.utils.cache import patch_vary_headers
from django.utils.http import cookie_date
from django.utils.importlib import import_module
from django.http import HttpResponseRedirect
from django.contrib.sessions.middleware import SessionMiddleware
# Obscure the session id when passing it around in HTML
-from cookieless.xteacrypt import crypt
+from cookieless.utils import CryptSession
LINKS_RE = r'<a(?P<pre_href>[^>]*?)href=["\'](?P<in_href>[^"\']*?)(?P<anchor>#\S+)?["\'](?P<post_href>[^>]*?)>'
@@ -32,9 +31,8 @@ def __init__(self):
"""
self._re_links = re.compile(LINKS_RE, re.I)
self._re_forms = re.compile('</form>', re.I)
-
+ self._sesh = CryptSession()
self.standard_session = SessionMiddleware()
- self.secret = settings.SECRET_KEY[:16]
def process_request(self, request):
""" Check if we have the session key from a cookie,
@@ -43,10 +41,10 @@ def process_request(self, request):
name = settings.SESSION_COOKIE_NAME
session_key = request.COOKIES.get(name, '')
if not session_key:
- session_key = self._decrypt(request,
+ session_key = self._sesh.decrypt(request,
request.POST.get(name, None))
if not session_key and getattr(settings, 'COOKIELESS_USE_GET', False):
- session_key = self._decrypt(request,
+ session_key = self._sesh.decrypt(request,
request.GET.get(name, ''))
if session_key:
request.COOKIES[name] = session_key
@@ -85,63 +83,18 @@ def process_response(self, request, response):
else:
return self.standard_session.process_response(request, response)
- def _prepare_url(self, url):
- patt = None
- if url.find('?') == -1:
- patt = '%s?'
- else:
- patt = '%s&amp;'
- return patt % (url,)
-
- def _encrypt(self, request, sessionid):
- """ Avoid showing plain sessionids """
- if not sessionid:
- return ''
- secret = self._secret(request)
- return crypt(secret, sessionid).encode('hex')
-
- def _decrypt(self, request, sessionid):
- """ Avoid showing plain sessionids
- Optionally require that a referer exists and matches the
- whitelist, or reset the session
- """
- if not sessionid:
- return ''
- secret = self._secret(request)
- if getattr(settings, 'COOKIELESS_HOSTS', []):
- referer = request.META.get('HTTP_REFERER', 'None')
- if referer == 'None':
- # End session unless a referer is passed
- return ''
- url = urlparse(referer)
- if url.hostname not in settings.COOKIELESS_HOSTS:
- err = '%s is unauthorised' % url.hostname
- raise Exception(err)
- return crypt(secret, sessionid.decode('hex'))
-
- def _secret(self, request):
- """ optionally make secret client dependent
- """
- if getattr(settings, 'COOKIELESS_CLIENT_ID', False):
- ip = request.META['REMOTE_ADDR']
- agent = request.META['HTTP_USER_AGENT']
- secret = crypt(self.secret, agent + ip)[:16]
- return secret
- else:
- return self.secret
-
def nocookies_response(self, request, response):
""" Option to rewrite forms and urls to add session automatically """
name = settings.SESSION_COOKIE_NAME
session_key = ''
if request.session.session_key and not request.path.startswith("/admin"):
- session_key = self._encrypt(request, request.session.session_key)
+ session_key = self._sesh.encrypt(request, request.session.session_key)
if type(response) is HttpResponseRedirect:
if not session_key:
session_key = ""
redirect_url = [x[1] for x in response.items() if x[0] == "Location"][0]
- redirect_url = self._prepare_url(redirect_url)
+ redirect_url = self._sesh.prepare_url(redirect_url)
return HttpResponseRedirect('%s%s=%s' % (redirect_url, name,
session_key))
@@ -152,7 +105,7 @@ def new_url(m):
anchor_value = m.groupdict().get("anchor")
return_str = '<a%shref="%s%s=%s%s"%s>' % (
m.groupdict()['pre_href'],
- self._prepare_url(m.groupdict()['in_href']),
+ self._sesh.prepare_url(m.groupdict()['in_href']),
name,
session_key,
anchor_value,
View
0 cookieless/templatetags/__init__.py
No changes.
View
76 cookieless/templatetags/cookieless_tags.py
@@ -0,0 +1,76 @@
+"""Add {% load cookieless %} after base to use these in a template
+
+NB: settings: Need to add django.core.context_processors.request
+if using manual tags so its available for templatetags/cookieless
+import django.conf.global_settings as DEFAULT_SETTINGS
+TEMPLATE_CONTEXT_PROCESSORS = DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS + ('django.core.context_processors.request', )
+"""
+
+from django.conf import settings
+from django import template
+from django.template.defaultfilters import stringfilter, striptags
+from django.utils.safestring import mark_safe
+from django.utils.html import escape
+
+from cookieless.utils import CryptSession
+
+register = template.Library()
+
+class BaseSessionNode(template.Node):
+
+ def __init__(self,):
+ self.request_var = template.Variable('request')
+ self._sesh = CryptSession()
+
+ def get_key(self, context):
+ request = self.request_var.resolve(context)
+ if request.session.session_key:
+ return self._sesh.encrypt(request,
+ request.session.session_key)
+ else:
+ return ''
+
+class FormSessionNode(BaseSessionNode):
+
+ def render(self, context):
+ session_key = self.get_key(context)
+ if session_key:
+ html = '<input type="hidden" name="%s" value="%s" />'
+ return mark_safe(html % (settings.SESSION_COOKIE_NAME,
+ session_key))
+ else:
+ return ''
+
+def session_form(parser, token):
+ return FormSessionNode()
+
+register.tag('session_token', session_form)
+
+class URLSessionNode(BaseSessionNode):
+
+ def __init__(self, url):
+ super(URLSessionNode, self).__init__()
+ self.url = self._sesh.prepare_url(url.replace('"',''))
+
+ def render(self, context):
+ session_key = self.get_key(context)
+ if session_key:
+ html = '"%s%s=%s"'
+ return mark_safe(html % (self.url,
+ settings.SESSION_COOKIE_NAME,
+ session_key))
+ else:
+ return ''
+
+def session_filter(parser, token):
+ try:
+ taglist = token.split_contents() # Not really useful
+ except ValueError:
+ raise template.TemplateSyntaxError("%r error" % token.contents.split()[0])
+ if len(taglist) > 1:
+ url = taglist[1]
+ return URLSessionNode(url)
+
+register.tag('session_url', session_filter)
+
+
View
12 cookieless/tests/settings.py
@@ -1,18 +1,23 @@
# Django settings for cookieless_test project.
+##### django-cookieless #####
# Rewrite URLs to add session id for no_cookies decorated views
# (if False then all page navigation must be via form posts)
COOKIELESS_USE_GET = True
# Rewriting the response automatically rather than use manual <% session_token %> <% session_url %>
-COOKIELESS_REWRITE = True
+COOKIELESS_REWRITE = False
+# NB: Need to add django.core.context_processors.request if using manual tags
+# so its available for templatetags/cookieless
+if not COOKIELESS_REWRITE:
+ import django.conf.global_settings as DEFAULT_SETTINGS
+ TEMPLATE_CONTEXT_PROCESSORS = DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS + ('django.core.context_processors.request', )
# Use client ip and browser to encode session key, to add some CSRF protection without being able to use cookies.
COOKIELESS_CLIENT_ID = True
-
# If this list is populated then only hosts that are specifically whitelisted# are allowed to post to the server. So any domains that the site is served # over should be added to the list. This helps protect against XSS attacks.
-
COOKIELESS_HOSTS = ['localhost', ]
+
DEBUG = True
TEMPLATE_DEBUG = DEBUG
@@ -130,6 +135,7 @@
'django.contrib.messages',
'django.contrib.staticfiles',
'cookieless.tests',
+ 'cookieless',
# Uncomment the next line to enable the admin:
'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
View
4 cookieless/tests/templates/classview.html
@@ -1,3 +1,4 @@
+{% load cookieless_tags %}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
@@ -8,8 +9,9 @@
<h1>Class view</h1>
- <p><a href="/function-view.html">Function view</a></p>
+ <p><a href={% session_url "/function-view.html" %}>Function view</a></p>
<form method="post" action="/function-view.html">{% csrf_token %}
+ {% session_token %}
<input type="text" name="test" value="test form" />
<input type="submit" />
</form>
View
6 cookieless/tests/views.py
@@ -4,12 +4,14 @@
from django.http import HttpResponse
import datetime
from django.utils.html import mark_safe
+from cookieless.utils import CryptSession
def my_function_view(request):
- """ Test function view """
+ """ Test function view with manually constructed sesh url """
request.session['funcview'] = 'my_function_view'
html = "<html><body><h1>Function view</h1>"
- html += '<p><a href="/">Class view</a></p><hr />'
+ html += '<p><a href="/?%s=%s">Class view</a></p><hr />'
+ html = html % CryptSession().key_tuple(request)
html += session_data(request) + '</body></html>'
return HttpResponse(html)
View
61 cookieless/utils.py
@@ -0,0 +1,61 @@
+""" Obscure the session id when passing it around in HTML """
+from django.conf import settings
+from urlparse import urlparse
+from cookieless.xteacrypt import crypt
+
+class CryptSession(object):
+ """ Tool to generate encrypted session id for
+ middleware or templatetags
+ """
+ def __init__(self):
+ self.secret = settings.SECRET_KEY[:16]
+
+ def prepare_url(self, url):
+ patt = None
+ if url.find('?') == -1:
+ patt = '%s?'
+ else:
+ patt = '%s&amp;'
+ return patt % (url,)
+
+ def encrypt(self, request, sessionid):
+ """ Avoid showing plain sessionids """
+ if not sessionid:
+ return ''
+ secret = self._secret(request)
+ return crypt(secret, sessionid).encode('hex')
+
+ def decrypt(self, request, sessionid):
+ """ Avoid showing plain sessionids
+ Optionally require that a referer exists and matches the
+ whitelist, or reset the session
+ """
+ if not sessionid:
+ return ''
+ secret = self._secret(request)
+ if getattr(settings, 'COOKIELESS_HOSTS', []):
+ referer = request.META.get('HTTP_REFERER', 'None')
+ if referer == 'None':
+ # End session unless a referer is passed
+ return ''
+ url = urlparse(referer)
+ if url.hostname not in settings.COOKIELESS_HOSTS:
+ err = '%s is unauthorised' % url.hostname
+ raise Exception(err)
+ return crypt(secret, sessionid.decode('hex'))
+
+ def key_tuple(self, request):
+ """ For use in generated html """
+ return (settings.SESSION_COOKIE_NAME,
+ self.encrypt(request, request.session.session_key))
+
+ def _secret(self, request):
+ """ optionally make secret client dependent
+ """
+ if getattr(settings, 'COOKIELESS_CLIENT_ID', False):
+ ip = request.META['REMOTE_ADDR']
+ agent = request.META['HTTP_USER_AGENT']
+ secret = crypt(self.secret, agent + ip)[:16]
+ return secret
+ else:
+ return self.secret

0 comments on commit a2c8156

Please sign in to comment.
Something went wrong with that request. Please try again.