Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

row level permissions in django

Is there a way to do row level permissions in django? I thought there wasn't but just noticed this in the docs:

Permissions can be set not only per type of object, but also per specific object instance. By using the has_add_permission(), has_change_permission() and has_delete_permission() methods provided by the ModelAdmin class, it is possible to customize permissions for different object instances of the same type.

https://docs.djangoproject.com/en/dev/topics/auth/

But i don't see any documentation on how to actually implement per instance permissions

like image 431
9-bits Avatar asked Aug 09 '12 20:08

9-bits


People also ask

What are object level permissions Django?

What are Object Level Permissions in DRF? In DRF, object level permissions are used to decide if a user should be allowed to interact with a particular object. The object is usually simply a model instance.

How do I restrict permissions in Django access?

Django admin allows access to users marked as is_staff=True . To disable a user from being able to access the admin, you should set is_staff=False . This holds true even if the user is a superuser. is_superuser=True .

How does Django permissions work?

By default, Django automatically gives add, change, and delete permissions to all models, which allow users with the permissions to perform the associated actions via the admin site. You can define your own permissions to models and grant them to specific users.


2 Answers

For an application i'm building i want to provide row level permission through a simple decorator. I can do this because the condition is just whether the request.user is the owner of the model object.

Following seems to work:

from functools import wraps
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist

def is_owner_permission_required(model, pk_name='pk'):
    def decorator(view_func):
        def wrap(request, *args, **kwargs):
            pk = kwargs.get(pk_name, None)
            if pk is None:
                raise RuntimeError('decorator requires pk argument to be set (got {} instead)'.format(kwargs))
            is_owner_func = getattr(model, 'is_owner', None)
            if is_owner_func is None:
                raise RuntimeError('decorator requires model {} to provide is_owner function)'.format(model))
            o=model.objects.get(pk=pk) #raises ObjectDoesNotExist
            if o.is_owner(request.user):
                return view_func(request, *args, **kwargs)
            else:
                raise PermissionDenied
        return wraps(view_func)(wrap)
    return decorator

The view:

@login_required
@is_owner_permission_required(Comment)
def edit_comment(request, pk):
    ...

Urls:

url(r'^comment/(?P<pk>\d+)/edit/$', 'edit_comment'),

The model:

class Comment(models.Model):
    user = models.ForeignKey(User, ...
    <...>
    def is_owner(self, user):
        return self.user == user

Any feedback or remarks are appreciated.

Paul Bormans

like image 78
Paul Bormans Avatar answered Sep 23 '22 17:09

Paul Bormans


The plumbing is there (this is from the bottom of the same page you linked):

Handling object permissions

Django's permission framework has a foundation for object permissions, though there is no implementation for it in the core. That means that checking for object permissions will always return False or an empty list (depending on the check performed). An authentication backend will receive the keyword parameters obj and user_obj for each object related authorization method and can return the object level permission as appropriate.

But no default implementation is provided. Since this is a common topic; there are lots of answers on SO. Check to the right and you'll see some listed.

The basis idea is to browse the django packages' perm grid and pick an implementation of object level permissions. I personally like django-guardian.

like image 26
Burhan Khalid Avatar answered Sep 26 '22 17:09

Burhan Khalid