From a73d7673785eddb5e7dac85874abedfae65b4299 Mon Sep 17 00:00:00 2001 From: Isaac Lee <124631592+ilee2u@users.noreply.github.com> Date: Thu, 13 Feb 2025 15:50:08 -0500 Subject: [PATCH] fix: grant course staff exam access tokens (#359) * fix: grant course staff exam access tokens * test: refine tests * chore: update docstring --- edx_exams/apps/api/v1/tests/test_views.py | 23 ++++++++++++++++++++++- edx_exams/apps/api/v1/views.py | 13 +++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/edx_exams/apps/api/v1/tests/test_views.py b/edx_exams/apps/api/v1/tests/test_views.py index dcc5b85e..ecfc44ad 100644 --- a/edx_exams/apps/api/v1/tests/test_views.py +++ b/edx_exams/apps/api/v1/tests/test_views.py @@ -654,6 +654,8 @@ def setUp(self): self.course_id = 'course-v1:edx+test+f19' + self.user.is_staff = False + self.due_date = timezone.now() + timedelta(minutes=5) self.exam = Exam.objects.create( resource_id=str(uuid.uuid4()), @@ -721,8 +723,10 @@ def test_exam_not_found(self): def test_access_not_granted(self): """ Verify the endpoint doesn't grant access for an exam - without an existing exam attempt or past due date. + without an existing exam attempt or past due date + while user is not course staff. """ + self.user.is_staff = False response = self.get_exam_access(self.user, self.url) self.assertEqual(403, response.status_code) @@ -815,6 +819,23 @@ def test_access_no_due_date(self, attempt_status, response_status): if response_status == 200: self.assert_valid_exam_access_token(response, self.user, no_due_date_exam) + @ddt.data( + (True, 200), + (False, 403), + ) + @ddt.unpack + def test_access_user_is_course_staff(self, is_course_staff, response_status): + """ + Verify the endpoint immediately grants access + for anyone who is course staff. + """ + self.user.is_staff = is_course_staff + + response = self.get_exam_access(self.user, self.url) + self.assertEqual(response_status, response.status_code) + if response_status == 200: + self.assert_valid_exam_access_token(response, self.user, self.exam) + def test_access_not_granted_if_hide_after_due(self): """ Verify the endpoint does not grant access for past-due exam diff --git a/edx_exams/apps/api/v1/views.py b/edx_exams/apps/api/v1/views.py index 83608e9f..ae23f32e 100644 --- a/edx_exams/apps/api/v1/views.py +++ b/edx_exams/apps/api/v1/views.py @@ -318,9 +318,10 @@ class ExamAccessTokensView(ExamsAPIView): View to create signed exam access tokens for a specific user and exam. Given an exam_id and user (in request), this view will either grant access - as an exam access token or not grant access. Access is granted if there is an - existing exam attempt (must be started if no due date or prior to due date or - verified if past the due date.) or if the exam is past its due date. + as an exam access token or not grant access. Access is granted if the requesting + user is course staff, if there is an existing exam attempt (must be started if + no due date or prior to due date or verified if past the due date), or if the + exam is past its due date. HTTP GET Provides an Exam Access Token as a cookie if access is granted. @@ -365,8 +366,12 @@ def get_response(cls, exam, user): grant_access = False response_status = status.HTTP_403_FORBIDDEN + # If user is course staff, then grant them access + if user.has_course_staff_permission(exam.course_id): + grant_access, response_status = True, status.HTTP_200_OK + # If exam attempt exists for user, then grant exam access - if exam_attempt is not None: + elif exam_attempt is not None: # If no due date or if before due date, grant access if attempt started # and get expiration window. if exam.due_date is None or timezone.now() < exam.due_date: