Django Rest API – Part 6 – Permissions

In this blog post we will see about groups/permissions.

What this means is suppose we have multiple user groups like

  1. Admin
  2. Employee (custom group)
  3. Guest

We will see how to setup this with DRF

So first there are two concepts to understands

a) Groups

b) Permissions

Groups is a collection of permissions and permissions is actual operations that be performed on an object.

So basically when we create a model django automatically created 4 permissions for it “create”, “read”, “update”, “delete”

So permissions are defined a model level i.e if a user can create, read,update, delete a model or not.

To understand this further login into django admin and go to add new group

So were we see permission of CRUD operation for our todo model

Let’s see how we can use it

For every user we can check some default permissions using methods here https://docs.djangoproject.com/en/2.2/topics/auth/default/#default-permissions

So lets’ test this out in shell

As can be see “foo” is admin user so he has permission to add todo, but “test” the new user we created doesn’t have permission. This is how we can check different permissions

Assuming you have an application with an app_label foo and a model named Bar, to test for basic permissions you should use:

  • add: user.has_perm('foo.add_bar')
  • change: user.has_perm('foo.change_bar')
  • delete: user.has_perm('foo.delete_bar')
  • view: user.has_perm('foo.view_bar')

Now we can assign permission to user “test” in 2 ways either via giving him specific permission or assigning him to group.

Let’s see both via shell

from django.contrib.auth import authenticate
user = authenticate(username="foo",password="bar")
from django.contrib.auth.models import Permission
perm = Permission.objects.get(name="Can add todo")
user.user_permissions.add(perm)
user.save()
user.has_perm("todo.add_todo")
>>> True

So this way via code we can assign permission and also check if it exists. Also this add permission/groups can be done via django admin as well.

We can also define custom permission for our models to make is more readable lets see how to do it.

//models.py
from django.db import models

# Create your models here.

from datetime import datetime
# Create your models here.

class Todo(models.Model):
    task = models.CharField(max_length=255)
    description = models.TextField(default="")
    priority = models.IntegerField(default=1)
    created = models.DateTimeField(default=datetime.now,blank=True) 

    class Meta:
        permissions = [
            ("change_priority", "Can change priority of a task"),
        ]

# python manage.py makemigrations
# python manage.py migrate

We add a custom permission here named “change_priority”. Its important to note here are that nothing major has happened, its only that we create a new permission named “change_priority”. All check’s weather user has permission to do this etc would be done at our end still.

But to test this out let’s continue with shell

from django.contrib.auth import authenticate
from django.contrib.auth.models import Permission
user = authenticate(username='test',password='test')
user.has_perm('todo.change_priority_todo')
>>> False
perm = Permission.objects.get(codename="change_priority")
user.user_permissions.add(perm)
user.save()
user.has_perm("todo.change_priority")
>>> False ## strange

user = authenticate(username="test",password="test")
user.has_perm("todo.change_priority")
>>> True

So it works..

Groups

Groups is another way to manage permission or rather much better. Groups are mostly used in project rather than managing permissions.

Let’s create a group via admin interface its quite simple.

I have create a group named “test_group” and assigned that group all permissions for todo model. Then created a new user and assigned to that group. Now at code level if we check the permissions

So this works.

We can also create groups via code, this can be found online.

Decorators

DRF provides us with different decorates to protect api routes lets see them in practice

AllowAny

permission_classes = (AllowAny,)

    def post(self, request):
     ...

This is very simple, this will allow any request

IsAuthenticated

permission_classes = (IsAuthenticated,)
    def get(self, request):
    ...

this will allow only authenticated users.

Also P.S. if you are using @api_view function we can add decorate like @permission_classes(IsAuthenticated,)

IsAdminUser

This will be allowed only for admin user and user with is_staff = true

IsAuthenticatedOrReadOnly

This will allow logged in users only, but on top of that access to only request type like GET, OPTIONS, HEAD

DjangoModelPermissions

This permission is to be used with viewsets only that have queryset attribute set. This will check all CRUD operations permission for a user and set permission for routes accordingly. To see this in action

//views.py
class TodoViewSet(viewsets.ModelViewSet):
    serializer_class = TodoSerializer
    queryset = Todo.objects.all()
    permission_classes = (DjangoModelPermissions,)

GET API without token gives error
After we pass token it works
Post action not allowed

So basically this will follow model based permission automatically.

Custom permissions

If we have to implement custom permission it would need to be done like this

from rest_framework.permissions import BasePermission


# Custom permission for users with "is_active" = True.
class IsActive(BasePermission):
    """
    Allows access only to "is_active" users.
    """
    def has_permission(self, request, view):
        return request.user and request.user.is_active

# Usage
from rest_framework.views import APIView
from rest_framework.response import Response

from .permissions import IsActive   # Path to our custom permission

class ExampleView(APIView):
    permission_classes = (IsActive,)

    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)

This is the basics of permission system, rest there are many more modules like django-guardian etc which can be used to optimize it

excellence-social-linkdin
excellence-social-facebook
excellence-social-instagram
excellence-social-skype