Features: 1) Add support for 'create' action with explicit 'ALLOW' flag in AddressViewSet; 2) Introduce refined permission handling with scoped user actions and public model visibility checks in EvibesPermission.
Fixes: 1) Ensure 'user' is set to authenticated user in AddressSerializer.create method. Extra: 1) Refactor EvibesPermission for clarity and comprehensive action handling; 2) Add detailed class-level docstring for EvibesPermission; 3) Simplify queryset filtering logic with USER_SCOPED_ACTIONS and explicit permission checks.
This commit is contained in:
parent
89f6594751
commit
475b0a0c94
3 changed files with 71 additions and 49 deletions
|
|
@ -14,59 +14,77 @@ class IsOwnerOrReadOnly(permissions.BasePermission):
|
||||||
|
|
||||||
|
|
||||||
class EvibesPermission(permissions.BasePermission):
|
class EvibesPermission(permissions.BasePermission):
|
||||||
def _get_permission_codename(self, action, view):
|
"""
|
||||||
action_permission_map = {
|
Custom permission class for EvibesViewSet endpoints.
|
||||||
"retrieve": "view",
|
|
||||||
"list": "view",
|
|
||||||
"create": "add",
|
|
||||||
"update": "change",
|
|
||||||
"partial_update": "change",
|
|
||||||
"destroy": "delete",
|
|
||||||
}
|
|
||||||
model_name = view.queryset.model._meta.model_name # Get the model name
|
|
||||||
permission_type = action_permission_map.get(action)
|
|
||||||
|
|
||||||
if permission_type:
|
- 'create' may be explicitly allowed via view.additional['create'] == 'ALLOW'.
|
||||||
return f"core.{permission_type}_{model_name}"
|
- Certain actions are scoped to the request.user’s own objects.
|
||||||
return None
|
- Standard model perms ('add', 'view', 'change', 'delete') are enforced for all other actions,
|
||||||
|
including for staff users.
|
||||||
|
- Publicly visible models allow anonymous list/retrieve.
|
||||||
|
"""
|
||||||
|
|
||||||
def has_queryset_permission(self, request, view, queryset):
|
ACTION_PERM_MAP = {
|
||||||
if request.user.is_staff:
|
'retrieve': 'view',
|
||||||
return queryset
|
'list': 'view',
|
||||||
if view.action in [
|
'create': 'add',
|
||||||
"buy",
|
'update': 'change',
|
||||||
"current",
|
'partial_update': 'change',
|
||||||
"add_order_product",
|
'destroy': 'delete',
|
||||||
"remove_order_product",
|
}
|
||||||
"add_wishlist_product",
|
|
||||||
"remove_wishlist_product",
|
USER_SCOPED_ACTIONS = {
|
||||||
"bulk_add_wishlist_products",
|
'buy', 'buy_unregistered', 'current',
|
||||||
"bulk_remove_wishlist_products",
|
'add_order_product', 'remove_order_product',
|
||||||
"autocomplete",
|
'add_wishlist_product', 'remove_wishlist_product',
|
||||||
]:
|
'bulk_add_wishlist_products', 'bulk_remove_wishlist_products',
|
||||||
return queryset.filter(user=request.user)
|
'autocomplete',
|
||||||
return queryset.filter(is_active=True)
|
}
|
||||||
|
|
||||||
def has_permission(self, request, view):
|
def has_permission(self, request, view):
|
||||||
action = getattr(view, "action", None)
|
action = getattr(view, 'action', None)
|
||||||
|
model = view.queryset.model
|
||||||
|
app_label = model._meta.app_label
|
||||||
|
model_name = model._meta.model_name
|
||||||
|
|
||||||
if action in [
|
if action == 'create' and view.additional.get('create') == 'ALLOW':
|
||||||
"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",
|
|
||||||
]:
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
required_permission = self._get_permission_codename(action, view)
|
if action in self.USER_SCOPED_ACTIONS:
|
||||||
|
|
||||||
if required_permission and request.user.has_perm(required_permission):
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return view.queryset.model.is_publicly_visible and action in ["retrieve", "list"]
|
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_queryset_permission(self, request, view, queryset):
|
||||||
|
"""
|
||||||
|
Filter the base queryset according to the action and user.
|
||||||
|
Staff users still require view permissions to see records.
|
||||||
|
"""
|
||||||
|
model = view.queryset.model
|
||||||
|
app_label = model._meta.app_label
|
||||||
|
model_name = model._meta.model_name
|
||||||
|
|
||||||
|
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}"):
|
||||||
|
if request.user.is_staff:
|
||||||
|
return queryset
|
||||||
|
return queryset.filter(is_active=True)
|
||||||
|
return queryset.none()
|
||||||
|
|
||||||
|
base = queryset.filter(is_active=True)
|
||||||
|
if view.action in ('update', 'partial_update'):
|
||||||
|
if request.user.has_perm(f"{app_label}.change_{model_name}"):
|
||||||
|
return base
|
||||||
|
if view.action == 'destroy':
|
||||||
|
if request.user.has_perm(f"{app_label}.delete_{model_name}"):
|
||||||
|
return base
|
||||||
|
return queryset.none()
|
||||||
|
|
|
||||||
|
|
@ -160,8 +160,11 @@ class AddressCreateSerializer(ModelSerializer): # noqa: F405
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Address
|
model = Address
|
||||||
fields = ["raw_data", "user"]
|
fields = ["raw_data"]
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
raw = validated_data.pop("raw_data")
|
raw = validated_data.pop("raw_data")
|
||||||
return Address.objects.create(raw_data=raw, **validated_data)
|
user = None
|
||||||
|
if self.context["request"].user.is_authenticated:
|
||||||
|
user = self.context["request"].user
|
||||||
|
return Address.objects.create(raw_data=raw, user=user, **validated_data)
|
||||||
|
|
|
||||||
|
|
@ -473,6 +473,7 @@ class WishlistViewSet(EvibesViewSet):
|
||||||
class AddressViewSet(EvibesViewSet):
|
class AddressViewSet(EvibesViewSet):
|
||||||
queryset = Address.objects.all()
|
queryset = Address.objects.all()
|
||||||
serializer_class = AddressSerializer
|
serializer_class = AddressSerializer
|
||||||
|
additional = {"create": "ALLOW"}
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
if self.action == 'create':
|
if self.action == 'create':
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue