from urllib.parse import urlparse, urlunparse from django.conf import settings from django.contrib import auth from django.contrib.auth import views as auth_views from django.core.exceptions import PermissionDenied from django.shortcuts import redirect from django.utils.translation import gettext_lazy as _ from django.views.generic import TemplateView, View from .forms import ElectionAuthForm from .utils import get_cas_client class NextPageMixin: def get_next_url(self): """Decide where to go after a successful login. Look for (in order): - a `next` GET parameter; - a `CASNEXT` session variable; - the `LOGIN_REDIRECT_URL` django setting. """ request = self.request next_url = request.GET.get("next") if next_url is None and "CASNEXT" in request.session: next_url = request.session["CASNEXT"] del request.session["CASNEXT"] if next_url is None: next_url = settings.LOGIN_REDIRECT_URL return next_url class LoginSelectView(NextPageMixin, TemplateView): """Simple page letting the user choose between password and CAS authentication.""" template_name = "auth/login_select.html" http_method_names = ["get"] def get(self, request, *args, **kwargs): if request.user.is_authenticated: return redirect(self.get_next_url()) return super().get(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["next"] = self.get_next_url() context["method"] = self.request.GET.get("method", "CAS") context["election_id"] = self.request.GET.get("election_id", None) return context class CASLoginView(NextPageMixin, View): """CAS authentication view. Implement the CAS authentication scheme: 1. We first redirect the user to the student CAS. 2. The user comes back with a ticket, we validate it to make sure the user is legit (validation is delegated to the ENSCASBackend). 3. We redirect the user to the next page. """ http_method_names = ["get"] def get(self, request, *args, **kwargs): ticket = request.GET.get("ticket") if not ticket: request.session["CASNEXT"] = self.get_next_url() cas_client = get_cas_client(request) return redirect(cas_client.get_login_url()) user = auth.authenticate(request, ticket=ticket) if user is None: raise PermissionDenied(_("Connexion échouée !")) auth.login(request, user) return redirect(self.get_next_url()) class PasswordLoginView(auth_views.LoginView): template_name = "auth/pwd_login.html" authentication_form = ElectionAuthForm def get_initial(self): return {"election_id": self.request.GET.get("election_id", None)} class LogoutView(auth_views.LogoutView): """Logout view. Tell Django to log the user out, then redirect to the CAS logout page if the user logged in via CAS. """ def setup(self, request): super().setup(request) if "CASCONNECTED" in request.session: del request.session["CASCONNECTED"] self.cas_connected = True else: self.cas_connected = False def get_next_page(self): next_page = super().get_next_page() if self.cas_connected: cas_client = get_cas_client(self.request) # If the next_url is local (no hostname), make it absolute so that the user # is correctly redirected from CAS. if not urlparse(next_page).netloc: request = self.request next_page = urlunparse( (request.scheme, request.get_host(), next_page, "", "", "") ) next_page = cas_client.get_logout_url(redirect_url=next_page) return next_page