pyramid_cubicweb/bwcompat.py
author Christophe de Vienne <christophe@unlish.com>
Thu, 31 Jul 2014 17:48:32 +0200
changeset 11492 b0b8942cdb80
child 11496 500615e26063
permissions -rw-r--r--
Separate into 4 modules * init_instance: load the cubicweb repository from the `pyramid_cubicweb.instance` configuration key * defaults: provides cw-like defaults for the authentication and session management * core: make cubicweb use the authentication and session management of pyramid. It assumes the application provides the auth policies and session factory, and that the `cubicweb.*` registry entries are correctly initialised. This is this only required module or pyramid_cubicweb, the other ones are optional if the application provides its own versions of what they do. * bwcompat: provides a catchall route that delegate the request handling to an old-fashion cubicweb publisher (ie using url_resolver and controllers). Related to #4291173

from pyramid import security
from pyramid.httpexceptions import HTTPSeeOther
from pyramid import httpexceptions

import cubicweb
import cubicweb.web

from cubicweb.web.application import CubicWebPublisher

from cubicweb.web import LogOut, cors

from pyramid_cubicweb.core import cw_to_pyramid


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):
        raise LogOut(url=goto_url)


class CubicWebPyramidHandler(object):
    def __init__(self, appli):
        self.appli = appli

    def __call__(self, request):
        """
        Handler that mimics what CubicWebPublisher.main_handle_request and
        CubicWebPublisher.core_handle do
        """

        # XXX The main handler of CW forbid anonymous https connections
        # I guess we can drop this "feature" but in doubt I leave this comment
        # so we don't forget about it. (cdevienne)

        req = request.cw_request
        vreg = request.registry['cubicweb.registry']

        try:
            try:
                with cw_to_pyramid(request):
                    cors.process_request(req, vreg.config)
                    ctrlid, rset = self.appli.url_resolver.process(req, req.path)

                    try:
                        controller = vreg['controllers'].select(
                            ctrlid, req, appli=self.appli)
                    except cubicweb.NoSelectableObject:
                        raise httpexceptions.HTTPUnauthorized(
                            req._('not authorized'))

                    req.update_search_state()
                    content = controller.publish(rset=rset)

                    # XXX this auto-commit should be handled by the cw_request cleanup
                    # or the pyramid transaction manager.
                    # It is kept here to have the ValidationError handling bw
                    # compatible
                    if req.cnx:
                        txuuid = req.cnx.commit()
                        # commited = True
                        if txuuid is not None:
                            req.data['last_undoable_transaction'] = txuuid
            except cors.CORSPreflight:
                request.response.status_int = 200
            except cubicweb.web.ValidationError as ex:
                # XXX The validation_error_handler implementation is light, we
                # should redo it better in cw_to_pyramid, so it can be properly
                # handled when raised from a cubicweb view.
                # BUT the real handling of validation errors should be done
                # earlier in the controllers, not here. In the end, the
                # ValidationError should never by handled here.
                content = self.appli.validation_error_handler(req, ex)
            except cubicweb.web.RemoteCallFailed as ex:
                # XXX The default pyramid error handler (or one that we provide
                # for this exception) should be enough
                # content = self.appli.ajax_error_handler(req, ex)
                raise

            if content is not None:
                request.response.body = content

            # XXX CubicWebPyramidRequest.headers_out should
            # access directly the pyramid response headers.
            request.response.headers.clear()
            for k, v in req.headers_out.getAllRawHeaders():
                for item in v:
                    request.response.headers.add(k, item)

        except LogOut as ex:
            # The actual 'logging out' logic should be in separated function
            # that is accessible by the pyramid views
            headers = security.forget(request)
            raise HTTPSeeOther(ex.url, headers=headers)
        # except AuthenticationError:
        # XXX I don't think it makes sens to catch this ex here (cdevienne)

        return request.response


def includeme(config):
    # 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.
    cwconfig = config.registry['cubicweb.config']
    repository = config.registry['cubicweb.repository']
    cwappli = CubicWebPublisher(
        repository, cwconfig,
        session_handler_fact=PyramidSessionHandler)
    handler = CubicWebPyramidHandler(cwappli)

    config.registry['cubicweb.appli'] = cwappli
    config.registry['cubicweb.handler'] = handler

    config.add_route('catchall', pattern='*path')
    config.add_view(handler, route_name='catchall')