diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 6fdee5f29..b426546a7 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -2,6 +2,7 @@ Provides a set of pluggable permission policies. """ from django.http import Http404 +from django.utils.translation import gettext_lazy as _ from rest_framework import exceptions @@ -91,6 +92,13 @@ class OR: def __init__(self, op1, op2): self.op1 = op1 self.op2 = op2 + self.message1 = getattr(op1, 'message', None) + self.message2 = getattr(op2, 'message', None) + self.message = self.message1 or self.message2 + if self.message1 and self.message2: + self.message = '"{0}" {1} "{2}"'.format( + self.message1, _('OR'), self.message2, + ) def has_permission(self, request, view): return ( diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 188112815..59009de68 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -516,6 +516,18 @@ class DeniedViewWithDetailAND3(PermissionInstanceView): permission_classes = (BasicPermWithDetail & AnotherBasicPermWithDetail,) +class DeniedViewWithDetailOR1(PermissionInstanceView): + permission_classes = (BasicPerm | BasicPermWithDetail,) + + +class DeniedViewWithDetailOR2(PermissionInstanceView): + permission_classes = (BasicPermWithDetail | BasicPerm,) + + +class DeniedViewWithDetailOR3(PermissionInstanceView): + permission_classes = (BasicPermWithDetail | AnotherBasicPermWithDetail,) + + class DeniedObjectView(PermissionInstanceView): permission_classes = (BasicObjectPerm,) @@ -536,6 +548,18 @@ class DeniedObjectViewWithDetailAND3(PermissionInstanceView): permission_classes = (AnotherBasicObjectPermWithDetail & BasicObjectPermWithDetail,) +class DeniedObjectViewWithDetailOR1(PermissionInstanceView): + permission_classes = (BasicObjectPerm | BasicObjectPermWithDetail,) + + +class DeniedObjectViewWithDetailOR2(PermissionInstanceView): + permission_classes = (BasicObjectPermWithDetail | BasicObjectPerm,) + + +class DeniedObjectViewWithDetailOR3(PermissionInstanceView): + permission_classes = (BasicObjectPermWithDetail | AnotherBasicObjectPermWithDetail,) + + denied_view = DeniedView.as_view() denied_view_with_detail = DeniedViewWithDetail.as_view() @@ -544,6 +568,10 @@ denied_view_with_detail_and_1 = DeniedViewWithDetailAND1.as_view() denied_view_with_detail_and_2 = DeniedViewWithDetailAND2.as_view() denied_view_with_detail_and_3 = DeniedViewWithDetailAND3.as_view() +denied_view_with_detail_or_1 = DeniedViewWithDetailOR1.as_view() +denied_view_with_detail_or_2 = DeniedViewWithDetailOR2.as_view() +denied_view_with_detail_or_3 = DeniedViewWithDetailOR3.as_view() + denied_object_view = DeniedObjectView.as_view() denied_object_view_with_detail = DeniedObjectViewWithDetail.as_view() @@ -552,6 +580,10 @@ denied_object_view_with_detail_and_1 = DeniedObjectViewWithDetailAND1.as_view() denied_object_view_with_detail_and_2 = DeniedObjectViewWithDetailAND2.as_view() denied_object_view_with_detail_and_3 = DeniedObjectViewWithDetailAND3.as_view() +denied_object_view_with_detail_or_1 = DeniedObjectViewWithDetailOR1.as_view() +denied_object_view_with_detail_or_2 = DeniedObjectViewWithDetailOR2.as_view() +denied_object_view_with_detail_or_3 = DeniedObjectViewWithDetailOR3.as_view() + class CustomPermissionsTests(TestCase): def setUp(self): @@ -593,6 +625,25 @@ class CustomPermissionsTests(TestCase): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(detail, CUSTOM_MESSAGE_1) + def test_permission_denied_with_custom_detail_or_1(self): + response = denied_view_with_detail_or_1(self.request, pk=1) + detail = response.data.get('detail') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(detail, CUSTOM_MESSAGE_1) + + def test_permission_denied_with_custom_detail_or_2(self): + response = denied_view_with_detail_or_2(self.request, pk=1) + detail = response.data.get('detail') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(detail, CUSTOM_MESSAGE_1) + + def test_permission_denied_with_custom_detail_or_3(self): + response = denied_view_with_detail_or_3(self.request, pk=1) + detail = response.data.get('detail') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + expected_message = '"{0}" OR "{1}"'.format(CUSTOM_MESSAGE_1, CUSTOM_MESSAGE_2) + self.assertEqual(detail, expected_message) + def test_permission_denied_for_object(self): response = denied_object_view(self.request, pk=1) detail = response.data.get('detail') @@ -625,6 +676,25 @@ class CustomPermissionsTests(TestCase): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(detail, CUSTOM_MESSAGE_2) + def test_permission_denied_for_object_with_custom_detail_or_1(self): + response = denied_object_view_with_detail_or_1(self.request, pk=1) + detail = response.data.get('detail') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(detail, CUSTOM_MESSAGE_1) + + def test_permission_denied_for_object_with_custom_detail_or_2(self): + response = denied_object_view_with_detail_or_2(self.request, pk=1) + detail = response.data.get('detail') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(detail, CUSTOM_MESSAGE_1) + + def test_permission_denied_for_object_with_custom_detail_or_3(self): + response = denied_object_view_with_detail_or_3(self.request, pk=1) + detail = response.data.get('detail') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + expected_message = '"{0}" OR "{1}"'.format(CUSTOM_MESSAGE_1, CUSTOM_MESSAGE_2) + self.assertEqual(detail, expected_message) + class PermissionsCompositionTests(TestCase):