Search code examples
djangodjango-rest-frameworkdjango-permissions

Django - How do I create custom permissions in DRF to restrict users from making API calls that they are not authorised to?


I am working on a web app that uses DRF as the backend and ReactJS as the frontend. My web app has users from different sales departments, and I would like to restrict the permission such that only the users from Sales Department A can see the sales projects that are tagged under Sales Department A, and if the users try to access a sales project page that they are not authorised to, it should return an error page. I tried googling for the answer, but I am not sure if the answer I found is the best solution for my problem. I saw solutions using Django Groups, but I was not sure how it works (so if someone could clarify it would be great!). I was hoping for a solution that would sort of have a check layer within the view or serializer, and from there would deduce which sales department the request.user is from, and hence whether or not the data should be released to them.

below is my models.py for more clarity on the structure i am using.

class UserProfile(models.Model):

    user = models.OneToOneField(User, on_delete=models.CASCADE)
    department = models.ManyToManyField('SalesDepartment', related_name='users', blank=True)
    contact_number = PhoneNumberField(blank=True)
    email = models.EmailField(max_length=255)
    email_password = models.CharField(max_length=255)
    profile_picture = models.ImageField(upload_to='profile/', default='profile/blank.png')

    def __str__(self):
         return str(self.user.username)


class SalesDepartment(models.Model):
    department_id = models.AutoField(primary_key=True)
    department_name = models.CharField(max_length=100)

    def __str__(self):
         return self.department_name

class SalesProject(models.Model):

    sales_project_id = models.AutoField(primary_key=True)
    sales_project_name = models.CharField(max_length=100)
    creation_date = models.DateField(default=timezone.localdate)
    sales_department = models.ForeignKey('SalesDepartment', on_delete=models.CASCADE)

    def __str__(self):
        return self.sales_project_name

The UserProfile model has a M2M field to the SalesDepartment model, and the SalesProject model has a Foreign Key relation to the SalesDepartment model. Hence, I am hoping to check for the user's sales department from the UserProfile model, and check to see if it is the same SalesDepartment as the one linked to the specific SalesProject that the API call is getting, and if so, return the response data as per usual, but if not, then return an error for the response data.

All help is appreciated, I am new to Django and DRF especially, hope that you guys would guide me to the correct path if I am misguided on how I am supposed to handle this authorisation problem. Thanks all in advance!


Solution

  • from rest_framework import permissions
    
    class IsTheUserYouWant(permissions.BasePermission):
        """
        Here I'm checking if the request.user, if the request is coming from a group that is
        allowed to perform this action or if the user here is an staff member, aka admin user.
        You can check whatever you want here since you have access to the user. So you  can
        check the sales_department like you mentioned in the question.
        """
        def has_permission(self, request, view):
            if request.user and request.user.groups.filter(name="Group You Created") or request.user.is_staff:
                return True
            return False
    

    Views.py example usage

    class SalesProjectSet(viewsets.ModelViewSet):
        queryset = SalesProject.objects.all()
        serializer_class = SalesProjectSerializer
        permission_classes = [IsTheUserYouWant]
    

    In the Django Admin page you can create a Group and add users to it If you want, then you can check in the permission if the user is from the group who are allowed.