diff --git a/lms/djangoapps/courseware/access.py b/lms/djangoapps/courseware/access.py index 37a895fa857c..0abe983b2a65 100644 --- a/lms/djangoapps/courseware/access.py +++ b/lms/djangoapps/courseware/access.py @@ -139,7 +139,7 @@ def has_access(user, action, obj, course_key=None): if not user: user = AnonymousUser() - if user != AnonymousUser() and course_key and _is_banned_from_course(user, course_key): + if not user.is_anonymous and course_key and is_banned_from_course(user, course_key): return ACCESS_DENIED # Preview mode is only accessible by staff. @@ -180,7 +180,7 @@ def has_access(user, action, obj, course_key=None): .format(type(obj))) -def _is_banned_from_course(user, course_key): +def is_banned_from_course(user, course_key): """ Checks if the user is banned from the course its trying to access """ diff --git a/lms/djangoapps/courseware/views/index.py b/lms/djangoapps/courseware/views/index.py index c0ea97a9faab..e8d5cdd897a3 100644 --- a/lms/djangoapps/courseware/views/index.py +++ b/lms/djangoapps/courseware/views/index.py @@ -8,8 +8,9 @@ import urllib from django.conf import settings -from django.contrib.auth.models import User +from django.contrib.auth.models import User, AnonymousUser from django.contrib.auth.views import redirect_to_login +from django.shortcuts import redirect from django.urls import reverse from django.http import Http404 from django.template.context_processors import csrf @@ -48,7 +49,7 @@ from xmodule.course_module import COURSE_VISIBILITY_PUBLIC from xmodule.x_module import PUBLIC_VIEW, STUDENT_VIEW from .views import CourseTabView -from ..access import has_access +from ..access import has_access, is_banned_from_course from ..courses import check_course_access, get_course_with_access, get_current_child, get_studio_url from ..entrance_exams import ( course_has_entrance_exam, @@ -106,6 +107,9 @@ def get(self, request, course_id, chapter=None, section=None, position=None): if not (request.user.is_authenticated or self.enable_unenrolled_access): return redirect_to_login(request.get_full_path()) + if not request.user.is_anonymous and self.course_key and is_banned_from_course(request.user, self.course_key): + return redirect(reverse('student_access_denied')) + self.original_chapter_url_name = chapter self.original_section_url_name = section self.chapter_url_name = chapter diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index 923f180d959a..89f18edd7269 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -38,7 +38,7 @@ import shoppingcart import survey.views from course_modes.models import CourseMode, get_course_prices -from courseware.access import has_access, has_ccx_coach_role +from courseware.access import has_access, has_ccx_coach_role, is_banned_from_course from courseware.access_utils import check_course_open_for_learner from courseware.courses import ( can_self_enroll_in_course, @@ -767,6 +767,10 @@ def course_about(request, course_id): """ course_key = CourseKey.from_string(course_id) + # if the user is banned + if not request.user.is_anonymous and course_key and is_banned_from_course(request.user, course_key): + return redirect(reverse('student_access_denied')) + # If a user is not able to enroll in a course then redirect # them away from the about page to the dashboard. if not can_self_enroll_in_course(course_key): diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index d6e5ee3e5b00..e396e3e70efd 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -158,6 +158,7 @@ SUCCESS_MESSAGE_TEMPLATE = _("The {report_type} report is being created. " "To view the status of the report, see Pending Tasks below.") +EMAIL_VALIDITY_RE = r'[\w_.+-]+@[\w-]+\.[\w\-.]+' def common_exceptions_400(func): """ @@ -3663,7 +3664,8 @@ def _ban_student_from_course(course_id, usernames_expiry_key_value): if not check_expiry_validity(raw_user, expiry, invalid_usernames): continue - if re.search(r'^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w+$', raw_user): + raw_email = re.findall(EMAIL_VALIDITY_RE, raw_user) + if raw_email and raw_email[0] == raw_user: banned_user, created = CourseEnrollmentBanned.objects.get_or_create( email=raw_user, course_id=course_id) @@ -3721,7 +3723,8 @@ def _unban_student_from_course(course_id, usernames_expiry_key_value): for raw_user in usernames_or_emails: reason = 'username does not exist already' - if re.search(r'^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w+$', raw_user): + raw_email = re.findall(EMAIL_VALIDITY_RE, raw_user) + if raw_email and raw_email[0] == raw_user: reason = 'email does not exist already' invalid_usernames.append( @@ -3755,7 +3758,7 @@ def _format_student_info_to_key_value(user_and_expiry_raw): user_and_expiry_raw = re.split(r'\n', user_and_expiry_raw) for raw_user in user_and_expiry_raw: - raw_user = [r for r in raw_user.split(',') if r.strip()] + raw_user = [r.strip() for r in raw_user.split(',') if r.strip()] if not raw_user or not raw_user[0]: continue @@ -3772,7 +3775,8 @@ def _format_student_info_to_key_value(user_and_expiry_raw): else: expiry = 'invalid' - if re.search(r'^[a-zA-Z]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', raw_user[0]): + raw_email = re.findall(EMAIL_VALIDITY_RE, raw_user[0]) + if raw_email and raw_email[0] == raw_user[0]: raw_user[0] = raw_user[0].lower() student_info[raw_user[0]] = expiry diff --git a/lms/djangoapps/static_template_view/urls.py b/lms/djangoapps/static_template_view/urls.py index 0371b97a561d..a10bc42fe131 100644 --- a/lms/djangoapps/static_template_view/urls.py +++ b/lms/djangoapps/static_template_view/urls.py @@ -27,6 +27,9 @@ # Press releases url(r'^press/([_a-zA-Z0-9-]+)$', views.render_press_release, name='press_release'), + + # lumsx + url(r'^student_access_denied$', views.render, {'template': 'access_denied.html'}, name="student_access_denied") ] # Only enable URLs for those marketing links actually enabled in the diff --git a/lms/static/js/instructor_dashboard/student_admin.js b/lms/static/js/instructor_dashboard/student_admin.js index 5521e82f2ac4..4812dda8fab5 100644 --- a/lms/static/js/instructor_dashboard/student_admin.js +++ b/lms/static/js/instructor_dashboard/student_admin.js @@ -105,7 +105,7 @@ url: $(event.target).data('endpoint'), data: { action: action, - identifiers: studentadmin.$user_ban_from_course_info_field.val(), + identifiers: studentadmin.$user_ban_from_course_info_field.val().toLowerCase(), }, success: studentadmin.clear_errors_then(function (data) { return studentadmin.display_ban_results(data, action); diff --git a/openedx/features/course_experience/views/course_home.py b/openedx/features/course_experience/views/course_home.py index d3a05a18bcc2..48ade4d50044 100644 --- a/openedx/features/course_experience/views/course_home.py +++ b/openedx/features/course_experience/views/course_home.py @@ -1,7 +1,8 @@ """ Views for the course home page. """ - +from django.contrib.auth.models import AnonymousUser +from django.shortcuts import redirect from django.urls import reverse from django.template.context_processors import csrf from django.template.loader import render_to_string @@ -12,7 +13,7 @@ from web_fragments.fragment import Fragment from course_modes.models import get_cosmetic_verified_display_price -from courseware.access import has_access +from courseware.access import has_access, is_banned_from_course from courseware.courses import can_self_enroll_in_course, get_course_info_section, get_course_with_access from lms.djangoapps.commerce.utils import EcommerceService from lms.djangoapps.course_goals.api import ( @@ -58,6 +59,10 @@ def get(self, request, course_id, **kwargs): """ Displays the home page for the specified course. """ + course_key = CourseKey.from_string(course_id) + if not request.user.is_anonymous and course_key and is_banned_from_course(request.user, course_key): + return redirect(reverse('student_access_denied')) + return super(CourseHomeView, self).get(request, course_id, 'courseware', **kwargs) def uses_bootstrap(self, request, course, tab):