|
35 | 35 | get_subject_role_assignments_in_scope, |
36 | 36 | get_subjects_for_role_in_scope, |
37 | 37 | unassign_role_from_subject_in_scope, |
| 38 | + unassign_subject_from_all_roles, |
38 | 39 | ) |
39 | 40 | from openedx_authz.constants import permissions, roles |
40 | 41 | from openedx_authz.constants.roles import ( |
@@ -976,3 +977,156 @@ def test_assign_role_creates_extended_casbin_rule(self): |
976 | 977 | self.assertIn(role_data.namespaced_key, extended_rule.casbin_rule_key) |
977 | 978 | self.assertIn(subject_data.namespaced_key, extended_rule.casbin_rule_key) |
978 | 979 | self.assertIn(scope_data.namespaced_key, extended_rule.casbin_rule_key) |
| 980 | + |
| 981 | + |
| 982 | + @ddt_data( |
| 983 | + # Test user with single role in single scope |
| 984 | + ("alice", ["lib:Org1:math_101"], {"library_admin"}), |
| 985 | + # Test user with multiple roles in different scopes |
| 986 | + ( |
| 987 | + "eve", |
| 988 | + ["lib:Org2:physics_401", "lib:Org2:chemistry_501", "lib:Org2:biology_601"], |
| 989 | + {"library_admin", "library_author", "library_user"}, |
| 990 | + ), |
| 991 | + # Test user with same role in multiple scopes |
| 992 | + ("liam", ["lib:Org4:art_101", "lib:Org4:art_201", "lib:Org4:art_301"], {"library_author"}), |
| 993 | + # Test user with multiple different roles in multiple scopes |
| 994 | + ( |
| 995 | + "peter", |
| 996 | + ["lib:Org6:project_alpha", "lib:Org6:project_beta", "lib:Org6:project_gamma", "lib:Org6:project_delta"], |
| 997 | + {"library_admin", "library_author", "library_contributor", "library_user"}, |
| 998 | + ), |
| 999 | + ) |
| 1000 | + @unpack |
| 1001 | + def test_unassign_subject_from_all_roles_removes_all_assignments(self, subject_name, scopes, expected_roles_before): |
| 1002 | + """Test that unassign_subject_from_all_roles removes all role assignments. |
| 1003 | +
|
| 1004 | + Expected result: |
| 1005 | + - Before unassignment: Subject has roles in specified scopes |
| 1006 | + - Function returns True indicating roles were removed |
| 1007 | + - After unassignment: Subject has no role assignments in any scope |
| 1008 | + - Querying role assignments returns empty list |
| 1009 | + """ |
| 1010 | + subject = SubjectData(external_key=subject_name) |
| 1011 | + |
| 1012 | + # Verify the subject has roles before unassignment |
| 1013 | + assignments_before = get_subject_role_assignments(subject) |
| 1014 | + self.assertGreater(len(assignments_before), 0) |
| 1015 | + |
| 1016 | + # Verify roles are what we expect before removal |
| 1017 | + roles_before = {r.external_key for assignment in assignments_before for r in assignment.roles} |
| 1018 | + self.assertEqual(roles_before, expected_roles_before) |
| 1019 | + |
| 1020 | + # Verify assignments exist in each expected scope |
| 1021 | + for scope_name in scopes: |
| 1022 | + scope_assignments = get_subject_role_assignments_in_scope(subject, ScopeData(external_key=scope_name)) |
| 1023 | + self.assertGreater(len(scope_assignments), 0) |
| 1024 | + |
| 1025 | + # Unassign all roles from the subject |
| 1026 | + result = unassign_subject_from_all_roles(subject) |
| 1027 | + |
| 1028 | + # Verify the function returns True (indicating roles were removed) |
| 1029 | + self.assertTrue(result) |
| 1030 | + |
| 1031 | + # Verify the subject has no role assignments after unassignment |
| 1032 | + assignments_after = get_subject_role_assignments(subject) |
| 1033 | + self.assertEqual(len(assignments_after), 0) |
| 1034 | + |
| 1035 | + # Verify no assignments in any of the previous scopes |
| 1036 | + for scope_name in scopes: |
| 1037 | + scope_assignments = get_subject_role_assignments_in_scope(subject, ScopeData(external_key=scope_name)) |
| 1038 | + self.assertEqual(len(scope_assignments), 0) |
| 1039 | + |
| 1040 | + def test_unassign_subject_with_no_roles_returns_false(self): |
| 1041 | + """Test that unassigning a subject with no roles returns False. |
| 1042 | +
|
| 1043 | + Expected result: |
| 1044 | + - Function returns False when subject has no role assignments |
| 1045 | + - No errors occur when trying to unassign from non-existent subject |
| 1046 | + """ |
| 1047 | + non_existent_subject = SubjectData(external_key="user_with_no_roles") |
| 1048 | + |
| 1049 | + # Verify the subject has no roles |
| 1050 | + assignments_before = get_subject_role_assignments(non_existent_subject) |
| 1051 | + self.assertEqual(len(assignments_before), 0) |
| 1052 | + |
| 1053 | + # Unassign all roles (should return False since there are none) |
| 1054 | + result = unassign_subject_from_all_roles(non_existent_subject) |
| 1055 | + |
| 1056 | + # Verify the function returns False (no roles to remove) |
| 1057 | + self.assertFalse(result) |
| 1058 | + |
| 1059 | + # Verify still no assignments after the operation |
| 1060 | + assignments_after = get_subject_role_assignments(non_existent_subject) |
| 1061 | + self.assertEqual(len(assignments_after), 0) |
| 1062 | + |
| 1063 | + def test_unassign_subject_does_not_affect_other_subjects(self): |
| 1064 | + """Test that unassigning one subject does not affect other subjects. |
| 1065 | +
|
| 1066 | + Expected result: |
| 1067 | + - When unassigning roles from one subject, other subjects retain their roles |
| 1068 | + - Other subjects with the same roles in the same scopes are unaffected |
| 1069 | + """ |
| 1070 | + # Use subjects that share the same scope |
| 1071 | + subject_to_unassign = SubjectData(external_key="grace") |
| 1072 | + other_subject = SubjectData(external_key="heidi") |
| 1073 | + shared_scope = ScopeData(external_key="lib:Org1:math_advanced") |
| 1074 | + |
| 1075 | + # Verify both subjects have roles in the shared scope before |
| 1076 | + grace_assignments_before = get_subject_role_assignments_in_scope(subject_to_unassign, shared_scope) |
| 1077 | + heidi_assignments_before = get_subject_role_assignments_in_scope(other_subject, shared_scope) |
| 1078 | + |
| 1079 | + self.assertGreater(len(grace_assignments_before), 0) |
| 1080 | + self.assertGreater(len(heidi_assignments_before), 0) |
| 1081 | + |
| 1082 | + # Unassign all roles from grace |
| 1083 | + result = unassign_subject_from_all_roles(subject_to_unassign) |
| 1084 | + self.assertTrue(result) |
| 1085 | + |
| 1086 | + # Verify grace has no assignments after unassignment |
| 1087 | + grace_assignments_after = get_subject_role_assignments(subject_to_unassign) |
| 1088 | + self.assertEqual(len(grace_assignments_after), 0) |
| 1089 | + |
| 1090 | + # Verify heidi still has her assignments |
| 1091 | + heidi_assignments_after = get_subject_role_assignments_in_scope(other_subject, shared_scope) |
| 1092 | + self.assertEqual(len(heidi_assignments_after), len(heidi_assignments_before)) |
| 1093 | + |
| 1094 | + # Verify heidi still has the library_contributor role |
| 1095 | + heidi_roles = {r.external_key for assignment in heidi_assignments_after for r in assignment.roles} |
| 1096 | + self.assertIn("library_contributor", heidi_roles) |
| 1097 | + |
| 1098 | + def test_unassign_and_reassign_subject(self): |
| 1099 | + """Test that a subject can be reassigned roles after being unassigned. |
| 1100 | +
|
| 1101 | + Expected result: |
| 1102 | + - Subject has roles initially |
| 1103 | + - After unassignment, subject has no roles |
| 1104 | + - Subject can be assigned new roles |
| 1105 | + - Newly assigned roles work correctly |
| 1106 | + """ |
| 1107 | + subject = SubjectData(external_key="bob") |
| 1108 | + new_scope = ScopeData(external_key="lib:Org1:new_library") |
| 1109 | + new_role = RoleData(external_key="library_admin") |
| 1110 | + |
| 1111 | + # Verify bob has roles initially |
| 1112 | + assignments_before = get_subject_role_assignments(subject) |
| 1113 | + self.assertGreater(len(assignments_before), 0) |
| 1114 | + |
| 1115 | + # Unassign all roles |
| 1116 | + result = unassign_subject_from_all_roles(subject) |
| 1117 | + self.assertTrue(result) |
| 1118 | + |
| 1119 | + # Verify no roles after unassignment |
| 1120 | + assignments_after_unassign = get_subject_role_assignments(subject) |
| 1121 | + self.assertEqual(len(assignments_after_unassign), 0) |
| 1122 | + |
| 1123 | + # Assign a new role in a new scope |
| 1124 | + assign_result = assign_role_to_subject_in_scope(subject, new_role, new_scope) |
| 1125 | + self.assertTrue(assign_result) |
| 1126 | + |
| 1127 | + # Verify the new assignment works |
| 1128 | + new_assignments = get_subject_role_assignments_in_scope(subject, new_scope) |
| 1129 | + self.assertEqual(len(new_assignments), 1) |
| 1130 | + |
| 1131 | + new_roles = {r.external_key for assignment in new_assignments for r in assignment.roles} |
| 1132 | + self.assertIn("library_admin", new_roles) |
0 commit comments