pyramid_cubicweb/__init__.py
author Christophe de Vienne <christophe@unlish.com>
Wed, 09 Jul 2014 17:14:32 +0200
changeset 11484 39768d122f97
parent 11482 151b8a4b9f3f
child 11485 3905c9f06d0e
permissions -rw-r--r--
Isolate the default handler and extend its role The handler now does the job of CubicWebPublisher.main_handle_request() and calls CubicWebPublisher.core_handle(). Instead of using config.add_notfound_view, a catchall route is defined and the handler plugged to it. Related to #4291173

from cubicweb.web.request import CubicWebRequestBase
from cubicweb.cwconfig import CubicWebConfiguration
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):
    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

    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


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)

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

    config.include('pyramid_cubicweb.handler')