Skip to content

Commit

Permalink
Implement new_session functionality (#282)
Browse files Browse the repository at this point in the history
  • Loading branch information
panagiks authored and asvetlov committed May 12, 2018
1 parent 0f921a1 commit 61c59b5
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 3 deletions.
19 changes: 19 additions & 0 deletions aiohttp_session/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,22 @@ async def get_session(request):
return session


async def new_session(request):
storage = request.get(STORAGE_KEY)
if storage is None:
raise RuntimeError(
"Install aiohttp_session middleware "
"in your aiohttp.web.Application")
else:
session = await storage.new_session()
if not isinstance(session, Session):
raise RuntimeError(
"Installed {!r} storage should return session instance "
"on .load_session() call, got {!r}.".format(storage, session))
request[SESSION_KEY] = session
return session


def session_middleware(storage):

if not isinstance(storage, AbstractStorage):
Expand Down Expand Up @@ -198,6 +214,9 @@ def _get_session_data(self, session):
data = {}
return data

async def new_session(self):
return Session(None, data=None, new=True, max_age=self.max_age)

@abc.abstractmethod
async def load_session(self, request):
pass
Expand Down
6 changes: 4 additions & 2 deletions demo/login_required_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from cryptography import fernet
from aiohttp import web
from aiohttp_session import setup, get_session
from aiohttp_session import setup, get_session, new_session
from aiohttp_session.cookie_storage import EncryptedCookieStorage


Expand Down Expand Up @@ -61,7 +61,9 @@ async def login(request):
# actually implement business logic to check user credentials
try:
user_id = DATABASE.index(user_signature)
session = await get_session(request)
# Always use `new_session` during login to guard against
# Session Fixation. See aiohttp-session#281
session = await new_session(request)
session['user_id'] = user_id
return web.HTTPFound(router['restricted'].url_for())
except ValueError:
Expand Down
25 changes: 25 additions & 0 deletions docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@ Public functions
See example below in :ref:`Session<aiohttp-session-session>`
section for :func:`get_session` usage.

.. function:: new_session(request)

A :ref:`coroutine<coroutine>` for getting a new session regardless
of whether a cookie exists.

.. warning::

Always use :func:`new_session` instead of :func:`get_session`
in your login views to guard against Session Fixation attacks!

Example usage::

from aiohttp_session import new_session

async def handler(request):
session = await new_session(request)
session.new == True # This will always be True

.. function:: session_middleware(storage)

Session middleware factory.
Expand Down Expand Up @@ -214,6 +232,13 @@ implement both :meth:`~AbstractStorage.load_session` and

.. versionadded:: 2.3

.. method:: new_session()

A :ref:`coroutine<coroutine>` for getting a new session regardless
of whether a cookie exists.

Return :class:`Session` instance.

.. method:: load_session(request)

An *abstract* :ref:`coroutine<coroutine>`, called by internal
Expand Down
49 changes: 48 additions & 1 deletion tests/test_get_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

from aiohttp.test_utils import make_mocked_request

from aiohttp_session import Session, get_session, SESSION_KEY, STORAGE_KEY
from aiohttp_session import (Session, get_session, SESSION_KEY, STORAGE_KEY,
new_session, AbstractStorage)


async def test_get_stored_session():
Expand Down Expand Up @@ -32,3 +33,49 @@ async def load_session(self, request):

with pytest.raises(RuntimeError):
await get_session(req)


async def test_get_new_session():
req = make_mocked_request('GET', '/')
session = Session('identity', data=None, new=False)

class Storage(AbstractStorage):
async def load_session(self, request):
pass

async def save_session(self, request, response, session):
pass

req[SESSION_KEY] = session
req[STORAGE_KEY] = Storage()

ret = await new_session(req)
assert ret is not session


async def test_get_new_session_no_storage():
req = make_mocked_request('GET', '/')
session = Session('identity', data=None, new=False)
req[SESSION_KEY] = session

with pytest.raises(RuntimeError):
await new_session(req)


async def test_get_new_session_bad_return():
req = make_mocked_request('GET', '/')

class Storage(AbstractStorage):
async def new_session(self):
return ''

async def load_session(self, request):
pass

async def save_session(self, request, response, session):
pass

req[STORAGE_KEY] = Storage()

with pytest.raises(RuntimeError):
await new_session(req)

0 comments on commit 61c59b5

Please sign in to comment.