pyramid_cubicweb/core.py
changeset 11492 b0b8942cdb80
child 11493 00e5cb9771c5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pyramid_cubicweb/core.py	Thu Jul 31 17:48:32 2014 +0200
@@ -0,0 +1,223 @@
+from contextlib import contextmanager
+from warnings import warn
+
+import rql
+
+from cubicweb.web.request import CubicWebRequestBase
+from cubicweb import repoapi
+
+import cubicweb
+import cubicweb.web
+
+from pyramid import security, httpexceptions
+from pyramid.httpexceptions import HTTPSeeOther
+
+from pyramid_cubicweb import authplugin
+
+import logging
+
+log = logging.getLogger(__name__)
+
+
+@contextmanager
+def cw_to_pyramid(request):
+    """Wrap a call to the cubicweb API.
+
+    All CW exceptions will be transformed into their pyramid equivalent.
+    When needed, some CW reponse bits may be converted too (mainly headers)"""
+    try:
+        yield
+    except cubicweb.web.Redirect as ex:
+        assert 300 <= ex.status < 400
+        raise httpexceptions.status_map[ex.status](ex.location)
+    except cubicweb.web.StatusResponse as ex:
+        warn('[3.16] StatusResponse is deprecated use req.status_out',
+             DeprecationWarning, stacklevel=2)
+        request.body = ex.content
+        request.status_int = ex.status
+    except cubicweb.web.Unauthorized as ex:
+        raise httpexceptions.HTTPForbidden(
+            request.cw_request._(
+                'You\'re not authorized to access this page. '
+                'If you think you should, please contact the site '
+                'administrator.'))
+    except cubicweb.web.Forbidden:
+        raise httpexceptions.HTTPForbidden(
+            request.cw_request._(
+                'This action is forbidden. '
+                'If you think it should be allowed, please contact the site '
+                'administrator.'))
+    except (rql.BadRQLQuery, cubicweb.web.RequestError) as ex:
+        raise
+
+
+class CubicWebPyramidRequest(CubicWebRequestBase):
+    def __init__(self, request):
+        self._request = request
+
+        self.path = request.upath_info
+
+        vreg = request.registry['cubicweb.appli'].vreg
+        https = request.scheme == 'https'
+
+        post = request.params
+        headers_in = request.headers
+
+        super(CubicWebPyramidRequest, self).__init__(vreg, https, post,
+                                                     headers=headers_in)
+
+    def is_secure(self):
+        return self._request.scheme == 'https'
+
+    def relative_path(self, includeparams=True):
+        path = self._request.path[1:]
+        if includeparams and self._request.query_string:
+            return '%s?%s' % (path, self._request.query_string)
+        return path
+
+    def instance_uri(self):
+        return self._request.application_url
+
+    def get_full_path(self):
+        path = self._request.path
+        if self._request.query_string:
+            return '%s?%s' % (path, self._request.query_string)
+        return path
+
+    def http_method(self):
+        return self._request.method
+
+    def _set_status_out(self, value):
+        self._request.response.status_int = value
+
+    def _get_status_out(self):
+        return self._request.response.status_int
+
+    status_out = property(_get_status_out, _set_status_out)
+
+
+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
+
+    with cw_to_pyramid(request):
+        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
+    user_eid = None
+
+    if '__login' in request.params:
+        login = request.params['__login']
+        password = request.params['__password']
+
+        try:
+            with repo.internal_cnx() as cnx:
+                user = repo.authenticate_user(cnx, login, password=password)
+                user_eid = user.eid
+        except cubicweb.AuthenticationError:
+            raise
+
+    if user_eid is not None:
+        headers = security.remember(request, user_eid)
+
+        raise HTTPSeeOther(
+            request.params.get('postlogin_path', '/'),
+            headers=headers)
+
+        response.headerlist.extend(headers)
+
+    response.text = render_view(request, 'login')
+    return response
+
+
+def _cw_cnx(request):
+    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_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']
+
+    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
+
+    # 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 session
+
+
+def _cw_request(request):
+    req = CubicWebPyramidRequest(request)
+    req.set_cnx(request.cw_cnx)
+    return req
+
+
+def get_principals(login, request):
+    repo = request.registry['cubicweb.repository']
+
+    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
+
+    return session.user.groups
+
+
+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):
+    repo = config.registry['cubicweb.repository']
+
+    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')