Merge pull request #21269 from cpennington/requires-level-permissions
Add a decorator that checks for course-level permissions
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
30
lms/djangoapps/instructor/permissions.py
Normal file
30
lms/djangoapps/instructor/permissions.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
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'
|
||||
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'
|
||||
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'
|
||||
|
||||
|
||||
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[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')
|
||||
@@ -148,6 +148,20 @@ from .tools import (
|
||||
strip_if_string
|
||||
)
|
||||
|
||||
from ..permissions import (
|
||||
ALLOW_STUDENT_TO_BYPASS_ENTRANCE_EXAM,
|
||||
ASSIGN_TO_COHORTS,
|
||||
EDIT_COURSE_ACCESS,
|
||||
EDIT_FORUM_ROLES,
|
||||
EDIT_INVOICE_VALIDATION,
|
||||
ENABLE_CERTIFICATE_GENERATION,
|
||||
GENERATE_CERTIFICATE_EXCEPTIONS,
|
||||
GENERATE_BULK_CERTIFICATE_EXCEPTIONS,
|
||||
GIVE_STUDENT_EXTENSION,
|
||||
VIEW_ISSUED_CERTIFICATES,
|
||||
)
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
TASK_SUBMISSION_OK = 'created'
|
||||
@@ -249,6 +263,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
|
||||
@@ -877,7 +913,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'",
|
||||
@@ -1143,7 +1179,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):
|
||||
"""
|
||||
@@ -1210,7 +1246,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.
|
||||
@@ -1388,7 +1424,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):
|
||||
"""
|
||||
@@ -2758,7 +2794,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",
|
||||
@@ -2851,7 +2887,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):
|
||||
"""
|
||||
@@ -2875,7 +2911,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):
|
||||
"""
|
||||
@@ -2986,7 +3022,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.
|
||||
@@ -3006,7 +3042,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):
|
||||
"""
|
||||
@@ -3270,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):
|
||||
@@ -3312,7 +3348,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):
|
||||
"""
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user