Controlling access: a Django permission apps comparison

Filipe Ximenes
January 14, 2016
<p>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 <a href="https://www.djangopackages.com/grids/g/perms/">full grid on permissions</a>, you should take a look.</p><h2 id="1-native-django-permissions">1. Native Django permissions</h2><p>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 <code>django.contrib.auth</code> in your <code>INSTALLED_APPS</code> django will automatically create <code>add</code>, <code>change</code>and <code>delete</code> 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.</p><p>But this is not limited to the admin. You can verify user permissions in your own views using the <code>has_perm</code> method provided in the user model.</p><pre><code class="language-python">if user.has_perm('foo.add_bar'): return HttpResponse("You are authorized to add content!") else: return HttpResponse("Permission to add denied") </code></pre><p>you can also check user permissions in your templates, using the <code>perms</code> variable that is automatically added to the template context:</p><pre><code>{% 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. </code></pre><p>You can also create your own permissions to the models:</p><pre><code class="language-python">class Content(models.Model): owner = models.ForeignKey(User) content = models.TextField() class Meta: permissions = ( ('view_content', 'View content'), ) </code></pre><p>Check the <a href="https://docs.djangoproject.com/en/dev/topics/auth/default/#permissions-and-authorization">official documentation</a> for more information on how to create new permissions, programmatically managing user permissions and working with groups.</p><h2 id="2-django-guardian">2. Django guardian</h2><p><a href="https://github.com/django-guardian/django-guardian">Django Guardian</a> 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:</p><pre><code class="language-python">&gt;&gt;&gt; from django.contrib.auth.models import User &gt;&gt;&gt; from contents.model import Content &gt;&gt;&gt; from guardian.shortcuts import assign_perm &gt;&gt;&gt; &gt;&gt;&gt; writer = User.objects.create(username='The Writer') &gt;&gt;&gt; joe = User.objects.create(username='joe') &gt;&gt;&gt; content = Content.objects.create( owner=writer, content='This is the content!') &gt;&gt;&gt; joe.has_perm('view_content', content) False &gt;&gt;&gt; assign_perm('view_content', joe, content) &gt;&gt;&gt; joe.has_perm('view_content', content) True </code></pre><p>You can do the same to groups:</p><pre><code class="language-python">&gt;&gt;&gt; from django.contrib.auth.models import Group &gt;&gt;&gt; group = Group.objects.create(name='reviewers') &gt;&gt;&gt; assign_perm('view_content', group, content) &gt;&gt;&gt; joe.groups.add(group) &gt;&gt;&gt; joe.has_perm('view_content', content) True </code></pre><p>It also has some very useful methods like <code>get_perms</code> and <code>get_objects_for_user</code>, decorators for your views and template tags.</p><h2 id="3-django-role-permissions">3. Django role permissions</h2><p><a href="https://github.com/vintasoftware/django-role-permissions">Django role permissions</a> 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 <code>roles</code> with a default set of permissions defined.</p><pre><code class="language-python">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, } </code></pre><p>Once we give a role to a user we will be able to make verifications about his permissions and also about his role.</p><pre><code class="language-python">&gt;&gt;&gt; from rolepermissions.verifications import has_permission, has_role &gt;&gt;&gt; has_permission(joe, 'view_content') False &gt;&gt;&gt; Reviewer.assign_role_to_user(joe) &gt;&gt;&gt; has_permission(joe, 'view_content') True &gt;&gt;&gt; has_role(joe, 'reviewer') True </code></pre><p>It also allows you to define object level permissions using programming logic:</p><pre><code class="language-python">from rolepermissions.permissions import register_object_checker @register_object_checker() def publish_content(role, user, obj): if obj.owner == user: return True return False </code></pre><p>and verify:</p><pre><code class="language-python">&gt;&gt;&gt; from rolepermissions.verifications import has_object_permission &gt;&gt;&gt; has_object_permission('publish_content', writer, content) </code></pre><p>You will also find helper functions, templates tags, decorators and mixins. This project is maintained by Vinta, so shameless plug here :)</p><p><strong>Update:</strong> Django role permissions now supports assigning multiple roles per user.</p><h2 id="4-rules">4. Rules</h2><p><a href="https://github.com/dfunckt/django-rules">Rules</a> 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:</p><pre><code class="language-python">@rules.predicate def is_content_writter(user, obj): return obj.owner == user @rules.predicate def is_admin_user(user, obj): return user.is_admin </code></pre><p>Then we associate our predicates to a rule:</p><pre><code class="language-python">rules.add_perm('contents.change_content', is_content_writter) </code></pre><p>We can even use operators to create complex rules that depend on multiple predicates:</p><pre><code class="language-python">is_admin_writer = is_content_writter &amp; is_admin_user rules.add_perm('contents.delete_content', is_admin_writer) </code></pre><p>and finally we can test our rules with objects:</p><pre><code class="language-python">&gt;&gt;&gt; joe.has_perm('contents.delete_content', content) False &gt;&gt;&gt; writer.has_perm('contents.delete_content', content) True </code></pre><p>Rules also provides decorators and mixins for your views and template tags. Another cool thing is that it can be used without Django.</p><hr><p>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!</p>