DEV Community

loading...
Cover image for Introducing django-cancan: authorization library for Django

Introducing django-cancan: authorization library for Django

pgorecki profile image Przemysław Górecki ・2 min read

I'd like to share with you my Django authorization library (https://github.com/pgorecki/django-cancan) I wrote some time ago and which I'm happily using for couple of my projects now.

My main motivation was to create a rule-based permission system which can be used both for checking per-object access rights and for generating access-based querysets. As a result, you do not need to write your own querysets for retrieving accessible objects, django-cancan will handle it automatically. For example, cancan can generate a queryset of articles which can be modified by the current user, based on a the user's role or permissions.

Here is an actual usage example from one my latest projects, with chat functionality: the exchange of messages between customers and consultants.

The data model is pretty simple:

class Thread(models.Model):
    customer = models.ForeignKey(User, ...)
    consultant = models.ForeignKey(User, ...)

class Message(models.Model):
    thread = models.ForeignKey(Thread, ...)
    content = models.TextField()
Enter fullscreen mode Exit fullscreen mode

First, you define a function that grants actions based on user role.

def define_access_rules(user, rules):
    # user == request.user
    if not user.is_authenticated:
        return

    rules.allow("create", Thread)
    rules.allow("create", Message)
    rules.allow("view", Thread, customer=user)
    rules.allow("view", Message, thread__customer=user)
    rules.allow("view", Message, thread__consultant=user)

    if user.is_staff:
        rules.allow("view", Thread, consultant=user)
        rules.allow("view", Message)

    if user.is_superuser:
        rules.allow("view", Thread)
        rules.allow("view", Message)
Enter fullscreen mode Exit fullscreen mode

In this example, client (logged-in user) is allowed to create conversation threads, view all threads he started, including all messages within those threads. On top of that, consultant (is_staff=True) can list threads he is assigned to, and superusers have unlimited access to both models.

Note that rules.allow use the same syntax as Model.objects.filter.

Then the access control is implemented by overriding the get_queryset method to return a list of Threads accessible by request.user and by adding has_permission method to check if user can perform a view action.

class ThreadList(ListView):
    model = Thread

    def get_queryset(self):
        ability = request.ability
        return ability.queryset_for("view", Thread)

class ThreadDetail(PermissionRequiredMixin, DetailView):
    model = Thread

    def test_func(self):
        ability = request.ability
        thread = self.get_object()
        return ability.can("view", thread)
Enter fullscreen mode Exit fullscreen mode

Check out project README for more examples, including template-level checks, DRF integrations, etc.

I hope you will enjoy django-cancan as much as I do :)

Discussion

pic
Editor guide