Skip to content

Commit

Permalink
🛂(backend) use playlist token in classroom apis
Browse files Browse the repository at this point in the history
As we are now using playlist tokens, it has to be allowed on apis.
  • Loading branch information
kernicPanel committed Aug 16, 2023
1 parent 89f7c66 commit 74159f7
Show file tree
Hide file tree
Showing 18 changed files with 179 additions and 66 deletions.
8 changes: 4 additions & 4 deletions src/backend/marsha/bbb/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class ClassroomViewSet(

permission_classes = [
(
core_permissions.IsTokenResourceRouteObject
core_permissions.IsPlaylistToken
& (core_permissions.IsTokenInstructor | core_permissions.IsTokenAdmin)
)
| (
Expand Down Expand Up @@ -142,7 +142,8 @@ def get_permissions(self):
]
elif self.action in ["retrieve", "service_join"]:
permission_classes = [
core_permissions.IsTokenResourceRouteObject
core_permissions.IsPlaylistToken
| core_permissions.IsTokenResourceRouteObject # needed for invite links
| (
core_permissions.UserIsAuthenticated # asserts request.resource is None
& (
Expand Down Expand Up @@ -188,7 +189,7 @@ def get_serializer_context(self):
# For LTI
| (
core_permissions.ResourceIsAuthenticated
& core_permissions.IsTokenResourceRouteObject
& core_permissions.IsPlaylistToken
& (
core_permissions.IsTokenInstructor
| core_permissions.IsTokenAdmin
Expand Down Expand Up @@ -592,7 +593,6 @@ def initiate_upload(self, request, pk=None, classroom_id=None):
serializer = serializers.ClassroomDocumentInitiateUploadSerializer(
data=request.data
)

if serializer.is_valid() is not True:
return Response(serializer.errors, status=400)

Expand Down
5 changes: 4 additions & 1 deletion src/backend/marsha/bbb/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from rest_framework import permissions

from marsha.bbb.models import Classroom
from marsha.core import models, permissions as core_permissions


Expand Down Expand Up @@ -33,7 +34,9 @@ def has_permission(self, request, view):
if not request.resource:
return False

return str(view.get_related_classroom_id()) == request.resource.id
return Classroom.objects.filter(
pk=view.get_related_classroom_id(), playlist_id=request.resource.id
).exists()


class BaseIsRelatedClassroomPlaylistRoleMixin:
Expand Down
8 changes: 2 additions & 6 deletions src/backend/marsha/bbb/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,13 +296,9 @@ def create(self, validated_data):
The "validated_data" dictionary is returned after modification.
"""
resource = self.context["request"].resource
classroom_id = self.context["request"].data.get("classroom_id")
classroom_id = self.context["view"].get_related_classroom_id()
if not validated_data.get("classroom_id"):
if resource:
validated_data["classroom_id"] = resource.id
elif classroom_id:
validated_data["classroom_id"] = classroom_id
validated_data["classroom_id"] = classroom_id

if not ClassroomDocument.objects.filter(
classroom_id=validated_data["classroom_id"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def test_api_classroom_bulk_delete_student(self):
classroom2 = ClassroomFactory()

jwt_token = StudentLtiTokenFactory(
resource=classroom1,
resource=classroom1.playlist,
permissions__can_update=True,
)

Expand Down Expand Up @@ -93,7 +93,7 @@ def test_api_classroom_bulk_delete_instructor(self):
"""LTI Token can't delete a list of classroom."""
classroom = ClassroomFactory()

jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom)
jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom.playlist)

response = self.client.delete(
self._api_url(),
Expand All @@ -113,7 +113,7 @@ def test_api_classroom_bulk_delete_instructor_with_playlist_token(self):
playlist = PlaylistFactory()
classroom = ClassroomFactory(playlist=playlist)

jwt_token = PlaylistLtiTokenFactory(resource=classroom)
jwt_token = PlaylistLtiTokenFactory(resource=classroom.playlist)

self.assertEqual(Classroom.objects.count(), 1)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def test_api_list_classroom_documents_student(self):
"""A student should not be able to fetch a list of classroom documents."""
classroom = ClassroomFactory()
ClassroomDocumentFactory.create_batch(3, classroom=classroom)
jwt_token = StudentLtiTokenFactory(resource=classroom)
jwt_token = StudentLtiTokenFactory(resource=classroom.playlist)

response = self.client.get(
f"/api/classrooms/{classroom.id}/classroomdocuments/",
Expand All @@ -63,7 +63,7 @@ def test_api_list_classroom_documents_instructor(self):
classroom_documents = ClassroomDocumentFactory.create_batch(
3, classroom=classroom
)
jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom)
jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom.playlist)

response = self.client.get(
f"/api/classrooms/{classroom.id}/classroomdocuments/?limit=2",
Expand Down Expand Up @@ -118,7 +118,7 @@ def test_api_list_classroom_documents_instructor_urls(self):
uploaded_on=now,
)
)
jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom)
jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom.playlist)

response = self.client.get(
f"/api/classrooms/{classroom.id}/classroomdocuments/",
Expand Down
4 changes: 2 additions & 2 deletions src/backend/marsha/bbb/tests/api/classroom/test_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def test_api_classroom_create_student(self):
classroom = ClassroomFactory()

jwt_token = StudentLtiTokenFactory(
resource=classroom,
resource=classroom.playlist,
permissions__can_update=True,
)

Expand All @@ -77,7 +77,7 @@ def test_api_classroom_create_instructor(self):
"""An instructor without playlist token should not be able to create a classroom."""
classroom = ClassroomFactory()

jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom)
jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom.playlist)

response = self.client.post(
"/api/classrooms/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}"
Expand Down
6 changes: 3 additions & 3 deletions src/backend/marsha/bbb/tests/api/classroom/test_delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def test_api_classroom_delete_student(self):
classroom = ClassroomFactory()

jwt_token = StudentLtiTokenFactory(
resource=classroom,
resource=classroom.playlist,
permissions__can_update=True,
)

Expand All @@ -72,7 +72,7 @@ def test_api_classroom_delete_instructor(self):
"""An instructor without playlist token should not be able to delete a classroom."""
classroom = ClassroomFactory()

jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom)
jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom.playlist)

