In this blog post we will see about groups/permissions.
What this means is suppose we have multiple user groups like
- Admin
- Employee (custom group)
- 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
permission_classes = (AllowAny,)
def post(self, request):
...
This is very simple, this will allow any request
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,)
This will be allowed only for admin user and user with is_staff = true
This will allow logged in users only, but on top of that access to only request type like GET, OPTIONS, HEAD
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,)
So basically this will follow model based permission automatically.
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