13

I have implemented my own User model class as follows. Note that is it NOT customizing django's auth.User model. I am new to this object permission knowledge and especially in this self-defined User model which is required in my project.

Could you give an example of adding per-object permission in this case?

from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin

class CustomUser(AbstractBaseUser, PermissionsMixin):
         email = models.EmailField(max_length=40, unique=True)
         #.... other fields are omitted

class Article(models.Model):
    title = models.CharField('title', max_length=120)
    body = models.TextField('body')
    author = models.ForeignKey(CustomUser)

Now, the object permission comes into play. Each user can create/update/delete/view their own article objects, but ONLY view others' articles without permission to update/delete them.

From the Django docs, the Model level permission does not apply here. If the Article is given model level update permission, then all users can update others' Articles.

I found out the django-guardian. However, there seems to be no hope for this self-defined CustomUser model, as it relies heavily on Django's auth.User model!

https://django-guardian.readthedocs.org/en/v1.2/userguide/custom-user-model.html

  1. My case is subclassing AbstractBaseUser instead of AbstractUser;
  2. This is not for the admin but only for my backend code logic;
  3. I am not using Django REST API here, but if REST API is proper, please give an example.
thinkdeep
  • 945
  • 1
  • 14
  • 32

4 Answers4

25

Object-level permissions are not built into Django, even when using the standard auth.User model. But the foundation is there in that Django's PermissionsMixin defines the has_perm method, which accepts a model instance. Django does nothing with it by default, but you can.

The has_perm method effectively passes the hard work off onto the registered authentication backends. So you can create a custom authentication backend specifically for performing your object-level permission checks. It does not need to actually handle authentication. It can be as simple as a single method on a basic class. Something like the following (untested) is all you should need:

class ObjectPermissionsBackend(object):

    def has_perm(self, user_obj, perm, obj=None):
        if not obj:
            return False # not dealing with non-object permissions

        if perm == 'view':
            return True # anyone can view
        elif obj.author_id == user_obj.pk:
            return True
        else:
            return False

Tell Django to use your custom backend using the AUTHENTICATION_BACKENDS setting. In settings.py:

AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend', 'path.to.ObjectPermissionsBackend')

Then, in your code:

if user.has_perm('edit', article_instance):
    # allow editing

See https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#custom-users-and-permissions and https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#specifying-authentication-backends

oogles
  • 1,202
  • 1
  • 20
  • 29
  • Thanks for the answer. I am also looking for some packages which do this well. – thinkdeep Oct 20 '15 at 23:59
  • In that case, [@schneck's answer](http://stackoverflow.com/a/33229986/405174) should work. I have not used django-guardian, but the link you gave seems to indicate that you _can_ use it with your own custom ``User`` model, it just warns that it will require a many-to-many relationship between that ``User`` model and the ``auth.Group`` model. Django's ``PermissionsMixin``, which you're already using, defines this relationship. Are you seeing a specific error attempting to use django-guardian with your model? – oogles Oct 21 '15 at 02:00
  • django-guardian involves creating objects and hard to use. But it is powerful, do you have an easy example? Their example project does not expose much functionality. So, I figured out some other packages to use. – thinkdeep Oct 21 '15 at 02:40
  • I don't have an example using django-guardian, no. As you say, it is powerful, but does appear to involve assigning permissions to every object - every Article in your example. You could use Django signals to automatically assign Article permissions to the author when an Article is first saved. But django-rules (as you mention in your answer) does seem more suited to your requirements. – oogles Oct 21 '15 at 03:07
1

I end up using logic based per-object permission so that it does not alter my database. It is django-rules which support my class based view. Remember to override the redirect_field_name, otherwise, you will end up with redirect loop if users are logged in.

thinkdeep
  • 945
  • 1
  • 14
  • 32
0

In the documentation page you posted, there is also stated:

Basically, if we subclass AbstractUser or define many-to-many relation with auth.Group (and give reverse relate name groups) we should be fine.

Since this is what you're doing, you should set AUTH_USER_MODEL as written in the Django documentention (also see the ticket and the commit code for Django 1.5 compatibility).

schneck
  • 10,556
  • 11
  • 49
  • 74
-3

You need to clear some basics on how permission works. Here it goes in a newbie understandable way: First step is to use Meta in a model and add permission for that model in model definition"itself. Now when you migrate in django, django comes to know 'hey! There is a custom permission which now onwards I have to consider. Well, let me add this to custom permissions' Now comes second step, you define user and give him exact same permission you have defined in the model of step one. (if you can make sense upto this point, djago-gurdia doc will seem bright and clear to you) Any ways, so when a user logins and in your code when you use a model, you can check(ok, virtually ask django that hey django! Do you remember I have defined a permission on this model. And given the same permission to a user. Can you please check and tell me whether the user logged in has actually that permission or not')

cyberspider789
  • 379
  • 3
  • 9