from django.contrib.auth.mixins import UserPassesTestMixin from django.http import Http404 from django.shortcuts import redirect from .messages import notify class PlatformStaffRequiredMixin(UserPassesTestMixin): """Gate for the platform control panel. Two rules: * **Staff only.** ``is_staff`` or ``is_superuser``. Anonymous visitors are sent to the login page; signed-in non-staff get a 403 (Django's AccessMixin already distinguishes those two cases). Staff must also hold a second factor — ``RequireMFAMiddleware`` enforces that, so the panel is 2FA-protected for free. * **Base domain only.** The panel manages *all* clubs, so it must not be reachable from inside one. If the tenant middleware resolved a club from the subdomain, the panel does not exist here. """ def dispatch(self, request, *args, **kwargs): if getattr(request, "club", None) is not None: raise Http404("The control panel is not available on a club subdomain.") return super().dispatch(request, *args, **kwargs) def test_func(self): user = self.request.user return user.is_staff or user.is_superuser class PlatformSuperuserRequiredMixin(PlatformStaffRequiredMixin): """Superusers only. Managing platform admins is the one thing staff may not do. The panel is gated on ``is_staff or is_superuser``, so if a staff member could grant themselves ``is_superuser`` the two would collapse into the same thing and ``is_superuser`` would stop being a security boundary. """ def test_func(self): return self.request.user.is_superuser class RedirectOnInvalidMixin: """A form submitted from a modal has nowhere sensible to re-render on error: the page that opened it has already moved on, and the view has no standalone template of its own. Redirect back to ``invalid_redirect_url_name`` instead, with the errors flattened into messages, rather than Django's default of re-rendering ``template_name``. """ invalid_redirect_url_name = None def get_invalid_redirect_kwargs(self): return {} def form_invalid(self, form): for error in form.errors.values(): notify(self.request, f"e|Couldn't save|{' '.join(error)}") return redirect(self.invalid_redirect_url_name, **self.get_invalid_redirect_kwargs())