Skip to content

Commit

Permalink
Merge pull request #14 from GomZik/master
Browse files Browse the repository at this point in the history
session.created property
  • Loading branch information
asvetlov committed Aug 11, 2015
2 parents d972c70 + 2de07f9 commit 70c998b
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 31 deletions.
37 changes: 32 additions & 5 deletions aiohttp_session/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import asyncio
from collections import MutableMapping
import json
import time

from aiohttp import web

Expand All @@ -20,13 +21,21 @@ def __init__(self, identity, *, data=None, new=False):
self._mapping = {}
self._identity = identity
self._new = new
if data is not None:
self._mapping.update(data)
created = data.get('created', None) if data else None
session_data = data.get('session', None) if data else None

if new or created is None:
self._created = int(time.time())
else:
self._created = created

if session_data is not None:
self._mapping.update(session_data)

def __repr__(self):
return '<{} [new:{}, changed:{}] {!r}>'.format(
return '<{} [new:{}, changed:{}, created:{}] {!r}>'.format(
self.__class__.__name__, self.new, self._changed,
self._mapping)
self.created, self._mapping)

@property
def new(self):
Expand All @@ -36,6 +45,14 @@ def new(self):
def identity(self):
return self._identity

@property
def created(self):
return self._created

@property
def empty(self):
return not bool(self._mapping)

def changed(self):
self._changed = True

Expand Down Expand Up @@ -143,6 +160,16 @@ def max_age(self):
def cookie_params(self):
return self._cookie_params

def _get_session_data(self, session):
if not session.empty:
data = {
'created': session.created,
'session': session._mapping
}
else:
data = {}
return data

@asyncio.coroutine
@abc.abstractmethod
def load_session(self, request):
Expand Down Expand Up @@ -188,5 +215,5 @@ def load_session(self, request):

@asyncio.coroutine
def save_session(self, request, response, session):
cookie_data = json.dumps(session._mapping)
cookie_data = json.dumps(self._get_session_data(session))
self.save_cookie(response, cookie_data)
6 changes: 4 additions & 2 deletions aiohttp_session/cookie_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ def load_session(self, request):

@asyncio.coroutine
def save_session(self, request, response, session):
if not session._mapping:
if session.empty:
return self.save_cookie(response, session._mapping)
cookie_data = json.dumps(session._mapping).encode('utf-8')

cookie_data = json.dumps(self._get_session_data(session)).encode(
'utf-8')
if len(cookie_data) % AES.block_size != 0:
# padding with spaces to full blocks
to_pad = AES.block_size - (len(cookie_data) % AES.block_size)
Expand Down
3 changes: 2 additions & 1 deletion aiohttp_session/redis_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ def save_session(self, request, response, session):
else:
key = str(key)
self.save_cookie(response, key)
data = self._encoder(session._mapping)

data = self._encoder(self._get_session_data(session))
with (yield from self._redis) as conn:
max_age = self.max_age
expire = max_age if max_age is not None else 0
Expand Down
25 changes: 22 additions & 3 deletions tests/test_cookie_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json
import socket
import unittest
import time

