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