diff --git a/.gitignore b/.gitignore index 41768084c..4e3d8072a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ *.db *~ .* +.tox/ +.venv/ /site/ /htmlcov/ diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index dfe89ed2a..e56838dce 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -108,8 +108,21 @@ class IsAuthenticated(BasePermission): """ Allows access only to authenticated users. """ + # DRF should authorize all OPTIONS requests by default #5616 + def has_permission(self, request, view): + if request.method == 'OPTIONS': + return True + return request.user and request.user.is_authenticated + + +class IsAuthenticatedOrOptionsOnly(BasePermission): + """ + Allows access only to authenticated users or for OPTIONS method. + """ def has_permission(self, request, view): + if request.method == 'OPTIONS': + return True return request.user and request.user.is_authenticated diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 15fd9303c..91430cc7a 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -542,6 +542,39 @@ class CustomPermissionsTests(TestCase): self.assertEqual(detail, self.custom_message) +class IsAuthenticatedOrOptionsOnlyAllowedView(generics.RetrieveUpdateDestroyAPIView): + queryset = BasicModel.objects.all() + serializer_class = BasicSerializer + authentication_classes = [authentication.BasicAuthentication] + permissions_classes = (permissions.IsAuthenticatedOrOptionsOnly,) + + +options_view = IsAuthenticatedOrOptionsOnlyAllowedView.as_view() + + +class IsAuthenticatedOrOptionsOnlyAllowedTests(TestCase): + def setUp(self): + BasicModel(text='foo').save() + User.objects.create_user('username', 'username@example.com', 'password') + + def test_options_allowed_if_not_authentificated(self): + self.request = factory.options('/1', format='json') + response = options_view(self.request, pk=1) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_options_get_not_allowed_if_not_authentificated(self): + credentials = basic_auth_header('username', 'wrongpassword') + self.request = factory.get('/1', format='json', HTTP_AUTHORIZATION=credentials) + response = options_view(self.request, pk=1) + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + + def test_options_get_allowed_if_authentificated(self): + credentials = basic_auth_header('username', 'password') + self.request = factory.get('/1', format='json', HTTP_AUTHORIZATION=credentials) + response = options_view(self.request, pk=1) + self.assertEqual(response.status_code, status.HTTP_200_OK) + + class FakeUser: def __init__(self, auth=False): self.is_authenticated = auth