Changeset - 4c5c59b96adc
[Not reviewed]
default
0 4 0
Thomas De Schampheleire - 11 years ago 2015-05-19 21:50:35
thomas.de.schampheleire@gmail.com
login: preserve GET arguments throughout login redirection (issue #104)

When redirecting a user to the login page and while handling this login and
redirecting to the original page, the GET arguments passed to the original
URL are lost through the login redirection process.

For example, when creating a pull request for a specific revision from the
repository changelog, there are rev_start and rev_end arguments passed in
the URL. Through the login redirection, they are lost.

Fix the issue by passing along the GET arguments to the login page, in the
login form action, and when redirecting back to the original page.
Tests are added to cover these cases, including tests with unicode GET
arguments (in URL encoding).
4 files changed with 75 insertions and 7 deletions:
0 comments (0 inline, 0 general)
kallithea/controllers/login.py
Show inline comments
 
@@ -42,6 +42,7 @@ from kallithea.lib.auth import AuthUser,
 
from kallithea.lib.auth_modules import importplugin
 
from kallithea.lib.base import BaseController, render
 
from kallithea.lib.exceptions import UserCreationError
 
from kallithea.lib.utils2 import safe_str
 
from kallithea.model.db import User, Setting
 
from kallithea.model.forms import LoginForm, RegisterForm, PasswordResetForm
 
from kallithea.model.user import UserModel
 
@@ -102,9 +103,14 @@ class LoginController(BaseController):
 
            came_from = url('home')
 
        return came_from
 

	
 
    def _redirect_to_origin(self, origin, headers=None):
 
        '''redirect to the original page, preserving any get arguments given'''
 
        request.GET.pop('came_from', None)
 
        raise HTTPFound(location=url(origin, **request.GET), headers=headers)
 

	
 
    def index(self):
 
        _default_came_from = url('home')
 
        came_from = self._validate_came_from(request.GET.get('came_from'))
 
        came_from = self._validate_came_from(safe_str(request.GET.get('came_from', '')))
 
        c.came_from = came_from or _default_came_from
 

	
 
        not_default = self.authuser.username != User.DEFAULT_USER
 
@@ -112,7 +118,7 @@ class LoginController(BaseController):
 

	
 
        # redirect if already logged in
 
        if self.authuser.is_authenticated and not_default and ip_allowed:
 
            raise HTTPFound(location=c.came_from)
 
            return self._redirect_to_origin(c.came_from)
 

	
 
        if request.POST:
 
            # import Login Form validator class
 
@@ -124,7 +130,8 @@ class LoginController(BaseController):
 
                headers = self._store_user_in_session(
 
                                        username=c.form_result['username'],
 
                                        remember=c.form_result['remember'])
 
                raise HTTPFound(location=c.came_from, headers=headers)
 
                return self._redirect_to_origin(c.came_from, headers)
 

	
 
            except formencode.Invalid, errors:
 
                defaults = errors.value
 
                # remove password from filling in form again
 
@@ -157,7 +164,8 @@ class LoginController(BaseController):
 

	
 
            if auth_info:
 
                headers = self._store_user_in_session(auth_info.get('username'))
 
                raise HTTPFound(location=c.came_from, headers=headers)
 
                return self._redirect_to_origin(c.came_from, headers)
 

	
 
        return render('/login.html')
 

	
 
    @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
kallithea/lib/auth.py
Show inline comments
 
@@ -711,7 +711,7 @@ def redirect_to_login(message=None):
 
    if message:
 
        h.flash(h.literal(message), category='warning')
 
    log.debug('Redirecting to login page, origin: %s' % p)
 
    return redirect(url('login_home', came_from=p))
 
    return redirect(url('login_home', came_from=p, **request.GET))
 

	
 
class LoginRequired(object):
 
    """
kallithea/templates/login.html
Show inline comments
 
@@ -16,7 +16,7 @@
 
        %endif
 
    </div>
 
    <div class="panel-body inner">
 
        ${h.form(h.url.current(came_from=c.came_from))}
 
        ${h.form(h.url.current(**request.GET))}
 
        <div class="form">
 
            <i class="icon-lock"></i>
 
            <!-- fields -->
kallithea/tests/functional/test_login.py
Show inline comments
 
@@ -66,7 +66,7 @@ class TestLoginController(TestController
 
          ('mailto:test@example.com',),
 
          ('file:///etc/passwd',),
 
          ('ftp://some.ftp.server',),
 
          ('http://other.domain',),
 
          ('http://other.domain/bl%C3%A5b%C3%A6rgr%C3%B8d',),
 
    ])
 
    def test_login_bad_came_froms(self, url_came_from):
 
        response = self.app.post(url(controller='login', action='index',
 
@@ -96,6 +96,66 @@ class TestLoginController(TestController
 
        response.mustcontain('invalid user name')
 
        response.mustcontain('invalid password')
 

	
 
    # verify that get arguments are correctly passed along login redirection
 

	
 
    @parameterized.expand([
 
        ({'foo':'one', 'bar':'two'}, ('foo=one', 'bar=two')),
 
        ({'blue': u'blå'.encode('utf-8'), 'green':u'grøn'},
 
             ('blue=bl%C3%A5', 'green=gr%C3%B8n')),
 
    ])
 
    def test_redirection_to_login_form_preserves_get_args(self, args, args_encoded):
 
        with fixture.anon_access(False):
 
            response = self.app.get(url(controller='summary', action='index',
 
                                        repo_name=HG_REPO,
 
                                        **args))
 
            self.assertEqual(response.status, '302 Found')
 
            for encoded in args_encoded:
 
                self.assertIn(encoded, response.location)
 

	
 
    @parameterized.expand([
 
        ({'foo':'one', 'bar':'two'}, ('foo=one', 'bar=two')),
 
        ({'blue': u'blå'.encode('utf-8'), 'green':u'grøn'},
 
             ('blue=bl%C3%A5', 'green=gr%C3%B8n')),
 
    ])
 
    def test_login_form_preserves_get_args(self, args, args_encoded):
 
        response = self.app.get(url(controller='login', action='index',
 
                                    came_from = '/_admin/users',
 
                                    **args))
 
        for encoded in args_encoded:
 
            self.assertIn(encoded, response.form.action)
 

	
 
    @parameterized.expand([
 
        ({'foo':'one', 'bar':'two'}, ('foo=one', 'bar=two')),
 
        ({'blue': u'blå'.encode('utf-8'), 'green':u'grøn'},
 
             ('blue=bl%C3%A5', 'green=gr%C3%B8n')),
 
    ])
 
    def test_redirection_after_successful_login_preserves_get_args(self, args, args_encoded):
 
        response = self.app.post(url(controller='login', action='index',
 
                                     came_from = '/_admin/users',
 
                                     **args),
 
                                 {'username': 'test_admin',
 
                                  'password': 'test12'})
 
        self.assertEqual(response.status, '302 Found')
 
        for encoded in args_encoded:
 
            self.assertIn(encoded, response.location)
 

	
 
    @parameterized.expand([
 
        ({'foo':'one', 'bar':'two'}, ('foo=one', 'bar=two')),
 
        ({'blue': u'blå'.encode('utf-8'), 'green':u'grøn'},
 
             ('blue=bl%C3%A5', 'green=gr%C3%B8n')),
 
    ])
 
    def test_login_form_after_incorrect_login_preserves_get_args(self, args, args_encoded):
 
        response = self.app.post(url(controller='login', action='index',
 
                                     came_from = '/_admin/users',
 
                                     **args),
 
                                 {'username': 'error',
 
                                  'password': 'test12'})
 

	
 
        response.mustcontain('invalid user name')
 
        response.mustcontain('invalid password')
 
        for encoded in args_encoded:
 
            self.assertIn(encoded, response.form.action)
 

	
 
    #==========================================================================
 
    # REGISTRATIONS
 
    #==========================================================================
0 comments (0 inline, 0 general)