Set up multiple session handlers on python webapp2 - python

I'm writing a simple web application in google appengine and python. In this application I need to handle two types of sessions:
the "long term session" that stores information about users, current page ecc, with long max_age parameter and the "short term session" with max_age about 20 minutes that keep an access token for the autentication via API.
I have implemented the following BaseHandler:
import webapp2
from webapp2_extras import sessions
class BaseHandler(webapp2.RequestHandler):
def dispatch(self):
# Get a session store for this request.
self.session_store = sessions.get_store(request=self.request)
try:
# Dispatch the request.
webapp2.RequestHandler.dispatch(self)
finally:
# Save all sessions.
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def session(self):
# Returns a session using the default cookie key.
return self.session_store.get_session(backend='memcache')
#webapp2.cached_property
def session_auth(self):
return self.session_store.get_session(backend='memcache', max_age=20*60)<code>
the problem is that all sessions have max_age=20*60 seconds (and not only the sessions accessible by self.session_auth)..
How should I solve this?
thanks

Try setting your config params:
config = {}
config['webapp2_extras.sessions'] = {
'session_max_age': 100000, # try None here also, though that is the default
}
app = webapp2.WSGIApplication([
('/', HomeHandler),
], debug=True, config=config)

Related

How to pass return kwargs from flask's before_request to endpoint functions [duplicate]

I found this code that times each response, but I'm not sure where g is supposed to come from. What is g?
#app.before_request
def before_request():
g.start = time.time()
#app.teardown_request
def teardown_request(exception=None):
diff = time.time() - g.start
print diff
g is an object provided by Flask. It is a global namespace for holding any data you want during a single app context. For example, a before_request handler could set g.user, which will be accessible to the route and other functions.
from flask import g
#app.before_request
def load_user():
user = User.query.get(request.session.get("user_id"))
g.user = user
#app.route("/admin")
def admin():
if g.user is None or not g.user.is_admin:
return redirect(url_for("index"))
An app context lasts for one request / response cycle, g is not appropriate for storing data across requests. Use a database, redis, the session, or another external data source for persisting data.
Note that the dev server and any web server will output timing information in the logs already. If you really want to profile your code, you can use the Werkzeug application profiler.

How to check session.modifed = True in response object in Django test?

