diff options
Diffstat (limited to 'rest_framework/tests/test_permissions.py')
-rw-r--r-- | rest_framework/tests/test_permissions.py | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/rest_framework/tests/test_permissions.py b/rest_framework/tests/test_permissions.py new file mode 100644 index 00000000..6caaf65b --- /dev/null +++ b/rest_framework/tests/test_permissions.py @@ -0,0 +1,195 @@ +from __future__ import unicode_literals +from django.contrib.auth.models import User, Permission +from django.db import models +from django.test import TestCase +from rest_framework import generics, status, permissions, authentication, HTTP_HEADER_ENCODING +from rest_framework.tests.utils import RequestFactory +import base64 +import json + +factory = RequestFactory() + + +class BasicModel(models.Model): + text = models.CharField(max_length=100) + + +class RootView(generics.ListCreateAPIView): + model = BasicModel + authentication_classes = [authentication.BasicAuthentication] + permission_classes = [permissions.DjangoModelPermissions] + + +class InstanceView(generics.RetrieveUpdateDestroyAPIView): + model = BasicModel + authentication_classes = [authentication.BasicAuthentication] + permission_classes = [permissions.DjangoModelPermissions] + +root_view = RootView.as_view() +instance_view = InstanceView.as_view() + + +def basic_auth_header(username, password): + credentials = ('%s:%s' % (username, password)) + base64_credentials = base64.b64encode(credentials.encode(HTTP_HEADER_ENCODING)).decode(HTTP_HEADER_ENCODING) + return 'Basic %s' % base64_credentials + + +class ModelPermissionsIntegrationTests(TestCase): + def setUp(self): + User.objects.create_user('disallowed', 'disallowed@example.com', 'password') + user = User.objects.create_user('permitted', 'permitted@example.com', 'password') + user.user_permissions = [ + Permission.objects.get(codename='add_basicmodel'), + Permission.objects.get(codename='change_basicmodel'), + Permission.objects.get(codename='delete_basicmodel') + ] + user = User.objects.create_user('updateonly', 'updateonly@example.com', 'password') + user.user_permissions = [ + Permission.objects.get(codename='change_basicmodel'), + ] + + self.permitted_credentials = basic_auth_header('permitted', 'password') + self.disallowed_credentials = basic_auth_header('disallowed', 'password') + self.updateonly_credentials = basic_auth_header('updateonly', 'password') + + BasicModel(text='foo').save() + + def test_has_create_permissions(self): + request = factory.post('/', json.dumps({'text': 'foobar'}), + content_type='application/json', + HTTP_AUTHORIZATION=self.permitted_credentials) + response = root_view(request, pk=1) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + def test_has_put_permissions(self): + request = factory.put('/1', json.dumps({'text': 'foobar'}), + content_type='application/json', + HTTP_AUTHORIZATION=self.permitted_credentials) + response = instance_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_has_delete_permissions(self): + request = factory.delete('/1', HTTP_AUTHORIZATION=self.permitted_credentials) + response = instance_view(request, pk=1) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + def test_does_not_have_create_permissions(self): + request = factory.post('/', json.dumps({'text': 'foobar'}), + content_type='application/json', + HTTP_AUTHORIZATION=self.disallowed_credentials) + response = root_view(request, pk=1) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_does_not_have_put_permissions(self): + request = factory.put('/1', json.dumps({'text': 'foobar'}), + content_type='application/json', + HTTP_AUTHORIZATION=self.disallowed_credentials) + response = instance_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_does_not_have_delete_permissions(self): + request = factory.delete('/1', HTTP_AUTHORIZATION=self.disallowed_credentials) + response = instance_view(request, pk=1) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_has_put_as_create_permissions(self): + # User only has update permissions - should be able to update an entity. + request = factory.put('/1', json.dumps({'text': 'foobar'}), + content_type='application/json', + HTTP_AUTHORIZATION=self.updateonly_credentials) + response = instance_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + + # But if PUTing to a new entity, permission should be denied. + request = factory.put('/2', json.dumps({'text': 'foobar'}), + content_type='application/json', + HTTP_AUTHORIZATION=self.updateonly_credentials) + response = instance_view(request, pk='2') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_options_permitted(self): + request = factory.options('/', content_type='application/json', + HTTP_AUTHORIZATION=self.permitted_credentials) + response = root_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('actions', response.data) + self.assertEqual(list(response.data['actions'].keys()), ['POST']) + + request = factory.options('/1', content_type='application/json', + HTTP_AUTHORIZATION=self.permitted_credentials) + response = instance_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('actions', response.data) + self.assertEqual(list(response.data['actions'].keys()), ['PUT']) + + def test_options_disallowed(self): + request = factory.options('/', content_type='application/json', + HTTP_AUTHORIZATION=self.disallowed_credentials) + response = root_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertNotIn('actions', response.data) + + request = factory.options('/1', content_type='application/json', + HTTP_AUTHORIZATION=self.disallowed_credentials) + response = instance_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertNotIn('actions', response.data) + + def test_options_updateonly(self): + request = factory.options('/', content_type='application/json', + HTTP_AUTHORIZATION=self.updateonly_credentials) + response = root_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertNotIn('actions', response.data) + + request = factory.options('/1', content_type='application/json', + HTTP_AUTHORIZATION=self.updateonly_credentials) + response = instance_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('actions', response.data) + self.assertEqual(list(response.data['actions'].keys()), ['PUT']) + + +class OwnerModel(models.Model): + text = models.CharField(max_length=100) + owner = models.ForeignKey(User) + + +class IsOwnerPermission(permissions.BasePermission): + def has_object_permission(self, request, view, obj): + return request.user == obj.owner + + +class OwnerInstanceView(generics.RetrieveUpdateDestroyAPIView): + model = OwnerModel + authentication_classes = [authentication.BasicAuthentication] + permission_classes = [IsOwnerPermission] + + +owner_instance_view = OwnerInstanceView.as_view() + + +class ObjectPermissionsIntegrationTests(TestCase): + """ + Integration tests for the object level permissions API. + """ + + def setUp(self): + User.objects.create_user('not_owner', 'not_owner@example.com', 'password') + user = User.objects.create_user('owner', 'owner@example.com', 'password') + + self.not_owner_credentials = basic_auth_header('not_owner', 'password') + self.owner_credentials = basic_auth_header('owner', 'password') + + OwnerModel(text='foo', owner=user).save() + + def test_owner_has_delete_permissions(self): + request = factory.delete('/1', HTTP_AUTHORIZATION=self.owner_credentials) + response = owner_instance_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + def test_non_owner_does_not_have_delete_permissions(self): + request = factory.delete('/1', HTTP_AUTHORIZATION=self.not_owner_credentials) + response = owner_instance_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) |