Blog

Ideas and insights from our team

Controlling access: a Django permission apps comparison


There are many ways to handle permissions in a project. For instance we may have model level permissions, object level permissions, fine grained user permission or role based. Either way we don't need to be writing any of those from scratch, Django ecosystem has a vast amount of permission handling apps that will help us with the task. In this post we will compare how some popular permission apps work so you know which one suits your project needs. If you want to explore a little more, Django Packages has a full grid on permissions, you should take a look.

1. Native Django permissions

As we would expect, Django has a native permission system that works very well in many situations and it's great if final users are making intense use of the Django admin interface. It is a model level permission system that can be applied to either single users or a group. If you have django.contrib.auth in your INSTALLED_APPS django will automatically create add, changeand delete permission types to every model in your system (or any one you add later). Giving or revoking this permissions to users will limit the actions they can take when they log in to the admin. Instead of managing user by user, you can also create a group, add users to it and manage the permissions for the whole group.

But this is not limited to the admin. You can verify user permissions in your own views using the has_perm method provided in the user model.

if user.has_perm('foo.add_bar'):
    return HttpResponse("You are authorized to add content!")
else:
    return HttpResponse("Permission to add denied")

you can also check user permissions in your templates, using the perms variable that is automatically added to the template context:

{% if perms.app_label.can_do_something %}
    This content will be shown users with can_do_something permission.
{% endif %}
This content will be shown to all users.

You can also create your own permissions to the models:

class Content(models.Model):
    owner = models.ForeignKey(User)
    content = models.TextField()

    class Meta:
        permissions = (
            ('view_content', 'View content'),
        )

Check the official documentation for more information on how to create new permissions, programmatically managing user permissions and working with groups.

2. Django guardian

Django Guardian is an third party app focused on handling object level permissions. While the native Django permissions manages the access to all objects in a model, with guardian we can control the access to a particular instance of a model. A good thing is that it has a similar interface to Django's:

>>> from django.contrib.auth.models import User
>>> from contents.model import Content
>>> from guardian.shortcuts import assign_perm
>>> 
>>> writer = User.objects.create(username='The Writer')
>>> joe = User.objects.create(username='joe')
>>> content = Content.objects.create(
    owner=writer, content='This is the content!')
>>> joe.has_perm('view_content', content)
False
>>> assign_perm('view_content', joe, content)
>>> joe.has_perm('view_content', content)
True

You can do the same to groups:

>>> from django.contrib.auth.models import Group
>>> group = Group.objects.create(name='reviewers')
>>> assign_perm('view_content', group, content)
>>> joe.groups.add(group)
>>> joe.has_perm('view_content', content)
True

It also has some very useful methods like get_perms and get_objects_for_user, decorators for your views and template tags.

3. Django role permissions

Django role permissions uses a more high level approach to manage access to views and objects. Instead of saying individually which user has access to which object, we will be defining roles with a default set of permissions defined.

from rolepermissions.roles import AbstractUserRole

class Writer(AbstractUserRole):
    available_permissions = {
        'create_content': True,
        'view_content': True,
    }

class Reviewer(AbstractUserRole):
    available_permissions = {
        'create_content': False,
        'view_content': True,
    }

Once we give a role to a user we will be able to make verifications about his permissions and also about his role.

>>> from rolepermissions.verifications import has_permission, has_role
>>> has_permission(joe, 'view_content')
False
>>> Reviewer.assign_role_to_user(joe)
>>> has_permission(joe, 'view_content')
True
>>> has_role(joe, 'reviewer')
True

It also allows you to define object level permissions using programming logic:

from rolepermissions.permissions import register_object_checker

@register_object_checker()
def publish_content(role, user, obj):
    if obj.owner == user:
        return True
    return False

and verify:

>>> from rolepermissions.verifications import has_object_permission
>>> has_object_permission('publish_content', writer, content)

You will also find helper functions, templates tags, decorators and mixins. This project is maintained by Vinta, so shameless plug here :)

Update: Django role permissions now supports assigning multiple roles per user.

4. Rules

Rules is a very interesting app that does not use any database model to manage object level permissions. Everything is done by verification functions, similar to the django-role-permissions object checkers but more powerful. We start by creating predicates:

@rules.predicate
def is_content_writter(user, obj):
    return obj.owner == user

@rules.predicate
def is_admin_user(user, obj):
    return user.is_admin

Then we associate our predicates to a rule:

rules.add_perm('contents.change_content', is_content_writter)

We can even use operators to create complex rules that depend on multiple predicates:

is_admin_writer = is_content_writter & is_admin_user
rules.add_perm('contents.delete_content', is_admin_writer)

and finally we can test our rules with objects:

>>> joe.has_perm('contents.delete_content', content)
False
>>> writer.has_perm('contents.delete_content', content)
True

Rules also provides decorators and mixins for your views and template tags. Another cool thing is that it can be used without Django.


I hope you got a quick glance over some of the Django permission apps available and is now able to choose the one that best suits your application. If you know some other interesting apps with different approach we'd love to hear about them in the comments!

About Filipe Ximenes

Bike enthusiast, software developer and former director at Python Brasil Association. Likes open source and how people interact in open source communities.

Comments