from aiohttp import web, request
from aiohttp_session import (Session, session_middleware,
Expand Down Expand Up @@ -45,7 +46,15 @@ def create_server(self, method, path, handler=None):
return app, srv, url

def make_cookie(self, data):
value = json.dumps(data)
if data:
session_data = {
'session': data,
'created': int(time.time())
}
else:
session_data = data

value = json.dumps(session_data)
return {'AIOHTTP_SESSION': value}

def test_create_new_sesssion(self):
Expand Down Expand Up @@ -75,6 +84,7 @@ def handler(request):
self.assertIsInstance(session, Session)
self.assertFalse(session.new)
self.assertFalse(session._changed)
self.assertIsNotNone(session.created)
self.assertEqual({'a': 1, 'b': 2}, session)
return web.Response(body=b'OK')

Expand All @@ -100,13 +110,22 @@ def handler(request):
@asyncio.coroutine
def go():
_, _, url = yield from self.create_server('GET', '/', handler)
cookies = self.make_cookie({'a': 1, 'b': 2})
resp = yield from request(
'GET', url,
cookies=self.make_cookie({'a': 1, 'b': 2}),
cookies=cookies,
loop=self.loop)
self.assertEqual(200, resp.status)
morsel = resp.cookies['AIOHTTP_SESSION']
self.assertEqual({'a': 1, 'b': 2, 'c': 3}, eval(morsel.value))
cookie_data = json.loads(morsel.value)
self.assertIn('session', cookie_data)
self.assertIn('a', cookie_data['session'])
self.assertIn('b', cookie_data['session'])
self.assertIn('c', cookie_data['session'])
self.assertIn('created', cookie_data)
self.assertEqual(cookie_data['session']['a'], 1)
self.assertEqual(cookie_data['session']['b'], 2)
self.assertEqual(cookie_data['session']['c'], 3)
self.assertTrue(morsel['httponly'])
self.assertEqual('/', morsel['path'])

Expand Down
24 changes: 19 additions & 5 deletions tests/test_encrypted_cookie_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import socket
import unittest
import base64
import time

from Crypto.Cipher import AES
from Crypto import Random
Expand Down Expand Up @@ -53,7 +54,15 @@ def create_server(self, method, path, handler=None):
return app, srv, url

def make_cookie(self, data):
cookie_data = json.dumps(data).encode('utf-8')
if data:
session_data = {
'session': data,
'created': int(time.time())
}
else:
session_data = data

cookie_data = json.dumps(session_data).encode('utf-8')
if len(cookie_data) % AES.block_size != 0:
# padding with spaces to full blocks
to_pad = AES.block_size - (len(cookie_data) % AES.block_size)
Expand Down Expand Up @@ -136,10 +145,15 @@ def go():
loop=self.loop)
self.assertEqual(200, resp.status)
morsel = resp.cookies['AIOHTTP_SESSION']
self.assertEqual(
{'a': 1, 'b': 2, 'c': 3},
self.decrypt(morsel.value)
)
cookie_data = self.decrypt(morsel.value)
self.assertIn('session', cookie_data)
self.assertIn('a', cookie_data['session'])
self.assertIn('b', cookie_data['session'])
self.assertIn('c', cookie_data['session'])
self.assertIn('created', cookie_data)
self.assertEqual(cookie_data['session']['a'], 1)
self.assertEqual(cookie_data['session']['b'], 2)
self.assertEqual(cookie_data['session']['c'], 3)
self.assertTrue(morsel['httponly'])
self.assertEqual('/', morsel['path'])

Expand Down
26 changes: 23 additions & 3 deletions tests/test_redis_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import unittest
import uuid
import aioredis
import time

from aiohttp import web, request
from aiohttp_session import Session, session_middleware, get_session
Expand Down Expand Up @@ -56,7 +57,14 @@ def create_server(self, method, path, handler=None, max_age=None):

@asyncio.coroutine
def make_cookie(self, data):
value = json.dumps(data)
if data:
session_data = {
'session': data,
'created': int(time.time())
}
else:
session_data = data
value = json.dumps(session_data)
key = uuid.uuid4().hex
with (yield from self.redis) as conn:
yield from conn.set('AIOHTTP_SESSION_' + key, value)
Expand Down Expand Up @@ -131,7 +139,14 @@ def go():
loop=self.loop)
self.assertEqual(200, resp.status)
value = yield from self.load_cookie(resp.cookies)
self.assertEqual({'a': 1, 'b': 2, 'c': 3}, value)
self.assertIn('session', value)
self.assertIn('a', value['session'])
self.assertIn('b', value['session'])
self.assertIn('c', value['session'])
self.assertIn('created', value)
self.assertEqual(value['session']['a'], 1)
self.assertEqual(value['session']['b'], 2)
self.assertEqual(value['session']['c'], 3)
morsel = resp.cookies['AIOHTTP_SESSION']
self.assertTrue(morsel['httponly'])
self.assertEqual('/', morsel['path'])
Expand Down Expand Up @@ -180,7 +195,12 @@ def go():
loop=self.loop)
self.assertEqual(200, resp.status)
value = yield from self.load_cookie(resp.cookies)
self.assertEqual({'a': 1, 'b': 2}, value)
self.assertIn('session', value)
self.assertIn('a', value['session'])
self.assertIn('b', value['session'])
self.assertIn('created', value)
self.assertEqual(value['session']['a'], 1)
self.assertEqual(value['session']['b'], 2)
morsel = resp.cookies['AIOHTTP_SESSION']
self.assertTrue(morsel['httponly'])
self.assertEqual(morsel['path'], '/')
Expand Down
55 changes: 43 additions & 12 deletions tests/test_session_dict.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import unittest
import time

