Skip to content

Commit 113922b

Browse files
committed
Permissions uses lazy evaluation when composing permissions
1 parent d110454 commit 113922b

File tree

2 files changed

+85
-4
lines changed

2 files changed

+85
-4
lines changed

rest_framework/permissions.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@ def __init__(self, op1, op2):
4444

4545
def has_permission(self, request, view):
4646
return (
47-
self.op1.has_permission(request, view) &
47+
self.op1.has_permission(request, view) and
4848
self.op2.has_permission(request, view)
4949
)
5050

5151
def has_object_permission(self, request, view, obj):
5252
return (
53-
self.op1.has_object_permission(request, view, obj) &
53+
self.op1.has_object_permission(request, view, obj) and
5454
self.op2.has_object_permission(request, view, obj)
5555
)
5656

@@ -62,13 +62,13 @@ def __init__(self, op1, op2):
6262

6363
def has_permission(self, request, view):
6464
return (
65-
self.op1.has_permission(request, view) |
65+
self.op1.has_permission(request, view) or
6666
self.op2.has_permission(request, view)
6767
)
6868

6969
def has_object_permission(self, request, view, obj):
7070
return (
71-
self.op1.has_object_permission(request, view, obj) |
71+
self.op1.has_object_permission(request, view, obj) or
7272
self.op2.has_object_permission(request, view, obj)
7373
)
7474

tests/test_permissions.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import base64
44
import unittest
55
import warnings
6+
from unittest import mock
67

78
import django
89
from django.contrib.auth.models import AnonymousUser, Group, Permission, User
@@ -600,3 +601,83 @@ def test_several_levels_and_precedence(self):
600601
permissions.IsAuthenticated
601602
)
602603
assert composed_perm().has_permission(request, None) is True
604+
605+
def test_or_lazyness(self):
606+
request = factory.get('/1', format='json')
607+
request.user = AnonymousUser()
608+
609+
with mock.patch.object(permissions.AllowAny, 'has_permission', return_value=True) as mock_allow:
610+
with mock.patch.object(permissions.IsAuthenticated, 'has_permission', return_value=False) as mock_deny:
611+
composed_perm = (permissions.AllowAny | permissions.IsAuthenticated)
612+
hasperm = composed_perm().has_permission(request, None)
613+
self.assertIs(hasperm, True)
614+
mock_allow.assert_called_once()
615+
mock_deny.assert_not_called()
616+
617+
with mock.patch.object(permissions.AllowAny, 'has_permission', return_value=True) as mock_allow:
618+
with mock.patch.object(permissions.IsAuthenticated, 'has_permission', return_value=False) as mock_deny:
619+
composed_perm = (permissions.IsAuthenticated | permissions.AllowAny)
620+
hasperm = composed_perm().has_permission(request, None)
621+
self.assertIs(hasperm, True)
622+
mock_deny.assert_called_once()
623+
mock_allow.assert_called_once()
624+
625+
def test_object_or_lazyness(self):
626+
request = factory.get('/1', format='json')
627+
request.user = AnonymousUser()
628+
629+
with mock.patch.object(permissions.AllowAny, 'has_object_permission', return_value=True) as mock_allow:
630+
with mock.patch.object(permissions.IsAuthenticated, 'has_object_permission', return_value=False) as mock_deny:
631+
composed_perm = (permissions.AllowAny | permissions.IsAuthenticated)
632+
hasperm = composed_perm().has_object_permission(request, None, None)
633+
self.assertIs(hasperm, True)
634+
mock_allow.assert_called_once()
635+
mock_deny.assert_not_called()
636+
637+
with mock.patch.object(permissions.AllowAny, 'has_object_permission', return_value=True) as mock_allow:
638+
with mock.patch.object(permissions.IsAuthenticated, 'has_object_permission', return_value=False) as mock_deny:
639+
composed_perm = (permissions.IsAuthenticated | permissions.AllowAny)
640+
hasperm = composed_perm().has_object_permission(request, None, None)
641+
self.assertIs(hasperm, True)
642+
mock_deny.assert_called_once()
643+
mock_allow.assert_called_once()
644+
645+
def test_and_lazyness(self):
646+
request = factory.get('/1', format='json')
647+
request.user = AnonymousUser()
648+
649+
with mock.patch.object(permissions.AllowAny, 'has_permission', return_value=True) as mock_allow:
650+
with mock.patch.object(permissions.IsAuthenticated, 'has_permission', return_value=False) as mock_deny:
651+
composed_perm = (permissions.AllowAny & permissions.IsAuthenticated)
652+
hasperm = composed_perm().has_permission(request, None)
653+
self.assertIs(hasperm, False)
654+
mock_allow.assert_called_once()
655+
mock_deny.assert_called_once()
656+
657+
with mock.patch.object(permissions.AllowAny, 'has_permission', return_value=True) as mock_allow:
658+
with mock.patch.object(permissions.IsAuthenticated, 'has_permission', return_value=False) as mock_deny:
659+
composed_perm = (permissions.IsAuthenticated & permissions.AllowAny)
660+
hasperm = composed_perm().has_permission(request, None)
661+
self.assertIs(hasperm, False)
662+
mock_allow.assert_not_called()
663+
mock_deny.assert_called_once()
664+
665+
def test_object_and_lazyness(self):
666+
request = factory.get('/1', format='json')
667+
request.user = AnonymousUser()
668+
669+
with mock.patch.object(permissions.AllowAny, 'has_object_permission', return_value=True) as mock_allow:
670+
with mock.patch.object(permissions.IsAuthenticated, 'has_object_permission', return_value=False) as mock_deny:
671+
composed_perm = (permissions.AllowAny & permissions.IsAuthenticated)
672+
hasperm = composed_perm().has_object_permission(request, None, None)
673+
self.assertIs(hasperm, False)
674+
mock_allow.assert_called_once()
675+
mock_deny.assert_called_once()
676+
677+
with mock.patch.object(permissions.AllowAny, 'has_object_permission', return_value=True) as mock_allow:
678+
with mock.patch.object(permissions.IsAuthenticated, 'has_object_permission', return_value=False) as mock_deny:
679+
composed_perm = (permissions.IsAuthenticated & permissions.AllowAny)
680+
hasperm = composed_perm().has_object_permission(request, None, None)
681+
self.assertIs(hasperm, False)
682+
mock_allow.assert_not_called()
683+
mock_deny.assert_called_once()

0 commit comments

Comments
 (0)