aio-libs / aiohttp-session Goto Github PK
View Code? Open in Web Editor NEWWeb sessions for aiohttp.web
Home Page: http://aiohttp-session.readthedocs.io/
License: Other
Web sessions for aiohttp.web
Home Page: http://aiohttp-session.readthedocs.io/
License: Other
I'm trying to build aiohttp and aiohttp-session under Yocto. It tries to run make clean
as part of the build process. make clean
runs python setup.py clean
. But that runs Python 2, and that fails because the build system doesn't have Python 2 with setuptools
installed.
Since aiohttp is Python 3 only, would it make more sense for the Makefile
to run python3 make clean
instead, to avoid such unusual Python 2 dependencies?
Here https://github.com/aio-libs/aiohttp_session/blob/master/aiohttp_session/redis_storage.py#L51
should be max_age=session.max_age
.
There is a window of opportunity for Session Fixation exploitation in the logic of RedisStorage.
As seen here: https://github.com/aio-libs/aiohttp-session/blob/master/aiohttp_session/__init__.py#L190
Get session data returns an empty dictionary for an empty (this includes invalidated) session.
Referring here: https://github.com/aio-libs/aiohttp-session/blob/master/aiohttp_session/redis_storage.py#L60
save_session takes this data and saves it in Redis.
As a result, an invalidated session will result to the session ID being present in Redis with an empty mapping as its value.
Now looking over at: https://github.com/aio-libs/aiohttp-session/blob/master/aiohttp_session/redis_storage.py#L50
RedisStorage's load_session only looks at the case where data (returned by reading from Redis) is None. This will happen only if the key (session ID) is not present in Redis (has either expired or was never inserted) but as we established above the key is never actually removed, just the value mapping emptied. As a result the load_session function will return a session with the presented session ID and not a new one, although there was no valid session in storage for this ID.
If this is not caught and mitigated by the web app the following scenario can unfold:
pip install crashed with strace:
(VENV) crwnlssmbp:aiohttp-session soullu$ python ./setup.py install
Traceback (most recent call last):
File "./setup.py", line 27, in
long_description='\n\n'.join((read('README.rst'), read('CHANGES.txt'))),
File "./setup.py", line 16, in read
return open(os.path.join(os.path.dirname(file), f)).read().strip()
FileNotFoundError: [Errno 2] No such file or directory: './CHANGES.txt'
Hi,
Could you add to docs a trivial example usage aiohttp-session
with aiohttp
and aioredis
?
Thanks.
Hello,
Tell me please, how I get session in test? For example, check last_visit is not None
from https://github.com/aio-libs/aiohttp-session/blob/master/demo/redis_storage.py ?
async def test_last_visit(client):
resp = await client.get('/')
assert resp.status == 200
# how get session?
Thanks
Actually the doc doesn't specify that they key inside Redis would be: AIOHTTP_SESSION_valueofthecookie
I'm using version 0.5.0 with python 3.6. I've followed the example code and am using the cookie storage. I keep getting the error AttributeError: 'Response' object has no attribute 'started' in aiohttp_session/__init__.py", line 138, in middleware
.
Here is my set up code.
app = web.Application()
aiohttp_jinja2.setup(
app, loader=jinja2.FileSystemLoader('MyApp/templates')
)
fernet_key = fernet.Fernet.generate_key()
secret_key = base64.urlsafe_b64decode(fernet_key)
setup(app, EncryptedCookieStorage(secret_key))
web.run_app(app, port=8000)
the error occurs only when I try to load any page registerd in the app.
Please never use underscores in python-packages names.
Python packages should also have short, all-lowercase names, although the use of underscores is discouraged.
We have a really problem. When our build system convert all dependencies to RPM (using FPM) yum is broken. We are can't use this package in our projects.
Hi, i have some error when add value to session.
ERROR:aiohttp.server:Error handling request
Traceback (most recent call last):
File "/home/ubuntu/chat/env/lib/python3.6/site-packages/aiohttp/web_protocol.py", line 410, in start
resp = yield from self._request_handler(request)
File "/home/ubuntu/chat/env/lib/python3.6/site-packages/aiohttp/web.py", line 326, in _handle
resp = yield from handler(request)
File "/home/ubuntu/chat/env/lib/python3.6/site-packages/aiohttp/web_middlewares.py", line 93, in impl
return (yield from handler(request))
File "/home/ubuntu/chat/env/lib/python3.6/site-packages/aiohttp_session/__init__.py", line 144, in middleware
yield from storage.save_session(request, response, session)
File "/home/ubuntu/chat/env/lib/python3.6/site-packages/aiohttp_session/redis_storage.py", line 60, in save_session
with (yield from self._redis) as conn:
TypeError: 'ConnectionsPool' object is not iterable
This is more of question than an issue (and maybe not even specific to aiohttp_session
), but I was wondering why are the keys using uuid4
when codecs.encode(urandom(16), 'hex')
would suffice?
Is it just a preference or is there some deeper reason?
Hello,
What do you think about it?
I need set identity
, which used how key
in redis, of session with custom key. And then I need remove session by that identity
from redis by message from amqp with this identity
.
Now identity
readonly property.
Example code
class LoginView(BaseView):
async def post(self):
session = await aiohttp_session.get_session(self.request)
if session.new:
session.identity = 'key'
session['user'] = {...}
Thanks.
Hi!
Is there a strong reason why aiohttp_session in its internal API relies strictly on cookies as the only medium to communicate session information to/from clients (https://github.com/aio-libs/aiohttp_session/blob/master/aiohttp_session/__init__.py#L160-L216)?
For instance, I can think of a REST API workflow that uses HTTP headers to provide both a session token and a csrf-preventing token. But in order to implement that kind of API contract, I have to either rely on my own version of a session-handling middleware (that would be about 98-99% similar to aiohttp_session), or go with wrappers around aiohttp_session that extract necessary pieces of information from set cookies and send them in different formats to the client.
I can also think of a possible improvement over that rigidity, by delegating the final implementation of how clients and a server exchange session credentials to a "proxy" ClientServerContract object, that would be constructed during the app initialization step and then used consistently by AbstractStorage (and its derivatives).
2017-11-20 03:35:36,263 ERROR aiohttp.server Error handling request
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/aiohttp/web_protocol.py", line 416, in start
resp = yield from self._request_handler(request)
File "/usr/local/lib/python3.6/site-packages/aiohttp/web.py", line 323, in _handle
resp = yield from handler(request)
File "/usr/local/lib/python3.6/site-packages/aiohttp_session/__init__.py", line 142, in factory
yield from storage.save_session(request, response, session)
File "/usr/local/lib/python3.6/site-packages/aiohttp_session/redis_storage.py", line 60, in save_session
with (yield from self._redis) as conn:
TypeError: 'ConnectionsPool' object is not iterable
There are breaking changes in aioredis 1.0, especially Python 3.3, 3.4 supports are dropped, See https://github.com/aio-libs/aioredis/releases/tag/v1.0.0
May be fixed by limiting the aioredis version or upgrading to the new API?
Would it be good to make other storages? For example Mongo
and CouchDB
. If yes, I would like to implement these. But need to be sure that it could be interesting.
need different session expiration policies.
I think aiohttp-session
should provide a way to explicitly request a new session, ignoring the existence of a cookie.
My initial thought is to pass an optional kwarg new=True
to get_session
, which in turn will pass it to the storage's load_session
function to force it to return a new Session ignoring the presence of any cookie.
This is another window for Session Fixation, although this does not affect aiohttp-session
itself like #272 but the application logic. I think that providing a way to explicitly request a new session (and encouraging the use of it in documentation and examples related to login functionality) would greatly benefit the users of aiohttp-session
in terms of writing more secure code.
Looking into OWASP's entry for Session Fixation:
When authenticating a user, it [the vulnerable web application] doesn’t assign a new session ID, making it possible to use an existent session ID.
@asvetlov do you agree with adding this feature ? If so I can have a PR ready during the weekend (to also add documentation and usage examples).
A timestamp indicating the time when this session was created.
For now get_session just cause this exception, and I don't understand what to do with this. I would prefer lib to create new session silently.
Would it be in scope of the project to add the option to silence Exceptions raised during storage.save_session
that happens here?
My reasoning is that if you have a long-running or computationally expensive request that completed successfully you wouldn't want to loose your response (since a raise at that point means that the response will not be available to the upper middleware) if for example a write that renews the last visit value of a user's session fails (insert any other non-essential info stored in a session).
Alternatively could we add a new Exception Class that will have the response as an attribute (so an upper middleware can still access the response)? Or add the attribute to the caught exception and re-raise it?
Also open for any other suggestions as to how the response could be preserved in case a raise occurs during storage.save_session
.
Hello,
let me start by thanking you for a great library that has saved me tons of time :)
Now on to the matter at hand. A lot of web services (and mainly websites) return an expired version of the logged in user's cookie upon logout. This helps since most browsers will delete expired cookies and the front-end doesn't have to bother with cookie management.
Is a feature like this, for example returning an expired cookie after session.invalidate, a) technically feasible and b) something that you would consider as a possible addition ?
Thank you for you time and thanks again for a great library :)
You must feel frustrated, if you try a new thing but fail in the simplest way. I run this code in windows 32 system.
the code is the example you give.
`
import asyncio
import time
from aiohttp import web
from aiohttp_session import get_session, session_middleware
from aiohttp_session.cookie_storage import EncryptedCookieStorage
@asyncio.coroutine
def handler(request):
session = yield from get_session(request)
session['last_visit'] = time.time()
return web.Response(body=b'OK')
@asyncio.coroutine
def init(loop):
app = web.Application(middlewares=[session_middleware(
EncryptedCookieStorage(b'1111'))])
app.router.add_route('GET', '/', handler)
srv = yield from loop.create_server(
app.make_handler(), '0.0.0.0', 8080)
return srv
loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
`
These lines https://github.com/aio-libs/aiohttp_session/blob/master/aiohttp_session/__init__.py#L161-L162 will never be executed in case of usage EncryptedCookieStorage:
Here https://github.com/aio-libs/aiohttp_session/blob/master/aiohttp_session/cookie_storage.py#L43-L47
in case of empty dict session._mapping
variable cookie_data
is b'{} '
and after all transformations b64coded
is a hash which is definitely not a False
boolean value.
As result condition if not cookie_data:
will always return False
and session will not be invalidated.
The docs says "max_age - Maximum age for session data, int seconds or None for infinite session.". But in reality session with max_age=None
lives until browser is closed.
Hello,
Actual behaviour
Key from redis does not deleted after session is invalidate.
Expected behaviour
Key from redis deleted after session is invalidate.
What do you think about it?
class LogoutView(BaseView):
async def post(self):
session = await aiohttp_session.get_session(self.request)
session.invalidate()
Thanks.
Hi!
Tests of test_response_types.py arn't pass!
Is this problem just for me?
I'm using Python 3.5.2.
Let's imagine standart approach for getting user ID from request session:
@asyncio.coroutine
def get_request_user(request):
session = yield from get_session(request)
user_id = session.get(SESSION_USER_KEY)
if not user_id:
return None
...
But with aiohttp_session==0.1.1
this makes no sense as session.get
returns session storage instance instead of looking up for key in _mapping
dict. Currenlty in this case I need to use:
try:
user_id = session[SESSION_USER_KEY]
except KeyError:
return None
or even
user_id = session._mapping.get(SESSION_USER_KEY)
but really prefer to have get
, setdefault
, and update
methods overriden in session storage, so don't need to call them with _mapping
attribute.
What do you think?
When i try do
session = yield from get_session(self.request)
session['user'] = user
with user is a document of mongo, my program occur a error:
Error handling request
Traceback (most recent call last):
File "/usr/local/lib/python3.4/dist-packages/aiohttp/server.py", line 266, in start
yield from self.handle_request(message, payload)
File "/usr/local/lib/python3.4/dist-packages/aiohttp/web.py", line 90, in handle_request
resp = yield from handler(request)
File "/usr/local/lib/python3.4/dist-packages/aiohttp_session/__init__.py", line 144, in middleware
yield from storage.save_session(request, response, session)
File "/usr/lib/python3.4/asyncio/coroutines.py", line 141, in coro
res = func(*args, **kw)
File "/usr/local/lib/python3.4/dist-packages/aiohttp_session/cookie_storage.py", line 52, in save_session
self._get_session_data(session)
File "/usr/lib/python3.4/json/__init__.py", line 230, in dumps
return _default_encoder.encode(obj)
File "/usr/lib/python3.4/json/encoder.py", line 192, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python3.4/json/encoder.py", line 250, in iterencode
return _iterencode(o, 0)
File "/usr/lib/python3.4/json/encoder.py", line 173, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: ObjectId('56d07ade0686372f8e45b3f1') is not JSON serializable
How do i replace 'json' encoder with 'bson.json_until' encoder?
https://github.com/aio-libs/aiohttp_session/blob/master/aiohttp_session/cookie_storage.py#L51
Here is gist I used for test: https://gist.github.com/imbolc/aa0f2cfc89f0a7994e5d
Repeat the refresh page, with such errors, sometimes without
` class WorkerPool:
def __init__(self, loop):
self.loop = asyncio.get_event_loop() or loop
self.webRouter = WebRoute(loop = loop)
async def init(self):
'''
app = Application(loop=self.loop)
fernet_key = fernet.Fernet.generate_key()
secret_key = base64.urlsafe_b64decode(fernet_key)
setup(app, EncryptedCookieStorage(secret_key))
app.router.add_static('/static/',path=str(PROJECT_ROOT / 'static'),name='static')
aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader(template_dir))
self.webRouter.base(app)
return app
try:
loop = asyncio.get_event_loop()
wp = WorkerPool(loop=loop)
app =loop.run_until_complete(wp.init())
except KeyboardInterrupt:
sys.stderr.flush()
`
As it seems from Issue 185 and some references in documentation aioredis will be adding Sentinel support in V1.0.
Is it within the project's scope to support both connection pools and sentinel pools?
If so I'd like to take a swing at implementing this feature once aioredis version that includes the feature is released.
Also should we choose to implement this I can already see tests being a bit tricky (not so much on the python side but more on the 'we will need to have a redis cluster to test against' side), so any leads in that field would be greatly appreciated.
My initial thought was to completely patch (mock) the class for the Sentinel Pool, but this carries the drawback that our tests will be as accurate as our mock class (i.e. if the mocked class is wrong and the tests are passing).
With max_age set, expiry is calculated using time.gmtime(time.monotonic() + max_age)
I don't think time.monotonic()
reliably gives number of seconds since the epoch, so on my server, expiry is calculated to a 1970 date e.g. expires=Sat, 31-Jan-1970 04:52:49 GMT
and a user using IE or Edge never gets a cookie. Interestingly Firefox and Chrome ignore the old expires value and the cookie works (I suspect it will never expire).
It seems to work for me to use time.time()
in place of time.monotonic()
but I wasn't clear on the intention of using the monotonic clock here?
Do we need some sort of flash messages support? Or may be this feature is out of scope of aiohttp_session?
Most frameworks have flash messages:
pyramid: http://docs.pylonsproject.org/projects/pyramid/en/1.0-branch/narr/sessions.html#flash-messages
flask: http://flask.pocoo.org/docs/0.10/patterns/flashing/
Particularly pyramid has it integrated to session object.
The aiohttp-session does not set cache-control headers (such as private), to prevent session based content to be cached by intermediate hosts; this could potentally lead to information leakage
First of all, apologies if this is not an issue and I'm just doing something wrong.
Without going into the reason (that is a topic for another potential issue), I am trying to use the SimpleCookieStorage class.
I have a simple server which uses a slightly modified piece of example code. This server works just fine with the EncryptedCookieStorage but it dies when I try and use it with SimpleCookieStorage.
Am I using the class incorrectly?
Server code below:
import asyncio
import time
from aiohttp import web
from aiohttp_session.cookie_storage import EncryptedCookieStorage
from aiohttp_session import setup, get_session, SimpleCookieStorage
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
async def handler(request):
session = await get_session(request)
last_visit = session.get('last_visit', 'Never')
if last_visit == 'Never':
message = "Welcome, I don't think you've visited here before."
else:
message = 'Welcome back, last visited: {} secs ago'.format(time.time() -
last_visit)
session['last_visit'] = time.time()
return web.Response(body=message.encode('utf-8'))
async def init(loop):
app = web.Application()
setup(app, SimpleCookieStorage())
#setup(app, EncryptedCookieStorage(b'Thirty two length bytes key.'))
app.router.add_route('GET', '/', handler)
srv = await loop.create_server(
app.make_handler(), '0.0.0.0', 8080)
return srv
loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
To make this code work, I can just comment the line invoking SimpleCookieStorage and uncomment the one invoking EncryptedCookieStorage.
The traceback I get when using the SimpleCookiStorage class is:
Error handling request
Traceback (most recent call last):
File "c:\projects\aio-rpc\venv\lib\site-packages\aiohttp\server.py", line 285, in start
yield from self.handle_request(message, payload)
File "c:\projects\aio-rpc\venv\lib\site-packages\aiohttp\web.py", line 90, in handle_request
resp = yield from handler(request)
File "c:\projects\aio-rpc\venv\lib\site-packages\aiohttp_session\__init__.py", line 129, in middleware
response = yield from handler(request)
File "server.py", line 12, in handler
session = await get_session(request)
File "c:\projects\aio-rpc\venv\lib\site-packages\aiohttp_session\__init__.py", line 107, in get_session
session = yield from storage.load_session(request)
File "C:\Users\gkuhn\AppData\Local\Continuum\Anaconda3\envs\py35\lib\asyncio\coroutines.py", line 206, in coro
res = func(*args, **kw)
File "c:\projects\aio-rpc\venv\lib\site-packages\aiohttp_session\__init__.py", line 237, in load_session
data = json.loads(cookie)
File "C:\Users\gkuhn\AppData\Local\Continuum\Anaconda3\envs\py35\lib\json\__init__.py", line 319, in loads
return _default_decoder.decode(s)
File "C:\Users\gkuhn\AppData\Local\Continuum\Anaconda3\envs\py35\lib\json\decoder.py", line 339, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "C:\Users\gkuhn\AppData\Local\Continuum\Anaconda3\envs\py35\lib\json\decoder.py", line 357, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
In README, there is:
from cryptorgraphy import fernet
- should be cryptography
There is also: Requires crypotgraphy library:
with a similar typo.
When trying the first example in README, the trivial usage example
, (and after fixing the typo)
I get:
File "c:\projects\asyncio_venv\lib\site-packages\aiohttp\web.py", line 305, in run_app loop = app.loop AttributeError: 'NoneType' object has no attribute 'loop'
I am using python 3.5.2 on Anaconda on Windows
It works on Windows
The project in https://github.com/inzem77/ws-chat
serving on ('0.0.0.0', 8080)
Error handling request
Traceback (most recent call last):
File "/home/inzem/sites/chat/ENV3.5/lib/python3.5/site-packages/aiohttp/server.py", line 272, in start
yield from self.handle_request(message, payload)
File "/home/inzem/sites/chat/ENV3.5/lib/python3.5/site-packages/aiohttp/web.py", line 87, in handle_request
resp = yield from handler(request)
File "/home/inzem/sites/chat/ENV3.5/lib/python3.5/site-packages/aiohttp_session/__init__.py", line 138, in middleware
yield from storage.save_session(request, response, session)
File "/home/inzem/sites/chat/ENV3.5/lib/python3.5/site-packages/aiohttp_session/redis_storage.py", line 48, in save_session
with (yield from self._redis) as conn:
AttributeError: __exit__
It's probably a collision between names but when you import
from aiohttp_session import setup
in your PyTest you get
self = <CallInfo when='setup' exception: setup() missing 2 required positional arguments: 'app' and 'storage'>, func = <function call_runtest_hook.<locals>.<lambda> at 0x104106ea0>, when = 'setup'
def __init__(self, func, when):
#: context of invocation: one of "setup", "call",
#: "teardown", "memocollect"
self.when = when
self.start = time()
try:
> self.result = func()
../../../miniconda3/envs/mcc.cassiny.io/lib/python3.5/site-packages/_pytest/runner.py:163:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../../miniconda3/envs/mcc.cassiny.io/lib/python3.5/site-packages/_pytest/runner.py:151: in <lambda>
return CallInfo(lambda: ihook(item=item, **kwds), when=when)
../../../miniconda3/envs/mcc.cassiny.io/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py:745: in __call__
return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
../../../miniconda3/envs/mcc.cassiny.io/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py:339: in _hookexec
return self._inner_hookexec(hook, methods, kwargs)
../../../miniconda3/envs/mcc.cassiny.io/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py:334: in <lambda>
_MultiCall(methods, kwargs, hook.spec_opts).execute()
../../../miniconda3/envs/mcc.cassiny.io/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py:613: in execute
return _wrapped_call(hook_impl.function(*args), self.execute)
../../../miniconda3/envs/mcc.cassiny.io/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py:254: in _wrapped_call
return call_outcome.get_result()
../../../miniconda3/envs/mcc.cassiny.io/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py:279: in get_result
raise ex[1].with_traceback(ex[2])
../../../miniconda3/envs/mcc.cassiny.io/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py:265: in __init__
self.result = func()
../../../miniconda3/envs/mcc.cassiny.io/lib/python3.5/site-packages/_pytest/vendored_packages/pluggy.py:614: in execute
res = hook_impl.function(*args)
../../../miniconda3/envs/mcc.cassiny.io/lib/python3.5/site-packages/_pytest/nose.py:39: in pytest_runtest_setup
call_optional(item.parent.obj, 'setup')
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
obj = <module 'tests.test_auth' (<_pytest.assertion.rewrite.AssertionRewritingHook object at 0x1033dd780>)>, name = 'setup'
def call_optional(obj, name):
method = getattr(obj, name, None)
isfixture = hasattr(method, "_pytestfixturefunction")
if method is not None and not isfixture and py.builtin.callable(method):
# If there's any problems allow the exception to raise rather than
# silently ignoring them
> method()
E TypeError: setup() missing 2 required positional arguments: 'app' and 'storage'
../../../miniconda3/envs/mcc.cassiny.io/lib/python3.5/site-packages/_pytest/nose.py:70: TypeError
Hi,
I think there are reasonable use cases where people would like to use sessions when using aiohttp without aiohttp.web
or cookies
.
For example
I know in a lot of cases standard HTTP sessions are used with cookies but since aiohttp is now being used in very diverse environments may be it would be useful to have at least one session storage backend which can fully function without aiohttp.web.Resquest/Response
objects.
Does this sound reasonable?
I am imagining something like the below ("Cookieless Redis Session Storage")
import asyncio
import json
import uuid
from aiohttp_session import AbstractStorage, Session
class RedisCookielessStorage(AbstractStorage):
"""Redis storage"""
def __init__(self, redis_pool, *, key_prefix="aiohttp_session",
domain=None, max_age=None, path='/',
secure=None, httponly=True,
encoder=json.dumps, decoder=json.loads,
key_factory=lambda: uuid.uuid4().hex):
super().__init__(domain=domain, max_age=max_age, path=path,
secure=secure, httponly=httponly)
self._encoder = encoder
self._decoder = decoder
self._key_prefix = key_prefix
self._key_factory = key_factory
self._redis = redis_pool
@property
def key_prefix(self):
return self._key_prefix
@asyncio.coroutine
def load_session(self, key=None):
if key is None:
return Session(None, data=None, new=True, max_age=self.max_age)
else:
with (yield from self._redis) as conn:
data = yield from conn.get(self.key_prefix + ':' + key)
if data is None:
return Session(key, data=None, new=True, max_age=self.max_age)
data = data.decode('utf-8')
try:
data = self._decoder(data)
except ValueError:
data = None
return Session(key, data=data, new=False, max_age=self.max_age)
@asyncio.coroutine
def save_session(self, session):
key = session.identity or self._key_factory()
data = self._encoder(self._get_session_data(session))
with (yield from self._redis) as conn:
max_age = session.max_age
expire = max_age if max_age is not None else 0
yield from conn.set(self.key_prefix + ':' + key,
data, expire=expire)
Then you can do something like
redis = await aioredis.create_pool(('localhost', 6379))
session_storage = RedisCookielessStorage(redis)
session = await session_storage.load_session('<YOUR_SESSION_KEY>')
...
await session_storage.save_session(session)
After looking over the code, I was alarmed by the fact, that no where could I see, that the domain was passed into the cookie jar. This means that if cookies had the same name, they would be overwritten (unless I'm wrong).
Added PR #267
I was considering using this lib until I saw in the docs that it used pickle:
Note
Keys and values of session data must be pickleable.
http://aiohttp-session.readthedocs.io/en/latest/reference.html#aiohttp_session.Session.changed
However, I can't seem to find any reference to pickle in the source, it seems like all of the storage backends use json, not pickle. Is this a doc bug? Should it say "json serializable" instead of "picklable"?
@web.middleware
should be used.
The task is trivial, I hope somebody will pick it up.
Like aiopg
We need to use dockerized redis and memcache servers in our tests.
It simplifies local development.
Hello guys, how to write cookie session to my react app, when i'm in developing mode? I'm syncing data via proxy.
In ideal I'm need to create session on my react app, because when I'm coping cookie to react and trying to read it on server app response with: "Cannot decrypt cookie value, create a new fresh session".
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.