pyramid_cubicweb/__init__.py
changeset 11482 151b8a4b9f3f
parent 11480 79ac26923432
child 11484 39768d122f97
--- a/pyramid_cubicweb/__init__.py	Fri Sep 12 09:28:32 2014 +0200
+++ b/pyramid_cubicweb/__init__.py	Sun Jul 06 18:06:10 2014 +0200
@@ -1,6 +1,21 @@
 from cubicweb.web.request import CubicWebRequestBase
 from cubicweb.cwconfig import CubicWebConfiguration
 from cubicweb.web.application import CubicWebPublisher
+from cubicweb import repoapi
+
+import cubicweb
+import cubicweb.web
+
+from pyramid import security
+from pyramid.httpexceptions import HTTPSeeOther
+
+from pyramid_cubicweb import authplugin
+
+import weakref
+
+import logging
+
+log = logging.getLogger(__name__)
 
 
 class CubicWebPyramidRequest(CubicWebRequestBase):
@@ -48,13 +63,80 @@
     status_out = property(_get_status_out, _set_status_out)
 
 
+class PyramidSessionHandler(object):
+    """A CW Session handler that rely on the pyramid API to fetch the needed
+    informations"""
+
+    def __init__(self, appli):
+        self.appli = appli
+
+    def get_session(self, req):
+        return req._request.cw_session
+
+    def logout(self, req, goto_url):
+        del req._request.session['cubicweb.sessionid']
+        if not req.session.closed:
+            req.session.repo.close(req.session.sessionid)
+        for name, value in security.forget(req._request):
+            req.headers_out.setHeader(name, value)
+        raise cubicweb.web.LogOut(url=goto_url)
+
+
+def render_view(request, vid, **kwargs):
+    vreg = request.registry['cubicweb.registry']
+    # XXX The select() function could, know how to handle a pyramid
+    # request, and feed it directly to the views that supports it.
+    # 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.set_stream()
+    view.render()
+    return view._stream.getvalue()
+
+
+def login(request):
+    repo = request.registry['cubicweb.repository']
+
+    response = request.response
+    userid = 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
+        except cubicweb.AuthenticationError:
+            raise
+
+    if userid is not None:
+        headers = security.remember(request, userid)
+
+        if 'postlogin_path' in request.params:
+            raise HTTPSeeOther(
+                request.params['postlogin_path'],
+                headers=headers)
+
+        response.headerlist.extend(headers)
+
+    response.text = render_view(request, 'login')
+    return response
+
+
 class CubicWebPyramidHandler(object):
     def __init__(self, appli):
         self.appli = appli
 
     def __call__(self, request):
-        req = CubicWebPyramidRequest(request)
-        request.response.body = self.appli.handle_request(req, req.path)
+        req = request.cw_request()
+        result = self.appli.handle_request(req, req.path)
+        if result is not None:
+            request.response.body = result
         request.response.headers.clear()
         for k, v in req.headers_out.getAllRawHeaders():
             for item in v:
@@ -62,11 +144,118 @@
         return request.response
 
 
+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):
+        if request.exception is not None:
+            cnx.rollback()
+        else:
+            cnx.commit()
+        cnx.__exit__(None, None, None)
+
+    request.add_finished_callback(cleanup)
+    cnx.__enter__()
+    return cnx
+
+
+def _cw_session(request):
+    repo = request.registry['cubicweb.repository']
+    config = request.registry['cubicweb.config']
+
+    sessionid = request.session.get('cubicweb.sessionid')
+
+    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')
+
+    return repo._sessions[sessionid]
+
+
+def _cw_request(request):
+    return weakref.ref(CubicWebPyramidRequest(request))
+
+
+def get_principals(userid, request):
+    repo = request.registry['cubicweb.repository']
+
+    sessionid = request.session.get('cubicweb.sessionid')
+
+    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
+
+
+from pyramid.authentication import SessionAuthenticationPolicy
+from pyramid.authorization import ACLAuthorizationPolicy
+from pyramid.session import SignedCookieSessionFactory
+
+
+def hello_world(request):
+    request.response.text = \
+        u"<html><body>Hello %s</body></html>" % request.cw_cnx.user.login
+    return request.response
+
+
 def includeme(config):
     appid = config.registry.settings['cubicweb.instance']
     cwconfig = CubicWebConfiguration.config_for(appid)
 
-    cwappli = CubicWebPublisher(cwconfig.repository(), cwconfig)
+    config.set_session_factory(
+        SignedCookieSessionFactory(
+            secret=config.registry.settings['session.secret']
+        ))
+
+    config.set_authentication_policy(
+        SessionAuthenticationPolicy(callback=get_principals))
+    config.set_authorization_policy(ACLAuthorizationPolicy())
+
+    config.registry['cubicweb.config'] = cwconfig
+    config.registry['cubicweb.repository'] = repo = cwconfig.repository()
+    config.registry['cubicweb.registry'] = repo.vreg
+
+    repo.system_source.add_authentifier(authplugin.DirectAuthentifier())
+
+    config.add_request_method(
+        _cw_session, name='cw_session', property=True, reify=True)
+    config.add_request_method(
+        _cw_cnx, name='cw_cnx', property=True, reify=True)
+    config.add_request_method(
+        _cw_request, name='cw_request', property=True, reify=True)
+
+    config.add_route('login', '/login')
+    config.add_view(login, route_name='login')
+
+    config.add_route('hello', '/hello')
+    config.add_view(hello_world, route_name='hello')
+
+    # Set up a defaut route to handle non-catched urls.
+    # This is to keep legacy compatibility for cubes that makes use of the
+    # cubicweb controllers.
+    cwappli = CubicWebPublisher(
+        cwconfig.repository(), cwconfig,
+        session_handler_fact=PyramidSessionHandler)
     handler = CubicWebPyramidHandler(cwappli)
 
     config.registry['cubicweb.appli'] = cwappli