From f8897bc9cc2a0b25a45b04637a186d4254aae021 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Sat, 31 Aug 2024 00:56:08 +0500 Subject: [PATCH 01/13] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/tests/test_api.py | 89 +++++++++++++++++++++ lms/djangoapps/instructor/views/api.py | 15 ++-- 2 files changed, 98 insertions(+), 6 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index 65d35221d4f5..122430772994 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -4675,3 +4675,92 @@ def test_get_certificate_for_user_no_certificate(self): f"The student {self.user} does not have certificate for the course {self.course.id.course}. Kindly " "verify student username/email and the selected course are correct and try again." ) + +from openedx.core.djangoapps.oauth_dispatch import jwt as jwt_api +from openedx.core.djangoapps.oauth_dispatch.adapters import DOTAdapter +from openedx.core.djangoapps.oauth_dispatch.tests.factories import AccessTokenFactory, ApplicationFactory +from common.djangoapps.student.tests.factories import ( + BetaTesterFactory, + CourseAccessRoleFactory, + CourseEnrollmentFactory, + GlobalStaffFactory, + InstructorFactory, + StaffFactory, + UserFactory +) + +class TestOauthInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTestCase): + """ + Test endpoints whereby instructors can change permissions + of other users. + + This test does NOT test whether the actions had an effect on the + database, that is the job of test_access. + This tests the response and action switch. + Actually, modify_access does not have a very meaningful + response yet, so only the status code is tested. + """ + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.course = CourseFactory.create() + + def setUp(self): + super().setUp() + + self.instructor = InstructorFactory(course_key=self.course.id) + self.client.login(username=self.instructor.username, password=self.TEST_PASSWORD) + + self.other_instructor = InstructorFactory(course_key=self.course.id) + self.other_staff = StaffFactory(course_key=self.course.id) + self.other_user = UserFactory() + + def test_end_points_with_oauth(self): + """ + Verify the endpoint using JWT authentication. + """ + self.client.logout() + dot_application = ApplicationFactory(user=self.other_user, authorization_grant_type='password') + access_token = AccessTokenFactory(user=self.other_user, application=dot_application) + oauth_adapter = DOTAdapter() + token_dict = { + 'access_token': access_token, + 'scope': 'email profile', + } + jwt_token = jwt_api.create_jwt_from_token(token_dict, oauth_adapter, use_asymmetric_key=True) + headers = { + 'HTTP_AUTHORIZATION': 'JWT ' + jwt_token + } + endpoints = [ + ('list_course_role_members', {'rolename': 'staff'}), + ('register_and_enroll_students', {}) + ] + + for endpoint, body in endpoints: + url = reverse(endpoint, kwargs={'course_id': str(self.course.id)}) + response = self.client.post( + url, + data=body, + **headers + ) + # JWT authentication works but it has no permissions. + assert response.status_code == 403 + + CourseAccessRoleFactory( + course_id=self.course.id, + user=self.other_user, + role="instructor", + org=self.course.id.org + ) + + for endpoint, body in endpoints: + url = reverse(endpoint, kwargs={'course_id': str(self.course.id)}) + + response = self.client.post( + url, + data=body, + **headers + ) + + assert response.status_code == 200 + diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index c6db96b3ca60..3214b62b8132 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -316,12 +316,13 @@ def post(self, request, course_id): # pylint: disable=too-many-statements to create the new account. The failure will be messaged in a response in the browser. """ - if not configuration_helpers.get_value( - 'ALLOW_AUTOMATED_SIGNUPS', - settings.FEATURES.get('ALLOW_AUTOMATED_SIGNUPS', False), - ): - return HttpResponseForbidden() - + # if not configuration_helpers.get_value( + # 'ALLOW_AUTOMATED_SIGNUPS', + # settings.FEATURES.get('ALLOW_AUTOMATED_SIGNUPS', False), + # ): + # return HttpResponseForbidden() + import pdb; + pdb.set_trace() course_id = CourseKey.from_string(course_id) warnings = [] row_errors = [] @@ -1114,6 +1115,8 @@ def post(self, request, course_id): Raises: Http404: If the course does not exist. """ + import pdb; + pdb.set_trace() course_id = CourseKey.from_string(course_id) course = get_course_with_access( request.user, 'instructor', course_id, depth=None From a51005a34b87375676eaae1928fcb353b47d8d4c Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Sat, 31 Aug 2024 01:21:50 +0500 Subject: [PATCH 02/13] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/tests/test_api.py | 54 ++++++++++----------- lms/djangoapps/instructor/views/api.py | 15 +++--- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index 122430772994..1a66c71897f2 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -4703,7 +4703,9 @@ class TestOauthInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollm @classmethod def setUpClass(cls): super().setUpClass() - cls.course = CourseFactory.create() + cls.course = CourseFactory.create( + entrance_exam_id='i4x://{}/{}/chapter/Entrance_exam'.format('test_org', 'test_course') + ) def setUp(self): super().setUp() @@ -4715,6 +4717,27 @@ def setUp(self): self.other_staff = StaffFactory(course_key=self.course.id) self.other_user = UserFactory() + def assert_all_end_points(self, expected_status_code, headers): + endpoints = [ + ('list_course_role_members', {'rolename': 'staff'}), + ('register_and_enroll_students', {}), + ('get_student_progress_url', { 'course_id': str(self.course.id), + 'unique_student_identifier': self.other_user.email + } + ), + ('list_entrance_exam_instructor_tasks', {'unique_student_identifier': self.other_user.email}) + ] + for endpoint, body in endpoints: + url = reverse(endpoint, kwargs={'course_id': str(self.course.id)}) + response = self.client.post( + url, + data=body, + **headers + ) + + # JWT authentication works but it has no permissions. + assert response.status_code == expected_status_code + def test_end_points_with_oauth(self): """ Verify the endpoint using JWT authentication. @@ -4731,36 +4754,11 @@ def test_end_points_with_oauth(self): headers = { 'HTTP_AUTHORIZATION': 'JWT ' + jwt_token } - endpoints = [ - ('list_course_role_members', {'rolename': 'staff'}), - ('register_and_enroll_students', {}) - ] - - for endpoint, body in endpoints: - url = reverse(endpoint, kwargs={'course_id': str(self.course.id)}) - response = self.client.post( - url, - data=body, - **headers - ) - # JWT authentication works but it has no permissions. - assert response.status_code == 403 - + self.assert_all_end_points(403, headers) CourseAccessRoleFactory( course_id=self.course.id, user=self.other_user, role="instructor", org=self.course.id.org ) - - for endpoint, body in endpoints: - url = reverse(endpoint, kwargs={'course_id': str(self.course.id)}) - - response = self.client.post( - url, - data=body, - **headers - ) - - assert response.status_code == 200 - + self.assert_all_end_points(200, headers) diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 3214b62b8132..c6db96b3ca60 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -316,13 +316,12 @@ def post(self, request, course_id): # pylint: disable=too-many-statements to create the new account. The failure will be messaged in a response in the browser. """ - # if not configuration_helpers.get_value( - # 'ALLOW_AUTOMATED_SIGNUPS', - # settings.FEATURES.get('ALLOW_AUTOMATED_SIGNUPS', False), - # ): - # return HttpResponseForbidden() - import pdb; - pdb.set_trace() + if not configuration_helpers.get_value( + 'ALLOW_AUTOMATED_SIGNUPS', + settings.FEATURES.get('ALLOW_AUTOMATED_SIGNUPS', False), + ): + return HttpResponseForbidden() + course_id = CourseKey.from_string(course_id) warnings = [] row_errors = [] @@ -1115,8 +1114,6 @@ def post(self, request, course_id): Raises: Http404: If the course does not exist. """ - import pdb; - pdb.set_trace() course_id = CourseKey.from_string(course_id) course = get_course_with_access( request.user, 'instructor', course_id, depth=None From c62bc4af143e1e42c316127020abd0a477658075 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Sat, 31 Aug 2024 23:01:09 +0500 Subject: [PATCH 03/13] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/tests/test_api.py | 44 +++++++++++++-------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index 1a66c71897f2..54d238c16ff8 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -4717,7 +4717,20 @@ def setUp(self): self.other_staff = StaffFactory(course_key=self.course.id) self.other_user = UserFactory() - def assert_all_end_points(self, expected_status_code, headers): + dot_application = ApplicationFactory(user=self.other_user, authorization_grant_type='password') + access_token = AccessTokenFactory(user=self.other_user, application=dot_application) + oauth_adapter = DOTAdapter() + token_dict = { + 'access_token': access_token, + 'scope': 'email profile', + } + jwt_token = jwt_api.create_jwt_from_token(token_dict, oauth_adapter, use_asymmetric_key=True) + self.headers = { + 'HTTP_AUTHORIZATION': 'JWT ' + jwt_token + } + + + def assert_all_end_points(self, expected_status_code): endpoints = [ ('list_course_role_members', {'rolename': 'staff'}), ('register_and_enroll_students', {}), @@ -4725,40 +4738,37 @@ def assert_all_end_points(self, expected_status_code, headers): 'unique_student_identifier': self.other_user.email } ), - ('list_entrance_exam_instructor_tasks', {'unique_student_identifier': self.other_user.email}) + ('list_entrance_exam_instructor_tasks', {'unique_student_identifier': self.other_user.email}), + ('list_email_content', {}), + ('show_student_extensions', {'student': self.other_user.email}) ] for endpoint, body in endpoints: url = reverse(endpoint, kwargs={'course_id': str(self.course.id)}) response = self.client.post( url, data=body, - **headers + **self.headers ) # JWT authentication works but it has no permissions. assert response.status_code == expected_status_code - def test_end_points_with_oauth(self): + def test_end_points_with_oauth_without_permissions(self): + """ + Verify the endpoint using JWT authentication. + """ + self.client.logout() + self.assert_all_end_points(403) + + def test_end_points_with_oauth_with_permissions(self): """ Verify the endpoint using JWT authentication. """ self.client.logout() - dot_application = ApplicationFactory(user=self.other_user, authorization_grant_type='password') - access_token = AccessTokenFactory(user=self.other_user, application=dot_application) - oauth_adapter = DOTAdapter() - token_dict = { - 'access_token': access_token, - 'scope': 'email profile', - } - jwt_token = jwt_api.create_jwt_from_token(token_dict, oauth_adapter, use_asymmetric_key=True) - headers = { - 'HTTP_AUTHORIZATION': 'JWT ' + jwt_token - } - self.assert_all_end_points(403, headers) CourseAccessRoleFactory( course_id=self.course.id, user=self.other_user, role="instructor", org=self.course.id.org ) - self.assert_all_end_points(200, headers) + self.assert_all_end_points(200) From 86257a5b7366b274875ae43a8fcc672150d8489e Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Mon, 2 Sep 2024 19:52:49 +0500 Subject: [PATCH 04/13] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/tests/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index 54d238c16ff8..679642a4a93f 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -4768,7 +4768,7 @@ def test_end_points_with_oauth_with_permissions(self): CourseAccessRoleFactory( course_id=self.course.id, user=self.other_user, - role="instructor", + role="staff", org=self.course.id.org ) self.assert_all_end_points(200) From effcc39c0bcbf2eb7d8fa731f49bc9f0d4315b64 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 4 Sep 2024 11:47:19 +0500 Subject: [PATCH 05/13] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/tests/test_api.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index 679642a4a93f..3ae20df735d9 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -4689,6 +4689,7 @@ def test_get_certificate_for_user_no_certificate(self): UserFactory ) + class TestOauthInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTestCase): """ Test endpoints whereby instructors can change permissions @@ -4700,6 +4701,7 @@ class TestOauthInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollm Actually, modify_access does not have a very meaningful response yet, so only the status code is tested. """ + @classmethod def setUpClass(cls): super().setUpClass() @@ -4712,9 +4714,6 @@ def setUp(self): self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password=self.TEST_PASSWORD) - - self.other_instructor = InstructorFactory(course_key=self.course.id) - self.other_staff = StaffFactory(course_key=self.course.id) self.other_user = UserFactory() dot_application = ApplicationFactory(user=self.other_user, authorization_grant_type='password') @@ -4729,15 +4728,14 @@ def setUp(self): 'HTTP_AUTHORIZATION': 'JWT ' + jwt_token } - def assert_all_end_points(self, expected_status_code): endpoints = [ ('list_course_role_members', {'rolename': 'staff'}), ('register_and_enroll_students', {}), - ('get_student_progress_url', { 'course_id': str(self.course.id), - 'unique_student_identifier': self.other_user.email - } - ), + ('get_student_progress_url', {'course_id': str(self.course.id), + 'unique_student_identifier': self.other_user.email + } + ), ('list_entrance_exam_instructor_tasks', {'unique_student_identifier': self.other_user.email}), ('list_email_content', {}), ('show_student_extensions', {'student': self.other_user.email}) From 7751d00bc17a703e15c1d70b3f84f1dac3d9def7 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 4 Sep 2024 12:15:01 +0500 Subject: [PATCH 06/13] feat: upgrading simple api to drf compatible. --- lms/djangoapps/instructor/tests/test_api.py | 39 +++++++++++---------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index 3ae20df735d9..fae85041781c 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -60,12 +60,15 @@ CourseFinanceAdminRole, CourseInstructorRole, ) -from common.djangoapps.student.tests.factories import BetaTesterFactory -from common.djangoapps.student.tests.factories import CourseEnrollmentFactory -from common.djangoapps.student.tests.factories import GlobalStaffFactory -from common.djangoapps.student.tests.factories import InstructorFactory -from common.djangoapps.student.tests.factories import StaffFactory -from common.djangoapps.student.tests.factories import UserFactory +from common.djangoapps.student.tests.factories import ( + BetaTesterFactory, + CourseAccessRoleFactory, + CourseEnrollmentFactory, + GlobalStaffFactory, + InstructorFactory, + StaffFactory, + UserFactory +) from lms.djangoapps.bulk_email.models import BulkEmailFlag, CourseEmail, CourseEmailTemplate from lms.djangoapps.certificates.data import CertificateStatuses from lms.djangoapps.certificates.tests.factories import ( @@ -94,12 +97,23 @@ from openedx.core.djangoapps.course_groups.cohorts import set_course_cohorted from openedx.core.djangoapps.django_comment_common.models import FORUM_ROLE_COMMUNITY_TA from openedx.core.djangoapps.django_comment_common.utils import seed_permissions_roles +from openedx.core.djangoapps.oauth_dispatch import jwt as jwt_api +from openedx.core.djangoapps.oauth_dispatch.adapters import DOTAdapter +from openedx.core.djangoapps.oauth_dispatch.tests.factories import AccessTokenFactory, ApplicationFactory from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin from openedx.core.djangoapps.user_api.preferences.api import delete_user_preference from openedx.core.lib.teams_config import TeamsConfig from openedx.core.lib.xblock_utils import grade_histogram from openedx.features.course_experience import RELATIVE_DATES_FLAG +from xmodule.fields import Date +from xmodule.modulestore import ModuleStoreEnum +from xmodule.modulestore.tests.django_utils import ( + TEST_DATA_SPLIT_MODULESTORE, + ModuleStoreTestCase, + SharedModuleStoreTestCase +) +from xmodule.modulestore.tests.factories import BlockFactory, CourseFactory from .test_tools import msk_from_problem_urlname @@ -4676,19 +4690,6 @@ def test_get_certificate_for_user_no_certificate(self): "verify student username/email and the selected course are correct and try again." ) -from openedx.core.djangoapps.oauth_dispatch import jwt as jwt_api -from openedx.core.djangoapps.oauth_dispatch.adapters import DOTAdapter -from openedx.core.djangoapps.oauth_dispatch.tests.factories import AccessTokenFactory, ApplicationFactory -from common.djangoapps.student.tests.factories import ( - BetaTesterFactory, - CourseAccessRoleFactory, - CourseEnrollmentFactory, - GlobalStaffFactory, - InstructorFactory, - StaffFactory, - UserFactory -) - class TestOauthInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTestCase): """ From c32d1506974df94db554d6639fd9e59d04dff00b Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 4 Sep 2024 14:57:53 +0500 Subject: [PATCH 07/13] test: Adding oauth tests for DRF apis. --- lms/djangoapps/instructor/tests/test_api.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index fae85041781c..b3a21dcad7cc 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -106,14 +106,6 @@ from openedx.core.lib.teams_config import TeamsConfig from openedx.core.lib.xblock_utils import grade_histogram from openedx.features.course_experience import RELATIVE_DATES_FLAG -from xmodule.fields import Date -from xmodule.modulestore import ModuleStoreEnum -from xmodule.modulestore.tests.django_utils import ( - TEST_DATA_SPLIT_MODULESTORE, - ModuleStoreTestCase, - SharedModuleStoreTestCase -) -from xmodule.modulestore.tests.factories import BlockFactory, CourseFactory from .test_tools import msk_from_problem_urlname From f11eb1abadbd30919e8e0f78039b8b49beb2391f Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Fri, 6 Sep 2024 19:59:48 +0500 Subject: [PATCH 08/13] test: Adding oauth tests for DRF apis. --- lms/djangoapps/instructor/tests/test_api.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index b3a21dcad7cc..2a3e43db3344 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -4685,14 +4685,7 @@ def test_get_certificate_for_user_no_certificate(self): class TestOauthInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTestCase): """ - Test endpoints whereby instructors can change permissions - of other users. - - This test does NOT test whether the actions had an effect on the - database, that is the job of test_access. - This tests the response and action switch. - Actually, modify_access does not have a very meaningful - response yet, so only the status code is tested. + Test endpoints using Oauth2 authentication. """ @classmethod @@ -4704,7 +4697,6 @@ def setUpClass(cls): def setUp(self): super().setUp() - self.instructor = InstructorFactory(course_key=self.course.id) self.client.login(username=self.instructor.username, password=self.TEST_PASSWORD) self.other_user = UserFactory() @@ -4722,6 +4714,9 @@ def setUp(self): } def assert_all_end_points(self, expected_status_code): + """ + Util method for verifying different end-points. + """ endpoints = [ ('list_course_role_members', {'rolename': 'staff'}), ('register_and_enroll_students', {}), From 981dcc4d0c8c5fd5135f99d5ce0220e39f3d5c06 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Mon, 9 Sep 2024 17:46:10 +0500 Subject: [PATCH 09/13] test: Adding oauth tests for DRF apis. --- lms/djangoapps/instructor/tests/test_api.py | 84 ++++++++++++--------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index 2a3e43db3344..d671ca3857cd 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -47,6 +47,7 @@ UNENROLLED_TO_ALLOWEDTOENROLL, UNENROLLED_TO_ENROLLED, UNENROLLED_TO_UNENROLLED, + CourseAccessRole, CourseEnrollment, CourseEnrollmentAllowed, ManualEnrollmentAudit, @@ -62,7 +63,6 @@ ) from common.djangoapps.student.tests.factories import ( BetaTesterFactory, - CourseAccessRoleFactory, CourseEnrollmentFactory, GlobalStaffFactory, InstructorFactory, @@ -91,7 +91,7 @@ generate_already_running_error_message ) from lms.djangoapps.instructor_task.data import InstructorTaskTypes -from lms.djangoapps.instructor_task.models import InstructorTask, InstructorTaskSchedule +from lms.djangoapps.instructor_task.models import InstructorTask, InstructorTaskSchedule, logger from lms.djangoapps.program_enrollments.tests.factories import ProgramEnrollmentFactory from openedx.core.djangoapps.course_date_signals.handlers import extract_dates from openedx.core.djangoapps.course_groups.cohorts import set_course_cohorted @@ -4683,6 +4683,7 @@ def test_get_certificate_for_user_no_certificate(self): ) +@patch.dict(settings.FEATURES, {'ALLOW_AUTOMATED_SIGNUPS': True}) class TestOauthInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTestCase): """ Test endpoints using Oauth2 authentication. @@ -4697,10 +4698,8 @@ def setUpClass(cls): def setUp(self): super().setUp() - self.instructor = InstructorFactory(course_key=self.course.id) - self.client.login(username=self.instructor.username, password=self.TEST_PASSWORD) - self.other_user = UserFactory() + self.other_user = UserFactory() dot_application = ApplicationFactory(user=self.other_user, authorization_grant_type='password') access_token = AccessTokenFactory(user=self.other_user, application=dot_application) oauth_adapter = DOTAdapter() @@ -4709,52 +4708,65 @@ def setUp(self): 'scope': 'email profile', } jwt_token = jwt_api.create_jwt_from_token(token_dict, oauth_adapter, use_asymmetric_key=True) + self.headers = { 'HTTP_AUTHORIZATION': 'JWT ' + jwt_token } - def assert_all_end_points(self, expected_status_code): - """ - Util method for verifying different end-points. - """ - endpoints = [ - ('list_course_role_members', {'rolename': 'staff'}), - ('register_and_enroll_students', {}), + # endpoints contains all urls with body and role. + self.endpoints = [ + ('list_course_role_members', {'rolename': 'staff'}, 'instructor'), + ('register_and_enroll_students', {}, 'staff'), ('get_student_progress_url', {'course_id': str(self.course.id), 'unique_student_identifier': self.other_user.email - } + }, 'staff' ), - ('list_entrance_exam_instructor_tasks', {'unique_student_identifier': self.other_user.email}), - ('list_email_content', {}), - ('show_student_extensions', {'student': self.other_user.email}) + ('list_entrance_exam_instructor_tasks', {'unique_student_identifier': self.other_user.email}, 'staff'), + ('list_email_content', {}, 'staff'), + ('show_student_extensions', {'student': self.other_user.email}, 'staff'), + ('list_email_content', {}, 'staff'), + ('list_report_downloads', { + "send-to": ["myself"], + "subject": "This is subject", + "message": "message" + }, 'data_researcher') ] - for endpoint, body in endpoints: - url = reverse(endpoint, kwargs={'course_id': str(self.course.id)}) - response = self.client.post( - url, - data=body, - **self.headers + + def assert_all_end_points(self, endpoint, body, role, add_role): + """ + Util method for verifying different end-points. + """ + if add_role: + role, _ = CourseAccessRole.objects.get_or_create( + course_id=self.course.id, + user=self.other_user, + role=role, + org=self.course.id.org ) - # JWT authentication works but it has no permissions. - assert response.status_code == expected_status_code + url = reverse(endpoint, kwargs={'course_id': str(self.course.id)}) + response = self.client.post( + url, + data=body, + **self.headers + ) + return response def test_end_points_with_oauth_without_permissions(self): """ - Verify the endpoint using JWT authentication. + Verify the endpoint using JWT authentication. But has not permissions. """ - self.client.logout() - self.assert_all_end_points(403) + for endpoint, body, role in self.endpoints: + with self.subTest(endpoint=endpoint, role=role, body=body): + response = self.assert_all_end_points(endpoint, body, role, False) + # JWT authentication works but it has no permissions. + assert response.status_code == 403, f"Failed for endpoint: {endpoint}" def test_end_points_with_oauth_with_permissions(self): """ - Verify the endpoint using JWT authentication. + Verify the endpoint using JWT authentication with permissions. """ - self.client.logout() - CourseAccessRoleFactory( - course_id=self.course.id, - user=self.other_user, - role="staff", - org=self.course.id.org - ) - self.assert_all_end_points(200) + for endpoint, body, role in self.endpoints: + with self.subTest(endpoint=endpoint, role=role, body=body): + response = self.assert_all_end_points(endpoint, body, role, True) + assert response.status_code == 200, f"Failed for endpoint: {endpoint}" From ee61c4f654eebcc7249945b8a327e87fe8efa631 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Mon, 9 Sep 2024 18:10:17 +0500 Subject: [PATCH 10/13] test: Adding oauth tests for DRF apis. --- lms/djangoapps/instructor/tests/test_api.py | 33 ++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index d671ca3857cd..516225d514af 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -4732,7 +4732,15 @@ def setUp(self): }, 'data_researcher') ] - def assert_all_end_points(self, endpoint, body, role, add_role): + self.fake_jwt = ('wyJUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJjaGFuZ2UtbWUiLCJleHAiOjE3MjU4OTA2NzIsImdyY' + 'W50X3R5cGUiOiJwYXNzd29yZCIsImlhdCI6MTcyNTg4NzA3MiwiaXNzIjoiaHR0cDovLzEyNy4wLjAuMTo4MDAwL29h' + 'XNlcl9pZCI6MX0' + '.ec8neWp1YAuF40ye4oeK40obaapUvjfNPUQCycrsajwvcu58KcuLc96sf0JKmMMMn7DH9N98hg8W38iwbhKif1kLsCKr' + 'tStl1u2XGvFkyMov8TtespbHit5LYRZpJwrhC1h50ru2buYj3isWrAElGPIDyAj0FAvSJnvJhWSMDtIwB2gxZI1DqOm' + 'M6mzT7JbOU4QH2PNZrb2EZ11F6k9I-HrHnLQymr4s0vyjMlcBWllW3y19futNCgsFFRMXI4Z9zIbspsy5bq_Skub' + 'dBpnl0P9x8vUJCAbFnJABAVPtF7F7nNsROQMKsZtQxaUUwdcYZi5qKL2GcgGfO0eTL4IbJA') + + def assert_all_end_points(self, endpoint, body, role, add_role, use_jwt=True): """ Util method for verifying different end-points. """ @@ -4744,21 +4752,38 @@ def assert_all_end_points(self, endpoint, body, role, add_role): org=self.course.id.org ) + if use_jwt: + headers = self.headers + else: + headers = { + 'HTTP_AUTHORIZATION': 'JWT ' + self.fake_jwt # this is fake jwt. + } + url = reverse(endpoint, kwargs={'course_id': str(self.course.id)}) response = self.client.post( url, data=body, - **self.headers + **headers ) return response + def test_end_points_with_oauth_without_jwt(self): + """ + Verify the endpoint using invalid JWT returns 401. + """ + for endpoint, body, role in self.endpoints: + with self.subTest(endpoint=endpoint, role=role, body=body): + response = self.assert_all_end_points(endpoint, body, role, False, False) + # JWT authentication works but it has no permissions. + assert response.status_code == 401, f"Failed for endpoint: {endpoint}" + def test_end_points_with_oauth_without_permissions(self): """ Verify the endpoint using JWT authentication. But has not permissions. """ for endpoint, body, role in self.endpoints: with self.subTest(endpoint=endpoint, role=role, body=body): - response = self.assert_all_end_points(endpoint, body, role, False) + response = self.assert_all_end_points(endpoint, body, role, False, True) # JWT authentication works but it has no permissions. assert response.status_code == 403, f"Failed for endpoint: {endpoint}" @@ -4768,5 +4793,5 @@ def test_end_points_with_oauth_with_permissions(self): """ for endpoint, body, role in self.endpoints: with self.subTest(endpoint=endpoint, role=role, body=body): - response = self.assert_all_end_points(endpoint, body, role, True) + response = self.assert_all_end_points(endpoint, body, role, True, True) assert response.status_code == 200, f"Failed for endpoint: {endpoint}" From b73fb3e4b24ee33156dda719a438c77a4cc757e2 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Mon, 9 Sep 2024 18:12:57 +0500 Subject: [PATCH 11/13] test: Adding oauth tests for DRF apis. --- lms/djangoapps/instructor/tests/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index 516225d514af..eb1e098a7581 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -4779,7 +4779,7 @@ def test_end_points_with_oauth_without_jwt(self): def test_end_points_with_oauth_without_permissions(self): """ - Verify the endpoint using JWT authentication. But has not permissions. + Verify the endpoint using JWT authentication. But has no permissions. """ for endpoint, body, role in self.endpoints: with self.subTest(endpoint=endpoint, role=role, body=body): From 61bc5a8e05942587840d64bee14bafa46e3d23fc Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Mon, 9 Sep 2024 18:27:37 +0500 Subject: [PATCH 12/13] test: Adding oauth tests for DRF apis. --- lms/djangoapps/instructor/tests/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index eb1e098a7581..aca3f548da69 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -91,7 +91,7 @@ generate_already_running_error_message ) from lms.djangoapps.instructor_task.data import InstructorTaskTypes -from lms.djangoapps.instructor_task.models import InstructorTask, InstructorTaskSchedule, logger +from lms.djangoapps.instructor_task.models import InstructorTask, InstructorTaskSchedule from lms.djangoapps.program_enrollments.tests.factories import ProgramEnrollmentFactory from openedx.core.djangoapps.course_date_signals.handlers import extract_dates from openedx.core.djangoapps.course_groups.cohorts import set_course_cohorted From 9f60b3a7794a0fd950587a430a3b1f0d0bbfc6f7 Mon Sep 17 00:00:00 2001 From: awais qureshi Date: Wed, 11 Sep 2024 17:19:55 +0500 Subject: [PATCH 13/13] feat!: upgrading simple api with DRF. --- lms/djangoapps/instructor/tests/test_api.py | 25 ++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index aca3f548da69..0042b42c7b46 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -4767,31 +4767,30 @@ def assert_all_end_points(self, endpoint, body, role, add_role, use_jwt=True): ) return response - def test_end_points_with_oauth_without_jwt(self): + def run_endpoint_tests(self, expected_status, add_role, use_jwt): """ - Verify the endpoint using invalid JWT returns 401. + Util method for running different end-points. """ for endpoint, body, role in self.endpoints: with self.subTest(endpoint=endpoint, role=role, body=body): - response = self.assert_all_end_points(endpoint, body, role, False, False) + response = self.assert_all_end_points(endpoint, body, role, add_role, use_jwt) # JWT authentication works but it has no permissions. - assert response.status_code == 401, f"Failed for endpoint: {endpoint}" + assert response.status_code == expected_status, f"Failed for endpoint: {endpoint}" + + def test_end_points_with_oauth_without_jwt(self): + """ + Verify the endpoint using invalid JWT returns 401. + """ + self.run_endpoint_tests(expected_status=401, add_role=False, use_jwt=False) def test_end_points_with_oauth_without_permissions(self): """ Verify the endpoint using JWT authentication. But has no permissions. """ - for endpoint, body, role in self.endpoints: - with self.subTest(endpoint=endpoint, role=role, body=body): - response = self.assert_all_end_points(endpoint, body, role, False, True) - # JWT authentication works but it has no permissions. - assert response.status_code == 403, f"Failed for endpoint: {endpoint}" + self.run_endpoint_tests(expected_status=403, add_role=False, use_jwt=True) def test_end_points_with_oauth_with_permissions(self): """ Verify the endpoint using JWT authentication with permissions. """ - for endpoint, body, role in self.endpoints: - with self.subTest(endpoint=endpoint, role=role, body=body): - response = self.assert_all_end_points(endpoint, body, role, True, True) - assert response.status_code == 200, f"Failed for endpoint: {endpoint}" + self.run_endpoint_tests(expected_status=200, add_role=True, use_jwt=True)