django-guardian how to make object inherit permissions of related object?
Asked Answered
A

2

6

I got two models:

class ContactGroup(models.Model):
    name = models.CharField(max_length=40)
    class Meta:
        permissions=(('view_group_contacts', 'View contacts from group'))


class Contact(models.Model):
    name = models.CharField(max_length=40)
    group = models.ForeignKey(ContactGroup)
    class Meta:
        permissions=(('view_contact', 'View contact'))        

How can I make django guardian consider ContactGroup permissions when I'm for example doing `get_objects_for_user(User, 'appname.view_contact) but still retain option for changing permission on individual Contact?(not for excluding, only to give permission to view single contact when user don't have the permission for whole group)

Abraham answered 19/5, 2014 at 9:40 Comment(1)
First idea was to override has_perm method to check obj.contactgroup.has_perm permissions if super has_perm returned false. But then I noticed that get_objects_for_user is not using has_perm so I would need to override it to - and probably all other guardian shortcuts functions. Second idea was to create custom model.Manager but I don't have access to request in Manager so it won't work. Now I'm thinking about automatically creating group for every contactGroup and storing permissions there but this may be nightmare to maintain. Maybe I should use different permission system?Abraham
F
2

Sorry, such behaviour is not supported by django-guardian. As for has_perm - it would be extremely inefficient to use it for querysets as we would need to perform >=1 query for each row in a table.

You could however perform get_objects_for_user firstly for ContactGroup, then for Contact and extend last queryset with results from the first one. Something like:

contact_groups = get_objects_for_user(user, 'appname.view_group_contacts', ContactGroup)
contacts = get_objects_for_user(user, 'appname.view_contact', Contact)

There is still problem of merging those but well, it's possible.

Foggia answered 19/5, 2014 at 13:53 Comment(0)
A
2

Very ugly workaround, it does not take for account changes in individual objects (it resets all permission to ContactGroup permissions if remove= False). But It can be rewritten to preserve changes if needed. I plan to attach it to "Sync Permissions with group" button so it will be fired only at user request. Main pro is its working with get_objects_for_user as intended.

def syncPerms(source, remove=False):
if not isinstance(source, ContactGroup):
    return False

contacts= source.client_set.all()
user_perms=get_users_with_perms(source, attach_perms=True)
for contact in contacts:
    for user, perm in user_perms.iteritems():
        if u'view_group_contacts' in perm:
            assign_perm('view_contact', user,client)
        else:
            if remove:
                remove_perm('view_contact', user, client)
Abraham answered 20/5, 2014 at 12:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.