A persistent session manager class for Quixote 2.x.
from quixote import get_request, get_publisher, get_cookie, get_response
from quixote.util import randbytes
from session3.Session import Session
A persistent session manager for Quixote.
class SessionManager:
ACCESS_TIME_RESOLUTION = 1 # in seconds
init takes a session store instance and (optionally) the
session class to use for storing session information. (This
defaults to Session.Session
).
def __init__(self, store_obj, session_class=Session):
self.store = store_obj
self.session_class = session_class
self.expired_sessions = {}
def __repr__(self):
return "<%s at %x>" % (self.__class__.__name__, id(self))
() -> Session
def get_session(self):
Fetch or create a session object for the current session, and return it. If a session cookie is found in the HTTP request object, use it to look up and return an existing session object. If no session cookie is found, create a new session.
Note that this method does not cause the new session to be stored in the session manager, nor does it drop a session cookie on the user. Those are both the responsibility of finish_successful_request().
config = get_publisher().config
id = self._get_session_id(config)
session = None
if id:
session = self.store.load_session(id)
if session is None:
session = self._create_session()
session._set_access_time(self.ACCESS_TIME_RESOLUTION)
return session
(session : Session) -> bool
def maintain_session(self, session):
Maintain session information. This method is called after servicing an HTTP request, just before the response is returned. If a session contains information a cookie is dropped on the client and True is returned. If not, the session is forcibly expired and False is returned.
if not session.has_info():
Session has no useful info – forget it. If it previously had useful information and no longer does, we have to explicitly forget it.
if session.id and self.has_session(session.id):
self.expire_session()
return 0
if session.id is None:
This is the first time this session has had useful info – store it and set the session cookie.
session.id = self._make_session_id()
self.set_session_cookie(session.id)
return 1
Expire the current session, ie. revoke the session cookie from the client, remove the session object from the current request, and list it for permanent removal.
def expire_session(self):
self.revoke_session_cookie()
request = get_request()
if request.session:
self.expired_sessions[request] = request.session
request.session = None
(session_id : string) -> boolean
def has_session(self, session_id):
Return true if a session identified by ‘session_id’ exists in the session manager.
return self.store.load_session(session_id)
Clear any residual session information for this request.
def clear_session(self, request):
if request in self.expired_sessions:
del self.expired_sessions[request]
– Hooks into the Quixote main loop ------------------------------
Called near the beginning of each request: after the HTTPRequest object has been built, but before we traverse the URL or call the callable object found by URL traversal.
def start_request(self):
session = self.get_session()
get_request().session = session
session.start_request()
Called near the end of each successful request. Not called if
def finish_successful_request(self):
there were any errors processing the request.
request = get_request()
session = request.session
keep session?
if session and self.maintain_session(session):
self.store.save_session(session)
or delete, because it’s expired?
elif request in self.expired_sessions:
session = self.expired_sessions[request]
if session.id:
self.store.delete_session(session)
self.clear_session(request)
Called near the end of a failed request (i.e. a exception that was
def finish_failed_request(self):
not a PublisherError was raised.
request = get_request()
self.clear_session(request)
– Session management -------------------------------------------- these build on the storage mechanism implemented by the above mapping methods, and are concerned with all the high- level details of managing web sessions
(id : string) -> Session
def new_session(self, id):
Return a new session object, ie. an instance of the session_class class passed to the constructor (defaults to Session).
return self.session_class(id)
() -> string
def _get_session_id(self, config):
Find the ID of the current session by looking for the session cookie in the request. Return None if no such cookie or the cookie has been expired, otherwise return the cookie’s value.
id = get_cookie(config.session_cookie_name)
if id == "" or id == "*del*":
return None
else:
return id
def _make_session_id(self):
Generate a session ID, which is just the value of the session cookie we are about to drop on the user. (It’s also the key used with the session manager mapping interface.)
id = None
while id is None or self.has_session(id):
id = randbytes(8) # 64-bit random number
return id
def _create_session(self):
Create a new session object, with no ID for now - one will be assigned later if we save the session.
return self.new_session(None)
def _set_cookie(self, value, **attrs):
config = get_publisher().config
name = config.session_cookie_name
if config.session_cookie_path:
path = config.session_cookie_path
else:
path = get_request().get_environ('SCRIPT_NAME')
if not path.endswith("/"):
path += "/"
domain = config.session_cookie_domain
Modified R J Ladyman 2010-11-23 to include secure and httponly as per Quixote 2.7b1
attrs = attrs.copy()
if config.session_cookie_secure:
attrs['secure'] = 1
if config.session_cookie_httponly:
attrs['httponly'] = 1
End of modification R J Ladyman 2010-11-23
get_response().set_cookie(name, value, domain=domain, path=path, **attrs)
return name
(session_id : string)
def set_session_cookie(self, session_id):
Ensure that a session cookie with value ‘session_id’ will be returned to the client via the response object.
self._set_cookie(session_id)
Remove the session cookie from the remote user’s session by resetting the value and maximum age in the response object. Also remove the cookie from the request so that further processing of this request does not see the cookie’s revoked value.
def revoke_session_cookie(self):
cookie_name = self._set_cookie("", max_age=0)
if get_cookie(cookie_name) is not None:
del get_request().cookies[cookie_name]
(must_exist : boolean = false) -> bool
def has_session_cookie(self, must_exist=False):
Return true if the request already has a cookie identifying a session object. If ‘must_exist’ is true, the cookie must correspond to a currently existing session; otherwise (the default), we just check for the existence of the session cookie and don’t inspect its content at all.
config = get_publisher().config
id = get_cookie(config.session_cookie_name)
if id is None:
return False
if must_exist:
return self.has_session(id)
else:
return True