response = self.client.delete(
f"/api/classrooms/{classroom.id}/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}"
Expand All @@ -86,7 +86,7 @@ def test_api_classroom_delete_instructor_with_playlist_token(self):
playlist = PlaylistFactory()
classroom = ClassroomFactory(playlist=playlist)

jwt_token = PlaylistLtiTokenFactory(resource=classroom)
jwt_token = PlaylistLtiTokenFactory(resource=classroom.playlist)

self.assertEqual(Classroom.objects.count(), 1)

Expand Down
4 changes: 2 additions & 2 deletions src/backend/marsha/bbb/tests/api/classroom/test_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def test_api_classroom_fetch_list_student(self):
classroom = ClassroomFactory()

jwt_token = StudentLtiTokenFactory(
resource=classroom,
resource=classroom.playlist,
permissions__can_update=True,
)

Expand All @@ -59,7 +59,7 @@ def test_api_fetch_list_instructor(self):
"""An instructor should not be able to fetch a classroom list."""
classroom = ClassroomFactory()

jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom)
jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom.playlist)

response = self.client.get(
"/api/classrooms/", HTTP_AUTHORIZATION=f"Bearer {jwt_token}"
Expand Down
120 changes: 114 additions & 6 deletions src/backend/marsha/bbb/tests/api/classroom/test_retrieve.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from marsha.bbb import serializers
from marsha.bbb.factories import ClassroomFactory, ClassroomRecordingFactory
from marsha.bbb.utils.tokens import create_classroom_stable_invite_jwt
from marsha.core.factories import (
OrganizationAccessFactory,
PlaylistAccessFactory,
Expand Down Expand Up @@ -50,7 +51,7 @@ def test_api_classroom_fetch_student(self, mock_get_meeting_infos):
"running": "true",
}

jwt_token = StudentLtiTokenFactory(resource=classroom)
jwt_token = StudentLtiTokenFactory(resource=classroom.playlist)

response = self.client.get(
f"/api/classrooms/{classroom.id!s}/",
Expand Down Expand Up @@ -108,7 +109,7 @@ def test_api_classroom_fetch_student_with_recordings(self, mock_get_meeting_info
"running": "true",
}

jwt_token = StudentLtiTokenFactory(resource=classroom)
jwt_token = StudentLtiTokenFactory(resource=classroom.playlist)

response = self.client.get(
f"/api/classrooms/{classroom.id!s}/",
Expand Down Expand Up @@ -182,7 +183,7 @@ def test_api_classroom_fetch_student_scheduled(self, mock_get_meeting_infos):
"running": "true",
}

jwt_token = StudentLtiTokenFactory(resource=classroom)
jwt_token = StudentLtiTokenFactory(resource=classroom.playlist)

response = self.client.get(
f"/api/classrooms/{classroom.id!s}/",
Expand Down Expand Up @@ -232,7 +233,7 @@ def test_api_classroom_fetch_instructor(self, mock_get_meeting_infos):
"running": "true",
}

jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom)
jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom.playlist)

response = self.client.get(
f"/api/classrooms/{classroom.id!s}/",
Expand Down Expand Up @@ -473,7 +474,7 @@ def test_api_classroom_fetch_with_recordings(self, mock_get_meeting_infos):
"running": "true",
}

jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom)
jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom.playlist)

response = self.client.get(
f"/api/classrooms/{classroom.id!s}/",
Expand Down Expand Up @@ -545,7 +546,7 @@ def test_api_classroom_fetch_from_LTI_inactive_conversion(
"running": "true",
}

jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom)
jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom.playlist)