[django 1.4, python 2.7]
We have a custom session engine that stores session to redis (Pretty much the same as db session engine, just stores in redis).
There are some public endpoints that the session middleware stores a session to redis every time it is invoked (session.modified is set to True), which is not our intention.
I am trying to write unit tests to check which endpoints do generate session.
# This endpoint would always save session to redis
def test_endpoint(request, **kwargs):
request.session['asdfsaf'] = 1
# Or, request.session.modified = True (Both fine)
return httplib.OK, "good"
Attempt 1:
response = self.client.post(reverse('test_endpoint', kwargs=self.kwargs), data={...})
print(response.status_code, response.content) # Ok
print('am i modified?', self.client.session.modified) # False
for i in range(10):
redis = get_redis_instance(db=i)
print("redis key", i, redis.keys()) # All return empty redis
This unit test is unable to detect a session is saved via django session middleware.
probably due to the fact that a new SessionStore is created every time this property is accessed (https://docs.djangoproject.com/en/2.2/topics/testing/tools/#persistent-state)
Attempt 2:
Taking inspiration from SessionMiddlewareTests:
request = RequestFactory().post(reverse('test_endpoint', kwargs=self.kwargs), data={...})
response = HttpResponse('Session test')
middleware = SessionMiddleware()
middleware.process_request(request)
# Handle the response through the middleware
response = middleware.process_response(request, response)
print('am i modified', request.session.modified) # False
for i in range(10):
redis = get_redis_instance(db=i)
print("redis key", i, redis.keys()) # All return empty redis
Again, I am unable to detect this endpoint is in fact storing session to redis.
How can I programmatically check whether an API creating a session? (that somehow creates it via setting session.modified flag True)?
Last thing I want to do is to test manually and checking redis db myself. (Over 200 public end points and unable to prevent similar issue happening in future).
Many thanks in advance!

How to close MySql connections using Sqlalchemy and Flask?

I built an API using Flask and I'm using a service (as below) to create my database connections.
class DatabaseService:
def __init__(self):
self.connection_string = "foo"
def create_connection(self):
engine = create_engine(self.connection_string)
Session = scoped_session(sessionmaker(bind=engine))
return Session
In my app.py I add and remove these sessions to Flask application context (g) as the docs suggests.
So I can reference to g.session always I need them.
def get_session():
if 'session' not int g:
session = database_service.create_session()
g.session = session
#app.teardown_appcontext
def shutdown_session(exception=None):
if 'session' in g:
g.session.remove()
return None
This way every request has your own session that will close after processing. Am I right?
I don't understand why the connections are still alive on my database after the request is already done.
Always I run the command show processlist I can see multiple connections sleeping from my API.
I see no problem opening and closing sessions per-request
my_session = Session(engine)
my_session.execute(some_query)
my_session.close()

Using pytest with gaesessions session middleware in appengine

When I run py.test --with-gae, I get the following error (I have pytest_gae plugin installed):
def get_current_session():
"""Returns the session associated with the current request."""
> return _tls.current_session
E AttributeError: 'thread._local' object has no attribute 'current_session'
gaesessions/__init__.py:50: AttributeError
I'm using pytest to test my google appengine application. The application runs fine when run in the localhost SDK or when deployed to GAE servers. I just can't figure out how to make pytest work with gaesessions.
My code is below:
test_handlers.py
from webtest import TestApp
import appengine_config
def pytest_funcarg__anon_user(request):
from main import app
app = appengine_config.webapp_add_wsgi_middleware(app)
return TestApp(app)
def test_session(anon_user):
from gaesessions import get_current_session
assert get_current_session()
appengine_config.py
from gaesessions import SessionMiddleware
def webapp_add_wsgi_middleware(app):
from google.appengine.ext.appstats import recording
app = recording.appstats_wsgi_middleware(app)
app = SessionMiddleware(app, cookie_key="replaced-with-this-boring-text")
return app
Relevant code from gaesessions:
# ... more code are not show here ...
_tls = threading.local()
def get_current_session():
"""Returns the session associated with the current request."""
return _tls.current_session
# ... more code are not show here ...
class SessionMiddleware(object):
"""WSGI middleware that adds session support.
``cookie_key`` - A key used to secure cookies so users cannot modify their
content. Keys should be at least 32 bytes (RFC2104). Tip: generate your
key using ``os.urandom(64)`` but do this OFFLINE and copy/paste the output
into a string which you pass in as ``cookie_key``. If you use ``os.urandom()``
to dynamically generate your key at runtime then any existing sessions will
become junk every time your app starts up!
``lifetime`` - ``datetime.timedelta`` that specifies how long a session may last. Defaults to 7 days.
``no_datastore`` - By default all writes also go to the datastore in case
memcache is lost. Set to True to never use the datastore. This improves
write performance but sessions may be occassionally lost.
``cookie_only_threshold`` - A size in bytes. If session data is less than this
threshold, then session data is kept only in a secure cookie. This avoids
memcache/datastore latency which is critical for small sessions. Larger
sessions are kept in memcache+datastore instead. Defaults to 10KB.
"""
def __init__(self, app, cookie_key, lifetime=DEFAULT_LIFETIME, no_datastore=False, cookie_only_threshold=DEFAULT_COOKIE_ONLY_THRESH):
self.app = app
self.lifetime = lifetime
self.no_datastore = no_datastore
self.cookie_only_thresh = cookie_only_threshold
self.cookie_key = cookie_key
if not self.cookie_key:
raise ValueError("cookie_key MUST be specified")
if len(self.cookie_key) < 32:
raise ValueError("RFC2104 recommends you use at least a 32 character key. Try os.urandom(64) to make a key.")
def __call__(self, environ, start_response):
# initialize a session for the current user
_tls.current_session = Session(lifetime=self.lifetime, no_datastore=self.no_datastore, cookie_only_threshold=self.cookie_only_thresh, cookie_key=self.cookie_key)
# create a hook for us to insert a cookie into the response headers
def my_start_response(status, headers, exc_info=None):
_tls.current_session.save() # store the session if it was changed
for ch in _tls.current_session.make_cookie_headers():
headers.append(('Set-Cookie', ch))
return start_response(status, headers, exc_info)
# let the app do its thing
return self.app(environ, my_start_response)
The problem is that your gae sessions is not yet called until the app is also called. The app is only called when you make a request to it. Try inserting a request call before you check for the session value. Check out the revised test_handlers.py code below.
def test_session(anon_user):
anon_user.get("/") # get any url to call the app to create a session.
from gaesessions import get_current_session
assert get_current_session()

Webapp2 sessions are lost between requests

I'm using webapp2_extras to set a session variable. If I print the variable after setting it the value is correct. The save_sessions call is also made. However, when I make the request again the variable isn't set.
Most of the code is taken from the webapp2 documentation:
import webapp2
from webapp2_extras import sessions
class BaseHandler(webapp2.RequestHandler):
def dispatch(self):
self.session_store = sessions.get_store(request=self.request)
try:
webapp2.RequestHandler.dispatch(self)
finally:
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def session(self):
return self.session_store.get_session()
class MainHandler(BaseHandler):
def get(self):
foo = self.session.get('foo')
print foo
self.session['foo'] = 'bar'
foo = self.session.get('foo')
print foo
self.response.write(BaseHandler)
config = {}
config["webapp2_extras.sessions"] = {
"secret_key": "key",
}
app = webapp2.WSGIApplication([
('/', MainHandler)
], debug=True, config=config)
This always returns "None something" but I want it to return "something something" for the second request.
I know this is a fairly old question but I just ran into the same problem and here is what solved it for me.
I altered the webapp2 config object to add in the auth param.
What used to be:
config = {}
config["webapp2_extras.sessions"] = {"secret_key": "key" }
app = webapp2.WSGIApplication([('/', MainHandler)], debug=True, config=config)
Should now be:
config = {}
config["webapp2_extras.sessions"] = {"secret_key": "key" }
config["webapp2_extras.auth"] = {'session_backend': 'securecookie'}
app = webapp2.WSGIApplication([('/', MainHandler)], debug=True, config=config)
Looking at my config I have this to set the location of the session store (for app engine the config is a bit different to the standard I understand)
#webapp2.cached_property
def session(self):
# Returns a session using the default cookie key.
return self.session_store.get_session(name='mc_session',
factory=sessions_memcache.MemcacheSessionFactory)
Try that perhaps? Otherwise your code look OK to me, without actually running it.
You'll also need this import:
from webapp2_extras import sessions_memcache
Webapp2 Memcache sessions
Assuming you are using the datastore as the session backend, this probably has to do with eventual consistency. If you try to postpone the second request for a few seconds, and then attempt the request you will probably see the session data, and that proves it.
Switching to securecookie may help, though it didn't work for me for some reason... Still looking into it.
A big part of my problem was actually due to my ignorance of the cookie standards. It is explained in this other stackoverflow question.
I had a similar issue. What fixed it for me was to set the backend to be different from the default (securecookie). setting it to memcache seemed to work for me.
#webapp2.cached_property
def session(self):
return self.session_store.get_session(backend="memcache")
I also set this in the config under .auth. Not sure if this overwrites the backend parameter for get_session anyway...
config["webapp2_extras.auth"] = {'session_backend': 'memcache'}

Categories