diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 8fb4569cb1..2c52865c93 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -255,6 +255,12 @@ class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions): """ authenticated_users_only = False + def has_permission(self, request, view): + if request.user and request.user.is_authenticated: + return bool(super().has_permission(request, view)) + + return bool(request.method in SAFE_METHODS) + class DjangoObjectPermissions(DjangoModelPermissions): """ diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 428480dc7e..6235fd2804 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -32,6 +32,10 @@ class RootView(generics.ListCreateAPIView): permission_classes = [permissions.DjangoModelPermissions] +class AnonReadOnlyRootView(RootView): + permission_classes = [permissions.DjangoModelPermissionsOrAnonReadOnly] + + class InstanceView(generics.RetrieveUpdateDestroyAPIView): queryset = BasicModel.objects.all() serializer_class = BasicSerializer @@ -65,6 +69,7 @@ class IgnoredGetQuerySetListView(GetQuerySetListView): get_queryset_list_view = GetQuerySetListView.as_view() empty_list_view = EmptyListView.as_view() ignored_get_queryset_list_view = IgnoredGetQuerySetListView.as_view() +anon_read_only_root_view = AnonReadOnlyRootView.as_view() def basic_auth_header(username, password): @@ -285,6 +290,39 @@ def get_queryset(self): with self.assertRaisesMessage(AssertionError, 'View.get_queryset() returned None'): view(request) + def test_allow_anonymous_safe_access_to_anon_read_only_view(self): + request = factory.get('/', HTTP_AUTHORIZATION='') + response = anon_read_only_root_view(request) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_deny_anonymous_unsafe_access_to_anon_read_only_view(self): + request = factory.post('/', {'text': 'foobar'}, format='json', + HTTP_AUTHORIZATION='') + response = anon_read_only_root_view(request) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_allow_permitted_safe_access_to_anon_read_only_view(self): + request = factory.get('/', HTTP_AUTHORIZATION=self.permitted_credentials) + response = anon_read_only_root_view(request) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_allow_permitted_unsafe_access_to_anon_read_only_view(self): + request = factory.post('/', {'text': 'foobar'}, format='json', + HTTP_AUTHORIZATION=self.permitted_credentials) + response = anon_read_only_root_view(request) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + def test_deny_disallowed_safe_access_to_anon_read_only_view(self): + request = factory.post('/', HTTP_AUTHORIZATION=self.disallowed_credentials) + response = anon_read_only_root_view(request) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_deny_disallowed_unsafe_to_anon_read_only_view(self): + request = factory.post('/', {'text': 'foobar'}, format='json', + HTTP_AUTHORIZATION=self.disallowed_credentials) + response = anon_read_only_root_view(request) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + class BasicPermModel(models.Model): text = models.CharField(max_length=100)