From fc017961abb114a790dc667a193972aaa39b88a6 Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Wed, 28 Dec 2022 16:16:28 +0100 Subject: [PATCH] Verify permissions --- .../__tests__/learning_path_json.json | 2 +- client/src/services/files.ts | 4 +-- server/config/urls.py | 4 +-- .../course/creators/test_course.py | 2 +- server/vbv_lernwelt/course/permissions.py | 22 ++++++++++++ .../course/tests/test_document_uploads.py | 34 ++++++++++--------- server/vbv_lernwelt/course/views.py | 14 ++++++-- 7 files changed, 58 insertions(+), 24 deletions(-) diff --git a/client/src/services/__tests__/learning_path_json.json b/client/src/services/__tests__/learning_path_json.json index 76113f79..b003a4a6 100644 --- a/client/src/services/__tests__/learning_path_json.json +++ b/client/src/services/__tests__/learning_path_json.json @@ -481,7 +481,7 @@ "value": { "first_name": "Patrizia", "last_name": "Huggel", - "email": "patrizia.huggel@example.com", + "email": "patrizia.huggel@eiger-versicherungen.ch", "photo": null, "biography": "" }, diff --git a/client/src/services/files.ts b/client/src/services/files.ts index aaf8e65e..44c18a4d 100644 --- a/client/src/services/files.ts +++ b/client/src/services/files.ts @@ -3,7 +3,7 @@ import { getCookieValue } from "@/router/guards"; import type { CircleDocument, DocumentUploadData } from "@/types"; async function startFileUpload(fileData: DocumentUploadData, courseSessionId: number) { - return await itPost(`/api/core/document/start`, { + return await itPost(`/api/core/document/start/`, { file_type: fileData.file.type, file_name: fileData.file.name, name: fileData.name, @@ -70,7 +70,7 @@ export async function uploadCircleDocument( const startData = await startFileUpload(data, courseSessionId); await uploadFile(startData, data.file); - const response = await itPost(`/api/core/file/finish`, { + const response = await itPost(`/api/core/file/finish/`, { file_id: startData.file_id, }); diff --git a/server/config/urls.py b/server/config/urls.py index 5dc6290e..de00dc75 100644 --- a/server/config/urls.py +++ b/server/config/urls.py @@ -83,11 +83,11 @@ urlpatterns = [ name="request_course_completion_for_user"), # test - path(r'api/core/document/start', document_upload_start, + path(r'api/core/document/start/', document_upload_start, name='file_upload_start'), path(r'api/core/document//', document_delete, name='document_delete'), - path(r'api/core/file/finish', document_upload_finish, + path(r'api/core/file/finish/', document_upload_finish, name='file_upload_finish'), path(r"api/core/document/local//", document_direct_upload, name='file_upload_local'), diff --git a/server/vbv_lernwelt/course/creators/test_course.py b/server/vbv_lernwelt/course/creators/test_course.py index 329f1a22..418f712f 100644 --- a/server/vbv_lernwelt/course/creators/test_course.py +++ b/server/vbv_lernwelt/course/creators/test_course.py @@ -138,7 +138,7 @@ def create_test_learning_path(user=None, skip_locales=True): { "last_name": "Huggel", "first_name": "Patrizia", - "email": "patrizia.huggel@example.com", + "email": "patrizia.huggel@eiger-versicherungen.ch", }, ), ], diff --git a/server/vbv_lernwelt/course/permissions.py b/server/vbv_lernwelt/course/permissions.py index d15fe28e..162fd2b2 100644 --- a/server/vbv_lernwelt/course/permissions.py +++ b/server/vbv_lernwelt/course/permissions.py @@ -1,4 +1,5 @@ from vbv_lernwelt.course.models import CourseSession, CourseSessionUser +from vbv_lernwelt.learnpath.models import LearningSequence def has_course_access_by_page_request(request, obj): @@ -24,3 +25,24 @@ def course_sessions_for_user_qs(user): course_sessions = CourseSession.objects.filter(coursesessionuser__user=user) return course_sessions + + +def is_circle_expert(user, learning_sequence, course) -> bool: + if user.is_superuser: + return True + + try: + ls = LearningSequence.objects.get(id=learning_sequence) + except LearningSequence.DoesNotExist: + return False + + if not CourseSession.objects.filter( + id=course, coursesessionuser__user=user + ).exists(): + return False + + for expert in ls.get_parent().circle.experts.raw_data: + if expert["value"]["email"] == user.email: + return True + + return False diff --git a/server/vbv_lernwelt/course/tests/test_document_uploads.py b/server/vbv_lernwelt/course/tests/test_document_uploads.py index 4a1d462c..3e46d26d 100644 --- a/server/vbv_lernwelt/course/tests/test_document_uploads.py +++ b/server/vbv_lernwelt/course/tests/test_document_uploads.py @@ -6,7 +6,7 @@ from vbv_lernwelt.course.consts import COURSE_TEST_ID from vbv_lernwelt.course.creators.test_course import create_test_course from vbv_lernwelt.course.models import CircleDocument, CourseSession, CourseSessionUser from vbv_lernwelt.files.models import File -from vbv_lernwelt.learnpath.models import Circle, LearningSequence +from vbv_lernwelt.learnpath.models import LearningSequence class DocumentUploadApiTestCase(APITestCase): @@ -29,7 +29,6 @@ class DocumentUploadApiTestCase(APITestCase): user=User.objects.get(username="patrizia.huggel@eiger-versicherungen.ch"), role=CourseSessionUser.Role.EXPERT, ) - csu.expert.add(Circle.objects.get(slug="test-lehrgang-lp-circle-basis")) self.test_data = { "file_name": "test.pdf", @@ -44,10 +43,11 @@ class DocumentUploadApiTestCase(APITestCase): def test_can_start_upload(self): ls = LearningSequence.objects.get( - slug="test-lehrgang-lp-circle-basis-ls-starten" + slug="test-lehrgang-lp-circle-analyse-ls-beobachten" ) + self.test_data["learning_sequence"] = ls.id - response = self.client.post(f"/api/core/document/start", self.test_data) + response = self.client.post(f"/api/core/document/start/", self.test_data) self.assertEqual(response.status_code, 200) self.assertNotEqual(response.data["url"], "") @@ -66,12 +66,12 @@ class DocumentUploadApiTestCase(APITestCase): def test_cannot_start_upload_in_other_circle(self): ls = LearningSequence.objects.get( - slug="test-lehrgang-lp-circle-analyse-ls-beenden" + slug="test-lehrgang-lp-circle-basis-ls-starten" ) self.test_data["learning_sequence"] = ls.id - response = self.client.post(f"/api/core/document/start", self.test_data) + response = self.client.post(f"/api/core/document/start/", self.test_data) - self.assertEqual(response.status_code, 401) + self.assertEqual(response.status_code, 403) def test_student_cannot_start_uploads(self): self.client.login(username="student", password="test") @@ -79,24 +79,24 @@ class DocumentUploadApiTestCase(APITestCase): slug="test-lehrgang-lp-circle-basis-ls-starten" ) self.test_data["learning_sequence"] = ls.id - response = self.client.post(f"/api/core/document/start", self.test_data) + response = self.client.post(f"/api/core/document/start/", self.test_data) - self.assertEqual(response.status_code, 401) + self.assertEqual(response.status_code, 403) def test_expert_can_finish_own_upload(self): ls = LearningSequence.objects.get( - slug="test-lehrgang-lp-circle-basis-ls-starten" + slug="test-lehrgang-lp-circle-analyse-ls-beobachten" ) self.test_data["learning_sequence"] = ls.id - response = self.client.post(f"/api/core/document/start", self.test_data) + response = self.client.post(f"/api/core/document/start/", self.test_data) self.assertEqual(response.status_code, 200) file_id = response.data["file_id"] response = self.client.post( - f"/api/core/file/finish", + f"/api/core/file/finish/", { - "file_id": response.data["id"], + "file_id": file_id, }, ) @@ -119,11 +119,11 @@ class DocumentUploadApiTestCase(APITestCase): response = self.client.post(f"/api/core/file/finish/", {"file_id": file.id}) - self.assertEqual(response.status_code, 401) + self.assertEqual(response.status_code, 403) def test_can_delete_document(self): ls = LearningSequence.objects.get( - slug="test-lehrgang-lp-circle-basis-ls-starten" + slug="test-lehrgang-lp-circle-analyse-ls-beobachten" ) file = File( original_file_name="test.pdf", @@ -155,8 +155,9 @@ class DocumentUploadApiTestCase(APITestCase): self.assertIsNone(file.upload_finished_at) def test_student_cannot_delete_document(self): + self.client.login(username="student", password="test") ls = LearningSequence.objects.get( - slug="test-lehrgang-lp-circle-basis-ls-starten" + slug="test-lehrgang-lp-circle-analyse-ls-beobachten" ) file = File( original_file_name="test.pdf", @@ -181,3 +182,4 @@ class DocumentUploadApiTestCase(APITestCase): # expert cannot upload in other course # expert cannot delete other upload +# exper cannot change course diff --git a/server/vbv_lernwelt/course/views.py b/server/vbv_lernwelt/course/views.py index 82775155..e235db7e 100644 --- a/server/vbv_lernwelt/course/views.py +++ b/server/vbv_lernwelt/course/views.py @@ -14,6 +14,7 @@ from vbv_lernwelt.course.models import ( from vbv_lernwelt.course.permissions import ( course_sessions_for_user_qs, has_course_access_by_page_request, + is_circle_expert, ) from vbv_lernwelt.course.serializers import ( CourseCompletionSerializer, @@ -165,10 +166,16 @@ def get_course_session_users(request, course_slug): @api_view(["POST"]) def document_upload_start(request): - # todo: check permissions serializer = DocumentUploadStartInputSerializer(data=request.data) serializer.is_valid(raise_exception=True) + if not is_circle_expert( + request.user, + serializer.validated_data["learning_sequence"], + serializer.validated_data["course_session"], + ): + raise PermissionDenied() + service = FileDirectUploadService(request.user) file, presigned_data = service.start( serializer.validated_data["file_name"], serializer.validated_data["file_type"] @@ -219,7 +226,10 @@ def document_direct_upload(request, file_id): @api_view(["DELETE"]) def document_delete(request, document_id): document = get_object_or_404(CircleDocument, id=document_id) - # todo: check real permissoin + if not is_circle_expert( + request.user, document.learning_sequence_id, document.course_session_id + ): + raise PermissionDenied() document.delete()