response = self.client.get(
f"/api/classrooms/{classroom.id!s}/",
Expand Down Expand Up @@ -584,3 +585,110 @@ def test_api_classroom_fetch_from_standalone_site_inactive_conversion(
self.assertEqual(response.status_code, 200)

self.assertFalse(response.json()["vod_conversion_enabled"])

@mock.patch.object(serializers, "get_meeting_infos")
def test_api_classroom_fetch_public_invite(self, mock_get_meeting_infos):
"""A public invited user should be allowed to fetch a classroom."""
classroom = ClassroomFactory()
mock_get_meeting_infos.return_value = {
"returncode": "SUCCESS",
"running": "true",
}

jwt_token = create_classroom_stable_invite_jwt(classroom)

response = self.client.get(
f"/api/classrooms/{classroom.id!s}/",
HTTP_AUTHORIZATION=f"Bearer {jwt_token}",
)
self.assertEqual(response.status_code, 200)
content = json.loads(response.content)
self.assertDictEqual(
{
"id": str(classroom.id),
"infos": {"returncode": "SUCCESS", "running": "true"},
"lti_id": str(classroom.lti_id),
"title": classroom.title,
"description": classroom.description,
"started": False,
"ended": False,
"meeting_id": str(classroom.meeting_id),
"welcome_text": classroom.welcome_text,
"playlist": {
"id": str(classroom.playlist.id),
"title": classroom.playlist.title,
"lti_id": classroom.playlist.lti_id,
},
"starting_at": None,
"estimated_duration": None,
"public_token": None,
"instructor_token": None,
"recordings": [],
"retention_date": None,
"enable_waiting_room": False,
"enable_chat": True,
"enable_presentation_supports": True,
"enable_recordings": True,
"recording_purpose": classroom.recording_purpose,
"enable_shared_notes": True,
"vod_conversion_enabled": True,
},
content,
)

@mock.patch.object(serializers, "get_meeting_infos")
def test_api_classroom_fetch_moderator_invite(self, mock_get_meeting_infos):
"""A moderator invited user should be allowed to fetch a classroom."""
classroom = ClassroomFactory()
mock_get_meeting_infos.return_value = {
"returncode": "SUCCESS",
"running": "true",
}

jwt_token = create_classroom_stable_invite_jwt(
classroom,
role=INSTRUCTOR,
permissions={
"can_update": True,
"can_access_dashboard": True,
},
)

response = self.client.get(
f"/api/classrooms/{classroom.id!s}/",
HTTP_AUTHORIZATION=f"Bearer {jwt_token}",
)
self.assertEqual(response.status_code, 200)
content = json.loads(response.content)
self.assertDictEqual(
{
"id": str(classroom.id),
"infos": {"returncode": "SUCCESS", "running": "true"},
"lti_id": str(classroom.lti_id),
"title": classroom.title,
"description": classroom.description,
"started": False,
"ended": False,
"meeting_id": str(classroom.meeting_id),
"welcome_text": classroom.welcome_text,
"playlist": {
"id": str(classroom.playlist.id),
"title": classroom.playlist.title,
"lti_id": classroom.playlist.lti_id,
},
"starting_at": None,
"estimated_duration": None,
"public_token": None,
"instructor_token": None,
"recordings": [],
"retention_date": None,
"enable_waiting_room": False,
"enable_chat": True,
"enable_presentation_supports": True,
"enable_recordings": True,
"recording_purpose": classroom.recording_purpose,
"enable_shared_notes": True,
"vod_conversion_enabled": True,
},
content,
)
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def test_api_bbb_create_student(self, mock_create_request):
"""A student should not be able to create a classroom."""
classroom = ClassroomFactory()

jwt_token = StudentLtiTokenFactory(resource=classroom)
jwt_token = StudentLtiTokenFactory(resource=classroom.playlist)

response = self.client.patch(
f"/api/classrooms/{classroom.id}/create/",
Expand All @@ -86,7 +86,7 @@ def test_api_bbb_create_new_classroom(
mock_get_meeting_infos.return_value = {"returncode": "SUCCESS"}
mock_create_request.return_value = {"returncode": "SUCCESS"}

jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom)
jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom.playlist)
data = {"title": "new title", "welcome_text": "Hello"}

response = self.client.patch(
Expand Down Expand Up @@ -126,7 +126,7 @@ def test_api_bbb_create_existing_classroom(
{"message": "A classroom already exists with that classroom ID."}
)

jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom)
jwt_token = InstructorOrAdminLtiTokenFactory(resource=classroom.playlist)
data = {"title": classroom.title, "welcome_text": classroom.welcome_text}

response = self.client.patch(
Expand Down
Loading

0 comments on commit 74159f7

Please sign in to comment.