from aiohttp_session import Session

Expand All @@ -11,52 +12,74 @@ def test_create(self):
self.assertTrue(s.new)
self.assertEqual('test_identity', s.identity)
self.assertFalse(s._changed)
self.assertIsNotNone(s.created)

def test_create2(self):
s = Session('test_identity', data={'some': 'data'})
s = Session('test_identity', data={'session': {'some': 'data'}})
self.assertEqual(s, {'some': 'data'})
self.assertFalse(s.new)
self.assertEqual('test_identity', s.identity)
self.assertFalse(s._changed)
self.assertIsNotNone(s.created)

def test_create3(self):
s = Session(identity=1, new=True)
self.assertEqual(s, {})
self.assertTrue(s.new)
self.assertEqual(s.identity, 1)
self.assertFalse(s._changed)
self.assertIsNotNone(s.created)

def test__repr__(self):
s = Session('test_identity', new=True)
self.assertEqual(str(s), '<Session [new:True, changed:False] {}>')
self.assertEqual(
str(s),
'<Session [new:True, changed:False, created:{0}] {{}}>'.format(
s.created))
s['foo'] = 'bar'
self.assertEqual(str(s),
"<Session [new:True, changed:True] {'foo': 'bar'}>")
self.assertEqual(
str(s),
"<Session [new:True, changed:True, created:{0}]"
" {{'foo': 'bar'}}>".format(s.created))

def test__repr__2(self):
s = Session('test_identity', data={'key': 123}, new=False)
self.assertEqual(str(s),
"<Session [new:False, changed:False] {'key': 123}>")
created = int(time.time()) - 1000
session_data = {
'session': {
'key': 123
},
'created': created
}
s = Session('test_identity', data=session_data, new=False)
self.assertEqual(
str(s),
"<Session [new:False, changed:False, created:{0}]"
" {{'key': 123}}>".format(created))
s.invalidate()
self.assertEqual(str(s), "<Session [new:False, changed:True] {}>")
self.assertEqual(
str(s),
"<Session [new:False, changed:True, created:{0}] {{}}>".format(
created))

def test_invalidate(self):
s = Session('test_identity', data={'foo': 'bar'})
s = Session('test_identity', data={'session': {'foo': 'bar'}})
self.assertEqual(s, {'foo': 'bar'})
self.assertFalse(s._changed)

s.invalidate()
self.assertEqual(s, {})
self.assertTrue(s._changed)
self.assertIsNotNone(s.created)

def test_invalidate2(self):
s = Session('test_identity', data={'foo': 'bar'})
s = Session('test_identity', data={'session': {'foo': 'bar'}})
self.assertEqual(s, {'foo': 'bar'})
self.assertFalse(s._changed)

s.invalidate()
self.assertEqual(s, {})
self.assertTrue(s._changed)
self.assertIsNotNone(s.created)

def test_operations(self):
s = Session('test_identity')
Expand All @@ -66,7 +89,7 @@ def test_operations(self):
self.assertNotIn('foo', s)
self.assertNotIn('key', s)

s = Session('test_identity', data={'foo': 'bar'})
s = Session('test_identity', data={'session': {'foo': 'bar'}})
self.assertEqual(len(s), 1)
self.assertEqual(s, {'foo': 'bar'})
self.assertEqual(list(s), ['foo'])
Expand Down Expand Up @@ -95,17 +118,25 @@ def test_operations(self):
self.assertNotIn('key', s)

def test_change(self):
s = Session('test_identity', new=False, data={'a': {'key': 'value'}})
created = int(time.time())
s = Session('test_identity', new=False, data={
'session': {
'a': {'key': 'value'}
},
'created': created
})
self.assertFalse(s._changed)

s['a']['key2'] = 'val2'
self.assertFalse(s._changed)
self.assertEqual({'a': {'key': 'value',
'key2': 'val2'}},
s)
self.assertEqual(s.created, created)

s.changed()
self.assertTrue(s._changed)
self.assertEqual({'a': {'key': 'value',
'key2': 'val2'}},
s)
self.assertEqual(s.created, created)

0 comments on commit 70c998b

Please sign in to comment.