store sessions in cookies, needs logic in main, thanks Nick Groenke

This commit is contained in:
Massimo Di Pierro
2012-06-10 01:18:30 -05:00
parent 6e136a6b47
commit dc5d075bc8
3 changed files with 559 additions and 7 deletions
+56 -6
View File
@@ -36,6 +36,13 @@ import os
import sys
import traceback
import threading
import hmac
import base64
try:
from Crypto.Cipher import AES
except ImportError:
from contrib import aes as AES
try:
from gluon.contrib.minify import minify
@@ -409,6 +416,7 @@ class Session(Storage):
migrate=True,
separate = None,
check_client=False,
cookie_key=None,
):
"""
separate can be separate=lambda(session_name): session_name[-2:]
@@ -421,9 +429,30 @@ class Session(Storage):
if not masterapp:
masterapp = request.application
response.session_id_name = 'session_id_%s' % masterapp.lower()
# Load session data from cookie
if cookie_key:
response.session_cookie_key = cookie_key
response.session_cookie_key2 = hashlib.md5(cookie_key).digest()
cookie_name = request.application.lower()+'_session_data'
response.session_cookie_name = cookie_name
if cookie_data in request.cookies:
cookie_value = request.cookies[cookie_name].value
cookie_parts = cookie_value.split(":")
enc = cookie_parts[2]
cipher = AES.new(cookie_key)
decrypted = cipher.decrypt(base64.b64decode(enc)).rstrip('{')
check = hmac.new(response.session_cookie_key2,enc).hexdigest()
if cookie_parts[0] == check:
session_data = cPickle.loads(decrypted)
self.update(session_data)
else:
return
if not db:
if global_settings.db_sessions is True or masterapp in global_settings.db_sessions:
if global_settings.db_sessions is True \
or masterapp in global_settings.db_sessions:
return
response.session_new = False
client = request.client and request.client.replace(':', '.')
@@ -441,11 +470,13 @@ class Session(Storage):
response.session_file = \
open(response.session_filename, 'rb+')
try:
portalocker.lock(response.session_file,portalocker.LOCK_EX)
portalocker.lock(response.session_file,
portalocker.LOCK_EX)
response.session_locked = True
self.update(cPickle.load(response.session_file))
response.session_file.seek(0)
oc = response.session_filename.split('/')[-1].split('-')[0]
oc = response.session_filename.split('/')[-1]\
.split('-')[0]
if check_client and client!=oc:
raise Exception, "cookie attack"
finally:
@@ -459,7 +490,8 @@ class Session(Storage):
response.session_id = '%s-%s' % (client, uuid)
if separate:
prefix = separate(response.session_id)
response.session_id = '%s/%s' % (prefix,response.session_id)
response.session_id = '%s/%s' % \
(prefix,response.session_id)
response.session_filename = \
os.path.join(up(request.folder), masterapp,
'sessions', response.session_id)
@@ -492,16 +524,25 @@ class Session(Storage):
migrate=table_migrate,
)
try:
# Get session data out of the database
# Key comes from the cookie
key = request.cookies[response.session_id_name].value
(record_id, unique_key) = key.split(':')
if record_id == '0':
raise Exception, 'record_id == 0'
# Select from database.
rows = db(table.id == record_id).select()
# Make sure the session data exists in the database
if len(rows) == 0 or rows[0].unique_key != unique_key:
raise Exception, 'No record'
# rows[0].update_record(locked=True)
# Unpickle the data
session_data = cPickle.loads(rows[0].session_data)
self.update(session_data)
except Exception:
@@ -540,8 +581,18 @@ class Session(Storage):
self._close(response)
self._forget = True
def _try_store_in_db(self, request, response):
def _try_store_in_cookie(self, request, response):
pad = lambda s: s + (32 - len(s) % 32) * '{'
data = cPickle.dumps(dict(self))
cipher = AES.new(response.session_cookie_key)
encrypted_data = base64.b64encode(cipher.encrypt(pad(data)))
signature = hmac.new(response.session_cookie_key2,encrypted_data)\
.hexdigest()
value = signature+':'+encrypted_data
response.cookies[response.session_cookie_name] = value
response.cookies[response.session_cookie_name]['path'] = '/'
def _try_store_in_db(self, request, response):
# don't save if file-based sessions, no session id, or session being forgotten
if not response.session_db or not response.session_id or self._forget:
return
@@ -618,4 +669,3 @@ class Session(Storage):