web/application.py
changeset 5223 6abd6e3599f4
parent 5174 78438ad513ca
child 5244 5467674ad101
--- a/web/application.py	Mon Apr 12 14:41:01 2010 +0200
+++ b/web/application.py	Tue Apr 13 12:19:24 2010 +0200
@@ -5,6 +5,8 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
+from __future__ import with_statement
+
 __docformat__ = "restructuredtext en"
 
 import sys
@@ -18,10 +20,11 @@
 from cubicweb import (
     ValidationError, Unauthorized, AuthenticationError, NoSelectableObject,
     RepositoryError, CW_EVENT_MANAGER)
+from cubicweb.dbapi import DBAPISession
 from cubicweb.web import LOGGER, component
 from cubicweb.web import (
-    StatusResponse, DirectResponse, Redirect, NotFound,
-    RemoteCallFailed, ExplicitLogin, InvalidSession, RequestError)
+    StatusResponse, DirectResponse, Redirect, NotFound, LogOut,
+    RemoteCallFailed, InvalidSession, RequestError)
 
 # make session manager available through a global variable so the debug view can
 # print information about web session
@@ -52,7 +55,7 @@
         for session in self.current_sessions():
             no_use_time = (time() - session.last_usage_time)
             total += 1
-            if session.anonymous_connection:
+            if session.anonymous_session:
                 if no_use_time >= self.cleanup_anon_session_time:
                     self.close_session(session)
                     closed += 1
@@ -76,9 +79,11 @@
         raise NotImplementedError()
 
     def open_session(self, req):
-        """open and return a new session for the given request
+        """open and return a new session for the given request. The session is
+        also bound to the request.
 
-        :raise ExplicitLogin: if authentication is required
+        raise :exc:`cubicweb.AuthenticationError` if authentication failed
+        (no authentication info found or wrong user/password)
         """
         raise NotImplementedError()
 
@@ -97,11 +102,24 @@
     def __init__(self, vreg):
         self.vreg = vreg
 
-    def authenticate(self, req):
-        """authenticate user and return corresponding user object
+    def validate_session(self, req, session):
+        """check session validity, reconnecting it to the repository if the
+        associated connection expired in the repository side (hence the
+        necessity for this method).
 
-        :raise ExplicitLogin: if authentication is required (no authentication
-        info found or wrong user/password)
+        raise :exc:`InvalidSession` if session is corrupted for a reason or
+        another and should be closed
+        """
+        raise NotImplementedError()
+
+    def authenticate(self, req):
+        """authenticate user using connection information found in the request,
+        and return corresponding a :class:`~cubicweb.dbapi.Connection` instance,
+        as well as login and authentication information dictionary used to open
+        the connection.
+
+        raise :exc:`cubicweb.AuthenticationError` if authentication failed
+        (no authentication info found or wrong user/password)
         """
         raise NotImplementedError()
 
@@ -165,9 +183,11 @@
             try:
                 session = self.get_session(req, sessionid)
             except InvalidSession:
+                # try to open a new session, so we get an anonymous session if
+                # allowed
                 try:
                     session = self.open_session(req)
-                except ExplicitLogin:
+                except AuthenticationError:
                     req.remove_cookie(cookie, self.SESSION_VAR)
                     raise
         # remember last usage time for web session tracking
@@ -183,7 +203,7 @@
         req.set_cookie(cookie, self.SESSION_VAR, maxage=None)
         # remember last usage time for web session tracking
         session.last_usage_time = time()
-        if not session.anonymous_connection:
+        if not session.anonymous_session:
             self._postlogin(req)
         return session
 
@@ -227,7 +247,7 @@
         """
         self.session_manager.close_session(req.cnx)
         req.remove_cookie(req.get_cookie(), self.SESSION_VAR)
-        raise AuthenticationError(url=goto_url)
+        raise LogOut(url=goto_url)
 
 
 class CubicWebPublisher(object):
@@ -271,7 +291,10 @@
         sessions (i.e. a new connection may be created or an already existing
         one may be reused
         """
-        self.session_handler.set_session(req)
+        try:
+            self.session_handler.set_session(req)
+        except AuthenticationError:
+            req.set_session(DBAPISession(None))
 
     # publish methods #########################################################
 
@@ -283,19 +306,18 @@
             return self.main_publish(path, req)
         finally:
             cnx = req.cnx
-            self._logfile_lock.acquire()
-            try:
-                try:
-                    result = ['\n'+'*'*80]
-                    result.append(req.url())
-                    result += ['%s %s -- (%.3f sec, %.3f CPU sec)' % q for q in cnx.executed_queries]
-                    cnx.executed_queries = []
-                    self._query_log.write('\n'.join(result).encode(req.encoding))
-                    self._query_log.flush()
-                except Exception:
-                    self.exception('error while logging queries')
-            finally:
-                self._logfile_lock.release()
+            if cnx is not None:
+                with self._logfile_lock:
+                    try:
+                        result = ['\n'+'*'*80]
+                        result.append(req.url())
+                        result += ['%s %s -- (%.3f sec, %.3f CPU sec)' % q
+                                   for q in cnx.executed_queries]
+                        cnx.executed_queries = []
+                        self._query_log.write('\n'.join(result).encode(req.encoding))
+                        self._query_log.flush()
+                    except Exception:
+                        self.exception('error while logging queries')
 
     @deprecated("[3.4] use vreg['controllers'].select(...)")
     def select_controller(self, oid, req):
@@ -340,7 +362,10 @@
                     # displaying the cookie authentication form
                     req.cnx.commit()
             except (StatusResponse, DirectResponse):
-                req.cnx.commit()
+                if req.cnx is not None:
+                    req.cnx.commit()
+                raise
+            except (AuthenticationError, LogOut):
                 raise
             except Redirect:
                 # redirect is raised by edit controller when everything went fine,
@@ -362,10 +387,13 @@
                 else:
                     # delete validation errors which may have been previously set
                     if '__errorurl' in req.form:
-                        req.del_session_data(req.form['__errorurl'])
+                        req.session.data.pop(req.form['__errorurl'], None)
                     raise
-            except (AuthenticationError, NotFound, RemoteCallFailed):
-                raise
+            except RemoteCallFailed, ex:
+                req.set_header('content-type', 'application/json')
+                raise StatusResponse(500, ex.dumps())
+            except NotFound:
+                raise StatusResponse(404, self.notfound_content(req))
             except ValidationError, ex:
                 self.validation_error_handler(req, ex)
             except (Unauthorized, BadRQLQuery, RequestError), ex:
@@ -388,7 +416,7 @@
                         'values': req.form,
                         'eidmap': req.data.get('eidmap', {})
                         }
-            req.set_session_data(req.form['__errorurl'], forminfo)
+            req.session.data[req.form['__errorurl']] = forminfo
             # XXX form session key / __error_url should be differentiated:
             # session key is 'url + #<form dom id', though we usually don't want
             # the browser to move to the form since it hides the global