I have a few models in my application which use django-rules to define permissions. More precisely, I have predicates which are used in the rules_permissions (inside the model's Meta) dictionary, under the view key. I am using django-rest-framework for views and serializers.
I'd like to restrict the views to not serialize nested data for which the user does not have the view permission. The code works fine for a single level of serialisation, but the predicates for nested objects are never evaluated.
For example, I have a Project model, and when querying for /projects/3/, the following data is returned:
{
"id": 3,
"name": "Test Project",
"data": [
{
"id": 3,
"user": 2,
"test": "test_adam"
},
{
"id": 4,
"user": 3,
"test": "test_eve"
}
]
}
However, as a user Adam, I should not be able to see the Data object which belongs to Eve. I should only see the serialised data for Data object with "id": 3.
So I've written a is_data_owner predicate and added it to rules_permissions of my Data class. But that predicate function is never called when querying for /projects/3/, even though the serializer is accessing the Data model to create the above, nested serialisation. I'd expect that since predicates related to Project are running and working just fine, all nested models accessed by the serializer will also have their predicates checked. But that's not the case.
Note: I am open to other tools which could provide the object-level permissions I need. I've used django-rules as I thought it would meet all my needs, and also because I like the idea of predicates and not having to store permissions in the database.
Serializer code (as requested):
class DataSerializer(ModelSerializer):
class Meta:
model = Data
fields = ["id", "user", "test"]
class ProjectSerializer(ModelSerializer):
data = DataSerializer(many=True)
class Meta:
model = Project
fields = ["id", "name", "data"]
Note that I am aware I could write the code in the serializer class to restrict the data that is returned, but I'm not very keen on having to write this (or even remember to do so) for every scenario. I'd instead prefer this all happened automatically.
class ProjectSerializer(ModelSerializer):
data = SerializerMethodField()
def get_data(self, instance: Project):
permission = instance.data.model.get_perm("view")
return DataSerializer(instance.data.filter(id__in=[
item.id for item in instance.data.all() if rules.has_perm(permission, self.context["request"].user, item)
]), many=True).data
If nothing works out of the box I will most likely just spend time preparing a custom serializer base class to automatically do it for me, for selected fields. But I would find it strange that this scenario isn't supported by some external tool already...