[login] split authentication logic from post authentication logic (closes #2200755)
authorPierre-Yves David <pierre-yves.david@logilab.fr>
Thu, 15 Mar 2012 17:42:31 +0100
changeset 8311 76a44a0d7f4b
parent 8310 87f2f18a77ef
child 8312 6c2119509fac
[login] split authentication logic from post authentication logic (closes #2200755) * The Session manager is now only in charge of providing a valid session. * LoginControllers are now used in all case but wrong credential. * The LoginControllers are in charge of redirecting the user to the page wanted to see in the first place, expected to see. * The login form is now always submitted to the login controller with an extra argument pointing to the url we should redirect too after successful authentication. The ``"log out first logic"`` logic on login controller is removed because: 1. Other web actor do not do that. 2. Removed code do not need to be reimplemented. 3. We can only get it to work again in a single case: use do a GET request on http://www.my-cw-stuff.io/login 4. I do not see it's purpose.
devtools/testlib.py
web/application.py
web/test/unittest_application.py
web/test/unittest_views_basecontrollers.py
web/views/basecontrollers.py
web/views/basetemplates.py
web/views/sessions.py
--- a/devtools/testlib.py	Thu Mar 01 12:08:35 2012 +0100
+++ b/devtools/testlib.py	Thu Mar 15 17:42:31 2012 +0100
@@ -697,13 +697,11 @@
 
     def assertAuthSuccess(self, req, origsession, nbsessions=1):
         sh = self.app.session_handler
-        path, params = self.expect_redirect(lambda x: self.app.connect(x), req)
+        self.app.connect(req)
         session = req.session
         self.assertEqual(len(self.open_sessions), nbsessions, self.open_sessions)
         self.assertEqual(session.login, origsession.login)
         self.assertEqual(session.anonymous_session, False)
-        self.assertEqual(path, 'view')
-        self.assertMessageEqual(req, params, 'welcome %s !' % req.user.login)
 
     def assertAuthFailure(self, req, nbsessions=0):
         self.app.connect(req)
--- a/web/application.py	Thu Mar 01 12:08:35 2012 +0100
+++ b/web/application.py	Thu Mar 15 17:42:31 2012 +0100
@@ -361,8 +361,6 @@
                     controller = self.vreg['controllers'].select(ctrlid, req,
                                                                  appli=self)
                 except NoSelectableObject:
-                    if ctrlid == 'login':
-                        raise Unauthorized(req._('log out first'))
                     raise Unauthorized(req._('not authorized'))
                 req.update_search_state()
                 result = controller.publish(rset=rset)
--- a/web/test/unittest_application.py	Thu Mar 01 12:08:35 2012 +0100
+++ b/web/test/unittest_application.py	Thu Mar 15 17:42:31 2012 +0100
@@ -308,12 +308,6 @@
         self.commit()
         self.assertEqual(vreg.property_value('ui.language'), 'en')
 
-    def test_login_not_available_to_authenticated(self):
-        req = self.request()
-        with self.assertRaises(Unauthorized) as cm:
-            self.app_publish(req, 'login')
-        self.assertEqual(str(cm.exception), 'log out first')
-
     def test_fb_login_concept(self):
         """see data/views.py"""
         self.set_auth_mode('cookie', 'anon')
@@ -343,7 +337,10 @@
     def test_cookie_auth_no_anon(self):
         req, origsession = self.init_authentication('cookie')
         self.assertAuthFailure(req)
-        form = self.app_publish(req, 'login')
+        try:
+            form = self.app_publish(req, 'login')
+        except Redirect, redir:
+            self.fail('anonymous user should get login form')
         self.assertTrue('__login' in form)
         self.assertTrue('__password' in form)
         self.assertEqual(req.cnx, None)
--- a/web/test/unittest_views_basecontrollers.py	Thu Mar 01 12:08:35 2012 +0100
+++ b/web/test/unittest_views_basecontrollers.py	Thu Mar 15 17:42:31 2012 +0100
@@ -776,9 +776,6 @@
         self.assertEqual(res, '12')
 
 
-
-
-
 class UndoControllerTC(CubicWebTC):
 
     def setup_database(self):
@@ -836,5 +833,20 @@
         self.assertURLPath(cm.exception.location, 'toto')
 
 
+class LoginControllerTC(CubicWebTC):
+
+    def test_login_with_dest(self):
+        req = self.request()
+        req.form = {'postlogin_path': '/elephants/babar'}
+        with self.assertRaises(Redirect) as cm:
+            self.ctrl_publish(req, ctrl='login')
+        self.assertEqual('/elephants/babar', cm.exception.location)
+
+    def test_login_no_dest(self):
+        req = self.request()
+        with self.assertRaises(Redirect) as cm:
+            self.ctrl_publish(req, ctrl='login')
+        self.assertEqual('/', cm.exception.location)
+
 if __name__ == '__main__':
     unittest_main()
--- a/web/views/basecontrollers.py	Thu Mar 01 12:08:35 2012 +0100
+++ b/web/views/basecontrollers.py	Thu Mar 15 17:42:31 2012 +0100
@@ -84,6 +84,17 @@
             # Cookie authentication
             return self.appli.need_login_content(self._cw)
 
+class LoginControllerForAuthed(Controller):
+    __regid__ = 'login'
+    __select__ = ~anonymous_user()
+
+    def publish(self, rset=None):
+        """log in the instance"""
+        path = self._cw.form.get('postlogin_path')
+        if not path:
+            path = '/'
+        raise Redirect(path)
+
 
 class LogoutController(Controller):
     __regid__ = 'logout'
--- a/web/views/basetemplates.py	Thu Mar 01 12:08:35 2012 +0100
+++ b/web/views/basetemplates.py	Thu Mar 15 17:42:31 2012 +0100
@@ -447,7 +447,11 @@
 
     def form_action(self):
         if self.action is None:
-            return login_form_url(self._cw)
+            # reuse existing redirection if it exist
+            target = self._cw.form.get('postlogin_path',
+                                       self._cw.relative_path())
+            return self._cw.build_url('login', __secure__=True,
+                                      postlogin_path=target)
         return super(LogForm, self).form_action()
 
 class LogForm(BaseLogForm):
@@ -507,12 +511,3 @@
         cw.html_headers.add_onload('jQuery("#__login:visible").focus()')
 
 LogFormTemplate = class_renamed('LogFormTemplate', LogFormView)
-
-
-def login_form_url(req):
-    if req.https:
-        return req.url()
-    httpsurl = req.vreg.config.get('https-url')
-    if httpsurl:
-        return req.url().replace(req.base_url(), httpsurl)
-    return req.url()
--- a/web/views/sessions.py	Thu Mar 01 12:08:35 2012 +0100
+++ b/web/views/sessions.py	Thu Mar 15 17:42:31 2012 +0100
@@ -95,18 +95,6 @@
         #      reopening. Is it actually a problem?
         if 'last_login_time' in req.vreg.schema:
             self._update_last_login_time(req)
-        args = req.form
-        for forminternal_key in ('__form_id', '__domid', '__errorurl'):
-            args.pop(forminternal_key, None)
-        path = req.relative_path(False)
-        if path in ('login', 'logout') or req.form.get('vid') == 'loggedout':
-            path = 'view'
-            args['__message'] = req._('welcome %s !') % req.user.login
-            if 'vid' in req.form and req.form['vid'] != 'loggedout':
-                args['vid'] = req.form['vid']
-            if 'rql' in req.form:
-                args['rql'] = req.form['rql']
-            raise Redirect(req.build_url(path, **args))
         req.set_message(req._('welcome %s !') % req.user.login)
 
     def _update_last_login_time(self, req):