From 507876ba71e0880da95ffdb55295fa3779367b4d Mon Sep 17 00:00:00 2001 From: Nicolas Clerc Date: Fri, 21 Jul 2023 15:27:26 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=82(backend)=20use=20playlist=20token?= =?UTF-8?q?=20in=20deposit=20apis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we are now using playlist tokens, it has to be allowed on apis. Also, old routes can't be used anymore, as token resource is now a playlist. --- src/backend/marsha/deposit/api.py | 104 +++++++++--------- src/backend/marsha/deposit/permissions.py | 18 +-- src/backend/marsha/deposit/serializers.py | 7 +- .../tests/api/depositedfiles/test_create.py | 22 ++-- .../tests/api/depositedfiles/test_delete.py | 15 ++- .../depositedfiles/test_initiate_upload.py | 30 +++-- .../tests/api/depositedfiles/test_update.py | 15 ++- .../tests/api/filedepositories/test_create.py | 2 +- .../tests/api/filedepositories/test_delete.py | 6 +- .../filedepositories/test_depositedfiles.py | 8 +- .../tests/api/filedepositories/test_list.py | 4 +- .../api/filedepositories/test_retrieve.py | 6 +- .../tests/api/filedepositories/test_update.py | 6 +- .../marsha/deposit/tests/api/test_options.py | 19 ++-- src/backend/marsha/deposit/urls.py | 10 +- 15 files changed, 154 insertions(+), 118 deletions(-) diff --git a/src/backend/marsha/deposit/api.py b/src/backend/marsha/deposit/api.py index 6c011d399a..461982d494 100644 --- a/src/backend/marsha/deposit/api.py +++ b/src/backend/marsha/deposit/api.py @@ -25,6 +25,20 @@ ) +class ObjectFileDepositoryRelatedMixin: + """ + Get the related depository id contained in resource. + + It exposes a function used to get the related depository. + """ + + def get_related_filedepository_id(self): + """Get the related file depository ID from the request.""" + + # The file depository ID in the URL is mandatory. + return self.kwargs.get("filedepository_id") + + class FileDepositoryFilter(django_filters.FilterSet): """Filter for file depository.""" @@ -93,7 +107,7 @@ def get_permissions(self): ] elif self.action in ["retrieve"]: permission_classes = [ - core_permissions.IsTokenResourceRouteObject + core_permissions.IsPlaylistToken & ( core_permissions.IsTokenInstructor | core_permissions.IsTokenAdmin @@ -104,7 +118,7 @@ def get_permissions(self): elif self.action in ["update", "partial_update", "destroy"]: permission_classes = [ ( - core_permissions.IsTokenResourceRouteObject + core_permissions.IsPlaylistToken & ( core_permissions.IsTokenInstructor | core_permissions.IsTokenAdmin @@ -195,57 +209,14 @@ def lti_select(self, request): } ) - @action( - methods=["get"], - detail=True, - url_path="depositedfiles", - permission_classes=[ - core_permissions.IsTokenInstructor - | core_permissions.IsTokenAdmin - | core_permissions.IsTokenStudent - | IsFileDepositoryPlaylistOrOrganizationAdmin - ], - ) - # pylint: disable=unused-argument - def depositedfiles(self, request, pk=None): - """Get deposited files from a file_depository. - - Calling the endpoint returns a list of deposited files. - - Parameters - ---------- - request : Type[django.http.request.HttpRequest] - The request on the API endpoint - pk : int - The primary key of the file_depository - - Returns - ------- - Type[rest_framework.response.Response] - HttpResponse carrying deposited files as a JSON object. - - """ - file_depository = self.get_object() - queryset = file_depository.deposited_files.all() - if request.resource and any( - x in LTI_ROLES.get(STUDENT) for x in request.resource.roles - ): - queryset = queryset.filter(author_id=request.resource.user.get("id")) - filter_set = DepositedFileFilter(request.query_params, queryset=queryset) - page = self.paginate_queryset(filter_set.qs) - serializer = serializers.DepositedFileSerializer( - page, - many=True, - context={"request": self.request}, - ) - return self.get_paginated_response(serializer.data) - class DepositedFileViewSet( APIViewMixin, ObjectPkMixin, ObjectRelatedMixin, + ObjectFileDepositoryRelatedMixin, mixins.CreateModelMixin, + mixins.ListModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet, @@ -272,7 +243,7 @@ def get_permissions(self): | core_permissions.IsTokenStudent | core_permissions.UserIsAuthenticated ] - elif self.action in ["create", "list", "metadata"]: + elif self.action in ["list", "metadata"]: permission_classes = [ core_permissions.IsTokenInstructor | core_permissions.IsTokenAdmin @@ -289,9 +260,44 @@ def get_permissions(self): permission_classes = self.permission_classes return [permission() for permission in permission_classes] + def list(self, request, *args, **kwargs): + """Get a list of deposited files. + + Calling the endpoint returns a list of deposited files. + + Parameters + ---------- + request : Type[django.http.request.HttpRequest] + The request on the API endpoint + + Returns + ------- + Type[rest_framework.response.Response] + HttpResponse carrying deposited files as a JSON object. + + """ + queryset = self.filter_queryset(self.get_queryset()) + if request.resource and any( + x in LTI_ROLES.get(STUDENT) for x in request.resource.roles + ): + queryset = queryset.filter(author_id=request.resource.user.get("id")) + filter_set = DepositedFileFilter(request.query_params, queryset=queryset) + page = self.paginate_queryset(filter_set.qs) + serializer = serializers.DepositedFileSerializer( + page, + many=True, + context={"request": self.request}, + ) + return self.get_paginated_response(serializer.data) + @action(methods=["post"], detail=True, url_path="initiate-upload") # pylint: disable=unused-argument - def initiate_upload(self, request, pk=None): + def initiate_upload( + self, + request, + pk=None, + filedepository_id=None, + ): """Get an upload policy for a deposited file. Calling the endpoint resets the upload state to `pending` and returns an upload policy to diff --git a/src/backend/marsha/deposit/permissions.py b/src/backend/marsha/deposit/permissions.py index d2beba4514..6b1f308802 100644 --- a/src/backend/marsha/deposit/permissions.py +++ b/src/backend/marsha/deposit/permissions.py @@ -1,9 +1,8 @@ """Custom permission classes for the Deposit app.""" -from django.core.exceptions import ObjectDoesNotExist - from rest_framework import permissions from marsha.core import models +from marsha.deposit.models import FileDepository def _is_organization_admin(user_id, file_depository_id): @@ -59,12 +58,10 @@ def has_permission(self, request, view): """ if not request.resource: return False - try: - return ( - str(view.get_related_object().file_depository.id) == request.resource.id - ) - except ObjectDoesNotExist: - return False + + return FileDepository.objects.filter( + pk=view.get_related_filedepository_id(), playlist_id=request.resource.id + ).exists() class IsFileDepositoryPlaylistOrOrganizationAdmin(permissions.BasePermission): @@ -108,10 +105,7 @@ def has_permission(self, request, view): which exists, and if the current user is an admin for the playlist this file depository is a part of or admin of the linked organization. """ - try: - file_depository_id = view.get_related_object().file_depository.id - except (AttributeError, ObjectDoesNotExist): - file_depository_id = request.data.get("file_depository") + file_depository_id = view.get_related_filedepository_id() if not file_depository_id: return False diff --git a/src/backend/marsha/deposit/serializers.py b/src/backend/marsha/deposit/serializers.py index 1c0ee36a69..3f0f8b2773 100644 --- a/src/backend/marsha/deposit/serializers.py +++ b/src/backend/marsha/deposit/serializers.py @@ -83,13 +83,10 @@ def create(self, validated_data): """ resource = self.context["request"].resource user = self.context["request"].user - file_depository_id = self.context["request"].data.get("file_depository") + file_depository_id = self.context["view"].get_related_filedepository_id() if not validated_data.get("file_depository_id"): - if resource: - validated_data["file_depository_id"] = resource.id - elif file_depository_id: - validated_data["file_depository_id"] = file_depository_id + validated_data["file_depository_id"] = file_depository_id if resource: validated_data["author_id"] = resource.user.get("id") diff --git a/src/backend/marsha/deposit/tests/api/depositedfiles/test_create.py b/src/backend/marsha/deposit/tests/api/depositedfiles/test_create.py index c530c23318..ada6fad42b 100644 --- a/src/backend/marsha/deposit/tests/api/depositedfiles/test_create.py +++ b/src/backend/marsha/deposit/tests/api/depositedfiles/test_create.py @@ -42,10 +42,10 @@ def test_api_deposited_file_create_student_with_user_fullname(self): """ file_depository = FileDepositoryFactory() - jwt_token = StudentLtiTokenFactory(resource=file_depository) + jwt_token = StudentLtiTokenFactory(resource=file_depository.playlist) response = self.client.post( - "/api/depositedfiles/", + f"/api/filedepositories/{file_depository.id}/depositedfiles/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", data=json.dumps( @@ -89,11 +89,13 @@ def test_api_deposited_file_create_student_with_username(self): file_depository = FileDepositoryFactory() jwt_token = StudentLtiTokenFactory( - resource=file_depository, user__user_fullname=None, user__username="student" + resource=file_depository.playlist, + user__user_fullname=None, + user__username="student", ) response = self.client.post( - "/api/depositedfiles/", + f"/api/filedepositories/{file_depository.id}/depositedfiles/", data=json.dumps( { "size": 123, @@ -121,11 +123,13 @@ def test_api_deposited_file_create_student_without_username(self): file_depository = FileDepositoryFactory() jwt_token = StudentLtiTokenFactory( - resource=file_depository, user__user_fullname=None, user__username=None + resource=file_depository.playlist, + user__user_fullname=None, + user__username=None, ) response = self.client.post( - "/api/depositedfiles/", + f"/api/filedepositories/{file_depository.id}/depositedfiles/", data=json.dumps( { "size": 123, @@ -153,7 +157,7 @@ def test_api_deposited_file_create_user_access_token(self): jwt_token = UserAccessTokenFactory(user=organization_access.user) response = self.client.post( - "/api/depositedfiles/", + f"/api/filedepositories/{file_depository.id}/depositedfiles/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", data=json.dumps( @@ -197,7 +201,7 @@ def test_api_deposited_file_create_user_access_token_organization_admin(self): jwt_token = UserAccessTokenFactory(user=organization_access.user) response = self.client.post( - "/api/depositedfiles/", + f"/api/filedepositories/{file_depository.id}/depositedfiles/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", data=json.dumps( @@ -240,7 +244,7 @@ def test_api_deposited_file_create_user_access_token_playlist_admin(self): jwt_token = UserAccessTokenFactory(user=playlist_access.user) response = self.client.post( - "/api/depositedfiles/", + f"/api/filedepositories/{file_depository.id}/depositedfiles/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", data=json.dumps( diff --git a/src/backend/marsha/deposit/tests/api/depositedfiles/test_delete.py b/src/backend/marsha/deposit/tests/api/depositedfiles/test_delete.py index 28bb216aa3..ebb0a2658f 100644 --- a/src/backend/marsha/deposit/tests/api/depositedfiles/test_delete.py +++ b/src/backend/marsha/deposit/tests/api/depositedfiles/test_delete.py @@ -33,7 +33,8 @@ def test_api_deposited_file_delete_student(self): self.assertEqual(DepositedFile.objects.count(), 1) response = self.client.delete( - f"/api/depositedfiles/{deposited_file.id}/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id}/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", ) @@ -49,7 +50,8 @@ def test_api_deposited_file_delete_instructor(self): self.assertEqual(DepositedFile.objects.count(), 1) response = self.client.delete( - f"/api/depositedfiles/{deposited_file.id!s}/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id!s}/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", ) @@ -66,7 +68,8 @@ def test_api_deposited_file_delete_user_access_token(self): self.assertEqual(DepositedFile.objects.count(), 1) response = self.client.delete( - f"/api/depositedfiles/{deposited_file.id}/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id}/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", ) @@ -82,7 +85,8 @@ def test_api_deposited_file_delete_user_access_token_organization_admin(self): self.assertEqual(DepositedFile.objects.count(), 1) response = self.client.delete( - f"/api/depositedfiles/{deposited_file.id!s}/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id!s}/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", ) @@ -100,7 +104,8 @@ def test_api_deposited_file_delete_user_access_token_playlist_admin(self): self.assertEqual(DepositedFile.objects.count(), 1) response = self.client.delete( - f"/api/depositedfiles/{deposited_file.id!s}/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id!s}/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", ) diff --git a/src/backend/marsha/deposit/tests/api/depositedfiles/test_initiate_upload.py b/src/backend/marsha/deposit/tests/api/depositedfiles/test_initiate_upload.py index c1567b04ae..3368264df1 100644 --- a/src/backend/marsha/deposit/tests/api/depositedfiles/test_initiate_upload.py +++ b/src/backend/marsha/deposit/tests/api/depositedfiles/test_initiate_upload.py @@ -49,7 +49,9 @@ def test_api_deposited_file_initiate_upload_student(self): upload_state=random.choice(["ready", "error"]), file_depository__id="ed08da34-7447-4141-96ff-5740315d7b99", ) - jwt_token = StudentLtiTokenFactory(resource=deposited_file.file_depository) + jwt_token = StudentLtiTokenFactory( + resource=deposited_file.file_depository.playlist + ) now = datetime(2018, 8, 8, tzinfo=baseTimezone.utc) with mock.patch.object(timezone, "now", return_value=now), mock.patch( @@ -57,7 +59,8 @@ def test_api_deposited_file_initiate_upload_student(self): ) as mock_dt: mock_dt.utcnow = mock.Mock(return_value=now) response = self.client.post( - f"/api/depositedfiles/{deposited_file.id}/initiate-upload/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id}/initiate-upload/", {"filename": "foo.pdf", "mimetype": "application/pdf", "size": 10}, HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", @@ -105,7 +108,9 @@ def test_api_deposited_file_initiate_upload_file_without_size(self): upload_state=random.choice(["ready", "error"]), file_depository__id="ed08da34-7447-4141-96ff-5740315d7b99", ) - jwt_token = StudentLtiTokenFactory(resource=deposited_file.file_depository) + jwt_token = StudentLtiTokenFactory( + resource=deposited_file.file_depository.playlist + ) now = datetime(2018, 8, 8, tzinfo=baseTimezone.utc) with mock.patch.object(timezone, "now", return_value=now), mock.patch( @@ -113,7 +118,8 @@ def test_api_deposited_file_initiate_upload_file_without_size(self): ) as mock_dt: mock_dt.utcnow = mock.Mock(return_value=now) response = self.client.post( - f"/api/depositedfiles/{deposited_file.id}/initiate-upload/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id}/initiate-upload/", {"filename": "foo.pdf", "mimetype": "application/pdf"}, HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", @@ -132,7 +138,9 @@ def test_api_deposited_file_initiate_upload_file_too_large(self): upload_state=random.choice(["ready", "error"]), file_depository__id="ed08da34-7447-4141-96ff-5740315d7b99", ) - jwt_token = StudentLtiTokenFactory(resource=deposited_file.file_depository) + jwt_token = StudentLtiTokenFactory( + resource=deposited_file.file_depository.playlist + ) now = datetime(2018, 8, 8, tzinfo=baseTimezone.utc) with mock.patch.object(timezone, "now", return_value=now), mock.patch( @@ -140,7 +148,8 @@ def test_api_deposited_file_initiate_upload_file_too_large(self): ) as mock_dt: mock_dt.utcnow = mock.Mock(return_value=now) response = self.client.post( - f"/api/depositedfiles/{deposited_file.id}/initiate-upload/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id}/initiate-upload/", {"filename": "foo.pdf", "mimetype": "application/pdf", "size": 100}, HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", @@ -174,7 +183,8 @@ def test_api_deposited_file_initiate_upload_user_access_token(self): ) as mock_dt: mock_dt.utcnow = mock.Mock(return_value=now) response = self.client.post( - f"/api/depositedfiles/{deposited_file.id}/initiate-upload/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id}/initiate-upload/", {"filename": "foo.pdf", "mimetype": "application/pdf", "size": 10}, HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", @@ -239,7 +249,8 @@ def test_api_deposited_file_initiate_upload_user_access_token_organization_admin ) as mock_dt: mock_dt.utcnow = mock.Mock(return_value=now) response = self.client.post( - f"/api/depositedfiles/{deposited_file.id}/initiate-upload/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id}/initiate-upload/", {"filename": "foo.pdf", "mimetype": "application/pdf", "size": 10}, HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", @@ -301,7 +312,8 @@ def test_api_deposited_file_initiate_upload_user_access_token_playlist_admin(sel ) as mock_dt: mock_dt.utcnow = mock.Mock(return_value=now) response = self.client.post( - f"/api/depositedfiles/{deposited_file.id}/initiate-upload/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id}/initiate-upload/", {"filename": "foo.pdf", "mimetype": "application/pdf", "size": 10}, HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", diff --git a/src/backend/marsha/deposit/tests/api/depositedfiles/test_update.py b/src/backend/marsha/deposit/tests/api/depositedfiles/test_update.py index d2ce810862..1571bb8e36 100644 --- a/src/backend/marsha/deposit/tests/api/depositedfiles/test_update.py +++ b/src/backend/marsha/deposit/tests/api/depositedfiles/test_update.py @@ -42,7 +42,8 @@ def test_api_deposited_file_update_student(self): data = {"read": True} response = self.client.patch( - f"/api/depositedfiles/{deposited_file.id}/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id}/", json.dumps(data), HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", @@ -58,7 +59,8 @@ def test_api_deposited_file_update_instructor(self): data = {"read": True} response = self.client.patch( - f"/api/depositedfiles/{deposited_file.id!s}/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id!s}/", data, HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", @@ -91,7 +93,8 @@ def test_api_deposited_file_update_user_access_token(self): data = {"read": True} response = self.client.patch( - f"/api/depositedfiles/{deposited_file.id}/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id}/", json.dumps(data), HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", @@ -107,7 +110,8 @@ def test_api_deposited_file_update_user_access_token_organization_admin(self): data = {"read": True} response = self.client.patch( - f"/api/depositedfiles/{deposited_file.id!s}/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id!s}/", data, HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", @@ -141,7 +145,8 @@ def test_api_deposited_file_update_user_access_token_playlist_admin(self): data = {"read": True} response = self.client.patch( - f"/api/depositedfiles/{deposited_file.id!s}/", + f"/api/filedepositories/{deposited_file.file_depository.id}" + f"/depositedfiles/{deposited_file.id!s}/", data, HTTP_AUTHORIZATION=f"Bearer {jwt_token}", content_type="application/json", diff --git a/src/backend/marsha/deposit/tests/api/filedepositories/test_create.py b/src/backend/marsha/deposit/tests/api/filedepositories/test_create.py index 972c559237..6d1a299402 100644 --- a/src/backend/marsha/deposit/tests/api/filedepositories/test_create.py +++ b/src/backend/marsha/deposit/tests/api/filedepositories/test_create.py @@ -66,7 +66,7 @@ def test_api_file_depository_create_student_with_playlist_token(self): def test_api_file_depository_create_instructor(self): """An instructor without playlist token should not be able to create a file_depository.""" file_depository = FileDepositoryFactory() - jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository) + jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository.playlist) response = self.client.post( "/api/filedepositories/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}" diff --git a/src/backend/marsha/deposit/tests/api/filedepositories/test_delete.py b/src/backend/marsha/deposit/tests/api/filedepositories/test_delete.py index c2ba1ec5b4..da261bf98f 100644 --- a/src/backend/marsha/deposit/tests/api/filedepositories/test_delete.py +++ b/src/backend/marsha/deposit/tests/api/filedepositories/test_delete.py @@ -54,7 +54,7 @@ def test_api_file_depository_delete_user_logged_in(self): def test_api_file_depository_delete_student(self): """A student user should not be able to delete a file_depository.""" file_depository = FileDepositoryFactory() - jwt_token = StudentLtiTokenFactory(resource=file_depository) + jwt_token = StudentLtiTokenFactory(resource=file_depository.playlist) self.assertEqual(FileDepository.objects.count(), 1) response = self.client.delete( @@ -69,7 +69,7 @@ def test_api_file_depository_delete_instructor_read_only(self): """An instructor should not be able to delete a file_depository in read_only.""" file_depository = FileDepositoryFactory() jwt_token = InstructorOrAdminLtiTokenFactory( - resource=file_depository, + resource=file_depository.playlist, permissions__can_update=False, ) @@ -85,7 +85,7 @@ def test_api_file_depository_delete_instructor_read_only(self): def test_api_file_depository_delete_instructor(self): """An instructor should be able to delete a file_depository.""" file_depository = FileDepositoryFactory() - jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository) + jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository.playlist) self.assertEqual(FileDepository.objects.count(), 1) response = self.client.delete( diff --git a/src/backend/marsha/deposit/tests/api/filedepositories/test_depositedfiles.py b/src/backend/marsha/deposit/tests/api/filedepositories/test_depositedfiles.py index 7669825c18..ccd9ddc59b 100644 --- a/src/backend/marsha/deposit/tests/api/filedepositories/test_depositedfiles.py +++ b/src/backend/marsha/deposit/tests/api/filedepositories/test_depositedfiles.py @@ -51,7 +51,7 @@ def test_api_file_depository_list_deposited_files_student(self): DepositedFileFactory.create_batch(3, file_depository=file_depository) owned_deposited_file = DepositedFileFactory(file_depository=file_depository) jwt_token = StudentLtiTokenFactory( - resource=file_depository, + resource=file_depository.playlist, permissions__can_update=True, user__id=owned_deposited_file.author_id, user__full_username=owned_deposited_file.author_name, @@ -90,7 +90,7 @@ def test_api_file_depository_list_deposited_files_instructor(self): deposited_files = DepositedFileFactory.create_batch( 3, file_depository=file_depository ) - jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository) + jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository.playlist) response = self.client.get( f"/api/filedepositories/{file_depository.id}/depositedfiles/?limit=2", @@ -140,7 +140,7 @@ def test_api_file_depository_list_deposited_files_instructor_filtered(self): deposited_files_new = DepositedFileFactory.create_batch( 2, file_depository=file_depository ) - jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository) + jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository.playlist) response = self.client.get( f"/api/filedepositories/{file_depository.id}/depositedfiles/?limit=10", @@ -291,7 +291,7 @@ def test_api_file_depository_list_deposited_files_instructor_signed_urls(self): deposited_files = DepositedFileFactory.create_batch( 3, file_depository=file_depository, uploaded_on=now ) - jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository) + jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository.playlist) now = datetime(2021, 11, 30, tzinfo=baseTimezone.utc) with mock.patch.object(timezone, "now", return_value=now), mock.patch( diff --git a/src/backend/marsha/deposit/tests/api/filedepositories/test_list.py b/src/backend/marsha/deposit/tests/api/filedepositories/test_list.py index f398edb8f2..5c49bf5d99 100644 --- a/src/backend/marsha/deposit/tests/api/filedepositories/test_list.py +++ b/src/backend/marsha/deposit/tests/api/filedepositories/test_list.py @@ -43,7 +43,7 @@ def test_api_file_depository_fetch_list_student(self): """A student should not be able to fetch a list of file_depository.""" file_depository = FileDepositoryFactory() jwt_token = StudentLtiTokenFactory( - resource=file_depository, + resource=file_depository.playlist, permissions__can_update=True, ) @@ -55,7 +55,7 @@ def test_api_file_depository_fetch_list_student(self): def test_api_file_depository_fetch_list_instructor(self): """An instructor should not be able to fetch a file_depository list.""" file_depository = FileDepositoryFactory() - jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository) + jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository.playlist) response = self.client.get( "/api/filedepositories/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}" diff --git a/src/backend/marsha/deposit/tests/api/filedepositories/test_retrieve.py b/src/backend/marsha/deposit/tests/api/filedepositories/test_retrieve.py index b41e45a6b2..69f95ed228 100644 --- a/src/backend/marsha/deposit/tests/api/filedepositories/test_retrieve.py +++ b/src/backend/marsha/deposit/tests/api/filedepositories/test_retrieve.py @@ -38,7 +38,7 @@ def setUpClass(cls): def test_api_file_depository_fetch_student(self): """A student should be allowed to fetch a file_depository.""" file_depository = FileDepositoryFactory() - jwt_token = StudentLtiTokenFactory(resource=file_depository) + jwt_token = StudentLtiTokenFactory(resource=file_depository.playlist) response = self.client.get( f"/api/filedepositories/{file_depository.id!s}/", @@ -68,7 +68,7 @@ def test_api_file_depository_fetch_from_other_file_depository(self): """ file_depository = FileDepositoryFactory() other_file_depository = FileDepositoryFactory() - jwt_token = StudentLtiTokenFactory(resource=other_file_depository) + jwt_token = StudentLtiTokenFactory(resource=other_file_depository.playlist) response = self.client.get( f"/api/filedepositories/{file_depository.id!s}/", @@ -79,7 +79,7 @@ def test_api_file_depository_fetch_from_other_file_depository(self): def test_api_file_depository_fetch_instructor(self): """An instructor should be able to fetch a file_depository.""" file_depository = FileDepositoryFactory() - jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository) + jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository.playlist) response = self.client.get( f"/api/filedepositories/{file_depository.id!s}/", diff --git a/src/backend/marsha/deposit/tests/api/filedepositories/test_update.py b/src/backend/marsha/deposit/tests/api/filedepositories/test_update.py index 3b181efdf0..15cf962617 100644 --- a/src/backend/marsha/deposit/tests/api/filedepositories/test_update.py +++ b/src/backend/marsha/deposit/tests/api/filedepositories/test_update.py @@ -55,7 +55,7 @@ def test_api_file_depository_update_user_logged_in(self): def test_api_file_depository_update_student(self): """A student user should not be able to update a file_depository.""" file_depository = FileDepositoryFactory() - jwt_token = StudentLtiTokenFactory(resource=file_depository) + jwt_token = StudentLtiTokenFactory(resource=file_depository.playlist) data = {"title": "new title"} response = self.client.patch( @@ -70,7 +70,7 @@ def test_api_file_depository_update_instructor_read_only(self): """An instructor should not be able to update a file_depository in read_only.""" file_depository = FileDepositoryFactory() jwt_token = InstructorOrAdminLtiTokenFactory( - resource=file_depository, + resource=file_depository.playlist, permissions__can_update=False, ) data = {"title": "new title"} @@ -86,7 +86,7 @@ def test_api_file_depository_update_instructor_read_only(self): def test_api_file_depository_update_instructor(self): """An instructor should be able to update a file_depository.""" file_depository = FileDepositoryFactory() - jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository) + jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository.playlist) data = {"title": "new title", "description": "Hello"} response = self.client.patch( diff --git a/src/backend/marsha/deposit/tests/api/test_options.py b/src/backend/marsha/deposit/tests/api/test_options.py index 964f23521e..e7e111dbe6 100644 --- a/src/backend/marsha/deposit/tests/api/test_options.py +++ b/src/backend/marsha/deposit/tests/api/test_options.py @@ -17,7 +17,10 @@ class DepositedFiletCreateAPITest(TestCase): def test_api_deposited_files_options_anonymous(self): """Anonymous user can't fetch the deposited files options endpoint""" - response = self.client.options("/api/depositedfiles/") + file_depository = FileDepositoryFactory() + response = self.client.options( + f"/api/filedepositories/{file_depository.id}/depositedfiles/" + ) self.assertEqual(response.status_code, 401) @@ -25,10 +28,11 @@ def test_api_deposited_files_options_anonymous(self): def test_api_deposited_files_options_as_student(self): """A student can fetch the deposited files options endpoint""" - deposited_file = FileDepositoryFactory() - jwt_token = StudentLtiTokenFactory(resource=deposited_file) + file_depository = FileDepositoryFactory() + jwt_token = StudentLtiTokenFactory(resource=file_depository.playlist) response = self.client.options( - "/api/depositedfiles/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}" + f"/api/filedepositories/{file_depository.id}/depositedfiles/", + HTTP_AUTHORIZATION=f"Bearer {jwt_token}", ) self.assertEqual(response.status_code, 200) self.assertEqual(response.json()["upload_max_size_bytes"], 10) @@ -37,11 +41,12 @@ def test_api_deposited_files_options_as_student(self): def test_api_deposited_files_options_instructor(self): """An instructor can fetch the deposited files options endpoint""" - classroom = FileDepositoryFactory() - jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom) + file_depository = FileDepositoryFactory() + jwt_token = InstructorOrAdminLtiTokenFactory(resource=file_depository.playlist) response = self.client.options( - "/api/depositedfiles/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}" + f"/api/filedepositories/{file_depository.id}/depositedfiles/", + HTTP_AUTHORIZATION=f"Bearer {jwt_token}", ) self.assertEqual(response.status_code, 200) diff --git a/src/backend/marsha/deposit/urls.py b/src/backend/marsha/deposit/urls.py index 21ef54c2ea..afe1bef963 100644 --- a/src/backend/marsha/deposit/urls.py +++ b/src/backend/marsha/deposit/urls.py @@ -12,7 +12,11 @@ router = DefaultRouter() router.register("filedepositories", FileDepositoryViewSet, basename="file_depository") -router.register("depositedfiles", DepositedFileViewSet, basename="deposited_file") + +filedepository_related_router = DefaultRouter() +filedepository_related_router.register( + "depositedfiles", DepositedFileViewSet, basename="deposited_file" +) urlpatterns = [ path( @@ -28,4 +32,8 @@ name="file_depository_lti_view", ), path("api/", include(router.urls)), + path( + "api/filedepositories//", + include(filedepository_related_router.urls), + ), ]