diff -r 000000000000 -r b97547f5f1fa goa/appobjects/sessions.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/goa/appobjects/sessions.py Wed Nov 05 15:52:50 2008 +0100 @@ -0,0 +1,271 @@ +"""persistent sessions stored in big table + +:organization: Logilab +:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr + +XXX TODO: +* cleanup persistent session +* use user as ancestor? +""" +__docformat__ = "restructuredtext en" + +from pickle import loads, dumps +from time import localtime, strftime + +from logilab.common.decorators import cached, clear_cache + +from cubicweb import UnknownEid, BadConnectionId +from cubicweb.dbapi import Connection, ConnectionProperties, repo_connect +from cubicweb.server.session import Session +from cubicweb.web import InvalidSession +from cubicweb.web.application import AbstractSessionManager +from cubicweb.web.application import AbstractAuthenticationManager + +from google.appengine.api.datastore import Key, Entity, Get, Put, Delete, Query +from google.appengine.api.datastore_errors import EntityNotFoundError +from google.appengine.api.datastore_types import Blob + +try: + del Connection.__del__ +except AttributeError: + pass # already deleted + + +class GAEAuthenticationManager(AbstractAuthenticationManager): + """authenticate user associated to a request and check session validity, + using google authentication service + """ + + def __init__(self, *args, **kwargs): + super(GAEAuthenticationManager, self).__init__(*args, **kwargs) + self._repo = self.config.repository(vreg=self.vreg) + + def authenticate(self, req, _login=None, _password=None): + """authenticate user and return an established connection for this user + + :raise ExplicitLogin: if authentication is required (no authentication + info found or wrong user/password) + """ + if _login is not None: + login, password = _login, _password + else: + login, password = req.get_authorization() + # remove possibly cached cursor coming from closed connection + clear_cache(req, 'cursor') + cnxprops = ConnectionProperties(self.vreg.config.repo_method, + close=False, log=False) + cnx = repo_connect(self._repo, login, password, cnxprops=cnxprops) + self._init_cnx(cnx, login, password) + # associate the connection to the current request + req.set_connection(cnx) + return cnx + + def _init_cnx(self, cnx, login, password): + cnx.anonymous_connection = self.config.is_anonymous_user(login) + cnx.vreg = self.vreg + cnx.login = login + cnx.password = password + + +class GAEPersistentSessionManager(AbstractSessionManager): + """manage session data associated to a session identifier""" + + def __init__(self, *args, **kwargs): + super(GAEPersistentSessionManager, self).__init__(*args, **kwargs) + self._repo = self.config.repository(vreg=self.vreg) + + def get_session(self, req, sessionid): + """return existing session for the given session identifier""" + # search a record for the given session + key = Key.from_path('CubicWebSession', 'key_' + sessionid, parent=None) + try: + record = Get(key) + except EntityNotFoundError: + raise InvalidSession() + repo = self._repo + if self.has_expired(record): + repo._sessions.pop(sessionid, None) + Delete(record) + raise InvalidSession() + # associate it with a repository session + try: + reposession = repo._get_session(sessionid) + user = reposession.user + # touch session to avoid closing our own session when sessions are + # cleaned (touch is done on commit/rollback on the server side, too + # late in that case) + reposession._touch() + except BadConnectionId: + # can't found session in the repository, this probably mean the + # session is not yet initialized on this server, hijack the repo + # to create it + # use an internal connection + ssession = repo.internal_session() + # try to get a user object + try: + user = repo.authenticate_user(ssession, record['login'], + record['password']) + finally: + ssession.close() + reposession = Session(user, self._repo, _id=sessionid) + self._repo._sessions[sessionid] = reposession + cnx = Connection(self._repo, sessionid) + return self._get_proxy(req, record, cnx, user) + + def open_session(self, req): + """open and return a new session for the given request""" + cnx = self.authmanager.authenticate(req) + # avoid rebuilding a user + user = self._repo._get_session(cnx.sessionid).user + # build persistent record for session data + record = Entity('CubicWebSession', name='key_' + cnx.sessionid) + record['login'] = cnx.login + record['password'] = cnx.password + record['anonymous_connection'] = cnx.anonymous_connection + Put(record) + return self._get_proxy(req, record, cnx, user) + + def close_session(self, proxy): + """close session on logout or on invalid session detected (expired out, + corrupted...) + """ + proxy.close() + + def current_sessions(self): + for record in Query('CubicWebSession').Run(): + yield ConnectionProxy(record) + + def _get_proxy(self, req, record, cnx, user): + proxy = ConnectionProxy(record, cnx, user) + user.req = req + req.set_connection(proxy, user) + return proxy + + +class ConnectionProxy(object): + + def __init__(self, record, cnx=None, user=None): + self.__record = record + self.__cnx = cnx + self.__user = user + self.__data = None + self.__is_dirty = False + self.sessionid = record.key().name()[4:] # remove 'key_' prefix + + def __repr__(self): + sstr = '') + self.w(u'%s web sessions closed
\n' % nbclosed) + # clean repository sessions + repo = self.config.repository(vreg=self.vreg) + nbclosed = repo.clean_sessions() + self.w(u'%s repository sessions closed
\n' % nbclosed) + self.w(u'%s remaining sessions
\n' % remaining) + self.w(u'') +