Let's say I have a model:
class Employee(models.Model):
first_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=60)
salary = models.DecimalField(decimal_places=2)
I want anyone to be able to access first_name and last_name but only want certain users to be able to read salary because this is confidential data.
And then I want to restrict write/update for salary to an even different kind of user.
How do I restrict field read/write/update depending on the request user?
EDIT:
This is in the GraphQL API context. I am using Graphene. I'd like to see a scalable solution in the resolver function.
Assuming that you have
employees = graphene.List(EmployeeType)
def resolve_employees(self, info, **kwargs):
return Employee.objects.all()
and
can_view_salary
and can_edit_salary
Then you'll need to define the EmployeeType
with a value of salary
that is dependent on the user. Something like
from graphene_django.types import DjangoObjectType
from myapp.models import Employee
class EmployeeType(DjangoObjectType):
class Meta:
model = Employee
def resolve_salary(self, info):
if info.context.user.has_perm('myapp.can_view_salary'):
return self.salary
return None
The important takeaway is that you're creating a custom resolve
function for the salary that is switching based on the value of a permission. You don't need to create any other resolvers for first_name
and last_name
.
Read the documentation first. But there isn't an example for doing an update.
In brief, here's the approach that you can take:
Mutation
methodclass MyMutations(graphene.ObjectType):
set_employee = SetEmployee.Field()
SetEmployee
that gets the Employee object and updates it. The salary field is ignored for certain users.class SetEmployee(graphene.Mutation):
class Arguments:
id = graphene.ID()
first_name = graphene.String()
last_name = graphene.String()
salary = graphene.String()
employee = graphene.Field(lambda: EmployeeType)
@classmethod
def mutate(cls, root, info, **args):
employee_id = args.get('employee_id')
# Fetch the employee object by id
employee = Employee.objects.get(id=employee_id)
first_name = args.get('first_name')
last_name = args.get('last_name')
salary = args.get('salary')
# Update the employee fields from the mutation inputs
if first_name:
employee.first_name = first_name
if last_name:
employee.last_name = last_name
if salary and info.context.user.has_perm('myapp.can_edit_salary'):
employee.salary = salary
employee.save()
return SetEmployee(employee=employee)
Note: when this answer was originally written, there was no Decimal field available in Graphene Django -- I avoided this issue by taking a string as an input.
Great response @MarkChackerian. However personally, I believe that returning a null value for a field on unauthorised access can be ambiguous, so I personally raise an exception from resolve method like that:
class UnauthorisedAccessError(GraphQLError):
def __init__(self, message, *args, **kwargs):
super(UnauthorisedAccessError, self).__init__(message, *args, **kwargs)
def resolve_salary(self, info):
if info.context.user.has_perm('myapp.can_view_salary'):
return self.salary
raise UnauthorisedAccessError(message='No permissions to see the salary!')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With