Add negation ~ operator to permissions composition (#6361)

This commit is contained in:
Adrien Brunet 2019-02-25 15:33:40 +01:00 committed by Carlton Gibson
parent 739b0a272a
commit 2daf6f1341
3 changed files with 49 additions and 2 deletions

View File

@ -134,7 +134,7 @@ Provided they inherit from `rest_framework.permissions.BasePermission`, permissi
} }
return Response(content) return Response(content)
__Note:__ it only supports & -and- and | -or-. __Note:__ it supports & (and), | (or) and ~ (not).
--- ---

View File

@ -24,6 +24,19 @@ class OperationHolderMixin:
def __ror__(self, other): def __ror__(self, other):
return OperandHolder(OR, other, self) return OperandHolder(OR, other, self)
def __invert__(self):
return SingleOperandHolder(NOT, self)
class SingleOperandHolder(OperationHolderMixin):
def __init__(self, operator_class, op1_class):
self.operator_class = operator_class
self.op1_class = op1_class
def __call__(self, *args, **kwargs):
op1 = self.op1_class(*args, **kwargs)
return self.operator_class(op1)
class OperandHolder(OperationHolderMixin): class OperandHolder(OperationHolderMixin):
def __init__(self, operator_class, op1_class, op2_class): def __init__(self, operator_class, op1_class, op2_class):
@ -73,6 +86,17 @@ class OR:
) )
class NOT:
def __init__(self, op1):
self.op1 = op1
def has_permission(self, request, view):
return not self.op1.has_permission(request, view)
def has_object_permission(self, request, view, obj):
return not self.op1.has_object_permission(request, view, obj)
class BasePermissionMetaclass(OperationHolderMixin, type): class BasePermissionMetaclass(OperationHolderMixin, type):
pass pass

View File

@ -580,7 +580,19 @@ class PermissionsCompositionTests(TestCase):
composed_perm = permissions.IsAuthenticated | permissions.AllowAny composed_perm = permissions.IsAuthenticated | permissions.AllowAny
assert composed_perm().has_permission(request, None) is True assert composed_perm().has_permission(request, None) is True
def test_several_levels(self): def test_not_false(self):
request = factory.get('/1', format='json')
request.user = AnonymousUser()
composed_perm = ~permissions.IsAuthenticated
assert composed_perm().has_permission(request, None) is True
def test_not_true(self):
request = factory.get('/1', format='json')
request.user = self.user
composed_perm = ~permissions.AllowAny
assert composed_perm().has_permission(request, None) is False
def test_several_levels_without_negation(self):
request = factory.get('/1', format='json') request = factory.get('/1', format='json')
request.user = self.user request.user = self.user
composed_perm = ( composed_perm = (
@ -591,6 +603,17 @@ class PermissionsCompositionTests(TestCase):
) )
assert composed_perm().has_permission(request, None) is True assert composed_perm().has_permission(request, None) is True
def test_several_levels_and_precedence_with_negation(self):
request = factory.get('/1', format='json')
request.user = self.user
composed_perm = (
permissions.IsAuthenticated &
~ permissions.IsAdminUser &
permissions.IsAuthenticated &
~(permissions.IsAdminUser & permissions.IsAdminUser)
)
assert composed_perm().has_permission(request, None) is True
def test_several_levels_and_precedence(self): def test_several_levels_and_precedence(self):
request = factory.get('/1', format='json') request = factory.get('/1', format='json')
request.user = self.user request.user = self.user