Opened 5 years ago

Last modified 13 months ago

#20218 new Bug

Default authorization backend returns False when queried for object level permissions

Reported by: soren@… Owned by: nobody
Component: contrib.auth Version: 1.5
Severity: Normal Keywords: auth
Cc: astronouth7303@… Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


The default auth backend, django.contrib.auth.backends.ModelBackend unconditioally returns False when queried through User.has_perm() if has_perm is passed an object.

I understand that erring on the side of caution is generally sound, but doing it this way forces generic consumers of the auth framework (e.g. Tastypie in my case) to know whether to pass an obj or not depending on the authentication backend chosen. Always passing an obj to has_perm will result in all requests being denied if using the default backend. Not passing it makes it impossible to apply object level permissions even though I've configured a capable authz backend for this.

Ticket #12462 suggests this is intentional, but doesn't give much of a rationale. It seems to me that if you don't want a user to be able to edit all objects of type XXX, don't give them the "app.change_XXX"?

If this isn't considered a bug, can you offer some advice on how to deal with this situation from a generic application like Tastypie? How should it determine when to pass an obj or not?

Change History (8)

comment:1 Changed 5 years ago by Anssi Kääriäinen

Triage Stage: UnreviewedAccepted

Yeah, I think the proper way would be to check the generic permission and return True/False based on that. It seems incorrect that user.has_perm() will return False if given any object, but True if no obj is given. The interpretation seems to be that the user has permission for all objects, but not for any single object which seems a bit strange.

Could get_all_permissions, when given an obj, return the set of all permissions applicable to that obj. That is, if obj is Model subclass, then query for all permissions for the obj's contenttype.

I am marking this as accepted. This will need very careful consideration from backwards compatibility viewpoint. It might be the resolution will need to be wontfix due to that.

comment:2 Changed 4 years ago by Tim Graham

Component: Uncategorizedcontrib.auth

comment:3 Changed 4 years ago by Carl Meyer

Type: UncategorizedBug

comment:4 Changed 3 years ago by adam-iris

I want to bump this, because I just spent 30 minutes trying to figure out why I wasn't seeing the behavior I expected from the documentation. (My expectation was exactly the behavior proposed here -- if obj is a model, return the permissions based on the content type.)

If nothing else, the documentation should note that what it's describing is inapplicable under the default configuration.

comment:5 Changed 3 years ago by Tim Graham

Patches welcome, Adam.

comment:6 Changed 13 months ago by Tim Graham

I closed #27528 as a duplicate.

comment:7 in reply to:  description Changed 13 months ago by Jamie Bliss

Replying to soren@…:

Ticket #12462 suggests this is intentional, but doesn't give much of a rationale. It seems to me that if you don't want a user to be able to edit all objects of type XXX, don't give them the "app.change_XXX"?

I think the rationale is that the current behavior handles the general permissions case (no object) while still falling through and allowing another provider to handle the object-specific case.

The snippet to I used to work around this.

class UseGeneralPermissions:
    Permissions provider that does object-level permissions by using general permissions.
    def has_perm(self, user_obj, perm, obj=None):
        if obj is None:
            return False
            # Retry using general permissions
            return user_obj.has_perm(perm)

As to how general and object-level permissions interact? That is completely unspecified, and there isn't a clear answer how it should be. 1. A general given overrides an object-level ungiven, 2. A general ungiven overrides an object-level given. 1 still allows efficient bulk operations but UIs must check permissions on each object to know to display actions. 2 allows UIs to display actions optimistically, but can prevent efficient bulk operations.

comment:8 Changed 13 months ago by Jamie Bliss

Cc: astronouth7303@… added
Note: See TracTickets for help on using tickets.
Back to Top