[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.
--- 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):