Hi My application needs to work with user agents which don't support cookies, and I also require sessions. The current session implementation can only use cookies to store the session ID.
The attached patch adds support for sessions without using cookies. It does this by adding a hidden input field to forms and a query parameter to links containing the session ID, and then checking web.input() for the ID. This is disabled by default with the config option 'cookieless', and requires BeautifulSoup[1] to modify the output. (I'm happy to replace BeautifulSoup with another parser if necessary.) Michael [1] http://www.crummy.com/software/BeautifulSoup/ -- http://michael.gorven.za.net PGP Key ID 6612FE85 S/MIME Key ID 91E03AF1
diff --git a/web/session.py b/web/session.py
index 996b58f..2d02b3f 100644
--- a/web/session.py
+++ b/web/session.py
@@ -3,7 +3,7 @@ Session Management
(from web.py)
"""
-import os, time, datetime, random, base64
+import os, time, datetime, random, base64, sys
try:
import cPickle as pickle
except ImportError:
@@ -15,6 +15,12 @@ except ImportError:
import sha
sha1 = sha.new
+try:
+ from BeautifulSoup import BeautifulSoup, Tag
+ from urlparse import urlsplit, urlunsplit
+except ImportError:
+ pass
+
import utils
import webapi as web
@@ -31,6 +37,7 @@ web.config.session_parameters = utils.storage({
'ignore_change_ip': True,
'secret_key': 'fLjUfxqXtfNoIldA0A0J',
'expired_message': 'Session expired',
+ 'cookieless': False,
})
class SessionExpired(web.HTTPError):
@@ -56,15 +63,23 @@ class Session(utils.ThreadedDict):
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
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)
self._check_expiry()
if self.session_id:
@@ -105,6 +120,36 @@ class Session(utils.ThreadedDict):
self.store[self.session_id] = dict(self)
else:
web.setcookie(cookie_name, self.session_id, expires=-1, domain=cookie_domain)
+
+ def _is_relative(self, url):
+ if url is None: return True
+ 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 not web.cookies().get(cookie_name) and 'BeautifulSoup' in sys.modules:
+ soup = BeautifulSoup(str(response))
+
+ # Add hidden input fields to forms
+ for form in soup.findAll('form', action=lambda(x): self._is_relative(x)):
+ input = Tag(soup, "input", [("type", "hidden"), ("name", cookie_name), ("id", cookie_name), ("value", self.session_id)])
+ form.insert(0, input)
+
+ # Add query parameters to relative links
+ param = cookie_name + '=' + self.session_id
+ for a in soup.findAll('a', href=lambda(x): self._is_relative(x)):
+ parts = list(urlsplit(a['href']))
+ if len(parts[3]) == 0:
+ parts[3] = param
+ else:
+ parts[3] += '&' + param
+ a['href'] = urlunsplit(parts)
+
+ return str(soup)
+ return response
def _generate_session_id(self):
"""Generate a random id for session"""
signature.asc
Description: This is a digitally signed message part.
