Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
115 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,3 +8,4 @@ pycassa>=1.6.0 | |
pymongo>=2.1 | ||
redis>=2.4.12 | ||
psycopg2>=2.4.5 | ||
pycrypto>=2.6.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import unittest | ||
import urllib | ||
from toto.session import TotoSession | ||
from toto.clientsessioncache import ClientCache, AESCipher | ||
from Crypto.Cipher import AES | ||
from time import time | ||
|
||
class TestClientCache(unittest.TestCase): | ||
|
||
def test_session_storage(self): | ||
cache = ClientCache(AESCipher('12345678901234561234567890123456')) | ||
user_id = 'test@toto.li' | ||
expires = time() + 1000.0 | ||
session_id = TotoSession.generate_id() | ||
session_data = {'session_id': session_id, 'expires': expires, 'user_id': user_id} | ||
session = TotoSession(None, session_data) | ||
session.session_id = session_id | ||
self.assertEqual(session.session_id, session_id) | ||
self.assertEqual(session.user_id, user_id) | ||
self.assertEqual(session.expires, expires) | ||
session['int'] = 1268935 | ||
session['float'] = 92385.03 | ||
session['str'] = 'some test' | ||
session_data = session.session_data() | ||
session_id = cache.store_session(session_data) | ||
url_safe = urllib.quote_plus(session_id) | ||
self.assertEqual(session_id, url_safe) | ||
new_session_data = cache.load_session(session_id) | ||
new_session = TotoSession(None, new_session_data) | ||
del session_data['session_id'] | ||
del new_session_data['session_id'] | ||
self.assertEquals(new_session_data, session_data) | ||
self.assertEqual(new_session.session_id, session_id) | ||
self.assertEqual(new_session['int'], 1268935) | ||
self.assertEqual(new_session['float'], 92385.03) | ||
self.assertEqual(new_session['str'], 'some test') | ||
self.assertEqual(new_session.user_id, user_id) | ||
self.assertEqual(new_session.expires, expires) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
from toto.session import * | ||
from copy import copy | ||
from base64 import urlsafe_b64encode, urlsafe_b64decode | ||
|
||
class AESCipher(object): | ||
'''A convenient cipher implementation for AES encryption and decryption. | ||
Create a new ``AESCipher`` with the given ``key`` and ``iv`` that wraps | ||
``Crypto.AES`` but is reusable and thread safe. For convenience, both | ||
the ``key`` and ``iv`` may be provided as one string, in which case the | ||
last ``AES.block_size`` (16) bytes will be used for ``iv``. | ||
''' | ||
|
||
def __init__(self, key, iv=None): | ||
from Crypto.Cipher import AES | ||
self.block_size = AES.block_size | ||
if not iv: | ||
iv = key[-self.block_size:] | ||
key = key[:-self.block_size] | ||
self.aes = lambda:AES.new(key, AES.MODE_CBC, iv) | ||
|
||
def encrypt(self, data): | ||
diff = self.block_size - (len(data) % self.block_size) | ||
return self.aes().encrypt(data + chr(diff) * diff) | ||
|
||
def decrypt(self, data): | ||
decrypted = self.aes().decrypt(data) | ||
return decrypted[:-ord(decrypted[-1])] | ||
|
||
class ClientCache(TotoSessionCache): | ||
'''A ``TotoSessionCache`` implementation that stores all session data with the | ||
client. Depending on use, the session may be sent as a header or cookie. | ||
``ClientCache`` works by storing the encrypted session state in | ||
the session ID and decrypting it on each request. When using this method, | ||
it is important to keep session state small as it can add significant | ||
overhead to each request. | ||
``cipher`` will be used to encrypt and decrypt the session data. It should | ||
be identical between all servers in a deployment to allow proper request | ||
balancing. ``cipher`` is expected to implement ``encrypt(data)`` and | ||
the reverse ``decrypt(data)`` both accepting and returning ``str`` objects. | ||
''' | ||
|
||
def __init__(self, cipher): | ||
self.cipher = cipher | ||
|
||
def store_session(self, session_data): | ||
persisted_data = copy(session_data) | ||
del persisted_data['session_id'] | ||
return urlsafe_b64encode(self.cipher.encrypt(TotoSession.dumps(session_data))) | ||
|
||
def load_session(self, session_id): | ||
session_data = TotoSession.loads(self.cipher.decrypt(urlsafe_b64decode(session_id))) | ||
session_data['session_id'] = session_id | ||
return session_data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters