diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index 3f707429f58d..962b4c7c74d1 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -1028,7 +1028,18 @@ class CourseFields(object): ), scope=Scope.settings ) - + restricted_organizations = List( + display_name=_("Restrict Course"), + help=_( + "Restrict course only to certain organizations, " + "the default values is ['*'], it means all organization can access this course. " + "To only allow ucsd emails, replace list with [\"ucsd.\"]. This allow all domains of ucsd i.e abc@ucsd.com, xyz@ucsd.edu." + "To further restric top level domains you can specify [domain].[top-level-domaain] i.e [\"ucsd.com\", \"ucsd.ca\", \"gmail.com\"]. " + "Similary, you can add as many domains in the list to allow specific organization students to access this course" + ), + scope=Scope.settings, + default=['*'], + ) class CourseModule(CourseFields, SequenceModule): # pylint: disable=abstract-method """ diff --git a/lms/djangoapps/courseware/access_utils.py b/lms/djangoapps/courseware/access_utils.py index 2529f80c09ea..89c6e726dd1c 100644 --- a/lms/djangoapps/courseware/access_utils.py +++ b/lms/djangoapps/courseware/access_utils.py @@ -2,7 +2,7 @@ Simple utility functions for computing access. It allows us to share code between access.py and block transformers. """ - +import re from datetime import datetime, timedelta from logging import getLogger @@ -167,3 +167,18 @@ def check_public_access(course, visibilities): return ACCESS_GRANTED return ACCESS_DENIED + +def check_organization_access(user, course): + """ + Checks if the urers belogs to certain organization + """ + organizations_regexes = course.restricted_organizations + if '*' in organizations_regexes: + return ACCESS_GRANTED + + email = user.email if user.is_authenticated else '' + for regex in organizations_regexes: + if re.match(f'^[a-zA-Z0-9_.+-]+@{regex}', email): + return ACCESS_GRANTED + + return ACCESS_DENIED diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py index d0cb3bbc85e1..6c1266475e97 100644 --- a/lms/djangoapps/courseware/courses.py +++ b/lms/djangoapps/courseware/courses.py @@ -49,6 +49,7 @@ from lms.djangoapps.courseware.access_utils import ( check_authentication, check_enrollment, + check_organization_access, ) from lms.djangoapps.courseware.courseware_access_exception import CoursewareAccessException from lms.djangoapps.courseware.exceptions import CourseAccessRedirect @@ -129,6 +130,7 @@ def get_course_with_access(user, action, course_key, depth=0, check_if_enrolled= """ course = get_course_by_id(course_key, depth) check_course_access_with_redirect(course, user, action, check_if_enrolled, check_survey_complete, check_if_authenticated) + check_course_organization_access_with_redirect(course, user) return course @@ -196,6 +198,15 @@ def _check_nonstaff_access(): # This access_response will be ACCESS_GRANTED return nonstaff_access_response +def check_course_organization_access_with_redirect(course, user): + """ + Check that the user has access to the organization courses + Only those users are allowed to see the course content that are either staff members or + are logedin with emails from certain organizations i.e ucsd.edu + """ + access_response = has_access(user, 'staff', course.id) or check_organization_access(user, course) + if not access_response: + raise CourseAccessRedirect(reverse('courses'), access_response) def check_course_access_with_redirect(course, user, action, check_if_enrolled=False, check_survey_complete=True, check_if_authenticated=False): """ @@ -685,7 +696,7 @@ def get_courses(user, org=None, filter_=None): ) return LazySequence( - (c for c in courses if has_access(user, permission_name, c)), + (c for c in courses if has_access(user, permission_name, c) and check_organization_access(user, get_course_by_id(c.id))), est_len=courses.count() )