diff --git a/lms/djangoapps/teams/csv.py b/lms/djangoapps/teams/csv.py index 50e8ed412630aebecacef8b03f2d804fa6987ca3..0143253d21c48b3d4471798d334ecaab9b13410a 100644 --- a/lms/djangoapps/teams/csv.py +++ b/lms/djangoapps/teams/csv.py @@ -11,7 +11,8 @@ from django.db.models import Prefetch from lms.djangoapps.teams.api import ( OrganizationProtectionStatus, user_organization_protection_status, - ORGANIZATION_PROTECTED_MODES + ORGANIZATION_PROTECTED_MODES, + user_protection_status_matches_team ) from lms.djangoapps.teams.models import CourseTeam, CourseTeamMembership from lms.djangoapps.program_enrollments.models import ProgramCourseEnrollment, ProgramEnrollment @@ -359,6 +360,7 @@ class TeamMembershipImportManager(object): """ Validates that only students enrolled in a masters track are on a single team. Disallows mixing of masters with other enrollment modes on a single team. + Masters track students can't be added to existing non-protected teams """ if(teamset_id, team_name) not in self.user_enrollment_by_team: self.user_enrollment_by_team[teamset_id, team_name] = set() @@ -368,8 +370,25 @@ class TeamMembershipImportManager(object): 'Team {} cannot have Master’s track users mixed with users in other tracks.'.format(team_name) self.add_error_and_check_if_max_exceeded(error_message) return False + if not self.is_enrollment_protection_for_existing_team_matches_user(user, team_name, teamset_id): + error_message = \ + 'User {} does not have access to team {}.'.format(user.username, team_name) + self.add_error_and_check_if_max_exceeded(error_message) + return False return True + def is_enrollment_protection_for_existing_team_matches_user(self, user, team_name, teamset_id): + """ + Applies only to existing teams. + Returns True if no violations + False if there is a mismatch + """ + try: + team = self.existing_course_teams[(team_name, teamset_id)] + return user_protection_status_matches_team(user, team) + except KeyError: + return True + def is_FERPA_bubble_breached(self, teamset_id, team_name): """ Ensures that FERPA bubble is not breached. diff --git a/lms/djangoapps/teams/tests/test_views.py b/lms/djangoapps/teams/tests/test_views.py index cc153862fb6c010999c6fc8c2f1f53bfe8bd5d7f..2526c14edf1399f7303fa9cd0d40b1c922095116 100644 --- a/lms/djangoapps/teams/tests/test_views.py +++ b/lms/djangoapps/teams/tests/test_views.py @@ -3077,3 +3077,29 @@ class TestBulkMembershipManagement(TeamAPITestCase): [user.username for user in team.users.all()], [user_name] ) + + def test_upload_assign_masters_learner_to_non_protected_team(self): + """ + Scenario: Attempt to add a learner enrolled in masters track to an existing, non-org protected team. + Outcome: Must fail + """ + masters_a = 'masters_a' + team = self.wind_team + self.create_and_enroll_student(username=masters_a, mode=CourseMode.MASTERS) + csv_content = 'user,mode,{}'.format(team.topic_id) + '\n' + csv_content += 'masters_a, masters,{}'.format(team.name) + csv_file = SimpleUploadedFile('test_file.csv', csv_content.encode('utf8'), content_type='text/csv') + self.client.login(username=self.users['course_staff'].username, password=self.users['course_staff'].password) + + response = self.make_call( + reverse('team_membership_bulk_management', args=[self.good_course_id]), + 400, method='post', + data={'csv': csv_file}, + user='staff' + ) + response_text = json.loads(response.content.decode('utf-8')) + expected_message = 'User {} does not have access to team {}.'.format( + masters_a, + team.name + ) + self.assertEqual(response_text['errors'][0], expected_message)