6
votes

I am trying to use django rules to configure object permissions in django and the django admin interface.

Now when I add permission rules, they will always only be called with the first param, but the object is always None.

For example I if I would create this predicate:

@rules.predicate
def is_book_author(user, book):
    return book.author == user

And then add it to the django permission set:

rules.add_perm('books.view_book', is_book_author)

Now when I log into the admin interface with a user, then the is_book_author will be called with the user and None. It will be called multiple times (once per object), but the object ist always None.

I am using rules 2.0.0 with django 2.1.1 and python 3.7.

Any ideas if I'm doing something wrong or how to configure django to call the predicate with the individual object?

1
Well at that moment the book does not exists yet. add_book is the permission to add a book, not to "save" the book. I think you should handle this in the form (or even in the form just "inject" the user, such that he/she can simply never add other data). - Willem Van Onsem
Thanks for your comment, but I have the problem with all 4 permissions. I replaced add_book in the question with the view_book permission to clarify. - Tigerware
@BluE I'm trying to solve your issue but everything works for me just fine. I used testapp to debug it and still cannot reproduce your issue. Could you provide whole files where you use ObjectPermissionsModelAdmin and add_perm with predicate? Take a look also on github.com/dfunckt/django-rules/blob/v2.0.0/tests/testapp/… Try to compare your code with that from the repo. - Kamil Niski
@KamilNiski I will update the question after work today to add the requested information. Quick question: Were you using the same django version, when you tried the example? - Tigerware
Yes, I used exactly the same versions of libraries as you specified - Kamil Niski

1 Answers

8
votes

As django-rules documentation states:

Django Admin does not support object-permissions, in the sense that it will never ask for permission to perform an action on an object, only whether a user is allowed to act on (any) instances of a model.

If you'd like to tell Django whether a user has permissions on a specific object, you'd have to override the following methods of a model's ModelAdmin:

has_view_permission(user, obj=None)
has_change_permission(user, obj=None)
has_delete_permission(user, obj=None)

rules comes with a custom ModelAdmin subclass, rules.contrib.admin.ObjectPermissionsModelAdmin, that overrides these methods to pass on the edited model instance to the authorization backends, thus enabling permissions per object in the Admin:

# books/admin.py
from django.contrib import admin
from rules.contrib.admin import ObjectPermissionsModelAdmin
from .models import Book

class BookAdmin(ObjectPermissionsModelAdmin):
    pass

admin.site.register(Book, BookAdmin)

Now this allows you to specify permissions like this:

rules.add_perm('books', rules.always_allow)
rules.add_perm('books.add_book', has_author_profile)
rules.add_perm('books.change_book', is_book_author_or_editor)
rules.add_perm('books.delete_book', is_book_author)

To preserve backwards compatibility, Django will ask for either view or change permission. For maximum flexibility, rules behaves subtly different: rules will ask for the change permission if and only if no rule exists for the view permission.