from typing import TYPE_CHECKING from django.contrib.auth import views as auth_views from django.contrib.auth.hashers import make_password from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.contrib.auth.models import Permission from django.contrib.messages.views import SuccessMessageMixin from django.db.models import QuerySet from django.urls import reverse, reverse_lazy from django.utils.translation import gettext_lazy as _ from django.views.generic import CreateView, FormView, ListView, TemplateView from elections.typing import AuthenticatedRequest from .forms import ElectionAuthForm, PwdUserForm, UserAdminForm from .utils import generate_password if TYPE_CHECKING: from elections.typing import User else: from django.contrib.auth import get_user_model User = get_user_model() # ############################################################################# # Mixin to restrict access to staff members # ############################################################################# class StaffMemberMixin(UserPassesTestMixin): """ Mixin permettant de restreindre l'accès aux membres `staff`, si la personne n'est pas connectée, renvoie sur la page d'authentification """ request: AuthenticatedRequest def test_func(self): return self.request.user.is_active and self.request.user.is_staff # ############################################################################# # Election Specific Login # ############################################################################# class ElectionLoginView(auth_views.LoginView): template_name = "auth/election_login.html" authentication_form = ElectionAuthForm def get_initial(self): return {"election_id": self.kwargs.get("election_id")} def get_context_data(self, **kwargs): kwargs.update({"election_id": self.kwargs.get("election_id")}) return super().get_context_data(**kwargs) # ############################################################################# # Admin Panel # ############################################################################# class AdminPanelView(StaffMemberMixin, TemplateView): template_name = "auth/admin-panel.html" # ############################################################################# # Creation of Password Accounts # ############################################################################# class CreatePwdAccount(StaffMemberMixin, SuccessMessageMixin, CreateView): model = User form_class = PwdUserForm template_name = "auth/create-user.html" success_url = reverse_lazy("auth.admin") success_message = _("Compte créé avec succès") def form_valid(self, form): # On enregistre un mot de passe aléatoire form.instance.password = make_password(generate_password(32)) return super().form_valid(form) # ############################################################################# # List of password and CAS users # ############################################################################# class AccountListView(StaffMemberMixin, ListView): model = User template_name = "auth/account-list.html" def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) qs: QuerySet = self.get_queryset() # pyright: ignore ctx["cas_users"] = qs.filter(username__startswith="cas__") ctx["pwd_users"] = qs.filter(username__startswith="pwd__") ctx["e_manager"] = User.objects.with_perm( Permission.objects.get(codename="election_admin"), backend="shared.auth.backends.PwdBackend", ) ctx["f_manager"] = User.objects.with_perm( Permission.objects.get(codename="faq_admin"), backend="shared.auth.backends.PwdBackend", ) return ctx # ############################################################################# # Permission management # ############################################################################# class PermissionManagementView(StaffMemberMixin, SuccessMessageMixin, FormView): form_class = UserAdminForm template_name = "auth/permission-management.html" success_message = _("Permissions modifiées avec succès !") def get_context_data(self, **kwargs): kwargs.update({"username": self.request.GET.get("user", None)}) return super().get_context_data(**kwargs) def get_initial(self): username = self.request.GET.get("user", None) if username is not None: user = User.objects.filter(username=username).first() if user is not None: return { "username": username, "full_admin": user.is_staff, "election_admin": user.has_perm("elections.election_admin"), "faq_admin": user.has_perm("faqs.faq_admin"), } return {} def get_success_url(self): return reverse("auth.permissions") + f"?user={self.user}" def form_valid(self, form): user = User.objects.get(username=form.cleaned_data["username"]) self.user = user.username # Kadenios admin user.is_staff = form.cleaned_data["full_admin"] # Election admin election_perm = Permission.objects.get(codename="election_admin") if form.cleaned_data["election_admin"]: election_perm.user_set.add(user) # pyright: ignore else: election_perm.user_set.remove(user) # pyright: ignore # FAQ admin faq_perm = Permission.objects.get(codename="faq_admin") if form.cleaned_data["faq_admin"]: faq_perm.user_set.add(user) # pyright: ignore else: faq_perm.user_set.remove(user) # pyright: ignore user.save() return super().form_valid(form) # ############################################################################# # List of special accounts # ############################################################################# class AdminAccountsView(LoginRequiredMixin, TemplateView): template_name = "auth/admin-accounts.html" def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx["admin"] = User.objects.filter(is_staff=True) ctx["e_manager"] = User.objects.with_perm( Permission.objects.get(codename="election_admin"), backend="shared.auth.backends.PwdBackend", ) ctx["f_manager"] = User.objects.with_perm( Permission.objects.get(codename="faq_admin"), backend="shared.auth.backends.PwdBackend", ) return ctx