Search code examples
pythondjangographene-django

Limit field permissions on Graphene Django


Lets say I got a user Type

class UserType(DjangoObjectType):
    class Meta:
        model = User
        fields = [
            "fieldA",
            "fieldB",
            "relationshipA",
            "relationshipB"
        ]

And I want fieldB and relationshipB to be visible only to the owner (user).
What is the best strategy to do this?
Initially Ive created a PublicUserType excluding private fields, but quickly I realized that it might not be scalable because not only I'll have to create private representation for UserType I'll might also create private representation for any relationship (relationshipA etc), and proper resolvers, and duplicate fragments etc.
Is there a best practice here?


Solution

  • My solution is:
    Create decorators.py file

    from functools import wraps
    from graphql import GraphQLError
    from api.utils import snake_to_camel_case
    
    def private_field(func):
        @wraps(func)
        def wrapper(self, info):
            user = info.context.user
    
            if user.id != self.id:
                field_name = func.__name__.replace('resolve_', '')
                field_name_camel_cased = snake_to_camel_case(field_name)
                raise GraphQLError(f'You do not have permissions to access {field_name}')
            
            return func(self, info)
        return wrapper
    

    snake_to_camel_case func:

    def snake_to_camel_case(string):
        components = string.split('_')
        return components[0] + ''.join(x.title() for x in components[1:])
    

    and finally on my schema file resolver:

    @private_field
        def resolve_private_field(self, info):
            return self.private_field
    

    Response should look something like that:

    {
        "errors": [
            {
                "message": "You do not have permissions to access privateField",
    ...
    

    Now obviously this solution is super specific for my needs atm but it should be easy enough to make it more generic and scalable for any scenario.