Designing Django Rest Framework role based authorization
Sounds like you want some field level security. You might look into using proxy models to give write access to a limited set of fields for the restricted users.
Another option might be to use a custom serializer class which applies readonly to some fields. get_serializer
on a ViewSet subclass could be a good place to do the pivot, you should find the current user in self.request.user
.
After giving some thought I found two solutions.
The first solution was (instead of dividing the models as I mentioned in the question) declaring different endpoints (URLs) for each type of action that has to happen. Then in each endpoint serializer
defining the valid parameters through the fields
class variable.
This means that if one role can only update certain fields, I would create a ModelViewSet
for this action and in the serializer
class related to this ModelViewSet only allow certain parameters.
This solution has many problems and is not quite RESTful. You might end up with dozens of different URLs. In my case, given that the API is not big and the actions were limited that might not have been a problem in the short term, but sure it would have been a problem when the requirements of the application changed.
The second (and chosen) solution was to define a permission/role table in which you define the endpoint where the action has to happen (if you have built your application in the correct way it can just be the ModelViewSet
class name), the action to be executed (list, retrieve, create...) the fields of the related model that can be interacted on and the role that can interact with those fields.
I would suggest using dictionaries as the used data structure for the permission table so each permission check is O(1).
Now each time you get a request to that endpoint, a permission check using that table should be done.
I implemented the permission check by overriding the check_permissions()
method in the ModelSetView
class. Be careful and keep the original check_permissions()
functionality in there too.