61 lines
1.7 KiB
Python
61 lines
1.7 KiB
Python
"""RBAC helpers and DRF permission classes.
|
|
|
|
Authorization is always enforced on the backend. The frontend may hide UI
|
|
elements, but that is never treated as a security boundary.
|
|
"""
|
|
from rest_framework.permissions import BasePermission
|
|
|
|
from apps.rbac.models import Permission
|
|
|
|
|
|
def has_perm(user, code: str) -> bool:
|
|
if not user or not user.is_authenticated:
|
|
return False
|
|
if user.is_superuser:
|
|
return True
|
|
return Permission.objects.filter(roles__user_roles__user=user, code=code).exists()
|
|
|
|
|
|
def has_any_perm(user, codes) -> bool:
|
|
return any(has_perm(user, c) for c in codes)
|
|
|
|
|
|
def user_role_slugs(user):
|
|
if not user or not user.is_authenticated:
|
|
return []
|
|
return list(user.user_roles.values_list("role__slug", flat=True))
|
|
|
|
|
|
class HasPermission(BasePermission):
|
|
"""Global permission check. Set `perm_code` (or `perm_codes`) on the view."""
|
|
|
|
perm_code = None
|
|
perm_codes = None
|
|
|
|
def has_permission(self, request, view):
|
|
codes = self.perm_codes or ([self.perm_code] if self.perm_code else None)
|
|
if not codes:
|
|
return True
|
|
return has_any_perm(request.user, codes)
|
|
|
|
|
|
class ActionPermission(BasePermission):
|
|
"""Maps a DRF viewset action name to a permission code."""
|
|
|
|
code_map = {}
|
|
|
|
def has_permission(self, request, view):
|
|
code = self.code_map.get(getattr(view, "action", None))
|
|
if code is None:
|
|
return True
|
|
return has_perm(request.user, code)
|
|
|
|
|
|
class IsOwner(BasePermission):
|
|
"""Object-level ownership for customers accessing their own resources."""
|
|
|
|
owner_field = "user"
|
|
|
|
def has_object_permission(self, request, view, obj):
|
|
return getattr(obj, self.owner_field, None) == request.user
|