Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions common/djangoapps/student/models/course_enrollment.py
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,14 @@ def enroll(cls, user, course_key, mode=None, check_access=False, can_upgrade=Fal
raise NonExistentCourseError # lint-amnesty, pylint: disable=raise-missing-from # noqa: B904

if check_access:
if not user.is_active:
log.warning(
"User %s failed to enroll in course %s because the account has not been activated.",
user.username,
str(course_key),
)
raise EnrollmentNotAllowed("Account must be activated before enrollment.")

if cls.is_enrollment_closed(user, course) and not can_upgrade:
log.warning(
"User %s failed to enroll in course %s because enrollment is closed (can_upgrade=%s).",
Expand Down
52 changes: 52 additions & 0 deletions common/djangoapps/student/tests/test_enrollment.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
CourseEnrollment,
CourseFullError,
EnrollmentClosedError,
EnrollmentNotAllowed,
)
from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole
from common.djangoapps.student.tests.factories import CourseEnrollmentAllowedFactory, UserFactory
Expand Down Expand Up @@ -521,3 +522,54 @@ def test_score_recalculation_on_enrollment_update(self):
countdown=SCORE_RECALCULATION_DELAY_ON_ENROLLMENT_UPDATE,
kwargs=local_task_args
)

def test_unit_inactive_user_enrollment_blocked(self):
"""
Unit test: CourseEnrollment.enroll() should raise EnrollmentNotAllowed
when user.is_active is False and check_access is True.
Bug #36402.
"""
self.user.is_active = False
self.user.save()
with pytest.raises(EnrollmentNotAllowed):
CourseEnrollment.enroll(self.user, self.course.id, check_access=True)

def test_integration_inactive_user_cannot_enroll_active_can(self):
"""
Integration test: An inactive user is blocked from enrollment, but after
activation the same user can enroll successfully.
Bug #36402.
"""
self.user.is_active = False
self.user.save()
with pytest.raises(EnrollmentNotAllowed):
CourseEnrollment.enroll(self.user, self.course.id, check_access=True)

self.user.is_active = True
self.user.save()
enrollment = CourseEnrollment.enroll(self.user, self.course.id, check_access=True)
assert enrollment.is_active
assert CourseEnrollment.is_enrolled(self.user, self.course.id)

def test_bug_36402_regression_inactive_user_enrollment(self):
"""
Regression test for Bug #36402: Non-active users can enroll in courses.
Before the fix, an inactive (unverified email) user could enroll freely.
After the fix, EnrollmentNotAllowed is raised when check_access=True.
"""
self.user.is_active = False
self.user.save()
with pytest.raises(EnrollmentNotAllowed, match="activated"):
CourseEnrollment.enroll(self.user, self.course.id, check_access=True)

def test_inactive_user_enrollment_bypasses_check_when_no_access_check(self):
"""
Server-to-server enrollment (check_access=False) should still allow
inactive user enrollment, since the caller is trusted.
Bug #36402 — ensures the fix does not break server-side enrollment.
"""
self.user.is_active = False
self.user.save()
enrollment = CourseEnrollment.enroll(self.user, self.course.id, check_access=False)
assert enrollment.is_active
assert CourseEnrollment.is_enrolled(self.user, self.course.id)