Commit d2ee2430 by Dave St.Germain

Add authentication and model permission checking to REST API.

parent 8e1dcac6
""" """
init init
""" """
from django.contrib.auth.models import User, Permission
from rest_framework.test import APITestCase
class APIAuthTestCase(APITestCase):
"""
TestCase that creates a readwrite and readonly user in setUp
"""
def setUp(self):
self.username = self.password = 'readwrite'
self.readwrite_user = User.objects.create_user(self.username, password=self.password)
self.readwrite_user.user_permissions = Permission.objects.filter(content_type__app_label='edxval')
self.readonly_user = User.objects.create_user('readonly', 'readonly')
self._login()
def _logout(self):
self.client.logout()
def _login(self, readonly=False):
if readonly:
username = password = 'readonly'
else:
username, password = self.username, self.password
self.client.login(username=username, password=password)
...@@ -10,14 +10,13 @@ from django.db import DatabaseError ...@@ -10,14 +10,13 @@ from django.db import DatabaseError
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from rest_framework import status from rest_framework import status
from rest_framework.test import APITestCase
from ddt import ddt, data from ddt import ddt, data
from edxval.models import Profile, Video, EncodedVideo from edxval.models import Profile, Video, EncodedVideo
from edxval import api as api from edxval import api as api
from edxval.api import ValCannotCreateError from edxval.api import ValCannotCreateError
from edxval.serializers import VideoSerializer from edxval.serializers import VideoSerializer
from edxval.tests import constants from edxval.tests import constants, APIAuthTestCase
@ddt @ddt
class CreateVideoTest(TestCase): class CreateVideoTest(TestCase):
...@@ -190,7 +189,7 @@ class GetVideoInfoTest(TestCase): ...@@ -190,7 +189,7 @@ class GetVideoInfoTest(TestCase):
) )
class GetVideoInfoTestWithHttpCalls(APITestCase): class GetVideoInfoTestWithHttpCalls(APIAuthTestCase):
""" """
Tests for the get_info_video, using the HTTP requests to populate database Tests for the get_info_video, using the HTTP requests to populate database
""" """
...@@ -200,9 +199,10 @@ class GetVideoInfoTestWithHttpCalls(APITestCase): ...@@ -200,9 +199,10 @@ class GetVideoInfoTestWithHttpCalls(APITestCase):
Creates EncodedVideo objects in database with HTTP requests. Creates EncodedVideo objects in database with HTTP requests.
The tests are similar to the GetVideoInfoTest class. This class The tests are similar to the GetVideoInfoTest class. This class
is to tests that we have the same results, using a populated is to test that we have the same results, using a populated
database via HTTP uploads. database via HTTP uploads.
""" """
super(GetVideoInfoTestWithHttpCalls, self).setUp()
Profile.objects.create(**constants.PROFILE_DICT_MOBILE) Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP) Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
url = reverse('video-list') url = reverse('video-list')
......
...@@ -4,13 +4,12 @@ Tests for Video Abstraction Layer views ...@@ -4,13 +4,12 @@ Tests for Video Abstraction Layer views
""" """
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from rest_framework import status from rest_framework import status
from rest_framework.test import APITestCase
from edxval.tests import constants from edxval.tests import constants, APIAuthTestCase
from edxval.models import Profile, Video from edxval.models import Profile, Video
class VideoDetail(APITestCase): class VideoDetail(APIAuthTestCase):
""" """
Tests Retrieve, Update and Destroy requests Tests Retrieve, Update and Destroy requests
""" """
...@@ -21,9 +20,29 @@ class VideoDetail(APITestCase): ...@@ -21,9 +20,29 @@ class VideoDetail(APITestCase):
""" """
Profile.objects.create(**constants.PROFILE_DICT_MOBILE) Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP) Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
super(VideoDetail, self).setUp()
# Tests for successful PUT requests. # Tests for successful PUT requests.
def test_anonymous_readonly(self):
"""
Tests that writing checks model permissions.
"""
self._logout()
url = reverse('video-list')
response = self.client.post(url, constants.VIDEO_DICT_ANIMAL, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_no_perms(self):
"""
Tests that writing checks model permissions, even for logged in users.
"""
self._logout()
self._login(readonly=True)
url = reverse('video-list')
response = self.client.post(url, constants.VIDEO_DICT_ANIMAL, format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_update_video(self): def test_update_video(self):
""" """
Tests PUTting a single video with no encoded videos. Tests PUTting a single video with no encoded videos.
...@@ -290,7 +309,7 @@ class VideoDetail(APITestCase): ...@@ -290,7 +309,7 @@ class VideoDetail(APITestCase):
) )
class VideoListTest(APITestCase): class VideoListTest(APIAuthTestCase):
""" """
Tests the creations of Videos via POST/GET Tests the creations of Videos via POST/GET
""" """
...@@ -300,6 +319,7 @@ class VideoListTest(APITestCase): ...@@ -300,6 +319,7 @@ class VideoListTest(APITestCase):
""" """
Profile.objects.create(**constants.PROFILE_DICT_MOBILE) Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP) Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
super(VideoListTest, self).setUp()
# Tests for successful POST 201 requests. # Tests for successful POST 201 requests.
def test_complete_set_two_encoded_video_post(self): def test_complete_set_two_encoded_video_post(self):
...@@ -310,6 +330,8 @@ class VideoListTest(APITestCase): ...@@ -310,6 +330,8 @@ class VideoListTest(APITestCase):
response = self.client.post( response = self.client.post(
url, constants.COMPLETE_SET_FISH, format='json' url, constants.COMPLETE_SET_FISH, format='json'
) )
# we can log out here, to test read-only
self._logout()
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
video = self.client.get("/edxval/video/").data video = self.client.get("/edxval/video/").data
self.assertEqual(len(video), 1) self.assertEqual(len(video), 1)
...@@ -435,7 +457,7 @@ class VideoListTest(APITestCase): ...@@ -435,7 +457,7 @@ class VideoListTest(APITestCase):
Tests number of queries for a Video with no Encoded Videos Tests number of queries for a Video with no Encoded Videos
""" """
url = reverse('video-list') url = reverse('video-list')
with self.assertNumQueries(4): with self.assertNumQueries(8):
self.client.post(url, constants.VIDEO_DICT_ZEBRA, format='json') self.client.post(url, constants.VIDEO_DICT_ZEBRA, format='json')
def test_queries_for_two_encoded_video(self): def test_queries_for_two_encoded_video(self):
...@@ -443,7 +465,7 @@ class VideoListTest(APITestCase): ...@@ -443,7 +465,7 @@ class VideoListTest(APITestCase):
Tests number of queries for a Video/EncodedVideo(2) pair Tests number of queries for a Video/EncodedVideo(2) pair
""" """
url = reverse('video-list') url = reverse('video-list')
with self.assertNumQueries(16): with self.assertNumQueries(20):
self.client.post(url, constants.COMPLETE_SET_FISH, format='json') self.client.post(url, constants.COMPLETE_SET_FISH, format='json')
def test_queries_for_single_encoded_videos(self): def test_queries_for_single_encoded_videos(self):
...@@ -451,11 +473,11 @@ class VideoListTest(APITestCase): ...@@ -451,11 +473,11 @@ class VideoListTest(APITestCase):
Tests number of queries for a Video/EncodedVideo(1) pair Tests number of queries for a Video/EncodedVideo(1) pair
""" """
url = reverse('video-list') url = reverse('video-list')
with self.assertNumQueries(10): with self.assertNumQueries(14):
self.client.post(url, constants.COMPLETE_SET_STAR, format='json') self.client.post(url, constants.COMPLETE_SET_STAR, format='json')
class VideoDetailTest(APITestCase): class VideoDetailTest(APIAuthTestCase):
""" """
Tests for GET Tests for GET
""" """
...@@ -465,6 +487,7 @@ class VideoDetailTest(APITestCase): ...@@ -465,6 +487,7 @@ class VideoDetailTest(APITestCase):
""" """
Profile.objects.create(**constants.PROFILE_DICT_MOBILE) Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP) Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
super(VideoDetailTest, self).setUp()
def test_get_all_videos(self): def test_get_all_videos(self):
""" """
...@@ -487,25 +510,26 @@ class VideoDetailTest(APITestCase): ...@@ -487,25 +510,26 @@ class VideoDetailTest(APITestCase):
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
response = self.client.post(url, constants.VIDEO_DICT_ZEBRA, format='json') response = self.client.post(url, constants.VIDEO_DICT_ZEBRA, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
with self.assertNumQueries(4): with self.assertNumQueries(6):
self.client.get("/edxval/video/").data self.client.get("/edxval/video/").data
response = self.client.post(url, constants.COMPLETE_SET_FISH, format='json') response = self.client.post(url, constants.COMPLETE_SET_FISH, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
with self.assertNumQueries(9): with self.assertNumQueries(11):
self.client.get("/edxval/video/").data self.client.get("/edxval/video/").data
response = self.client.post(url, constants.COMPLETE_SET_STAR, format='json') response = self.client.post(url, constants.COMPLETE_SET_STAR, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
with self.assertNumQueries(12): with self.assertNumQueries(14):
self.client.get("/edxval/video/").data self.client.get("/edxval/video/").data
class SubtitleDetailTest(APITestCase): class SubtitleDetailTest(APIAuthTestCase):
""" """
Tests for subtitle API Tests for subtitle API
""" """
def setUp(self): def setUp(self):
Profile.objects.create(**constants.PROFILE_DICT_MOBILE) Profile.objects.create(**constants.PROFILE_DICT_MOBILE)
Profile.objects.create(**constants.PROFILE_DICT_DESKTOP) Profile.objects.create(**constants.PROFILE_DICT_DESKTOP)
super(SubtitleDetailTest, self).setUp()
def test_get_subtitle_content(self): def test_get_subtitle_content(self):
""" """
......
...@@ -3,6 +3,7 @@ Views file for django app edxval. ...@@ -3,6 +3,7 @@ Views file for django app edxval.
""" """
from rest_framework import generics from rest_framework import generics
from rest_framework.permissions import DjangoModelPermissionsOrAnonReadOnly
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.views.decorators.http import last_modified from django.views.decorators.http import last_modified
...@@ -32,6 +33,7 @@ class VideoList(generics.ListCreateAPIView): ...@@ -32,6 +33,7 @@ class VideoList(generics.ListCreateAPIView):
""" """
GETs or POST video objects GETs or POST video objects
""" """
permission_classes = (DjangoModelPermissionsOrAnonReadOnly,)
queryset = Video.objects.all().prefetch_related("encoded_videos") queryset = Video.objects.all().prefetch_related("encoded_videos")
lookup_field = "edx_video_id" lookup_field = "edx_video_id"
serializer_class = VideoSerializer serializer_class = VideoSerializer
...@@ -41,6 +43,7 @@ class ProfileList(generics.ListCreateAPIView): ...@@ -41,6 +43,7 @@ class ProfileList(generics.ListCreateAPIView):
""" """
GETs or POST video objects GETs or POST video objects
""" """
permission_classes = (DjangoModelPermissionsOrAnonReadOnly,)
queryset = Profile.objects.all() queryset = Profile.objects.all()
lookup_field = "profile_name" lookup_field = "profile_name"
serializer_class = ProfileSerializer serializer_class = ProfileSerializer
...@@ -50,6 +53,7 @@ class VideoDetail(generics.RetrieveUpdateDestroyAPIView): ...@@ -50,6 +53,7 @@ class VideoDetail(generics.RetrieveUpdateDestroyAPIView):
""" """
Gets a video instance given its edx_video_id Gets a video instance given its edx_video_id
""" """
permission_classes = (DjangoModelPermissionsOrAnonReadOnly,)
lookup_field = "edx_video_id" lookup_field = "edx_video_id"
queryset = Video.objects.all() queryset = Video.objects.all()
serializer_class = VideoSerializer serializer_class = VideoSerializer
...@@ -59,6 +63,7 @@ class SubtitleDetail(MultipleFieldLookupMixin, generics.RetrieveUpdateDestroyAPI ...@@ -59,6 +63,7 @@ class SubtitleDetail(MultipleFieldLookupMixin, generics.RetrieveUpdateDestroyAPI
""" """
Gets a subtitle instance given its id Gets a subtitle instance given its id
""" """
permission_classes = (DjangoModelPermissionsOrAnonReadOnly,)
lookup_fields = ("video__edx_video_id", "language") lookup_fields = ("video__edx_video_id", "language")
queryset = Subtitle.objects.all() queryset = Subtitle.objects.all()
serializer_class = SubtitleSerializer serializer_class = SubtitleSerializer
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment