Fixes: None; Extra: 1) Simplify queryset filtering logic for "list" and "retrieve" actions;
138 lines
5 KiB
Python
138 lines
5 KiB
Python
from rest_framework import permissions
|
||
|
||
|
||
class IsOwner(permissions.BasePermission):
|
||
def has_object_permission(self, request, view, obj):
|
||
return obj.user == request.user
|
||
|
||
|
||
class IsOwnerOrReadOnly(permissions.BasePermission):
|
||
def has_object_permission(self, request, view, obj):
|
||
if request.method in permissions.SAFE_METHODS:
|
||
return True
|
||
return obj.user == request.user
|
||
|
||
|
||
class EvibesPermission(permissions.BasePermission):
|
||
"""
|
||
Custom permission class for EvibesViewSet endpoints.
|
||
|
||
- 'create' may be explicitly allowed via view.additional['create'] == 'ALLOW'.
|
||
- Certain actions are scoped to the request.user’s own objects.
|
||
- Standard model perms ('add', 'view', 'change', 'delete') are enforced for all other actions,
|
||
including for staff users.
|
||
- Publicly visible models allow anonymous list/retrieve.
|
||
- If an instance or queryset has a "user" attribute, ensure that the request.user is the same,
|
||
unless the user is an admin with the required django permission.
|
||
"""
|
||
|
||
ACTION_PERM_MAP = {
|
||
"retrieve": "view",
|
||
"list": "view",
|
||
"create": "add",
|
||
"update": "change",
|
||
"partial_update": "change",
|
||
"destroy": "delete",
|
||
}
|
||
|
||
USER_SCOPED_ACTIONS = {
|
||
"list",
|
||
"retrieve",
|
||
"buy",
|
||
"buy_unregistered",
|
||
"current",
|
||
"add_order_product",
|
||
"remove_order_product",
|
||
"add_wishlist_product",
|
||
"remove_wishlist_product",
|
||
"bulk_add_wishlist_products",
|
||
"bulk_remove_wishlist_products",
|
||
"autocomplete",
|
||
}
|
||
|
||
def has_permission(self, request, view):
|
||
action = getattr(view, "action", None)
|
||
model = view.queryset.model
|
||
app_label = model._meta.app_label
|
||
model_name = model._meta.model_name
|
||
|
||
if action == "create" and view.additional.get("create") == "ALLOW":
|
||
return True
|
||
|
||
if action in self.USER_SCOPED_ACTIONS:
|
||
return True
|
||
|
||
perm_prefix = self.ACTION_PERM_MAP.get(action)
|
||
if perm_prefix:
|
||
codename = f"{perm_prefix}_{model_name}"
|
||
if request.user.has_perm(f"{app_label}.{codename}"):
|
||
return True
|
||
|
||
return bool(
|
||
action in ("list", "retrieve")
|
||
and getattr(model, "is_publicly_visible", False)
|
||
)
|
||
|
||
def has_object_permission(self, request, view, obj):
|
||
if request.method in permissions.SAFE_METHODS:
|
||
return True
|
||
|
||
if hasattr(obj, "user"):
|
||
if obj.user == request.user:
|
||
return True
|
||
# Allow admins who hold the required model permission
|
||
app_label = obj._meta.app_label
|
||
model_name = obj._meta.model_name
|
||
action = getattr(view, "action", None)
|
||
perm_prefix = self.ACTION_PERM_MAP.get(action)
|
||
return bool(perm_prefix and request.user.has_perm(f"{app_label}.{perm_prefix}_{model_name}"))
|
||
|
||
model = view.queryset.model
|
||
app_label = model._meta.app_label
|
||
model_name = model._meta.model_name
|
||
action = getattr(view, "action", None)
|
||
perm_prefix = self.ACTION_PERM_MAP.get(action)
|
||
return bool(perm_prefix and request.user.has_perm(f"{app_label}.{perm_prefix}_{model_name}"))
|
||
|
||
def has_queryset_permission(self, request, view, queryset):
|
||
"""
|
||
Filter the base queryset according to the action and user.
|
||
For models with a "user" field, restrict access to records belonging to the request user
|
||
unless the admin holds the needed permissions.
|
||
"""
|
||
model = view.queryset.model
|
||
app_label = model._meta.app_label
|
||
model_name = model._meta.model_name
|
||
|
||
if hasattr(model, "user"):
|
||
if view.action in self.USER_SCOPED_ACTIONS:
|
||
return queryset.filter(user=request.user)
|
||
if view.action in ("list", "retrieve"):
|
||
if request.user.has_perm(f"{app_label}.view_{model_name}"):
|
||
return queryset
|
||
return queryset.none()
|
||
|
||
base = queryset.filter(is_active=True, user=request.user)
|
||
if request.user.is_staff and request.user.has_perm(
|
||
f"{app_label}.{self.ACTION_PERM_MAP.get(view.action)}_{model_name}"
|
||
):
|
||
return queryset.filter(is_active=True)
|
||
return base
|
||
|
||
if view.action in ("list", "retrieve"):
|
||
if request.user.has_perm(f"{app_label}.view_{model_name}"):
|
||
if request.user.is_staff:
|
||
return queryset
|
||
return queryset.filter(is_active=True)
|
||
return queryset.none()
|
||
|
||
base = queryset.filter(is_active=True)
|
||
match view.action:
|
||
case "update" | "partial_update":
|
||
if request.user.has_perm(f"{app_label}.change_{model_name}"):
|
||
return base
|
||
case "destroy":
|
||
if request.user.has_perm(f"{app_label}.delete_{model_name}"):
|
||
return base
|
||
|
||
return queryset.none()
|