web/views/authentication.py
changeset 5223 6abd6e3599f4
parent 4916 4b8cdda342ae
child 5251 b675edd05c19
--- a/web/views/authentication.py	Mon Apr 12 14:41:01 2010 +0200
+++ b/web/views/authentication.py	Tue Apr 13 12:19:24 2010 +0200
@@ -12,7 +12,7 @@
 from cubicweb import AuthenticationError, BadConnectionId
 from cubicweb.view import Component
 from cubicweb.dbapi import repo_connect, ConnectionProperties
-from cubicweb.web import ExplicitLogin, InvalidSession
+from cubicweb.web import InvalidSession
 from cubicweb.web.application import AbstractAuthenticationManager
 
 class NoAuthInfo(Exception): pass
@@ -28,9 +28,10 @@
         """
         raise NotImplementedError()
 
-    def authenticated(self, req, cnx, retreiver):
+    def authenticated(self, retreiver, req, cnx, login, authinfo):
         """callback when return authentication information have opened a
-        repository connection successfully
+        repository connection successfully. Take care req has no session
+        attached yet, hence req.execute isn't available.
         """
         pass
 
@@ -59,50 +60,51 @@
         self.authinforetreivers = sorted(vreg['webauth'].possible_objects(vreg),
                                     key=lambda x: x.order)
         assert self.authinforetreivers
+        # 2-uple login / password, login is None when no anonymous access
+        # configured
         self.anoninfo = vreg.config.anonymous_user()
+        if self.anoninfo[0]:
+            self.anoninfo = (self.anoninfo[0], {'password': self.anoninfo[1]})
 
     def validate_session(self, req, session):
-        """check session validity, and return eventually hijacked session
+        """check session validity, reconnecting it to the repository if the
+        associated connection expired in the repository side (hence the
+        necessity for this method). Return the connected user on success.
 
-        :raise InvalidSession:
-          if session is corrupted for a reason or another and should be closed
+        raise :exc:`InvalidSession` if session is corrupted for a reason or
+        another and should be closed
         """
         # with this authentication manager, session is actually a dbapi
         # connection
-        cnx = session
+        cnx = session.cnx
         login = req.get_authorization()[0]
+        # check cnx.login and not user.login, since in case of login by
+        # email, login and cnx.login are the email while user.login is the
+        # 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 = cnx.user(req)
-            # check cnx.login and not user.login, since in case of login by
-            # email, login and cnx.login are the email while user.login is the
-            # actual user login
-            if login and cnx.login != login:
-                cnx.close()
-                raise InvalidSession('login mismatch')
         except BadConnectionId:
             # check if a connection should be automatically restablished
-            if (login is None or login == cnx.login):
-                cnx = self._authenticate(req, cnx.login, cnx.authinfo)
+            if (login is None or login == session.login):
+                cnx = self._authenticate(session.login, session.authinfo)
                 user = cnx.user(req)
-                # backport session's data
-                cnx.data = session.data
+                session.cnx = cnx
             else:
                 raise InvalidSession('bad connection id')
-        # associate the connection to the current request
-        req.set_connection(cnx, user)
-        return cnx
+        return user
 
     def authenticate(self, req):
-        """authenticate user and return corresponding user object
+        """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 ExplicitLogin: if authentication is required (no authentication
-        info found or wrong user/password)
-
-        Note: this method is violating AuthenticationManager interface by
-        returning a session instance instead of the user. This is expected by
-        the InMemoryRepositorySessionManager.
+        raise :exc:`cubicweb.AuthenticationError` if authentication failed
+        (no authentication info found or wrong user/password)
         """
         for retreiver in self.authinforetreivers:
             try:
@@ -110,44 +112,28 @@
             except NoAuthInfo:
                 continue
             try:
-                cnx = self._authenticate(req, login, authinfo)
-            except ExplicitLogin:
+                cnx = self._authenticate(login, authinfo)
+            except AuthenticationError:
                 continue # the next one may succeed
             for retreiver_ in self.authinforetreivers:
-                retreiver_.authenticated(req, cnx, retreiver)
-            break
-        else:
-            # false if no authentication info found, eg this is not an
-            # authentication failure
-            if 'login' in locals():
-                req.set_message(req._('authentication failure'))
-            cnx = self._open_anonymous_connection(req)
-        return cnx
+                retreiver_.authenticated(retreiver, req, cnx, login, authinfo)
+            return cnx, login, authinfo
+        # 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)
+            cnx.anonymous_connection = True
+            return cnx, login, authinfo
+        raise AuthenticationError()
 
-    def _authenticate(self, req, login, authinfo):
+    def _authenticate(self, login, authinfo):
         cnxprops = ConnectionProperties(self.vreg.config.repo_method,
                                         close=False, log=self.log_queries)
-        try:
-            cnx = repo_connect(self.repo, login, cnxprops=cnxprops, **authinfo)
-        except AuthenticationError:
-            raise ExplicitLogin()
-        self._init_cnx(cnx, login, authinfo)
-        # associate the connection to the current request
-        req.set_connection(cnx)
+        cnx = repo_connect(self.repo, login, cnxprops=cnxprops, **authinfo)
+        # decorate connection
+        cnx.vreg = self.vreg
         return cnx
 
-    def _open_anonymous_connection(self, req):
-        # restore an anonymous connection if possible
-        login, password = self.anoninfo
-        if login:
-            cnx = self._authenticate(req, login, {'password': password})
-            cnx.anonymous_connection = True
-            return cnx
-        raise ExplicitLogin()
-
-    def _init_cnx(self, cnx, login, authinfo):
-        # decorate connection
-        cnx.vreg = self.vreg
-        cnx.login = login
-        cnx.authinfo = authinfo
-