- Notifications
You must be signed in to change notification settings - Fork302
Description
Let's say we have config like this:
views.py
classCustomerProfileViewSet(viewsets.ModelViewSet):permission_classes= (IsCustomerOrCreation,)classProductViewSet(viewsets.ModelViewSet):permission_classes= []defget_object(self,):customer_pk=self.kwargs.get('customer_pk',None)ifcustomer_pkisnotNone:customer=get_object_or_None(Customer,pk=customer_pk)ifcustomerisnotNone:returncustomer.favourite_productreturnsuper(ProductViewSet,self).get_object()
urls.py
url(r'^api/customer/(?P<pk>\d+)/$',CustomerProfileViewSet.as_view({'get':'retrieve'})),url(r'^api/customer/(?P<customer_pk>\d+)/favorite-product$',ProductViewSet.as_view({'get':'retrieve'})),
GET api/customer/1/
Will give 403 error for any user whose id differs from 1. And that is fine.
Then if anon user goes toapi/customer/1/favourite-product/
they will get 200 response with product payload, because permissions for product checked inProductViewSet
, which have no permissions for any role. But I think it is wrong and should return 403, since we have to check permission for customer first. We actually just told everybody what is customer's 1 favorite product!
I understand that permissions it is out of json api spec, but may be there is a nice way I can handle permissions with this lib correctly?
For now I do it like below:
A RelatedMixin class will get a related entity and will find corresponding a serializer for that. And all that stuff will happenafter we check permissions for parent ("customer" in the case) entity
classRelatedMixin(object):serializer_mapping= {}field_name_mapping= {}defget_related(self,request,*args,**kwargs):serializer_kwargs= {}instance=self.get_related_instance()ifcallable(instance):instance=instance()ifhasattr(instance,'all'):instance=instance.all()ifinstanceisNone:returnResponse(data=None)ifisinstance(instance,Iterable):serializer_kwargs['many']=Trueserializer=self.get_serializer(instance,**serializer_kwargs)returnResponse(serializer.data)defget_serializer_class(self):field_name=self.get_related_field_name()returnself.serializer_mapping[field_name]defget_related_field_name(self):field_name=self.kwargs['related_field']iffield_nameinself.field_name_mapping:returnself.field_name_mapping[field_name]returnfield_namedefget_related_instance(self):try:returngetattr(self.get_object(),self.get_related_field_name())exceptAttributeError:fromrest_framework.exceptionsimportNotFoundraiseNotFound
and then just make a view that will handle all Customer's related entities:
classCustomerProfileRelatedViewSet(RelatedMixin,CustomerProfileViewSet):serializer_mapping= {'favourite_product':ProductSerializer,#'order_list': OrderSerializer}
urls.py # Only one url route required for all related entities
url(r'^api/customer/(?P<customer_pk>\d+)/(?P<related_field>\w+)/$',CustomerProfileRelatedViewSet.as_view({'get':'get_related'})),
What do you think about it ? If you like the idea I'm ready to come up with PR