-
Notifications
You must be signed in to change notification settings - Fork 30
Description
Because C.has_permission and C.has_object_permission both run self.evaluate_permissions() in a disconnected manner, this leads to the following logical error when OR-ing permissions:
Given
A = hpA & hopA, and
B = hpB & hopB,
A || B should really get evaluated unitarily, as
(hpA & hopA) || (hpB & hopB),
but instead gets evaluated as
(hpA || hpB) & (hopA || hopB)
which is incorrect, and leads to the following problem:
Consider
class A(BasePermission):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
return False
class B(BasePermission):
def has_permission(self, request, view):
return False
def has_object_permission(self, request, view, obj):
return TrueC(A) | C(B) should always return false for object-level access, but it returns true.
I saw that in #1 you dismissed maintaining state during the initial has_permission run as something downstream developers should implement, but it really needs to be done in rest_condition for logical correctness. If maintaining state, in the case above "the whole of B" would get marked as False during the initial phase, and B.has_object_permission would never get to run.
On an unrelated note, this would also be the stepping stone for dealing sensibly with mixing permissions with different combinations of has_permissions / has_object_permissions (both #1 and #2). When the method is missing (presuming one didn't inherit from BasePermission), it should be a no-op -- basically behaving like it returned True when AND-ed, and False when OR-ed.