3

I'd like to have multiples layers of permissions for an API using Django Rest Framework, how can I best achieve this?

Specifically the three categories of authorization I have are:

  • Roles: Model level access, such as admin and different customer types.
  • GroupAccess: Per object, group access such as a team of users.
  • Sensitivities: Per object, additional tags for sensitive information.

The second two categories apply equally across all models and it would be nice to not need separate permissions for each model.

Idea 1:

Create a model for each category inheriting from the standard django auth group. Doing these as proxy groups, to be logically different in my code, but as consistent with standard authorization as possible. Then use django-guardian to enable the object level permissions.

Idea 2:

Use the standard groups for roles and assign model level permissions based on these groups. For the object level permissions write a custom permission classes in Django Rest Framework to check the object level permissions against the user.

User
  • 339
  • 5
  • 16
  • 1
    I have used idea 2 in my university project and it's working well. 3 different types of users in my case, admin, I used the standard admin field that Django already provide in the user model, a Doctor model and a Professional model, both Doctor and Professional are in different groups, and there are object permissions for them, noone of them can delete some protected models, only Admin user can delete, and Professional can't edit objects, only create and visualize (Get). – RonanFelipe Jul 09 '19 at 02:16

1 Answers1

3

I have recently designed such an architecture so the first thing came into my mind would be like this:

  1. Roles: You can override django's built-in AbstractUser class by adding role-level choices such as:
# models.py
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    """Custom user model with an extra type field"""
    SUPER_USER = 1
    OTHER_ROLE = 2
    SOME_OTHER_ROLE = 3

    USER_TYPE_CHOICES = (
        (SUPER_USER, 'Super user'),
        (OTHER_ROLE, 'Other role'),
        (SOME_OTHER_ROLE, 'Some other role'),
    )

    user_type = models.PositiveSmallIntegerField(choices=USER_TYPE_CHOICES)

# -------------------------------------------------------------------------

# Don't forget to set this User model as your default model
# settings.py
AUTH_USER_MODEL = 'my_app.User'

  1. You can get use of django's built-in Group model and put a ForeignKey to your Team models and do object-level permission manually.
# models.py
from django.contrib.auth.models import Group
from django.db import models

def Team(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE)

# -------------------------------------------------------------------------

# You can do object-level permission per group by
# ...
if team.group in permitted_groups:
    # permission granted
    pass
else:
    # permission not granted
    pass
# ...
  1. You can define a Tag model and add as ManyToManyField to your sensitive information model. Similar to the second solution above, you can manually do object-level permission during runtime by relying on your current information's tags.
cagrias
  • 1,847
  • 3
  • 13
  • 24