Implementing Role-Based Access Control in Django
Tags: python, tutorial, webdev, security
Published: 2023-11-01 14:47:55

**Author:** Boluwatife Fayemi

## Introduction

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)](https://www.digitalguardian.com/blog/what-role-based-access-control-rbac-examples-benefits-and-more) and how to implement it in [Django](https://www.djangoproject.com/).

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:
1. Have knowledge of Python and Django 
2. Have **Python 3.7** and above installed

## 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:
1. **User:** This models the users of an application. 
2. **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.  
3. **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.

[![Local Image](https://github.com/Permify/permify/assets/34595361/c5d8f84d-ca9e-4c98-9c25-3b64db0f0a06)](https://github.com/Permify/permify/)

## 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:
1. **Student** - can view his own record
2. **Teacher** - can create, update, view, and delete student records.
3. **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](https://docs.djangoproject.com/en/4.2/topics/db/examples/many_to_many/) 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.

1. Run the command `python manage.py createsuperuser`
2. Go to [http://127.0.0.1:8000/admin/](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.

1. Create an app named record by running the command `python manage.py startapp record`.
 
2. Copy the code below into the models.py file in the record application.

```python
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}”
```

3. Add the newly created app to the `INSTALLED_APPS` list in the `settings.py` file.
4. Run the commands `python manage.py makemigrations` and `python manage.py migrate` to create the table for the student assessment record.

5. Register the model in Django admin by adding the following to the admin.py

```python
from django.contrib import admin
from .models import StudentAssessmentRecord


@admin.register(StudentAssessmentRecord)
class StudentAssessmentRecordAdmin(admin.ModelAdmin):
    list_display = ["id", "student", "score"]
```

6. Login to the admin site to see the tables.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f1jyznj9j3dw3pdua7v6.png)

### Creating Roles

As mentioned, we’ll use the `Group` model to represent Roles. So, let’s create a **Teacher** group from the admin dashboard.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tc8dfiiio2t1hvderog7.png)

### 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:
1. Can view - give users **read** access to the instances of the model.
2. Can change - give users **update** access to the instances of the model.
3. Can add - give users **create** access to the instances of the model.
4. 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

1. Run the command `python manage.py shell`

![python-manage-command](https://github.com/Permify/permify/assets/34595361/b7d9c04a-4c51-479e-8cd8-83d062b7125f)

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:
1. name (Required): 255 characters or fewer.
2. content_type (Required): A reference to the [django_content_type](https://docs.djangoproject.com/en/4.2/ref/contrib/contenttypes/) database table, which contains a record for each installed model.
3. 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:

1. Import Permissions model and content type model
2. Get content type for the student assessment record model
3. Get the permission object
4. Add the permission object to the many-to-many field - permissions of student_group

![adding-permissions-1](https://github.com/Permify/permify/assets/34595361/487bd43a-bc98-4576-99ad-ff10a35c6cd2)
![adding-permissions-2](https://github.com/Permify/permify/assets/34595361/2cf0196c-3488-4a5b-9ef9-b90794c6f4f8)

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.

1. Get content type for the User and StudentAssessmentRecord models.
2. Get all permission objects for the user content type and student assessment record content type.

![get-all-permission-objects](https://github.com/Permify/permify/assets/34595361/1cc4038b-1533-4585-ae94-4d9e3d8ba2bd)

3. Create the principal group.
4. Combine all permissions objects for user and student assessment records into one query set using the [OR operator (pipe character)](https://docs.djangoproject.com/en/4.2/ref/models/querysets/#or).
5. Assign the combined permissions to the principal group by [setting](https://docs.djangoproject.com/en/4.2/ref/models/relations/#django.db.models.fields.related.RelatedManager.set) permissions for the principal group.

![principal group](https://github.com/Permify/permify/assets/34595361/a3bb6911-59f1-4ffd-8202-98580b151143)

### 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.

![cache-refetch](https://github.com/Permify/permify/assets/34595361/e0d58879-2984-4e70-919b-eee629c081cc)

**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](https://docs.djangoproject.com/en/4.2/topics/auth/default/#permissions) 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](https://github.com/Permify/permify). It's a Google Zanzibar-based open-source authorization service that seamlessly builds fine-grained permissions at scale. 

If you need any help on authorization, we're happy to help you get started. Join our [Discord community](https://discord.com/invite/Uf2jwnyfDX) and share your use case!