| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172 |
- from __future__ import annotations
- from functools import wraps
- from typing import Iterable, Callable, Optional
- from django.contrib import messages
- from django.contrib.auth.views import redirect_to_login
- from django.shortcuts import redirect
- from django.urls import reverse
- def permissions_required(*perms: str, any_perm: bool = False, message: Optional[str] = None, login_view: str = "admin_frontend:login"):
- """Decorator to enforce one or more Django permissions on a view.
- - If the user is not authenticated, redirects to the admin login, preserving next.
- - If permissions fail, adds an error message and redirects back to the current URL.
- Usage:
- @permissions_required('recycle_core.assign_driver')
- def my_view(...):
- ...
- @permissions_required('app.perm_a', 'app.perm_b') # ALL required
- def another(...):
- ...
- @permissions_required('app.perm_a', 'app.perm_b', any_perm=True) # ANY required
- def another(...):
- ...
- """
- def decorator(view_func: Callable):
- @wraps(view_func)
- def _wrapped(request, *args, **kwargs):
- if not request.user.is_authenticated:
- return redirect_to_login(request.get_full_path(), login_url=reverse(login_view))
- # Normalize permissions list
- perms_list: Iterable[str] = perms or []
- allowed = False
- if any_perm:
- allowed = any(request.user.has_perm(p) for p in perms_list) if perms_list else True
- else:
- # ALL required
- allowed = request.user.has_perms(perms_list) if perms_list else True
- if not allowed:
- msg = message or (
- "You do not have the required permission." if len(perms_list) <= 1
- else "You do not have the required permissions."
- )
- try:
- messages.error(request, msg)
- except Exception:
- pass
- # Avoid redirect loops on POST-only endpoints by preferring HTTP_REFERER
- referer = request.META.get("HTTP_REFERER")
- if referer:
- return redirect(referer)
- # Fallback to a safe dashboard
- try:
- return redirect(reverse("admin_frontend:dashboard"))
- except Exception:
- return redirect("/")
- return view_func(request, *args, **kwargs)
- return _wrapped
- return decorator
|