Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#31093 closed New feature (wontfix)

Extend permission backend with get_queryset(user, model).

Reported by: James Pic Owned by: nobody
Component: contrib.auth Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by James Pic)

Permissions on objects are based on two mechanisms that developers have to implement:

  • returning if a user has a permission on an object instance
  • filtering a queryset based on a user object and eventually a permission name

Currently, permission backend allows developers to implement the first mechanism: you can allow a specific permission on an object with the permission backend.

This works extremely well even for complex use cases: you get an model object, a user, a permission name and you can return True.

Exemple:

    def has_perm(self, user_obj, perm, obj=None):    
        if not user_obj.is_authenticated or not isinstance(obj, SomeModel):    
            return False
    
        return user_obj.is_superuser or obj.related_model_fk in user_obj.related_model_m2m.all()    

However, permission framework should also allow developers to implement the second security mechanism: getting a filtered queryset with objects a user should be able to see, eventually for a given permission. Such implementation could look like:

    def filter_queryset(self, user_obj, perm, queryset=None):
        if not queryset.model == SomeModel:
            return queryset

        if not user_obj.is_authenticated:    
            return queryset.none()    
    
        return queryset.filter(related_model_fk__in=user_obj.related_model_m2m.all())

The admin views could use this, and django.contrib.auth could provide generic views extensions which do check permissions, removing the need to share a mixin that just does return a Mixin with a get_queryset method to complement the code that they have in the permission backend. It would reduce chances to make a mistake when updating permission code if it's all at the same place, an opinion that I consider suited for a framework like Django.

I consider that the subject of making ModelChoiceFields to be able to benefit from this is out of the scope of this ticket, but I could bring it up for discussion if this feature is implemented (ie. DRF serializers have a "context" variables where the request object is set by default, which allows to do user-based validation: a pretty standard requirement).

Change History (8)

comment:1 by James Pic, 5 years ago

Description: modified (diff)

comment:2 by James Pic, 5 years ago

Description: modified (diff)

comment:3 by James Pic, 5 years ago

Description: modified (diff)

comment:4 by James Pic, 5 years ago

Description: modified (diff)

comment:5 by James Pic, 5 years ago

Description: modified (diff)

comment:6 by James Pic, 5 years ago

Description: modified (diff)

comment:7 by Mariusz Felisiak, 5 years ago

Resolution: wontfix
Status: newclosed
Summary: Extend permission backend with get_queryset(user, model)Extend permission backend with get_queryset(user, model).
Version: 3.0master

Django's Admin already handles permissions properly. IMO a generic method for filtering a queryset based on an user object (and/or a permission name) shouldn't be added to the builtin authentication back-ends. You can start a discussion on DevelopersMailingList if you don't agree.

comment:8 by James Pic, 5 years ago

Note: See TracTickets for help on using tickets.
Back to Top