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