Use new repoapi for the web stack
The publisher now link repoapi.ClientConnection to request. and explicitly
control there scope.
Web side, appobject._cw.cnx is now a repoapi.ClientConnection.
This actually kill webonly possibility until the repoapi is able to use some
RPC.
The change in the authentication stack is very hasty and need cleanup
--- a/devtools/testlib.py Tue Jun 25 10:59:01 2013 +0200
+++ b/devtools/testlib.py Thu Jun 27 18:21:04 2013 +0200
@@ -838,7 +838,8 @@
def assertAuthSuccess(self, req, origsession, nbsessions=1):
sh = self.app.session_handler
session = self.app.get_session(req)
- req.set_session(session)
+ clt_cnx = repoapi.ClientConnection(session)
+ req.set_cnx(clt_cnx)
self.assertEqual(len(self.open_sessions), nbsessions, self.open_sessions)
self.assertEqual(session.login, origsession.login)
self.assertEqual(session.anonymous_session, False)
--- a/web/application.py Tue Jun 25 10:59:01 2013 +0200
+++ b/web/application.py Thu Jun 27 18:21:04 2013 +0200
@@ -35,7 +35,7 @@
ValidationError, Unauthorized, Forbidden,
AuthenticationError, NoSelectableObject,
BadConnectionId, CW_EVENT_MANAGER)
-from cubicweb.dbapi import anonymous_session
+from cubicweb.repoapi import anonymous_cnx
from cubicweb.web import LOGGER, component
from cubicweb.web import (
StatusResponse, DirectResponse, Redirect, NotFound, LogOut,
@@ -50,12 +50,14 @@
@contextmanager
def anonymized_request(req):
- orig_session = req.session
- req.set_session(anonymous_session(req.vreg))
+ orig_cnx = req.cnx
+ anon_clt_cnx = anonymous_cnx(orig_cnx._session.repo)
+ req.set_cnx(anon_clt_cnx)
try:
- yield req
+ with anon_clt_cnx:
+ yield req
finally:
- req.set_session(orig_session)
+ req.set_cnx(orig_cnx)
class AbstractSessionManager(component.Component):
"""manage session data associated to a session identifier"""
@@ -338,16 +340,22 @@
try:
try:
session = self.get_session(req)
- req.set_session(session)
+ from cubicweb import repoapi
+ cnx = repoapi.ClientConnection(session)
+ req.set_cnx(cnx)
except AuthenticationError:
# Keep the dummy session set at initialisation.
# such session with work to an some extend but raise an
# AuthenticationError on any database access.
- pass
+ import contextlib
+ @contextlib.contextmanager
+ def dummy():
+ yield
+ cnx = dummy()
# XXX We want to clean up this approach in the future. But
# several cubes like registration or forgotten password rely on
# this principle.
- assert req.session is not None
+
# DENY https acces for anonymous_user
if (req.https
and req.session.anonymous_session
@@ -358,7 +366,8 @@
# handler
try:
### Try to generate the actual request content
- content = self.core_handle(req, path)
+ with cnx:
+ content = self.core_handle(req, path)
# Handle user log-out
except LogOut as ex:
# When authentification is handled by cookie the code that
--- a/web/request.py Tue Jun 25 10:59:01 2013 +0200
+++ b/web/request.py Thu Jun 27 18:21:04 2013 +0200
@@ -1082,7 +1082,7 @@
-CubicWebRequestBase = DBAPICubicWebRequestBase
+CubicWebRequestBase = ConnectionCubicWebRequestBase
## HTTP-accept parsers / utilies ##############################################
--- a/web/test/unittest_application.py Tue Jun 25 10:59:01 2013 +0200
+++ b/web/test/unittest_application.py Thu Jun 27 18:21:04 2013 +0200
@@ -31,6 +31,7 @@
from cubicweb.web.views.basecontrollers import ViewController
from cubicweb.web.application import anonymized_request
from cubicweb.dbapi import DBAPISession, _NeedAuthAccessMock
+from cubicweb import repoapi
class FakeMapping:
"""emulates a mapping module"""
@@ -336,7 +337,7 @@
# req.form['__password'] = self.admpassword
# self.assertAuthFailure(req)
# option allow-email-login set
- origsession.login = address
+ #origsession.login = address
self.set_option('allow-email-login', True)
req.form['__login'] = address
req.form['__password'] = self.admpassword
@@ -360,19 +361,21 @@
def _test_auth_anon(self, req):
asession = self.app.get_session(req)
- req.set_session(asession)
+ # important otherwise _reset_cookie will not use the right session
+ req.set_cnx(repoapi.ClientConnection(asession))
self.assertEqual(len(self.open_sessions), 1)
self.assertEqual(asession.login, 'anon')
self.assertTrue(asession.anonymous_session)
self._reset_cookie(req)
def _test_anon_auth_fail(self, req):
- self.assertEqual(len(self.open_sessions), 1)
+ self.assertEqual(1, len(self.open_sessions))
session = self.app.get_session(req)
- req.set_session(session)
+ # important otherwise _reset_cookie will not use the right session
+ req.set_cnx(repoapi.ClientConnection(session))
self.assertEqual(req.message, 'authentication failure')
self.assertEqual(req.session.anonymous_session, True)
- self.assertEqual(len(self.open_sessions), 1)
+ self.assertEqual(1, len(self.open_sessions))
self._reset_cookie(req)
def test_http_auth_anon_allowed(self):
@@ -397,19 +400,19 @@
req.form['__password'] = self.admpassword
self.assertAuthSuccess(req, origsession)
self.assertRaises(LogOut, self.app_handle_request, req, 'logout')
- self.assertEqual(len(self.open_sessions), 0)
+ self.assertEqual(0, len(self.open_sessions))
def test_anonymized_request(self):
req = self.request()
- self.assertEqual(req.session.login, self.admlogin)
+ self.assertEqual(self.admlogin, req.session.user.login)
# admin should see anon + admin
- self.assertEqual(len(list(req.find_entities('CWUser'))), 2)
+ self.assertEqual(2, len(list(req.find_entities('CWUser'))))
with anonymized_request(req):
- self.assertEqual(req.session.login, 'anon')
+ self.assertEqual('anon', req.session.login, 'anon')
# anon should only see anon user
- self.assertEqual(len(list(req.find_entities('CWUser'))), 1)
- self.assertEqual(req.session.login, self.admlogin)
- self.assertEqual(len(list(req.find_entities('CWUser'))), 2)
+ self.assertEqual(1, len(list(req.find_entities('CWUser'))))
+ self.assertEqual(self.admlogin, req.session.login)
+ self.assertEqual(2, len(list(req.find_entities('CWUser'))))
def test_non_regr_optional_first_var(self):
req = self.request()
--- a/web/test/unittest_session.py Tue Jun 25 10:59:01 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""unit tests for cubicweb.web.application
-
-:organization: Logilab
-:copyright: 2001-2011 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
-"""
-from cubicweb.devtools.testlib import CubicWebTC
-from cubicweb.web import InvalidSession
-
-class SessionTC(CubicWebTC):
-
- def test_session_expiration(self):
- sm = self.app.session_handler.session_manager
- # make is if the web session has been opened by the session manager
- sm._sessions[self.cnx.sessionid] = self.websession
- sessionid = self.websession.sessionid
- self.assertEqual(len(sm._sessions), 1)
- self.assertEqual(self.websession.sessionid, self.websession.cnx.sessionid)
- # fake the repo session is expiring
- self.repo.close(sessionid)
- try:
- # fake an incoming http query with sessionid in session cookie
- # don't use self.request() which try to call req.set_session
- req = self.requestcls(self.vreg)
- self.assertRaises(InvalidSession, sm.get_session, req, sessionid)
- self.assertEqual(len(sm._sessions), 0)
- finally:
- # avoid error in tearDown by telling this connection is closed...
- self.cnx._closed = True
-
-if __name__ == '__main__':
- from logilab.common.testlib import unittest_main
- unittest_main()
--- a/web/views/authentication.py Tue Jun 25 10:59:01 2013 +0200
+++ b/web/views/authentication.py Thu Jun 27 18:21:04 2013 +0200
@@ -127,13 +127,6 @@
# actual user login
if login and session.login != login:
raise InvalidSession('login mismatch')
- try:
- # calling cnx.user() check connection validity, raise
- # BadConnectionId on failure
- user = session.cnx.user(req)
- except BadConnectionId:
- raise InvalidSession('bad connection id')
- return user
def authenticate(self, req):
"""authenticate user using connection information found in the request,
@@ -149,27 +142,24 @@
except NoAuthInfo:
continue
try:
- cnx = self._authenticate(login, authinfo)
+ session = self._authenticate(login, authinfo)
except AuthenticationError:
retriever.cleanup_authentication_information(req)
continue # the next one may succeed
for retriever_ in self.authinforetrievers:
- retriever_.authenticated(retriever, req, cnx, login, authinfo)
- return cnx, login
+ retriever_.authenticated(retriever, req, session, login, authinfo)
+ return session, login
# false if no authentication info found, eg this is not an
# authentication failure
if 'login' in locals():
req.set_message(req._('authentication failure'))
login, authinfo = self.anoninfo
if login:
- cnx = self._authenticate(login, authinfo)
- return cnx, login
+ session = self._authenticate(login, authinfo)
+ return session, login
raise AuthenticationError()
def _authenticate(self, login, authinfo):
- cnxprops = ConnectionProperties(close=False, log=self.log_queries)
- cnx = _repo_connect(self.repo, login, cnxprops=cnxprops, **authinfo)
- # decorate connection
- cnx.vreg = self.vreg
- return cnx
+ sessionid = self.repo.connect(login, **authinfo)
+ return self.repo._sessions[sessionid]
--- a/web/views/sessions.py Tue Jun 25 10:59:01 2013 +0200
+++ b/web/views/sessions.py Thu Jun 27 18:21:04 2013 +0200
@@ -26,6 +26,7 @@
from cubicweb.web import InvalidSession, Redirect
from cubicweb.web.application import AbstractSessionManager
from cubicweb.dbapi import ProgrammingError, DBAPISession
+from cubicweb import repoapi
class InMemoryRepositorySessionManager(AbstractSessionManager):
@@ -53,13 +54,14 @@
if sessionid not in self._sessions:
raise InvalidSession()
session = self._sessions[sessionid]
- if session.cnx:
- try:
- user = self.authmanager.validate_session(req, session)
- except InvalidSession:
- # invalid session
- self.close_session(session)
- raise
+ try:
+ user = self.authmanager.validate_session(req, session)
+ except InvalidSession:
+ self.close_session(session)
+ raise
+ if session.closed:
+ self.close_session(session)
+ raise InvalidSession()
return session
def open_session(self, req):
@@ -69,8 +71,7 @@
raise :exc:`cubicweb.AuthenticationError` if authentication failed
(no authentication info found or wrong user/password)
"""
- cnx, login = self.authmanager.authenticate(req)
- session = DBAPISession(cnx, login)
+ session, login = self.authmanager.authenticate(req)
self._sessions[session.sessionid] = session
return session
@@ -87,31 +88,25 @@
# reopening. Is it actually a problem?
if 'last_login_time' in req.vreg.schema:
self._update_last_login_time(session)
- req.set_message(req._('welcome %s !') % session.cnx.user().login)
+ req.set_message(req._('welcome %s !') % session.user.login)
def _update_last_login_time(self, session):
# XXX should properly detect missing permission / non writeable source
# and avoid "except (RepositoryError, Unauthorized)" below
try:
- cu = session.cnx.cursor()
- cu.execute('SET X last_login_time NOW WHERE X eid %(x)s',
- {'x' : session.cnx.user().eid})
- session.cnx.commit()
+ cnx = repoapi.ClientConnection(session)
+ with cnx:
+ cnx.execute('SET X last_login_time NOW WHERE X eid %(x)s',
+ {'x' : session.user.eid})
+ cnx.commit()
except (RepositoryError, Unauthorized):
- session.cnx.rollback()
- except Exception:
- session.cnx.rollback()
- raise
+ pass
def close_session(self, session):
"""close session on logout or on invalid session detected (expired out,
corrupted...)
"""
self.info('closing http session %s' % session.sessionid)
- del self._sessions[session.sessionid]
- if session.cnx:
- try:
- session.cnx.close()
- except (ProgrammingError, BadConnectionId): # expired on the repository side
- pass
- session.cnx = None
+ self._sessions.pop(session.sessionid, None)
+ if not session.closed:
+ session.repo.close(session.id)