Use short-lived cubicweb sessions to let pyramid actually handle the web sessions
authorChristophe de Vienne <christophe@unlish.com>
Tue, 15 Jul 2014 14:25:15 +0200
changeset 11485 3905c9f06d0e
parent 11484 39768d122f97
child 11486 cadcedf11b7e
Use short-lived cubicweb sessions to let pyramid actually handle the web sessions Related to #4291173
pyramid_cubicweb/__init__.py
pyramid_cubicweb/handler.py
--- a/pyramid_cubicweb/__init__.py	Wed Jul 09 17:14:32 2014 +0200
+++ b/pyramid_cubicweb/__init__.py	Tue Jul 15 14:25:15 2014 +0200
@@ -69,7 +69,7 @@
     # On the other hand, we could refine the View concept and decide it works
     # with a cnx, and never with a WebRequest
 
-    view = vreg['views'].select(vid, request.cw_request(), **kwargs)
+    view = vreg['views'].select(vid, request.cw_request, **kwargs)
 
     view.set_stream()
     view.render()
@@ -80,27 +80,25 @@
     repo = request.registry['cubicweb.repository']
 
     response = request.response
-    userid = None
+    user_eid = None
 
     if '__login' in request.params:
         login = request.params['__login']
         password = request.params['__password']
 
         try:
-            sessionid = repo.connect(login, password=password)
-            request.session['cubicweb.sessionid'] = sessionid
-            session = repo._sessions[sessionid]
-            userid = session.user.eid
+            with repo.internal_cnx() as cnx:
+                user = repo.authenticate_user(cnx, login, password=password)
+                user_eid = user.eid
         except cubicweb.AuthenticationError:
             raise
 
-    if userid is not None:
-        headers = security.remember(request, userid)
+    if user_eid is not None:
+        headers = security.remember(request, user_eid)
 
-        if 'postlogin_path' in request.params:
-            raise HTTPSeeOther(
-                request.params['postlogin_path'],
-                headers=headers)
+        raise HTTPSeeOther(
+            request.params.get('postlogin_path', '/'),
+            headers=headers)
 
         response.headerlist.extend(headers)
 
@@ -109,8 +107,6 @@
 
 
 def _cw_cnx(request):
-    # XXX We should not need to use the session. A temporary one should be
-    # enough. (by using repoapi.connect())
     cnx = repoapi.ClientConnection(request.cw_session)
 
     def cleanup(request):
@@ -125,50 +121,50 @@
     return cnx
 
 
+def _cw_close_session(request):
+    request.cw_session.close()
+
+
 def _cw_session(request):
+    """Obtains a cw session from a pyramid request"""
     repo = request.registry['cubicweb.repository']
     config = request.registry['cubicweb.config']
 
-    sessionid = request.session.get('cubicweb.sessionid')
+    if not request.authenticated_userid:
+        login, password = config.anonymous_user()
+        sessionid = repo.connect(login, password=password)
+        session = repo._sessions[sessionid]
+        request.add_finished_callback(_cw_close_session)
+    else:
+        session = request._cw_cached_session
 
-    if sessionid not in repo._sessions:
-        if not request.authenticated_userid:
-            login, password = config.anonymous_user()
-            sessionid = repo.connect(login, password=password)
-            request.session['cubicweb.sessionid'] = sessionid
-        else:
-            sessionid = request.session.get('cubicweb.sessionid')
+    # XXX Ideally we store the cw session data in the pyramid session.
+    # BUT some data in the cw session data dictionnary makes pyramid fail.
+    #session.data = request.session
 
-    return repo._sessions[sessionid]
+    return session
 
 
 def _cw_request(request):
-    return weakref.ref(CubicWebPyramidRequest(request))
+    req = CubicWebPyramidRequest(request)
+    req.set_cnx(request.cw_cnx)
+    return req
 
 
-def get_principals(userid, request):
+def get_principals(login, request):
     repo = request.registry['cubicweb.repository']
 
-    sessionid = request.session.get('cubicweb.sessionid')
+    try:
+        sessionid = repo.connect(
+            str(login), __pyramid_directauth=authplugin.EXT_TOKEN)
+        session = repo._sessions[sessionid]
+        request._cw_cached_session = session
+        request.add_finished_callback(_cw_close_session)
+    except:
+        log.exception("Failed")
+        raise
 
-    if sessionid is None or sessionid not in repo._sessions:
-        try:
-            sessionid = repo.connect(
-                str(userid), __pyramid_directauth=authplugin.EXT_TOKEN)
-        except:
-            log.exception("Failed")
-            raise
-        request.session['cubicweb.sessionid'] = sessionid
-
-    #session = repo._session[sessionid]
-
-    with repo.internal_cnx() as cnx:
-        groupnames = [r[1] for r in cnx.execute(
-            'Any X, N WHERE X is CWGroup, X name N, '
-            'U in_group X, U eid %(userid)s',
-            {'userid': userid})]
-
-    return groupnames
+    return session.user.groups
 
 
 from pyramid.authentication import SessionAuthenticationPolicy
--- a/pyramid_cubicweb/handler.py	Wed Jul 09 17:14:32 2014 +0200
+++ b/pyramid_cubicweb/handler.py	Tue Jul 15 14:25:15 2014 +0200
@@ -36,8 +36,7 @@
         # core_handle and CubicWebPublisher altogether so that the various CW
         # exceptions are converted to their pyramid equivalent.
 
-        req = request.cw_request()
-        req.set_cnx(request.cw_cnx)
+        req = request.cw_request
 
         # XXX The main handler of CW forbid anonymous https connections
         # I guess we can drop this "feature" but in doubt I leave this comment
@@ -55,9 +54,6 @@
         except LogOut as ex:
             # The actual 'logging out' logic should be in separated function
             # that is accessible by the pyramid views
-            del req._request.session['cubicweb.sessionid']
-            if not req.session.closed:
-                req.session.repo.close(req.session.sessionid)
             headers = security.forget(request)
             raise HTTPSeeOther(ex.url, headers=headers)
         except Redirect as ex: