Repository: encode/django-rest-framework

Issue:
View with no `authentication_classes` failing permission should raise 403
## Checklist
- [x] I have verified that that issue exists against the `master` branch of Django REST framework.
- [x] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [x] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [x] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](http://www.django-rest-framework.org/topics/third-party-resources/#about-third-party-packages) where possible.)
- [x] I have reduced the issue to the simplest possible case.
- [x] I have included a failing test as a pull request. (#4040)
## Steps to reproduce

Create a view with no `authentication_classes` set and a `permission_classes` set. Query the view in order to fail the permission check.
## Expected behavior

A 403 with the permission's message should be returned.
## Actual behavior

A 401 with a "Not authenticated" message is returned.

This is slightly related to #3754, the main difference being that this issue is only concerned about view with no `authentication_classes` and #3754 is about prioritizing permissions over authentication in views with `authentication_classes`.



Discussion:

Resolving patch:
diff --git a/rest_framework/views.py b/rest_framework/views.py
--- a/rest_framework/views.py
+++ b/rest_framework/views.py
@@ -162,7 +162,7 @@ def permission_denied(self, request, message=None):
         """
         If request is not permitted, determine what kind of exception to raise.
         """
-        if not request.successful_authenticator:
+        if request.authenticators and not request.successful_authenticator:
             raise exceptions.NotAuthenticated()
         raise exceptions.PermissionDenied(detail=message)
 


Test patch:
diff --git a/tests/test_authentication.py b/tests/test_authentication.py
--- a/tests/test_authentication.py
+++ b/tests/test_authentication.py
@@ -321,3 +321,28 @@ def test_failing_auth_accessed_in_renderer(self):
         response = self.view(request)
         content = response.render().content
         self.assertEqual(content, b'not authenticated')
+
+
+class NoAuthenticationClassesTests(TestCase):
+    def test_permission_message_with_no_authentication_classes(self):
+        """
+        An unauthenticated request made against a view that containes no
+        `authentication_classes` but do contain `permissions_classes` the error
+        code returned should be 403 with the exception's message.
+        """
+
+        class DummyPermission(permissions.BasePermission):
+            message = 'Dummy permission message'
+
+            def has_permission(self, request, view):
+                return False
+
+        request = factory.get('/')
+        view = MockView.as_view(
+            authentication_classes=(),
+            permission_classes=(DummyPermission,),
+        )
+        response = view(request)
+        self.assertEqual(response.status_code,
+                         status.HTTP_403_FORBIDDEN)
+        self.assertEqual(response.data, {'detail': 'Dummy permission message'})