pyramid_cubicweb/handler.py
author Christophe de Vienne <christophe@unlish.com>
Tue, 15 Jul 2014 17:37:50 +0200
changeset 11487 04252e9ff549
parent 11485 3905c9f06d0e
child 11488 baa0988a6029
permissions -rw-r--r--
Skip core_handle, add a context manager to handle cubicweb errors The context manager is also used to catch errors in render_view. It handles the 'external' errors raised by cubicweb code. The more internal errors, the one that should occur only in url resolving and cubicweb controllers, are handled directly in CubicWebPyramidHandler. ValidationError is handled by CubicWebPyramidHandler for now, but should probably be handled by cw_to_pyramid 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 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.
                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

            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')