Keep GET parameters throughout entire login process
This commit is contained in:
parent
48055d1cfd
commit
b726583084
|
@ -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"""
|
||||||
|
|
|
@ -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"""
|
||||||
|
|
Reference in New Issue