Objects with permissions assigned by django-guardian are not visible in admin

I use django-guardian to control permission for each object.

For this user, I allow permission for one object:

joe = User.objects.get(username="joe") mytask = Task.objects.get(pk=1) assign('add_task', joe, mytask) assign('change_task', joe, mytask) assign('delete_task', joe, mytask) 

and I get, as expected:

 In [57]: joe.has_perm("add_task", mytask) Out[57]: True In [58]: joe.has_perm("change_task", mytask) Out[58]: True In [59]: joe.has_perm("delete_task", mytask) Out[59]: True 

In admin.py, I also inherit TaskAdmin from GuardedModelAdmin instead of admin.ModelAdmin

Now, when I connect to my site using joe, from the administrator I get:

 You don't have permission to edit anything 

Maybe I can not edit mytask object?

Do I need to set some permissions with the built-in model-based permission system?

Did I miss something?

EDIT

I tried adding the user_can_access_owned_objects_only parameter, which should deal with my problem, but I still cannot see anything in my admin ...

 class TaskAdmin(GuardedModelAdmin): user_can_access_owned_objects_only = True pass admin.site.register(Task, TaskAdmin) 

thanks

+6
source share
2 answers

To see only instances owned by the current user, I grant him all permissions

 add_task=Permission.objects.get(codename="add_task") change_task=Permission.objects.get(codename="change_task") delete_task=Permission.objects.get(codename="delete_task") joe.user_permissions.add(add_task) joe.user_permissions.add(change_task) joe.user_permissions.add(delete_task) 

then I set the permission for multiple instances using guardian.shortcuts.assign and I filter the request in admin:

 class TaskAdmin(admin.ModelAdmin): def queryset(self, request): if request.user.is_superuser: return super(TaskAdmin, self).queryset(request) return get_objects_for_user(user=request.user, perms=['add_task', 'change_task', 'delete_task'], klass=Task) 

This is far from ideal, but I can not find another solution.

+3
source

The Django admin, especially having w / change permission, is a little rude. The internal ModelAdmin.has_change_permission() method covers the view permission check, which is actually missing.

Secondly, GuardedModelAdmin includes property validation (via user_can_access_owned_objects_only ) and forms for managing permissions at the row level. It does not provide any other row level access policies for the Django admin.

For a typical row level resolution scene in Django Admin, I would like to suggest code, here I entered the optional "view" permission:

 class ExtendedGuardedModelAdmin(GuardedModelAdmin): def queryset(self, request): qs = super(ExtendedGuardedModelAdmin, self).queryset(request) # Check global permission if super(ExtendedGuardedModelAdmin, self).has_change_permission(request) \ or (not self.list_editable and self.has_view_permission(request)): return qs # No global, filter by row-level permissions. also use view permission if the changelist is not editable if self.list_editable: return get_objects_for_user(request.user, [self.opts.get_change_permission()], qs) else: return get_objects_for_user(request.user, [self.opts.get_change_permission(), self.get_view_permission( )], qs, any_perm=True) def has_change_permission(self, request, obj=None): if super(ExtendedGuardedModelAdmin, self).has_change_permission(request, obj): return True if obj is None: # Here check global 'view' permission or if there is any changeable items return self.has_view_permission(request) or self.queryset(request).exists() else: # Row-level checking return request.user.has_perm(self.opts.get_change_permission(), obj) def get_view_permission(self): return 'view_%s' % self.opts.object_name.lower() def has_view_permission(self, request, obj=None): return request.user.has_perm(self.opts.app_label + '.' + self.get_view_permission(), obj) def has_delete_permission(self, request, obj=None): return super(ExtendedGuardedModelAdmin, self).has_delete_permission(request, obj) \ or (obj is not None and request.user.has_perm(self.opts.get_delete_permission(), obj)) 

This way you can achieve a more flexible permission check, now user permissions are global, user obj permissions are based on the line level:

  • joe.user_permissions.add(add_task)
    joe can add new tasks (no permission to add at row level)
  • joe.user_permissions.add(change_task)
    joe can change ALL tasks
  • joe.user_permissions.add(delete_task)
    joe can delete ALL tasks
  • assign(Task._meta.get_change_permission(), joe, obj)
    joe can modify the obj task, see the changelog containing obj, as well as other mutable tasks.
  • assign(Task._meta.get_delete_permission(), joe, obj)
    joe can remove Task obj
  • assign('view_task', joe, obj)
    [optional] joe can view Task obj (you can check this permission on the Admin settings page).
  • joe.user_permissions.add(Permission.objects.get(codename='view_task', ...))
    [optional] joe can view all tasks in the change list if the change list is not editable. This is useful if joe can pick up items from raw_id_fields without permission to change.
  • ...

You can safely ignore the "view" permission if it is useless to you.

Currently django.contrib.admin.util.get_deleted_objects does not comply with obj during permission check, if you need to check row level permission during deletion, fix get_deleted_objects by changing the line if not user.has_perm(p): to if not user.has_perm(p, obj): Relative tickets 13539 and 16862

+2
source

Source: https://habr.com/ru/post/917507/


All Articles