Implementing Role-Based Access Control in Django
In this guide, we are going to focus on Role Based Access control (RBAC) and how to implement it in Django.
Access control is an important component in every application that requires data security, privacy, and privileged access. The purpose of having access control in an application is to be able to restrict what each user can do or access.
There are many models of access control, however, in this guide, we are going to focus on Role Based Access Control (RBAC) and how to implement it in Django.
Table Of Contents
- Overview of Django Authentication and Authorization
- Walkthrough of Implementation of RBAC
- Performing Access Control Checks - Applying Permissions
- Conclusion
We are going to make use of Django's built-in authentication system to allow (or disallow) users from accessing resources or executing actions depending on the permissions associated with the roles assigned to the users.
Prerequisites
In order to follow this article, you need to meet the following requirements:
- Have knowledge of Python and Django
- Have Python 3.7 and above installed
Overview of Django Authentication and Authorization
Django, being a high-level battery-included Python framework, comes with an authentication system.
This authentication system is collectively referred to as the authentication and authorization system. That means, we can use the built-in Django authentication system to handle both authentication (user identification) and authorization (permissions).
The following are the components or models of the built-in authentication system that can be used to implement RBAC:
- User: This models the users of an application.
- Group: This models a group of users in an application. That is, users can be categorized into various groups. For our implementation of RBAC, we’ll use this model (group model) for roles.
- Permission: This models the actions users can perform within the application, enabling precise control over access rights.
We’ll explore more on each of these components in the walkthrough section.
Walkthrough of Implementation of RBAC
Simple Case Study
Let’s implement RBAC for a School Management System in order to explore the implementation of RBAC.
The following roles should exist in the system:
- Student - can view his own record
- Teacher - can create, update, view, and delete student records.
- Principal - can create, read, update, and delete users and has all permissions on student records.
In this case study, the resource or data we’ll mainly be restricting access to is the Student Assessment record.
User, Group, and Permission
As mentioned earlier, the authentication system provides a User model. It’s important to know that the authentication system is bundled as an application in django.contrib.auth
. This application contains the core functionality and the necessary models needed.
The authentication system is available by default in every project created using the django-admin startproject
command. In as much as django.contrib.auth
exists in INSTALLED_APPS
constants in a project’s settings.py file.
Running the command python manage.py migrate
will create the tables for the default database models. Hence, the user, group, and permission will now be available.
Note: There is a many-to-many relationship between the user and permission and also between the group and permission.
Django Admin Site
In order to create objects interactively, let’s create a super user that can access the Django admin dashboard.
- Run the command
python manage.py createsuperuser
- Go to http://127.0.0.1:8000/admin/ and log in.
Student Assessment Record
Let’s do the following in order to create the student assessment record model.
-
Create an app named record by running the command
python manage.py startapp record
. -
Copy the code below into the models.py file in the record application.
from django.db import models
from django.contrib.auth.models import User
class StudentAssessmentRecord(models.Model):
student = models.ForeignKey(User, on_delete=models.CASCADE)
score = models.IntegerField()
def __str__(self):
return f"{self.student.username}-Score:{self.score}”
-
Add the newly created app to the
INSTALLED_APPS
list in thesettings.py
file. -
Run the commands
python manage.py makemigrations
andpython manage.py migrate
to create the table for the student assessment record. -
Register the model in Django admin by adding the following to the admin.py
from django.contrib import admin
from .models import StudentAssessmentRecord
@admin.register(StudentAssessmentRecord)
class StudentAssessmentRecordAdmin(admin.ModelAdmin):
list_display = ["id", "student", "score"]
- Login to the admin site to see the tables.
Creating Roles
As mentioned, we’ll use the Group
model to represent Roles. So, let’s create a Teacher group from the admin dashboard.
Default Permissions
In the previous section, we created a Teacher group and assigned specific permissions via the Django admin site. If you've been curious about the origin of those permissions, this section provides the clarity you've been looking for.
For every model created, Django usually creates four default permissions for the model which are:
- Can view - give users read access to the instances of the model.
- Can change - give users update access to the instances of the model.
- Can add - give users create access to the instances of the model.
- Can delete - give users delete access to the instances of the model.
As a result of the above, when we created the student assessment record model, those four default permissions were created. That’s why they are available as part of the permissions list while creating the Teacher group.
Managing User, Group, and Permission Programmatically
We have used the Django admin dashboard to create the Teacher group. We can also create and do the same thing programmatically as well. Let’s do that next
- Run the command
python manage.py shell
Yes, we have just created the student group. We imported the Group
model from the authentication application and created the Student group with the name Student.
How do we add the Permissions to the student group?
We’ll make use of the related manager .permissions -
student_group.permissions`.
Before we can start adding the permissions, let’s get to know more about Permission.
The Permission objects have the following fields:
- name (Required): 255 characters or fewer.
- content_type (Required): A reference to the django_content_type database table, which contains a record for each installed model.
- codename (Required): 100 characters or fewer.
Remember that four permissions are created for every model by default. Let’s get to know Django’s naming convention for Permission.
Permission names take the form <APP_NAME>.<PERMISSION_CODENAME>
In our case, the permission to view all instances of the student assessment record in the record application will be: record.view_studentassessmentrecord
Let’s assign the student group one of those default permissions. student_group.permissions.add(permission) Remember, that we’ll need to pass in the id of the permission object or the permission object itself.
Tip: Treat it in the same way as any other Django model, no difference.
Let’s go back to the shell or run the command python manage.py shell
again in your terminal in order to perform the following steps:
- Import Permissions model and content type model
- Get content type for the student assessment record model
- Get the permission object
- Add the permission object to the many-to-many field - permissions of student_group
For the Principal role, let’s create the Principal group and assign the group permissions to Create, change, view, and delete:
- User
- Student assessment record.
Let’s do so programmatically.
- Get content type for the User and StudentAssessmentRecord models.
- Get all permission objects for the user content type and student assessment record content type.
- Create the principal group.
- Combine all permissions objects for user and student assessment records into one query set using the OR operator (pipe character).
- Assign the combined permissions to the principal group by setting permissions for the principal group.
Performing Access Control Checks - Applying Permissions
So far, we have been talking about Users, Roles, and Permissions. Now, let’s talk about putting these permissions into use. Because merely having the roles and permissions in place does not automatically restrict any actions within our application. To implement effective access control, we need to put these permissions to practical use by conducting access control checks.
Access control checks allow us to make use of the permissions created to either grant (or deny) access to users to different parts of the application. In other words, a user gets access to a privileged part of an application if the user (or the group the user belongs to) has permission to access that part.
To test for permissions, we can use user.has_perm()
method.
Note: The ModelBackend caches permissions on the user object after the first retrieval, which is usually sufficient for most request-response cycles. However, if you find yourself adding permissions and immediately checking them (e.g., in a test or view), it's advisable to re-fetch the user from the database to ensure the new permissions associated with user groups take effect.
In a template, you can access permission associated with the authenticated user in the template variable {{ perms }}
.
To learn more about Django permissions and authentication system, refer to the Django documentation for comprehensive information and detailed examples. Happy exploring!
Conclusion
In conclusion, we've explored the implementation of Role-Based Access Control (RBAC) using Django's native features, providing a solid foundation for access control in your applications.
As you continue to refine your application's security and scalability, you'll find that managing granular permissions at scale becomes increasingly challenging. At that point consider exploring Permify. It's a Google Zanzibar-based open-source authorization service that seamlessly builds fine-grained permissions at scale.
With Permify, you'll have a powerful tool to elevate your access control game effortlessly.