ahopkins / sanic-session Goto Github PK
View Code? Open in Web Editor NEWProvides server-backed sessions for Sanic using Redis, Memcache and more.
Home Page: https://sanic-session.readthedocs.io
License: MIT License
Provides server-backed sessions for Sanic using Redis, Memcache and more.
Home Page: https://sanic-session.readthedocs.io
License: MIT License
If you use redis
or memcache
as backend, a cleanup may be needed, like below code:
class BaseSessionInterface:
async def close(self, *args, **kwargs):
pass
class RedisSessionInterface(BaseSessionInterface):
async def close(self, *args, **kwargs):
redis_connection = await self.redis_getter()
redis_connection.close()
class MemcacheSessionInterface(BaseSessionInterface):
async def close(self, *args, **kwargs):
memcache_connection = await self.memcache_connection
await memcache_connection.close()
# then in app code:
@app.linstener('after_server_stop'):
await session.close()
Hello.
This is my simple code:
response = file(join(dirname(__file__),'websocket.html'))
if not request['session'].get('sessionid'):
request['session']['sessionid'] = generator()
if i try print(request)
, i get this result:
{'session': <SessionDict {}>}
after set sessionid
the result was this : {'session': <SessionDict {'sessionid': 'vjRsqwaMVOcP808OXTBDxR9UY9rnQjkfPF0f'}>}
But after this, i was get error with this traceback:
Traceback (most recent call last):
File "/home/narnik/Программы/SanicProjects/venv/lib/python3.6/site-packages/sanic/app.py", line 495, in handle_request
response)
File "/usr/lib64/python3.6/asyncio/coroutines.py", line 109, in __next__
return self.gen.send(None)
File "/home/narnik/Программы/SanicProjects/venv/lib/python3.6/site-packages/sanic/app.py", line 621, in _run_response_middleware
_response = await _response
File "/usr/lib64/python3.6/asyncio/coroutines.py", line 109, in __next__
return self.gen.send(None)
File "main.py", line 27, in save_session
await si.save(request, response)
File "/usr/lib64/python3.6/asyncio/coroutines.py", line 109, in __next__
return self.gen.send(None)
File "/home/narnik/Программы/SanicProjects/venv/lib/python3.6/site-packages/sanic_session/in_memory_session_interface.py", line 83, in save
self._set_cookie_expiration(request, response)
File "/home/narnik/Программы/SanicProjects/venv/lib/python3.6/site-packages/sanic_session/base.py", line 29, in _set_cookie_expiration
response.cookies[self.cookie_name] = request['session'].sid
AttributeError: 'NoneType' object has no attribute 'cookies'
2017-07-22 18:41:32 - (sanic)[ERROR]: NoneType: None
Only one interface to the end user and because they share many same arguments, like domain, expiry, httponly, cookie_name, prefix
which can be reduced for some lines, like this:
# __init__.py
class SessionInterface:
def __new__(cls, *args, **kwargs):
backend = kwargs.pop('backend', 'memory')
if backend == 'redis':
interface_class = RedisSessionInterface
elif backend == 'memcache':
interface_class = MemcacheSessionInterface
else:
interface_class = InMemorySessionInterface
return interface_class(*args, **kwargs)
class BaseSessionInterface:
def __init__(self, domain=None, expiry=2592000,
httponly=True, cookie_name='session',
prefix='session:', **kwargs):
self.expiry = expiry
self.prefix = prefix
self.cookie_name = cookie_name
self.domain = domain
self.httponly = httponly
class InMemorySessionInterface(BaseSessionInterface):
def __init__(self, *args, **kwargs):
super().__init__(**kwargs)
self.session_store = ExpiringDict()
class MemcacheSessionInterface(BaseSessionInterface):
def __init__(self, memcache_connection, *args, **kwargs):
super().__init__(**kwargs)
self.memcache_connection = memcache_connection
# memcache has a maximum 30-day cache limit
if self.expiry > 2592000:
self.expiry = 0
class RedisSessionInterface(BaseSessionInterface):
def __init__(self, redis_getter: Callable=None, *args, **kwargs):
super().__init__(**kwargs)
self.redis_getter = redis_getter
# memory
session = SessionInterface(**kwargs)
# redis
session = SessionInterface(redis_getter, backend='redis', **kwargs)
# memcache
session = SessionInterface(memcache_connection, backend='memcache', **kwargs)
With Sanic 20.3.0.
The session doesn't save values.
in file base.py, line 139-140:
if not hasattr(request, "session"):
return
should be removed ?
The release on PyPI doesn't save any data in sessions, please update to the commit with the fixes @mikekeda made.
Setting variables on Sanic instances is deprecated and will be removed in version 21.9. You should change your Sanic instance to use instance.ctx.extensions instead.
file:sanic_session/init.py,line 26 and line 28 , change "app.extensions" to "app.ctx.extensions"
Is there a planned date for a new release with all the latest changes? Currently deploying using git and the master branch, but would like to be able to deploy directly off of pypi... :)
how to delete the session in Memory? tks
Let it sit for a while to refresh the front-end page(about 15min),then redis timeout,but my redis-server set "timeout 0"。How should i deal with this problem?
#------------my code------------
from sanic import Sanic
import settings
import aioredis
from sanic_session import AIORedisSessionInterface, Session
......
app = Sanic("yunwei")
session = Session()
......
@app.before_server_start
async def before_server_start(app, loop):
app.ctx.redis_session = await aioredis.create_redis_pool(
(settings.REDIS_HOST, settings.REDIS_PORT), minsize=10, maxsize=20, loop=loop,
password=settings.REDIS_PASSWORD, db=settings.REDIS_SESSION_DB, encoding='utf-8'
)
session.init_app(app, interface=AIORedisSessionInterface(app.ctx.redis_session, expiry=settings.SESSION_EXPIRY))
@app.after_server_stop
async def after_server_stop(app, loop):
app.ctx.redis_session.close()
await app.ctx.redis_session.wait_closed()
#------------error message-----------
[2021-06-22 16:03:06 +0800] [18589] [ERROR] Exception occurred in one of response middleware handlers
Traceback (most recent call last):
File "/data/web/py39code/lib/python3.9/site-packages/sanic/request.py", line 183, in respond
response = await self.app._run_response_middleware(
File "/data/web/py39code/lib/python3.9/site-packages/sanic/app.py", line 1074, in _run_response_middleware
_response = await _response
File "/data/web/py39code/lib/python3.9/site-packages/sanic_session/init.py", line 41, in save_session
await self.interface.save(request, response)
File "/data/web/py39code/lib/python3.9/site-packages/sanic_session/base.py", line 150, in save
await self._delete_key(key)
File "/data/web/py39code/lib/python3.9/site-packages/sanic_session/aioredis.py", line 79, in _delete_key
await self.redis.delete(key)
File "/data/web/py39code/lib/python3.9/site-packages/aioredis/util.py", line 59, in wait_convert
result = await fut
File "/data/web/py39code/lib/python3.9/site-packages/aioredis/connection.py", line 186, in _read_data
obj = await self._reader.readobj()
File "/data/web/py39code/lib/python3.9/site-packages/aioredis/stream.py", line 102, in readobj
await self._wait_for_data('readobj')
File "/usr/local/python39/lib/python3.9/asyncio/streams.py", line 517, in _wait_for_data
await self._waiter
TimeoutError: [Errno 110] Connection timed out
I wrote this bit of code to make a variable called session
available in jinja templates which contains the session dict. Use the jinja_render
or jinja_response
methods and pass in the template path instead of using jinja's get_template
and render
methods.
import os
from sanic.response import html
from jinja2 import Environment, FileSystemLoader
template_envirnoment = Environment(loader=FileSystemLoader(os.getcwd() + '/view/html'))
get_template = template_envirnoment.get_template
# using this method attaches the session to the template
def jinja_render(template_path, **kwargs):
import inspect
frame = inspect.currentframe()
try:
if inspect.getframeinfo(frame.f_back)[2] == 'jinja_response':
session = frame.f_back.f_back.f_locals['request']['session']
else:
session = frame.f_back.f_locals['request']['session']
return get_template(template_path).render(session=session, **kwargs)
except Exception as e:
return get_template(template_path).render(**kwargs)
finally:
# forgetting to delete the frame can cause reference cycles
# https://docs.python.org/3/library/inspect.html#the-interpreter-stack
del frame
def jinja_response(template_path, **kwargs):
return html(jinja_render(template_path, **kwargs))
use the aioredis demo:
https://sanic-session.readthedocs.io/en/latest/using_the_interfaces.html#redis-aioredis
and response header always has set-cookie, even use same session
// event comment out foo +=1
Is this normal?
// use set-cookie only when cookie has change ?
$ curl -sv 'http://localhost:8006/' --cookie 'session=ac97cd2a67e54328a6d06ba8e471cacd'
> GET / HTTP/1.1
> Host: localhost:8006
> User-Agent: curl/7.68.0
> Accept: */*
> Cookie: session=ac97cd2a67e54328a6d06ba8e471cacd
>
< HTTP/1.1 200 OK
< Set-Cookie: session=ac97cd2a67e54328a6d06ba8e471cacd; Path=/; HttpOnly; expires=Thu, 03-Mar-2022 20:23:27 GMT; Max-Age=2592000
< content-length: 38
< connection: keep-alive
< content-type: text/plain; charset=utf-8
<
foo: <SessionDict {'foo': {'val': 0}}>
$ curl -sv 'http://localhost:8006/' --cookie 'session=ac97cd2a67e54328a6d06ba8e471cacd'
> GET / HTTP/1.1
> Host: localhost:8006
> User-Agent: curl/7.68.0
> Accept: */*
> Cookie: session=ac97cd2a67e54328a6d06ba8e471cacd
>
< HTTP/1.1 200 OK
< Set-Cookie: session=ac97cd2a67e54328a6d06ba8e471cacd; Path=/; HttpOnly; expires=Thu, 03-Mar-2022 20:23:29 GMT; Max-Age=2592000
< content-length: 38
< connection: keep-alive
< content-type: text/plain; charset=utf-8
<
foo: <SessionDict {'foo': {'val': 0}}>
$ curl -sv 'http://localhost:8006/' --cookie 'session=ac97cd2a67e54328a6d06ba8e471cacd'
> GET / HTTP/1.1
> Host: localhost:8006
> User-Agent: curl/7.68.0
> Accept: */*
> Cookie: session=ac97cd2a67e54328a6d06ba8e471cacd
>
< HTTP/1.1 200 OK
< Set-Cookie: session=ac97cd2a67e54328a6d06ba8e471cacd; Path=/; HttpOnly; expires=Thu, 03-Mar-2022 20:23:30 GMT; Max-Age=2592000
< content-length: 38
< connection: keep-alive
< content-type: text/plain; charset=utf-8
<
foo: <SessionDict {'foo': {'val': 0}}>
every request will got cookie with new expires value.
In memory session interface, if use multi workers in sanic, somethins will be wrong!
For example, if we changed the session in a request, but only one process changed. but else do not changed!
Hi, can "sanic_session" work with "aioredis" naturally, as the document demo with "asyncio_redis"?
If I don't misunderstand, in "redis_session_interface.py", we get redis connect as below:
redis_connection = await self.redis_getter()
this works fine with asyncio_redis, but in aioredis, when call get()
with ConnectionsPool
get a ContextManager
, not a connection, so it will promote a error and cannot get a connection. You need to get connection with acquire()
Thanks for attention!
@xen Are you willing to transfer this project to me and I will continue maintaining it? (Both the repo and the PyPI package.)
While trying to run redis to create authentication, I encounter such exception:
sys:1: RuntimeWarning: coroutine 'Loop.create_server' was never awaited
Code:
@app.listener('before_server_start')
async def server_init(app, loop):
app.redis = await aioredis.create_redis_pool(app.config['redis']) <- exception here KeyError: 'redis'
session.init_app(app, interface=AIORedisSessionInterface(app.redis))
aioredis example error in sanic 22.9.0,no problem in sanic 22.6.2
I think websocket requests in Sanic don't have responses (they're long-lived); so when I add a sanic-session session it tries to save cookies for the websocket requests as well, which gives the error above. It works better for me if I check for response being None in base.py _set_cookie_expiration
.
In sanic 22.12.0 they changed the internal structure of app's middleware collections (request_middleware, response_middleware)
Look at this commit: sanic-org/sanic@2abe66b
Now they don't store pure middleware functions there but instead they use Middleware class for each of them.
It still works somehow but looks like middleware order is broken and other unpredictable bugs may occur.
Why don't you just use register_middleware method as suggested in docs? (https://sanic.dev/en/guide/basics/middleware.html#attaching-middleware)
In order to improve security and reach nowadays standards, it would be nice to have the session cookie cryptographically signed.
when app.run(workers=4), works >1, session not eixsts,
I think it is a bug, when works > 1, it happend.
Currently, it is not possible to store objects inside a session containing UUID, datetimes, date, and time values. Substituting ujson for orjson would fix this. Maybe a configuration of the used json implementation would be the best solution.
With websockets, I commonly have multiple requests in flight, since the websocket one is long-lasting. I'm using sessions for auth, and I have this issue:
The reason for this seems to be that each request is using a clone of the SessionDict. If there was just one SessionDict per session key and all requests in that session got that dict, then when the user logged in, the single SessionDict would be updated to True, and then when the websocket closed down, it would just save auth=True as expected.
Is this expected behavior? I'd think it would be better if there was one SessionDict per key, and all requests with that key would share the same session dict so they'd see each other's changes while they're running.
I made a simple change to cache and reuse sessions in memory in base.py; that works OK for me. I'll add it to this issue to see if you like it.
I'm trying to use the mongodb version and this is my code:
app = Sanic(__name__)
Session(app, MongoDBSessionInterface(app, coll='rainboard_session', expiry=365 * 24 * 60 * 60, sessioncookie=True, secure=True))
But when starting the sanic server, i get this error:
Traceback (most recent call last):
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\site-packages\sanic\app.py", line 1128, in run
serve(**server_settings)
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\site-packages\sanic\server.py", line 891, in serve
trigger_events(after_start, loop)
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\site-packages\sanic\server.py", line 693, in trigger_events
loop.run_until_complete(result)
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\asyncio\base_events.py", line 583, in run_until_complete
return future.result()
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\site-packages\sanic_session\mongodb.py", line 123, in apply_session_indexes
await _SessionModel.create_index("sid")
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\site-packages\sanic_motor\__init__.py", line 359, in create_index
coll = cls.get_collection(db)
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\site-packages\sanic_motor\__init__.py", line 176, in get_collection
db = cls.__dbkey__ or cls.__app__.name
AttributeError: 'NoneType' object has no attribute 'name'
Traceback (most recent call last):
File "app.py", line 82, in <module>
app.run(port=app.config.PORT)
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\site-packages\sanic\app.py", line 1128, in run
serve(**server_settings)
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\site-packages\sanic\server.py", line 891, in serve
trigger_events(after_start, loop)
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\site-packages\sanic\server.py", line 693, in trigger_events
loop.run_until_complete(result)
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\asyncio\base_events.py", line 583, in run_until_complete
return future.result()
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\site-packages\sanic_session\mongodb.py", line 123, in apply_session_indexes
await _SessionModel.create_index("sid")
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\site-packages\sanic_motor\__init__.py", line 359, in create_index
coll = cls.get_collection(db)
File "C:\Users\Jason\AppData\Local\Programs\Python\Python37\lib\site-packages\sanic_motor\__init__.py", line 176, in get_collection
db = cls.__dbkey__ or cls.__app__.name
AttributeError: 'NoneType' object has no attribute 'name'
Not quite sure how to fix it or if the mongodb interface is even ready for use since I didnt see it on readthedocs
Hello.
I made similar to what was specified in readme:
app.py:
from sanic_session import InMemorySessionInterface
session_interface = InMemorySessionInterface()
@app.middleware('request')
async def add_session_to_request(request):
# before each request initialize a session
# using the client's request
await session_interface.open(request)
@app.middleware('response')
async def save_session(request, response):
# after each request save the session,
# pass the response to set client cookies
await session_interface.save(request, response)
views.py:
@app.route('/gatekeeper')
def give_login_page(request):
context = {
'csrf_input': 'some',
'static': link_to_static,
}
if 'shit' not in request['session']:
print('was absent ______________')
request['session'] = '888' # <<<<<<<< fails on this line
else:
print(f"it's present, and == {request['session']} ________")
return jinja.render('gatekeeper/login_page.html', request, **context)
but I receive such error:
File "/home/vlad/my_projects/web/times_new_novel_asyncio/venv/lib/python3.6/site-packages/sanic/app.py", line 727, in _run_response_middleware
_response = await _response
File "/home/vlad/my_projects/web/times_new_novel_asyncio/main/app.py", line 34, in save_session
await session_interface.save(request, response)
File "/home/vlad/my_projects/web/times_new_novel_asyncio/venv/lib/python3.6/site-packages/sanic_session/in_memory_session_interface.py", line 67, in save
key = self.prefix + request['session'].sid
AttributeError: 'str' object has no attribute 'sid'
I'll try to figure out the matter myself, but, seems, something is wrong.
Anyway I think, that developers might know, which errors happen often in their software =)
Best regards,
Vladislav
Why do I follow the readme
Can't load session
Python error
ImportError: cannot import name 'Session'
have any idea?
Installed package
Package Version
aiofiles 0.3.2
AoikLiveReload 0.1.0
argh 0.26.2
httptools 0.0.11
Logbook 1.4.0
pathtools 0.1.2
pip 10.0.1
PyYAML 3.13
sanic 0.7.0
Sanic-Auth 0.2.0
sanic-session 0.1.5
setuptools 39.0.1
ujson 1.35
uvloop 0.11.0
watchdog 0.8.3
websockets 5.0.1
python version 3.7.0
When installing from the master branch (pip install git+https://github.com/xen/sanic_session
) (not replicable on pypi release, probably because README.md may have been changed after latest release), this error occurs:
Collecting git+https://github.com/xen/sanic_session
Cloning https://github.com/xen/sanic_session to c:\users\jason\appdata\local\temp\pip-req-build-0tfgky2v
Running command git clone -q https://github.com/xen/sanic_session 'C:\Users\Jason\AppData\Local\Temp\pip-req-build-0tfgky2v'
ERROR: Command errored out with exit status 1:
command: 'c:\users\jason\appdata\local\programs\python\python37\python.exe' -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'C:\\Users\\Jason\\AppData\\Local\\Temp\\pip-req-build-0tfgky2v\\setup.py'"'"'; __file__='"'"'C:\\Users\\Jason\\AppData\\Local\\Temp\\pip-req-build-0tfgky2v\\setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base 'C:\Users\Jason\AppData\Local\Temp\pip-pip-egg-info-n60qy5q4'
cwd: C:\Users\Jason\AppData\Local\Temp\pip-req-build-0tfgky2v\
Complete output (7 lines):
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Users\Jason\AppData\Local\Temp\pip-req-build-0tfgky2v\setup.py", line 4, in <module>
long_description = fh.read()
File "c:\users\jason\appdata\local\programs\python\python37\lib\encodings\cp1252.py", line 23, in decode
return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x90 in position 2490: character maps to <undefined>
----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
Fixed in PR #71, just wanted to create this issue to track it
RedisSessionInterface (app.get_redis_pool, expiry = 4 * 3600 * 100)
when exiting the browser cookie is still invalid
Using the latest released version of Sanic (19.3.1) I get the following.
2019-03-24T19:32:58.955 ERROR app.py#966 Exception occurred in one of response middleware handlers
Traceback (most recent call last):
File "/usr/lib/python3.6/site-packages/sanic/app.py", line 958, in handle_request
request, response
File "/usr/lib/python3.6/site-packages/sanic/app.py", line 1238, in _run_response_middleware
_response = await _response
File "/usr/lib/python3.6/site-packages/ui/server.py", line 704, in save_session
await session.save(request, response)
File "/usr/lib/python3.6/site-packages/sanic_session/base.py", line 142, in save
self._set_cookie_props(request, response)
File "/usr/lib/python3.6/site-packages/sanic_session/base.py", line 50, in _set_cookie_props
response.cookies[self.cookie_name]['expires'] = self._calculate_expires(self.expiry)
File "/usr/lib/python3.6/site-packages/sanic/cookies.py", line 116, in __setitem__
"Cookie 'expires' property must be a datetime"
TypeError: Cookie 'expires' property must be a datetime
I’ve been seeing sporadic Overflow errors but I can’t tell in what conditions that this is happening. The traceback does not tell me whole lot about what is happening either.
Would you be able to guess what might be happening — or alternatively, does it have to do with spf
?
Sep 05 22:53:14 : 2020-09-05 22:53:14,534 ERROR sanic.error:985 | Exception occurred in one of response middleware handlers
Sep 05 22:53:14 : Traceback (most recent call last):
Sep 05 22:53:14 : File "/path/to/lib/python3.6/site-packages/sanic/app.py", line 977, in handle_request
Sep 05 22:53:14 : request, response, request_name=name
Sep 05 22:53:14 : File "/path/to/lib/python3.6/site-packages/spf/framework.py", line 686, in _run_response_middleware_19_12
Sep 05 22:53:14 : _response = await _response
Sep 05 22:53:14 : File "/path/to/lib/python3.6/site-packages/sanic_session/__init__.py", line 41, in save_session
Sep 05 22:53:14 : await self.interface.save(request, response)
Sep 05 22:53:14 : File "/path/to/lib/python3.6/site-packages/sanic_session/base.py", line 156, in save
Sep 05 22:53:14 : val = ujson.dumps(dict(req[self.session_name]))
Sep 05 22:53:14 : OverflowError: Maximum recursion level reached
Would you be interested in an aiopg interface?
I am planning to write one for my own use and can submit a pull request once I am done if you'd be interested!
The save and open method is basically all the same for all interfaces. So that code is repeated across all interfaces. So it would probably be better if each interface had private methods that implements the specific call to the store that is required.
That way you could delete quite a lot of code from the project, and it'd be a bit easier to start a new interface without thinking too much about the logic around.
Traceback (most recent call last):
File "/home/noah_howerton/.local/lib/python3.6/site-packages/sanic/app.py", line 640, in handle_request
response)
File "/usr/lib/python3.6/asyncio/coroutines.py", line 110, in __next__
return self.gen.send(None)
File "/home/noah_howerton/.local/lib/python3.6/site-packages/sanic/app.py", line 811, in _run_response_middleware
_response = await _response
File "/usr/lib/python3.6/asyncio/coroutines.py", line 110, in __next__
return self.gen.send(None)
File "/home/noah_howerton/.local/lib/python3.6/site-packages/sanic_session/__init__.py", line 42, in save_session
await self.interface.save(request, response)
File "/usr/lib/python3.6/asyncio/coroutines.py", line 110, in __next__
return self.gen.send(None)
File "/home/noah_howerton/.local/lib/python3.6/site-packages/sanic_session/base.py", line 127, in save
val = ujson.dumps(dict(request['session']))
UnicodeEncodeError: 'utf-8' codec can't encode characters in position 2-3: surrogates not allowed
<SessionDict {'foo': 1, 'csrf': 'c5b6daf17f280d24712397a1fb1019e3e346f393', '_auth': {'uid': ObjectId('5c08f46a4a67770543e775b2'), 'name': '[email protected]'}}>
So it looks like this is failing with BSON "ObjectID" when trying to serialize to json.
I guess you can rely on the end user (me) to "fix" this by ensuring anything passed into the cookie is already easily serializable to json.
Another option is to use Pickle instead of ujson ... or maybe another json serializer that handles BSON better (you can write a real simple serializer that checks for ObjectId or use json_util in pymongo pkg)? I guess with cookies pickle puts you in kind of a pickle due to security issues (is that still a problem in 3.x though?)
Pickle is obv. going to be a lot more agnostic when handling python objects. I guess there's sort of a weak attempt to handle this sort of issue when you guys dump things into a "dict" but obv. that's not quite enough to strip out python objects that aren't going to easily serialize with ujson.
I guess maybe the first option (requiring end user to send already JSON serialized data into cookie) is probably the safest and most secure option. Though I'm not sure how I feel about that given you end up with a bunch of redundant layers of serialization code if you go this way.
Also, since you already have support for mongodb backend ... and any more complicated/customized session-object is likely going to have an ObjectId or two it might make sense to simply support BSON a little bit better rather than leave this for the end user to sort out :/
thx
Session(app, interface=InMemorySessionInterface(expiry=3000))
@app.route("/session")
async def test(request):
response = text(request['session']['foo'])
request["session"]["foo"] = "bar"
# how to set foo1's expiry is 300
request["session"]["foo1"]["expiry"] = 300
# how to set foo2's expiry is 600
request["session"]["foo2"]["expiry"] = 600
return response
I used the demo code provided:
from sanic import Sanic
from sanic.response import text
from sanic_session import Session, InMemorySessionInterface
app = Sanic()
session = Session(app, interface=InMemorySessionInterface())
@app.route("/")
async def index(request):
# interact with the session like a normal dict
if not request['session'].get('foo'):
request['session']['foo'] = 0
request['session']['foo'] += 1
return text(request['session']['foo'])
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, auto_reload=False)
And I get the following error:
Traceback (most recent call last):
File "/home/justin/Coding/LearnReact/SanicBasics/pybin/lib/python3.8/site-packages/sanic/app.py", line 942, in handle_request
response = await self._run_request_middleware(
File "/home/justin/Coding/LearnReact/SanicBasics/pybin/lib/python3.8/site-packages/sanic/app.py", line 1304, in _run_request_middleware
response = await response
File "/home/justin/Coding/LearnReact/SanicBasics/pybin/lib/python3.8/site-packages/sanic_session/__init__.py", line 34, in add_session_to_request
await self.interface.open(request)
File "/home/justin/Coding/LearnReact/SanicBasics/pybin/lib/python3.8/site-packages/sanic_session/base.py", line 120, in open
request[self.session_name] = session_dict
TypeError: 'Request' object does not support item assignment
It seems that this code is expecting a dict like object to set the session data.
any plan to add init_app
method so that no need to write add_session_to_request
and save_session
for every time use it?
Hello,
If I'm not mistaken, the cookie is not signed.
This is a security risk: someone could alter the browser's cookie sid and be identified as someone else by the server.
You should use itsdangerous to prevent someone from altering the cookie.
That would require asking for a secret(and an optional salt) when creating the Session.
cf sanic_cookiesession for an example
In any case, please add a big warning in the doc/readme while the signature is not implemented.
Session wan't save in IE/EDGE, but in FIREFOX/CHROME everything works fine.
Tried on different mashines.
But custom cookies works fine.
So every page update creates new session on server.
Any help, please?
pip install sanic_session does not include code in the commit b4a2975
Update the code to pip library as well.
sanic-session/sanic_session/base.py
Lines 164 to 169 in 551de4b
Hello,
I was reading your code and stumbled upon the linked code lines.
There I don't understand why line 167 doesn't throw an error.
Because if if not req[self.session_name]
evaluates to True
how can there be a modified
field?
Best
Simon
I saw the docs and was disappointed it wasn't supported but it seems like it is by reading the source code
In RedisSessionInterface.save
specifically this line of code:
await redis_connection.delete([key])
throws an exception stating unhashable type list
I was using aioredis 1.0.0b1 and python 3.6.5
When I updated to aioredis 1.1.0
the exception is bit more descriptive:
Argument ['session:46d03770f1274b0f94823326e3b8d16a'] expected to be of bytearray, bytes, float, int, or str type
Is there any chance of updating the library to call
await redis_connection.delete(key)
instead?
https://github.com/xen/sanic_session/blob/master/docs/source/testing.rst
In the doc,
Use this session ID to retrieve the server-stored session data from the
session_interface
.
but there is no session_interface
variable in main.py
.
I think main.py should contains the following lines:
if os.environ.get('TESTING'):
session_interface = InMemorySessionInterface()
else:
session_interface = RedisSessionInterface(redis.get_redis_pool)
Session(app, interface = session_interface)
Hi,
I'm using the sanic_session + asyncio-redis to handle the sessions in my web applications. But sometimes it generates below error(attached below) and happens randomly. It seems the operation on cookies are causing this issue, but I have no clue on the root cause, is there anyone who also met the same issue? Expecting suggestions and ideas. Thank you!
raceback (most recent call last):
8/27/2018 10:36:07 PM File "/usr/local/lib/python3.6/site-packages/sanic/app.py", line 580, in handle_request
8/27/2018 10:36:07 PM response)
8/27/2018 10:36:07 PM File "/usr/local/lib/python3.6/site-packages/spf/framework.py", line 487, in _run_response_middleware
8/27/2018 10:36:07 PM _response = await _response
8/27/2018 10:36:07 PM File "/usr/local/lib/python3.6/site-packages/sanic_session/__init__.py", line 42, in save_session
8/27/2018 10:36:07 PM await self.interface.save(request, response)
8/27/2018 10:36:07 PM File "/usr/local/lib/python3.6/site-packages/sanic_session/base.py", line 129, in save
8/27/2018 10:36:07 PM self._set_cookie_expiration(request, response)
8/27/2018 10:36:07 PM File "/usr/local/lib/python3.6/site-packages/sanic_session/base.py", line 35, in _set_cookie_expiration
8/27/2018 10:36:07 PM response.cookies[self.cookie_name] = request['session'].sid
8/27/2018 10:36:07 PM File "/usr/local/lib/python3.6/site-packages/sanic/cookies.py", line 59, in __setitem__
8/27/2018 10:36:07 PM self.headers[cookie_header] = cookie
8/27/2018 10:36:07 PM File "/usr/local/lib/python3.6/site-packages/sanic/server.py", line 54, in __setitem__
8/27/2018 10:36:07 PM return super().__setitem__(key.casefold(), value)
8/27/2018 10:36:07 PMAttributeError: 'MultiHeader' object has no attribute 'casefold'
.
app
import os
import aioredis
from sanic_session import Session, AIORedisSessionInterface
from sanic import Sanic
app = Sanic(__name__)
session = Session(app)
@app.listener('before_server_start')
async def server_init(app, loop):
app.redis = await aioredis.create_redis_pool(os.getenv("REDIS_URL"))
session.init_app(app, interface=AIORedisSessionInterface(app.redis))
print("Init")
if __name__ == '__main__':
app.run(host="0.0.0.0", port=os.getenv("PORT", default=5000), debug=os.getenv("DEBUG_MODE", default=False))
routes
@bp.post("/")
async def save_data(request):
request.ctx.session["data"] = request.json
print(request.ctx.session.get("data"))
return response.json({"message": "Done"}, status=200)
@bp.get("/")
async def get_data(request):
return response.json(
request.ctx.session.get("data")
)
When I called get method, it return me null, but I write to the session not null
Help me please
Hey,
About a week ago, I contributed some code here. A while after, I wanted to add some features, some that would break backward compatibility and that would require me to rewrite a big portion of the tests. So I decided I will fork sanic_session
and write my own lib.
While writing the library, I saw 2 minor security issues that I think should be addressed:
Possibility for a user assigning an SID instead of strictly being generated server-side.
It would be nice to provide an easy interface for refreshing an sid of a session_dict for authentication libraries to easily avoid session fixation attacks.
(Unprivileged user having the same SID as a privileged user)
Before writing sanic_cookies
I made some commits to my fork of sanic_session
, however the changes weren't complete and not properly tested, so I wouldn't recommend blindly copying from them. I think what's better is that the 2 repos can somehow merge for a version 2.0? (this and sanic_cookies
https://github.com/omarryhan/sanic-cookies)
Unfortunately, I'm very busy these days so I can't push quick fixes to sanic_session
. I'll be more than happy to do code reviews if you want me to. I'll be even happier if someone can review my code @ sanic_cookies
Cheers :)
A new LTS for Sanic is available. Can we upgrade this package to support the new LTS / python 3.6?
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.