Skip to content

Add optional support for sessions without client cookies #86

Open
wants to merge 10 commits into from
View
70 web/session.py
@@ -5,6 +5,8 @@
import os, time, datetime, random, base64
import os.path
+from cStringIO import StringIO
+from urlparse import urlsplit, urlunsplit
try:
import cPickle as pickle
except ImportError:
@@ -33,7 +35,8 @@
'secret_key': 'fLjUfxqXtfNoIldA0A0J',
'expired_message': 'Session expired',
'httponly': True,
- 'secure': False
+ 'secure': False,
+ 'cookieless': False,
})
class SessionExpired(web.HTTPError):
@@ -83,17 +86,25 @@ def _processor(self, handler):
self._load()
try:
- return handler()
+ response = handler()
finally:
self._save()
+ if self._config.cookieless:
+ response = self._add_session(response)
+
+ return response
+
def _load(self):
"""Load the session from the store, by the id from cookie"""
cookie_name = self._config.cookie_name
cookie_domain = self._config.cookie_domain
httponly = self._config.httponly
self.session_id = web.cookies().get(cookie_name)
+ if self._config.cookieless and not self.session_id:
+ self.session_id = web.input().get(cookie_name)
+
# protection against session_id tampering
if self.session_id and not self._valid_session_id(self.session_id):
self.session_id = None
@@ -135,6 +146,61 @@ def _save(self):
self.store[self.session_id] = dict(self._data)
else:
self._setcookie(self.session_id, expires=-1)
+
+ def _is_relative(self, url):
+ split = urlsplit(url)
+ return split[0] == '' and split[1] == ''
+
+ def _add_session(self, response):
+ cookie_name = self._config.cookie_name
+
+ # Only process response if client didn't provide a session cookie
+ if web.cookies().get(cookie_name):
+ return response
+
+ from lxml import html, etree
+ types = {
+ 'application/xhtml+xml': etree,
+ 'application/xml': etree,
+ 'text/html': html,
+ }
+
+ content_type = None
+ for header, value in web.ctx.get('headers', []):
+ if header.lower() == 'content-type':
+ content_type = value.split(';')[0].lower()
+ content_type_params = dict((k.lower().strip(), v) for k,v in (part.split('=', 1) for part in value.split(';')[1:]))
+
+ if content_type not in types:
+ return response
+
+ tree = types[content_type]
+ doc = tree.parse(StringIO(str(response)))
+ tostring_kwargs = {'encoding': content_type_params.get('charset', 'utf-8')}
+ if None in doc.getroot().nsmap:
+ ns = '{%s}' % doc.getroot().nsmap[None]
+ tostring_kwargs['xml_declaration'] = True
+ else:
+ ns = ''
+
+ # Add hidden input fields to forms
+ for form in doc.iterfind('.//%sform' % ns):
+ if 'action' not in form.attrib or self._is_relative(form.attrib['action']):
+ input = etree.Element('input', type='hidden', name=cookie_name, id=cookie_name, value=self.session_id)
+ form.append(input)
+
+ # Add query parameters to relative links
+ param = cookie_name + '=' + self.session_id
+ for a in doc.iterfind('.//%sa' % ns):
+ if 'href' in a.attrib and self._is_relative(a.attrib['href']):
+ parts = list(urlsplit(a.attrib['href']))
+ if len(parts[3]) == 0:
+ parts[3] = param
+ else:
+ parts[3] += '&' + param
+ a.attrib['href'] = urlunsplit(parts)
+
+ return tree.tostring(doc, **tostring_kwargs)
def _setcookie(self, session_id, expires='', **kw):
cookie_name = self._config.cookie_name
Something went wrong with that request. Please try again.