From 2e1fec921c9dab00b5a9301ffd5dff25d801de29 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 31 Jul 2019 14:28:35 -0400 Subject: [PATCH 01/13] Add a decorator that checks for course-level permissions --- lms/djangoapps/instructor/views/api.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 8e7163f1ed..71cfa36dd8 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -249,6 +249,28 @@ def require_level(level): return decorator +def require_course_permission(permission): + """ + Decorator with argument that requires a specific permission of the requesting + user. If the requirement is not satisfied, returns an + HttpResponseForbidden (403). + + Assumes that request is in args[0]. + Assumes that course_id is in kwargs['course_id']. + """ + def decorator(func): # pylint: disable=missing-docstring + def wrapped(*args, **kwargs): + request = args[0] + course = get_course_by_id(CourseKey.from_string(kwargs['course_id'])) + + if request.user.has_perm(permission, course): + return func(*args, **kwargs) + else: + return HttpResponseForbidden() + return wrapped + return decorator + + def require_sales_admin(func): """ Decorator for checking sales administrator access before executing an HTTP endpoint. This decorator From c270273e13213eaf8d41d5d0f99efd5be8256aad Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 31 Jul 2019 14:31:35 -0400 Subject: [PATCH 02/13] Convert get_issued_certificates to use requires_course_permission --- lms/djangoapps/instructor/permissions.py | 12 ++++++++++++ lms/djangoapps/instructor/views/api.py | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 lms/djangoapps/instructor/permissions.py diff --git a/lms/djangoapps/instructor/permissions.py b/lms/djangoapps/instructor/permissions.py new file mode 100644 index 0000000000..85d2153f01 --- /dev/null +++ b/lms/djangoapps/instructor/permissions.py @@ -0,0 +1,12 @@ +""" +Permissions for the instructor dashboard and associated actions +""" + +from bridgekeeper import perms +from courseware.rules import HasAccessRule + + +VIEW_ISSUED_CERTIFICATES = 'instructor.view_issued_certificates' + + +perms[VIEW_ISSUED_CERTIFICATES] = HasAccessRule('staff') diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 71cfa36dd8..216e4d320e 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -148,6 +148,8 @@ from .tools import ( strip_if_string ) +from ..permissions import VIEW_ISSUED_CERTIFICATES + log = logging.getLogger(__name__) TASK_SUBMISSION_OK = 'created' @@ -1232,7 +1234,7 @@ def re_validate_invoice(obj_invoice): @transaction.non_atomic_requests @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) -@require_level('staff') +@require_course_permission(VIEW_ISSUED_CERTIFICATES) def get_issued_certificates(request, course_id): """ Responds with JSON if CSV is not required. contains a list of issued certificates. From a2ea80dbcad943788775696237d9b1997c0d56be Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 31 Jul 2019 14:42:55 -0400 Subject: [PATCH 03/13] Convert mark_student_can_skip_entrance_exam to require_course_permission --- lms/djangoapps/instructor/permissions.py | 3 ++- lms/djangoapps/instructor/views/api.py | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lms/djangoapps/instructor/permissions.py b/lms/djangoapps/instructor/permissions.py index 85d2153f01..690dca6817 100644 --- a/lms/djangoapps/instructor/permissions.py +++ b/lms/djangoapps/instructor/permissions.py @@ -5,8 +5,9 @@ Permissions for the instructor dashboard and associated actions from bridgekeeper import perms from courseware.rules import HasAccessRule - +ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM = 'instructor.allow_student_to_bypass_entrance_exam' VIEW_ISSUED_CERTIFICATES = 'instructor.view_issued_certificates' +perms[ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM] = HasAccessRule('staff') perms[VIEW_ISSUED_CERTIFICATES] = HasAccessRule('staff') diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 216e4d320e..d430ebfce7 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -148,7 +148,11 @@ from .tools import ( strip_if_string ) -from ..permissions import VIEW_ISSUED_CERTIFICATES +from ..permissions import ( + ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM, + VIEW_ISSUED_CERTIFICATES, +) + log = logging.getLogger(__name__) @@ -3030,7 +3034,7 @@ def enable_certificate_generation(request, course_id=None): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) -@require_level('staff') +@require_course_permission(ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM) @require_POST def mark_student_can_skip_entrance_exam(request, course_id): """ From 04e550132441621d6b2277adf23ca5449a856aa2 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 31 Jul 2019 14:46:53 -0400 Subject: [PATCH 04/13] Convert add_users_to_cohorts to require_course_permission --- lms/djangoapps/instructor/permissions.py | 2 ++ lms/djangoapps/instructor/views/api.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/instructor/permissions.py b/lms/djangoapps/instructor/permissions.py index 690dca6817..a3c9e779d5 100644 --- a/lms/djangoapps/instructor/permissions.py +++ b/lms/djangoapps/instructor/permissions.py @@ -6,8 +6,10 @@ from bridgekeeper import perms from courseware.rules import HasAccessRule ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM = 'instructor.allow_student_to_bypass_entrance_exam' +ASSIGN_TO_COHORTS = 'instructor.assign_to_cohorts' VIEW_ISSUED_CERTIFICATES = 'instructor.view_issued_certificates' perms[ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM] = HasAccessRule('staff') +perms[ASSIGN_TO_COHORTS] = HasAccessRule('staff') perms[VIEW_ISSUED_CERTIFICATES] = HasAccessRule('staff') diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index d430ebfce7..b23c54f7e7 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -150,6 +150,7 @@ from .tools import ( from ..permissions import ( ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM, + ASSIGN_TO_COHORTS, VIEW_ISSUED_CERTIFICATES, ) @@ -1416,7 +1417,7 @@ def _cohorts_csv_validator(file_storage, file_to_validate): @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_POST -@require_level('staff') +@require_course_permission(ASSIGN_TO_COHORTS) @common_exceptions_400 def add_users_to_cohorts(request, course_id): """ From 7d55483fbeafe55c9d4ded9cbce12dc8ea44950f Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 31 Jul 2019 14:49:21 -0400 Subject: [PATCH 05/13] Convert modify_access to require_course_permission --- lms/djangoapps/instructor/permissions.py | 2 ++ lms/djangoapps/instructor/views/api.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/instructor/permissions.py b/lms/djangoapps/instructor/permissions.py index a3c9e779d5..11d1cb7554 100644 --- a/lms/djangoapps/instructor/permissions.py +++ b/lms/djangoapps/instructor/permissions.py @@ -7,9 +7,11 @@ from courseware.rules import HasAccessRule ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM = 'instructor.allow_student_to_bypass_entrance_exam' ASSIGN_TO_COHORTS = 'instructor.assign_to_cohorts' +EDIT_COURSE_ACCESS = 'instructor.edit_course_access' VIEW_ISSUED_CERTIFICATES = 'instructor.view_issued_certificates' perms[ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM] = HasAccessRule('staff') perms[ASSIGN_TO_COHORTS] = HasAccessRule('staff') +perms[EDIT_COURSE_ACCESS] = HasAccessRule('instructor') perms[VIEW_ISSUED_CERTIFICATES] = HasAccessRule('staff') diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index b23c54f7e7..c54f369536 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -151,6 +151,7 @@ from .tools import ( from ..permissions import ( ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM, ASSIGN_TO_COHORTS, + EDIT_COURSE_ACCESS, VIEW_ISSUED_CERTIFICATES, ) @@ -906,7 +907,7 @@ def bulk_beta_modify_access(request, course_id): @require_POST @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) -@require_level('instructor') +@require_course_permission(EDIT_COURSE_ACCESS) @require_post_params( unique_student_identifier="email or username of user to change access", rolename="'instructor', 'staff', 'beta', or 'ccx_coach'", From 11677d052cba4885463e0b99eab6af08d0403f83 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Fri, 2 Aug 2019 14:47:42 -0400 Subject: [PATCH 06/13] Switch update_forum_role_membership over to using a StaffAccessRule with query checking --- lms/djangoapps/instructor/permissions.py | 2 ++ lms/djangoapps/instructor/views/api.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/instructor/permissions.py b/lms/djangoapps/instructor/permissions.py index 11d1cb7554..3e96ca403e 100644 --- a/lms/djangoapps/instructor/permissions.py +++ b/lms/djangoapps/instructor/permissions.py @@ -8,10 +8,12 @@ from courseware.rules import HasAccessRule ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM = 'instructor.allow_student_to_bypass_entrance_exam' ASSIGN_TO_COHORTS = 'instructor.assign_to_cohorts' EDIT_COURSE_ACCESS = 'instructor.edit_course_access' +EDIT_FORUM_ROLES = 'instructor.edit_forum_roles' VIEW_ISSUED_CERTIFICATES = 'instructor.view_issued_certificates' perms[ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM] = HasAccessRule('staff') perms[ASSIGN_TO_COHORTS] = HasAccessRule('staff') perms[EDIT_COURSE_ACCESS] = HasAccessRule('instructor') +perms[EDIT_FORUM_ROLES] = HasAccessRule('staff') perms[VIEW_ISSUED_CERTIFICATES] = HasAccessRule('staff') diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index c54f369536..f9802304d1 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -152,6 +152,7 @@ from ..permissions import ( ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM, ASSIGN_TO_COHORTS, EDIT_COURSE_ACCESS, + EDIT_FORUM_ROLES, VIEW_ISSUED_CERTIFICATES, ) @@ -2788,7 +2789,7 @@ def send_email(request, course_id): @require_POST @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) -@require_level('staff') +@require_course_permission(EDIT_FORUM_ROLES) @require_post_params( unique_student_identifier="email or username of user to change access", rolename="the forum role", From c3d86c9b3c07bdcc8716634c30b7aedf067a43ea Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Fri, 2 Aug 2019 14:51:17 -0400 Subject: [PATCH 07/13] Switch sale_validation over to using a StaffAccessRule with query checking --- lms/djangoapps/instructor/permissions.py | 2 ++ lms/djangoapps/instructor/views/api.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/instructor/permissions.py b/lms/djangoapps/instructor/permissions.py index 3e96ca403e..cb6e979b97 100644 --- a/lms/djangoapps/instructor/permissions.py +++ b/lms/djangoapps/instructor/permissions.py @@ -9,6 +9,7 @@ ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM = 'instructor.allow_student_to_bypass_entr ASSIGN_TO_COHORTS = 'instructor.assign_to_cohorts' EDIT_COURSE_ACCESS = 'instructor.edit_course_access' EDIT_FORUM_ROLES = 'instructor.edit_forum_roles' +EDIT_INVOICE_VALIDATION = 'instructor.edit_invoice_validation' VIEW_ISSUED_CERTIFICATES = 'instructor.view_issued_certificates' @@ -16,4 +17,5 @@ perms[ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM] = HasAccessRule('staff') perms[ASSIGN_TO_COHORTS] = HasAccessRule('staff') perms[EDIT_COURSE_ACCESS] = HasAccessRule('instructor') perms[EDIT_FORUM_ROLES] = HasAccessRule('staff') +perms[EDIT_INVOICE_VALIDATION] = HasAccessRule('staff') perms[VIEW_ISSUED_CERTIFICATES] = HasAccessRule('staff') diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index f9802304d1..d543e3eaa0 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -153,6 +153,7 @@ from ..permissions import ( ASSIGN_TO_COHORTS, EDIT_COURSE_ACCESS, EDIT_FORUM_ROLES, + EDIT_INVOICE_VALIDATION, VIEW_ISSUED_CERTIFICATES, ) @@ -1174,7 +1175,7 @@ def get_sale_order_records(request, course_id): # pylint: disable=unused-argume return instructor_analytics.csvs.create_csv_response("e-commerce_sale_order_records.csv", csv_columns, datarows) -@require_level('staff') +@require_course_permission(EDIT_INVOICE_VALIDATION) @require_POST def sale_validation(request, course_id): """ From 1aed6c882bcdfe9fb1025e828f5d2df69281b181 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Fri, 2 Aug 2019 14:53:45 -0400 Subject: [PATCH 08/13] Switch change_due_date over to using a StaffAccessRule with query checking --- lms/djangoapps/instructor/permissions.py | 2 ++ lms/djangoapps/instructor/views/api.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/instructor/permissions.py b/lms/djangoapps/instructor/permissions.py index cb6e979b97..897a4d7b84 100644 --- a/lms/djangoapps/instructor/permissions.py +++ b/lms/djangoapps/instructor/permissions.py @@ -10,6 +10,7 @@ ASSIGN_TO_COHORTS = 'instructor.assign_to_cohorts' EDIT_COURSE_ACCESS = 'instructor.edit_course_access' EDIT_FORUM_ROLES = 'instructor.edit_forum_roles' EDIT_INVOICE_VALIDATION = 'instructor.edit_invoice_validation' +GIVE_STUDENT_EXTENSION = 'instructor.give_student_extension' VIEW_ISSUED_CERTIFICATES = 'instructor.view_issued_certificates' @@ -18,4 +19,5 @@ perms[ASSIGN_TO_COHORTS] = HasAccessRule('staff') perms[EDIT_COURSE_ACCESS] = HasAccessRule('instructor') perms[EDIT_FORUM_ROLES] = HasAccessRule('staff') perms[EDIT_INVOICE_VALIDATION] = HasAccessRule('staff') +perms[GIVE_STUDENT_EXTENSION] = HasAccessRule('staff') perms[VIEW_ISSUED_CERTIFICATES] = HasAccessRule('staff') diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index d543e3eaa0..b8c4c0bc35 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -154,6 +154,7 @@ from ..permissions import ( EDIT_COURSE_ACCESS, EDIT_FORUM_ROLES, EDIT_INVOICE_VALIDATION, + GIVE_STUDENT_EXTENSION, VIEW_ISSUED_CERTIFICATES, ) @@ -2883,7 +2884,7 @@ def _display_unit(unit): @require_POST @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) -@require_level('staff') +@require_course_permission(GIVE_STUDENT_EXTENSION) @require_post_params('student', 'url', 'due_datetime') def change_due_date(request, course_id): """ From 32f154d3ead7aabaefb5c44ee8cd1943b29f9809 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Fri, 2 Aug 2019 14:54:25 -0400 Subject: [PATCH 09/13] Switch reset_due_date over to using a StaffAccessRule with query checking --- lms/djangoapps/instructor/views/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index b8c4c0bc35..3123087eab 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -2908,7 +2908,7 @@ def change_due_date(request, course_id): @require_POST @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) -@require_level('staff') +@require_course_permission(GIVE_STUDENT_EXTENSION) @require_post_params('student', 'url') def reset_due_date(request, course_id): """ From b85527535397212826807af1bf03b9cc0e7bb04d Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Fri, 2 Aug 2019 14:58:42 -0400 Subject: [PATCH 10/13] Switch enable_certificate_generation over to using a StaffAccessRule with query checking --- lms/djangoapps/instructor/permissions.py | 3 +++ lms/djangoapps/instructor/views/api.py | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/instructor/permissions.py b/lms/djangoapps/instructor/permissions.py index 897a4d7b84..a79ec9b0a9 100644 --- a/lms/djangoapps/instructor/permissions.py +++ b/lms/djangoapps/instructor/permissions.py @@ -3,6 +3,7 @@ Permissions for the instructor dashboard and associated actions """ from bridgekeeper import perms +from bridgekeeper.rules import is_staff from courseware.rules import HasAccessRule ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM = 'instructor.allow_student_to_bypass_entrance_exam' @@ -10,6 +11,7 @@ ASSIGN_TO_COHORTS = 'instructor.assign_to_cohorts' EDIT_COURSE_ACCESS = 'instructor.edit_course_access' EDIT_FORUM_ROLES = 'instructor.edit_forum_roles' EDIT_INVOICE_VALIDATION = 'instructor.edit_invoice_validation' +ENABLE_CERTIFICATE_GENERATION = 'instructor.enable_certificate_generation' GIVE_STUDENT_EXTENSION = 'instructor.give_student_extension' VIEW_ISSUED_CERTIFICATES = 'instructor.view_issued_certificates' @@ -19,5 +21,6 @@ perms[ASSIGN_TO_COHORTS] = HasAccessRule('staff') perms[EDIT_COURSE_ACCESS] = HasAccessRule('instructor') perms[EDIT_FORUM_ROLES] = HasAccessRule('staff') perms[EDIT_INVOICE_VALIDATION] = HasAccessRule('staff') +perms[ENABLE_CERTIFICATE_GENERATION] = is_staff perms[GIVE_STUDENT_EXTENSION] = HasAccessRule('staff') perms[VIEW_ISSUED_CERTIFICATES] = HasAccessRule('staff') diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 3123087eab..56ad3e407b 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -154,6 +154,7 @@ from ..permissions import ( EDIT_COURSE_ACCESS, EDIT_FORUM_ROLES, EDIT_INVOICE_VALIDATION, + ENABLE_CERTIFICATE_GENERATION, GIVE_STUDENT_EXTENSION, VIEW_ISSUED_CERTIFICATES, ) @@ -3019,7 +3020,7 @@ def generate_example_certificates(request, course_id=None): # pylint: disable=u return redirect(_instructor_dash_url(course_key, section='certificates')) -@require_global_staff +@require_course_permission(ENABLE_CERTIFICATE_GENERATION) @require_POST def enable_certificate_generation(request, course_id=None): """Enable/disable self-generated certificates for a course. From 23a91ba0f2cdc8c5f0fe49f5e88665a6d7eb0029 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Fri, 2 Aug 2019 15:00:23 -0400 Subject: [PATCH 11/13] Switch generate_bulk_certificate_exceptions over to using a StaffAccessRule with query checking --- lms/djangoapps/instructor/permissions.py | 2 ++ lms/djangoapps/instructor/views/api.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/instructor/permissions.py b/lms/djangoapps/instructor/permissions.py index a79ec9b0a9..c581e1e11a 100644 --- a/lms/djangoapps/instructor/permissions.py +++ b/lms/djangoapps/instructor/permissions.py @@ -12,6 +12,7 @@ EDIT_COURSE_ACCESS = 'instructor.edit_course_access' EDIT_FORUM_ROLES = 'instructor.edit_forum_roles' EDIT_INVOICE_VALIDATION = 'instructor.edit_invoice_validation' ENABLE_CERTIFICATE_GENERATION = 'instructor.enable_certificate_generation' +GENERATE_BULK_CERTIFICATE_EXCEPTIONS = 'instructor.generate_bulk_certificate_exceptions' GIVE_STUDENT_EXTENSION = 'instructor.give_student_extension' VIEW_ISSUED_CERTIFICATES = 'instructor.view_issued_certificates' @@ -22,5 +23,6 @@ perms[EDIT_COURSE_ACCESS] = HasAccessRule('instructor') perms[EDIT_FORUM_ROLES] = HasAccessRule('staff') perms[EDIT_INVOICE_VALIDATION] = HasAccessRule('staff') perms[ENABLE_CERTIFICATE_GENERATION] = is_staff +perms[GENERATE_BULK_CERTIFICATE_EXCEPTIONS] = is_staff perms[GIVE_STUDENT_EXTENSION] = HasAccessRule('staff') perms[VIEW_ISSUED_CERTIFICATES] = HasAccessRule('staff') diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 56ad3e407b..443cce5453 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -155,6 +155,7 @@ from ..permissions import ( EDIT_FORUM_ROLES, EDIT_INVOICE_VALIDATION, ENABLE_CERTIFICATE_GENERATION, + GENERATE_BULK_CERTIFICATE_EXCEPTIONS, GIVE_STUDENT_EXTENSION, VIEW_ISSUED_CERTIFICATES, ) @@ -3346,7 +3347,7 @@ def generate_certificate_exceptions(request, course_id, generate_for=None): @cache_control(no_cache=True, no_store=True, must_revalidate=True) -@require_global_staff +@require_course_permission(GENERATE_BULK_CERTIFICATE_EXCEPTIONS) @require_POST def generate_bulk_certificate_exceptions(request, course_id): """ From e0981f3b9609d19a626b40dd7f042164e863014b Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Fri, 2 Aug 2019 15:04:03 -0400 Subject: [PATCH 12/13] Switch generate_certificate_exceptions over to using a StaffAccessRule with query checking --- lms/djangoapps/instructor/permissions.py | 2 ++ lms/djangoapps/instructor/views/api.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/instructor/permissions.py b/lms/djangoapps/instructor/permissions.py index c581e1e11a..d47a4b28f1 100644 --- a/lms/djangoapps/instructor/permissions.py +++ b/lms/djangoapps/instructor/permissions.py @@ -12,6 +12,7 @@ EDIT_COURSE_ACCESS = 'instructor.edit_course_access' EDIT_FORUM_ROLES = 'instructor.edit_forum_roles' EDIT_INVOICE_VALIDATION = 'instructor.edit_invoice_validation' ENABLE_CERTIFICATE_GENERATION = 'instructor.enable_certificate_generation' +GENERATE_CERTIFICATE_EXCEPTIONS = 'instructor.generate_certificate_exceptions' GENERATE_BULK_CERTIFICATE_EXCEPTIONS = 'instructor.generate_bulk_certificate_exceptions' GIVE_STUDENT_EXTENSION = 'instructor.give_student_extension' VIEW_ISSUED_CERTIFICATES = 'instructor.view_issued_certificates' @@ -23,6 +24,7 @@ perms[EDIT_COURSE_ACCESS] = HasAccessRule('instructor') perms[EDIT_FORUM_ROLES] = HasAccessRule('staff') perms[EDIT_INVOICE_VALIDATION] = HasAccessRule('staff') perms[ENABLE_CERTIFICATE_GENERATION] = is_staff +perms[GENERATE_CERTIFICATE_EXCEPTIONS] = is_staff perms[GENERATE_BULK_CERTIFICATE_EXCEPTIONS] = is_staff perms[GIVE_STUDENT_EXTENSION] = HasAccessRule('staff') perms[VIEW_ISSUED_CERTIFICATES] = HasAccessRule('staff') diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 443cce5453..60afef59e7 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -155,6 +155,7 @@ from ..permissions import ( EDIT_FORUM_ROLES, EDIT_INVOICE_VALIDATION, ENABLE_CERTIFICATE_GENERATION, + GENERATE_CERTIFICATE_EXCEPTIONS, GENERATE_BULK_CERTIFICATE_EXCEPTIONS, GIVE_STUDENT_EXTENSION, VIEW_ISSUED_CERTIFICATES, @@ -3305,7 +3306,7 @@ def get_student(username_or_email, course_key): @transaction.non_atomic_requests @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) -@require_global_staff +@require_course_permission(GENERATE_CERTIFICATE_EXCEPTIONS) @require_POST @common_exceptions_400 def generate_certificate_exceptions(request, course_id, generate_for=None): From 0cd8f58bcc0e71cec792a97798936cd1a73de437 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Fri, 2 Aug 2019 15:37:07 -0400 Subject: [PATCH 13/13] Remove all instances of `from instructor import` imports --- lms/djangoapps/bulk_enroll/views.py | 2 +- .../ccx/migrations/0005_change_ccx_coach_to_staff.py | 2 +- lms/djangoapps/class_dashboard/dashboard_data.py | 2 +- lms/djangoapps/discussion/rest_api/views.py | 2 +- lms/djangoapps/instructor_analytics/tests/test_basic.py | 2 +- lms/djangoapps/instructor_analytics/tests/test_csvs.py | 2 +- .../instructor_analytics/tests/test_distributions.py | 2 +- lms/djangoapps/instructor_task/tasks_helper/enrollments.py | 4 ++-- lms/djangoapps/instructor_task/tasks_helper/grades.py | 4 ++-- lms/djangoapps/instructor_task/tasks_helper/misc.py | 4 ++-- lms/djangoapps/instructor_task/tests/test_tasks_helper.py | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lms/djangoapps/bulk_enroll/views.py b/lms/djangoapps/bulk_enroll/views.py index f5a1bef36b..a62d7302f9 100644 --- a/lms/djangoapps/bulk_enroll/views.py +++ b/lms/djangoapps/bulk_enroll/views.py @@ -14,7 +14,7 @@ from rest_framework.response import Response from rest_framework.views import APIView from bulk_enroll.serializers import BulkEnrollmentSerializer -from instructor.views.api import students_update_enrollment +from lms.djangoapps.instructor.views.api import students_update_enrollment from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, get_cohort_by_name from openedx.core.djangoapps.course_groups.models import CourseUserGroup from openedx.core.djangoapps.enrollments.views import EnrollmentUserThrottle diff --git a/lms/djangoapps/ccx/migrations/0005_change_ccx_coach_to_staff.py b/lms/djangoapps/ccx/migrations/0005_change_ccx_coach_to_staff.py index 66191f9145..a42d0f0119 100644 --- a/lms/djangoapps/ccx/migrations/0005_change_ccx_coach_to_staff.py +++ b/lms/djangoapps/ccx/migrations/0005_change_ccx_coach_to_staff.py @@ -10,7 +10,7 @@ from django.db import migrations from django.http import Http404 from courseware.courses import get_course_by_id -from instructor.access import allow_access, revoke_access +from lms.djangoapps.instructor.access import allow_access, revoke_access log = logging.getLogger("edx.ccx") diff --git a/lms/djangoapps/class_dashboard/dashboard_data.py b/lms/djangoapps/class_dashboard/dashboard_data.py index f39642aed7..c4a04d54c4 100644 --- a/lms/djangoapps/class_dashboard/dashboard_data.py +++ b/lms/djangoapps/class_dashboard/dashboard_data.py @@ -11,7 +11,7 @@ from opaque_keys.edx.locator import BlockUsageLocator from six import text_type from courseware import models -from instructor_analytics.csvs import create_csv_response +from lms.djangoapps.instructor_analytics.csvs import create_csv_response from util.json_request import JsonResponse from xmodule.modulestore.django import modulestore from xmodule.modulestore.inheritance import own_metadata diff --git a/lms/djangoapps/discussion/rest_api/views.py b/lms/djangoapps/discussion/rest_api/views.py index af2179e9a3..e534872c0b 100644 --- a/lms/djangoapps/discussion/rest_api/views.py +++ b/lms/djangoapps/discussion/rest_api/views.py @@ -19,7 +19,7 @@ from rest_framework.viewsets import ViewSet from six import text_type from discussion.views import get_divided_discussions -from instructor.access import update_forum_role +from lms.djangoapps.instructor.access import update_forum_role from lms.djangoapps.discussion.django_comment_client.utils import available_division_schemes from lms.djangoapps.discussion.rest_api.api import ( create_comment, diff --git a/lms/djangoapps/instructor_analytics/tests/test_basic.py b/lms/djangoapps/instructor_analytics/tests/test_basic.py index c2118deae7..e9e3bc3268 100644 --- a/lms/djangoapps/instructor_analytics/tests/test_basic.py +++ b/lms/djangoapps/instructor_analytics/tests/test_basic.py @@ -20,7 +20,7 @@ from six.moves import range, zip from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory from courseware.tests.factories import InstructorFactory -from instructor_analytics.basic import ( +from lms.djangoapps.instructor_analytics.basic import ( AVAILABLE_FEATURES, PROFILE_FEATURES, STUDENT_FEATURES, diff --git a/lms/djangoapps/instructor_analytics/tests/test_csvs.py b/lms/djangoapps/instructor_analytics/tests/test_csvs.py index c943619fec..e496f6964c 100644 --- a/lms/djangoapps/instructor_analytics/tests/test_csvs.py +++ b/lms/djangoapps/instructor_analytics/tests/test_csvs.py @@ -6,7 +6,7 @@ import pytest from django.test import TestCase from six.moves import range -from instructor_analytics.csvs import create_csv_response, format_dictlist, format_instances +from lms.djangoapps.instructor_analytics.csvs import create_csv_response, format_dictlist, format_instances class TestAnalyticsCSVS(TestCase): diff --git a/lms/djangoapps/instructor_analytics/tests/test_distributions.py b/lms/djangoapps/instructor_analytics/tests/test_distributions.py index ad3a829642..1ca18523cc 100644 --- a/lms/djangoapps/instructor_analytics/tests/test_distributions.py +++ b/lms/djangoapps/instructor_analytics/tests/test_distributions.py @@ -6,7 +6,7 @@ from django.test import TestCase from opaque_keys.edx.locator import CourseLocator from six.moves import range -from instructor_analytics.distributions import AVAILABLE_PROFILE_FEATURES, profile_distribution +from lms.djangoapps.instructor_analytics.distributions import AVAILABLE_PROFILE_FEATURES, profile_distribution from student.models import CourseEnrollment from student.tests.factories import UserFactory diff --git a/lms/djangoapps/instructor_task/tasks_helper/enrollments.py b/lms/djangoapps/instructor_task/tasks_helper/enrollments.py index 4843cbc3ff..ba745c4caa 100644 --- a/lms/djangoapps/instructor_task/tasks_helper/enrollments.py +++ b/lms/djangoapps/instructor_task/tasks_helper/enrollments.py @@ -14,8 +14,8 @@ from pytz import UTC from courseware.courses import get_course_by_id from edxmako.shortcuts import render_to_string -from instructor_analytics.basic import enrolled_students_features, list_may_enroll -from instructor_analytics.csvs import format_dictlist +from lms.djangoapps.instructor_analytics.basic import enrolled_students_features, list_may_enroll +from lms.djangoapps.instructor_analytics.csvs import format_dictlist from lms.djangoapps.instructor.paidcourse_enrollment_report import PaidCourseEnrollmentReportProvider from lms.djangoapps.instructor_task.models import ReportStore from shoppingcart.models import ( diff --git a/lms/djangoapps/instructor_task/tasks_helper/grades.py b/lms/djangoapps/instructor_task/tasks_helper/grades.py index c97887b065..5ca6f833f2 100644 --- a/lms/djangoapps/instructor_task/tasks_helper/grades.py +++ b/lms/djangoapps/instructor_task/tasks_helper/grades.py @@ -22,8 +22,8 @@ from six.moves import zip, zip_longest from course_blocks.api import get_course_blocks from courseware.courses import get_course_by_id from courseware.user_state_client import DjangoXBlockUserStateClient -from instructor_analytics.basic import list_problem_responses -from instructor_analytics.csvs import format_dictlist +from lms.djangoapps.instructor_analytics.basic import list_problem_responses +from lms.djangoapps.instructor_analytics.csvs import format_dictlist from lms.djangoapps.certificates.models import CertificateWhitelist, GeneratedCertificate, certificate_info_for_user from lms.djangoapps.grades.api import CourseGradeFactory from lms.djangoapps.grades.api import context as grades_context diff --git a/lms/djangoapps/instructor_task/tasks_helper/misc.py b/lms/djangoapps/instructor_task/tasks_helper/misc.py index f505db4944..f836b34c92 100644 --- a/lms/djangoapps/instructor_task/tasks_helper/misc.py +++ b/lms/djangoapps/instructor_task/tasks_helper/misc.py @@ -18,8 +18,8 @@ from django.core.files.storage import DefaultStorage from openassessment.data import OraAggregateData from pytz import UTC -from instructor_analytics.basic import get_proctored_exam_results -from instructor_analytics.csvs import format_dictlist +from lms.djangoapps.instructor_analytics.basic import get_proctored_exam_results +from lms.djangoapps.instructor_analytics.csvs import format_dictlist from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort from openedx.core.djangoapps.course_groups.models import CourseUserGroup from survey.models import SurveyAnswer diff --git a/lms/djangoapps/instructor_task/tests/test_tasks_helper.py b/lms/djangoapps/instructor_task/tests/test_tasks_helper.py index bf4c760f51..1bf308fda9 100644 --- a/lms/djangoapps/instructor_task/tests/test_tasks_helper.py +++ b/lms/djangoapps/instructor_task/tests/test_tasks_helper.py @@ -33,7 +33,7 @@ from capa.tests.response_xml_factory import MultipleChoiceResponseXMLFactory from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory from courseware.tests.factories import InstructorFactory -from instructor_analytics.basic import UNAVAILABLE, list_problem_responses +from lms.djangoapps.instructor_analytics.basic import UNAVAILABLE, list_problem_responses from lms.djangoapps.certificates.models import CertificateStatuses, GeneratedCertificate from lms.djangoapps.certificates.tests.factories import CertificateWhitelistFactory, GeneratedCertificateFactory from lms.djangoapps.grades.models import PersistentCourseGrade