62 lines
2.3 KiB
Python
62 lines
2.3 KiB
Python
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())
|