Keep GET parameters throughout entire login process

This commit is contained in:
Jens Langhammer 2019-02-27 12:35:48 +01:00
parent 48055d1cfd
commit b726583084
2 changed files with 16 additions and 9 deletions

View File

@ -4,6 +4,7 @@ from logging import getLogger
from django.contrib.auth import login from django.contrib.auth import login
from django.contrib.auth.mixins import UserPassesTestMixin from django.contrib.auth.mixins import UserPassesTestMixin
from django.shortcuts import get_object_or_404, redirect, reverse from django.shortcuts import get_object_or_404, redirect, reverse
from django.utils.http import urlencode
from django.views.generic import View from django.views.generic import View
from passbook.core.models import Factor, User from passbook.core.models import Factor, User
@ -13,6 +14,12 @@ from passbook.lib.utils.urls import is_url_absolute
LOGGER = getLogger(__name__) LOGGER = getLogger(__name__)
def _redirect_with_qs(view, get_query_set=None):
"""Wrapper to redirect whilst keeping GET Parameters"""
target = reverse(view)
if get_query_set:
target += '?' + urlencode({key: value for key, value in get_query_set.items()})
return redirect(target)
class AuthenticationView(UserPassesTestMixin, View): class AuthenticationView(UserPassesTestMixin, View):
"""Wizard-like Multi-factor authenticator""" """Wizard-like Multi-factor authenticator"""
@ -37,7 +44,7 @@ class AuthenticationView(UserPassesTestMixin, View):
# Function from UserPassesTestMixin # Function from UserPassesTestMixin
if 'next' in self.request.GET: if 'next' in self.request.GET:
return redirect(self.request.GET.get('next')) return redirect(self.request.GET.get('next'))
return redirect(reverse('passbook_core:overview')) return _redirect_with_qs('passbook_core:overview', self.request.GET)
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
# Extract pending user from session (only remember uid) # Extract pending user from session (only remember uid)
@ -46,7 +53,7 @@ class AuthenticationView(UserPassesTestMixin, View):
User, id=self.request.session[AuthenticationView.SESSION_PENDING_USER]) User, id=self.request.session[AuthenticationView.SESSION_PENDING_USER])
else: else:
# No Pending user, redirect to login screen # No Pending user, redirect to login screen
return redirect(reverse('passbook_core:auth-login')) return _redirect_with_qs('passbook_core:auth-login', request.GET)
# Write pending factors to session # Write pending factors to session
if AuthenticationView.SESSION_PENDING_FACTORS in request.session: if AuthenticationView.SESSION_PENDING_FACTORS in request.session:
self.pending_factors = request.session[AuthenticationView.SESSION_PENDING_FACTORS] self.pending_factors = request.session[AuthenticationView.SESSION_PENDING_FACTORS]
@ -101,8 +108,8 @@ class AuthenticationView(UserPassesTestMixin, View):
self.pending_factors self.pending_factors
self.request.session[AuthenticationView.SESSION_FACTOR] = next_factor self.request.session[AuthenticationView.SESSION_FACTOR] = next_factor
LOGGER.debug("Rendering Factor is %s", next_factor) LOGGER.debug("Rendering Factor is %s", next_factor)
# return redirect(reverse('passbook_core:auth-process', kwargs={'factor': next_factor})) # return _redirect_with_qs('passbook_core:auth-process', kwargs={'factor': next_factor})
return redirect(reverse('passbook_core:auth-process')) return _redirect_with_qs('passbook_core:auth-process', self.request.GET)
# User passed all factors # User passed all factors
LOGGER.debug("User passed all factors, logging in") LOGGER.debug("User passed all factors, logging in")
return self._user_passed() return self._user_passed()
@ -112,7 +119,7 @@ class AuthenticationView(UserPassesTestMixin, View):
This should only be shown if user authenticated successfully, but is disabled/locked/etc""" This should only be shown if user authenticated successfully, but is disabled/locked/etc"""
LOGGER.debug("User invalid") LOGGER.debug("User invalid")
self.cleanup() self.cleanup()
return redirect(reverse('passbook_core:auth-denied')) return _redirect_with_qs('passbook_core:auth-denied', self.request.GET)
def _user_passed(self): def _user_passed(self):
"""User Successfully passed all factors""" """User Successfully passed all factors"""
@ -123,9 +130,9 @@ class AuthenticationView(UserPassesTestMixin, View):
# Cleanup # Cleanup
self.cleanup() self.cleanup()
next_param = self.request.GET.get('next', None) next_param = self.request.GET.get('next', None)
if next_param and is_url_absolute(next_param): if next_param and not is_url_absolute(next_param):
return redirect(next_param) return redirect(next_param)
return redirect(reverse('passbook_core:overview')) return _redirect_with_qs('passbook_core:overview')
def cleanup(self): def cleanup(self):
"""Remove temporary data from session""" """Remove temporary data from session"""

View File

@ -12,7 +12,7 @@ from django.utils.translation import ugettext as _
from django.views import View from django.views import View
from django.views.generic import FormView from django.views.generic import FormView
from passbook.core.auth.view import AuthenticationView from passbook.core.auth.view import AuthenticationView, _redirect_with_qs
from passbook.core.exceptions import PasswordPolicyInvalid from passbook.core.exceptions import PasswordPolicyInvalid
from passbook.core.forms.authentication import LoginForm, SignUpForm from passbook.core.forms.authentication import LoginForm, SignUpForm
from passbook.core.models import Invitation, Nonce, Source, User from passbook.core.models import Invitation, Nonce, Source, User
@ -73,7 +73,7 @@ class LoginView(UserPassesTestMixin, FormView):
return self.invalid_login(self.request) return self.invalid_login(self.request)
self.request.session.flush() self.request.session.flush()
self.request.session[AuthenticationView.SESSION_PENDING_USER] = pre_user.pk self.request.session[AuthenticationView.SESSION_PENDING_USER] = pre_user.pk
return redirect(reverse('passbook_core:auth-process')) return _redirect_with_qs('passbook_core:auth-process', self.request.GET)
def invalid_login(self, request: HttpRequest, disabled_user: User = None) -> HttpResponse: def invalid_login(self, request: HttpRequest, disabled_user: User = None) -> HttpResponse:
"""Handle login for disabled users/invalid login attempts""" """Handle login for disabled users/invalid login attempts"""