Permissions in Django REST Framework
Permissions in Django REST Framework
Objectives
By the end of this article, you should be able to explain:
DRF Permissions
In DRF, permissions, along with authentication and throttling, are used to grant or deny access for
different classes of users to different parts of an API.
Authentication and authorization work hand in hand. Authentication is always executed before
authorization.
While authentication is the process of checking a user's identity (the user the request came from, the
token that it was signed with), authorization is a process of checking if the request user has the
necessary permissions for executing the request (are they a super user, are they the creators of the
object).
View Permissions
APIView has two methods that check for permissions:
# rest_framework/views.py
class APIView(View):
# other methods
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
1/9
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
When the request comes in, authentication is performed. If the authentication isn't successful, a
NotAuthenticated error is raised. After that, the permissions get checked in a loop, and if any of them
fail, a PermissionDenied error is raised. Finally, a throttling check is performed against the request.
class MessageSingleAPI(APIView):
With ViewSets and Generic Views, check_object_permissions is called after the object is retrieved
from the database for all detail views.
# rest_framework/generics.py
class GenericAPIView(views.APIView):
# other methods
def get_object(self):
"""
2/9
Returns the object the view is displaying.
return obj
Permission is checked for all permissions and if any one of them return False, a PermissionDenied
error is raised.
Permission classes
Permissions in DRF are defined as a list of permission classes. You can either create your own or use
one of the seven built-in classes. All permission classes, either custom or built-in, extend from the
BasePermission class:
class BasePermission(metaclass=BasePermissionMetaclass):
3/9
methods to conditionally return True.
has_permission
has_permission is used to decide whether a request and a user are allowed to access a specific view
For example:
has_permission possesses knowledge about the request, but not about the object of the request.
has_object_permission
has_object_permission is used to decide whether a specific user is allowed to interact with a
specific object
For example:
Besides the knowledge of the request, has_object_permission also possesses data about the object
of the request. The method executes after the object is retrieved from the database.
4/9
When any has_permission returns False, the has_object_permission doesn't get
checked. The request is immediately rejected.
has_permission vs has_object_permission
What's the difference between has_permission and has_object_permission in Django REST
Framework?
Again, for:
List views, only has_permission is executed and the request is either granted or refused access.
If access is refused, the objects never get retrieved.
Detail views, has_permission is executed and then only if permission is granted,
has_object_permission is executed after the object is retrieved.
5/9
With regard to the built-in DRF permission classes, all of them override has_permission while only
DjangoObjectPermissions overrides has_object_permission:
For more on the built-in permission classes, be sure to check out the second article in this
series, Built-in Permission Classes in Django REST Framework.
For more on custom permission classes, be sure to check out the second article in this
series, Custom Permission Classes in Django REST Framework.
Correct Usage
class AuthorOrReadOnly(permissions.BasePermission):
6/9
This permission class allows only the author of the object access to it:
Results:
Incorrect Usage
Let's look at a permission that won't do what we want, to better understand what's going on:
class AuthenticatedOnly(permissions.BasePermission):
This permission denies access to the unauthenticated user, but the check is done in
has_object_permission instead of has_permission.
7/9
Even though the auto-generated browsable API shows the delete button, the unauthenticated user can't
delete the message.
1. The list view only checks has_permission. So, since the custom class doesn't have one, it
checks has_permission from the BasePermission, which unconditionally returns True.
2. The detail view first checks the has_permission (again, always True). Then it checks
has_object_permission, which denies access to unauthenticated users.
That's why in this example unauthenticated requests don't have access to detail views, but they do have
access to list views.
8/9
authenticated user
Permission is granted for the
Result Permission is always granted
authenticated users
This permission class was created only to show how the two methods work. You should use
the built-in class IsAuthenticated class rather than creating your own.
Conclusion
All permissions, either custom or built-in, in Django REST Framework leverage either has_permission
or has_object_permission or both to restrict access to API endpoints.
While has_permission has no restrictions as to when it can be used, it doesn't have access to the
desired object. Because of this, it's more of a "generic" permission check to ensure that the request and
user can access the view. On the other hand, since has_object_permission has access to the
object, the criteria can be much more specific, but it has many limitations as to when it can be used.
Keep in mind, that if you don't override the methods, they will always return True, granting unlimited
access. Only has_permission impacts the access to list views while they both impact the access to
detail view.
Knowing and understanding how both of these methods work is especially important when creating
custom permission classes.
9/9