Search code examples
djangodjango-rest-frameworkdjango-viewsdjango-permissions

Object level authorization in a Django Rest Framework viewset


I'm creating an API with Django Rest Framework (DRF) and wondering where I should handle object level authorization.

So far I've created an Organization model and a custom user model with email being the unique identifier instead of username. Organizations and users are currently connected through a many-to-many field.

What I'd like to do is make sure that when a user hits my API they're only able to do the standard CRUD actions on the models that are linked to the users respective organization. As an example here's my current UserViewSet where I've overriden then get_queryset method to filter the User queryset to only return other users who are apart of the same organizations as the user calling the API:

class UserViewSet(viewsets.ModelViewSet):
    serializer_class = UserSerializer

    def get_queryset(self):
        User = get_user_model()
        user = self.request.user
        organizations = user.organization.all()
        return User.objects.filter(organization__in=organizations)

What's considered best practice to extend these limitations to other viewset actions? For example, if I want to make sure that the user can only create and add other users into the organizations they're linked to, should I override the create method in the viewset and perform a validation there that the organization which was passed in the request data is the same as the organization that the user calling the API belongs to?

My gut feeling is that I'd end up breaking DRY this way, because I'd be overriding all the viewset actions and repeating nearly identical overrides. Is this intuition wrong? I guess I could separate the "validations" into a separate services.py file and call them in the overriden actions. Should I instead offload these checks to custom permissions? Or should I disregard the views completely and put the validation into the serializers?


Solution

  • Actually, you need different tools for different DRF CRUD actions. Personaly, I love to use rules package

    • name=XXX-list : /XXX/
      • list : permissions through get_queryset() filtering
      • create : permissions through rules and payload validation with serializer
    • name=XXX-detail : /XXX/{id}
      • retrieve : permissions through get_queryset() filtering
      • partial_update, update and destroy : permissions through rules and get_queryset() filtering

    You will probably need to write a custom DjangoObjectPermission class to integrate rules