|
24 | 24 | get_subject_role_assignments_for_role_in_scope, |
25 | 25 | get_subject_role_assignments_in_scope, |
26 | 26 | unassign_role_from_subject_in_scope, |
| 27 | + unassign_subjects_from_all_roles, |
27 | 28 | ) |
28 | 29 | from openedx_authz.engine.enforcer import AuthzEnforcer |
29 | 30 | from openedx_authz.engine.utils import migrate_policy_between_enforcers |
@@ -796,3 +797,170 @@ def test_get_all_role_assignments_in_scope(self, scope_name, expected_assignment |
796 | 797 | self.assertEqual(len(role_assignments), len(expected_assignments)) |
797 | 798 | for assignment in role_assignments: |
798 | 799 | self.assertIn(assignment, expected_assignments) |
| 800 | + |
| 801 | + @ddt_data( |
| 802 | + # Test user with single role in single scope |
| 803 | + ("alice", ["lib:Org1:math_101"], {"library_admin"}), |
| 804 | + # Test user with multiple roles in different scopes |
| 805 | + ("eve", ["lib:Org2:physics_401", "lib:Org2:chemistry_501", "lib:Org2:biology_601"], |
| 806 | + {"library_admin", "library_author", "library_user"}), |
| 807 | + # Test user with same role in multiple scopes |
| 808 | + ("liam", ["lib:Org4:art_101", "lib:Org4:art_201", "lib:Org4:art_301"], {"library_author"}), |
| 809 | + # Test user with multiple different roles in multiple scopes |
| 810 | + ("peter", ["lib:Org6:project_alpha", "lib:Org6:project_beta", "lib:Org6:project_gamma", "lib:Org6:project_delta"], |
| 811 | + {"library_admin", "library_author", "library_contributor", "library_user"}), |
| 812 | + ) |
| 813 | + @unpack |
| 814 | + def test_unassign_subject_from_all_roles_removes_all_assignments( |
| 815 | + self, subject_name, scopes, expected_roles_before |
| 816 | + ): |
| 817 | + """Test that unassign_subjects_from_all_roles removes all role assignments. |
| 818 | +
|
| 819 | + Expected result: |
| 820 | + - Before unassignment: Subject has roles in specified scopes |
| 821 | + - Function returns True indicating roles were removed |
| 822 | + - After unassignment: Subject has no role assignments in any scope |
| 823 | + - Querying role assignments returns empty list |
| 824 | + """ |
| 825 | + subject = SubjectData(external_key=subject_name) |
| 826 | + |
| 827 | + # Verify the subject has roles before unassignment |
| 828 | + assignments_before = get_subject_role_assignments(subject) |
| 829 | + self.assertGreater(len(assignments_before), 0) |
| 830 | + |
| 831 | + # Verify roles are what we expect before removal |
| 832 | + roles_before = { |
| 833 | + r.external_key for assignment in assignments_before for r in assignment.roles |
| 834 | + } |
| 835 | + self.assertEqual(roles_before, expected_roles_before) |
| 836 | + |
| 837 | + # Verify assignments exist in each expected scope |
| 838 | + for scope_name in scopes: |
| 839 | + scope_assignments = get_subject_role_assignments_in_scope( |
| 840 | + subject, ScopeData(external_key=scope_name) |
| 841 | + ) |
| 842 | + self.assertGreater(len(scope_assignments), 0) |
| 843 | + |
| 844 | + # Unassign all roles from the subject |
| 845 | + result = unassign_subjects_from_all_roles(subject) |
| 846 | + |
| 847 | + # Verify the function returns True (indicating roles were removed) |
| 848 | + self.assertTrue(result) |
| 849 | + |
| 850 | + # Verify the subject has no role assignments after unassignment |
| 851 | + assignments_after = get_subject_role_assignments(subject) |
| 852 | + self.assertEqual(len(assignments_after), 0) |
| 853 | + |
| 854 | + # Verify no assignments in any of the previous scopes |
| 855 | + for scope_name in scopes: |
| 856 | + scope_assignments = get_subject_role_assignments_in_scope( |
| 857 | + subject, ScopeData(external_key=scope_name) |
| 858 | + ) |
| 859 | + self.assertEqual(len(scope_assignments), 0) |
| 860 | + |
| 861 | + def test_unassign_subject_with_no_roles_returns_false(self): |
| 862 | + """Test that unassigning a subject with no roles returns False. |
| 863 | +
|
| 864 | + Expected result: |
| 865 | + - Function returns False when subject has no role assignments |
| 866 | + - No errors occur when trying to unassign from non-existent subject |
| 867 | + """ |
| 868 | + non_existent_subject = SubjectData(external_key="user_with_no_roles") |
| 869 | + |
| 870 | + # Verify the subject has no roles |
| 871 | + assignments_before = get_subject_role_assignments(non_existent_subject) |
| 872 | + self.assertEqual(len(assignments_before), 0) |
| 873 | + |
| 874 | + # Unassign all roles (should return False since there are none) |
| 875 | + result = unassign_subjects_from_all_roles(non_existent_subject) |
| 876 | + |
| 877 | + # Verify the function returns False (no roles to remove) |
| 878 | + self.assertFalse(result) |
| 879 | + |
| 880 | + # Verify still no assignments after the operation |
| 881 | + assignments_after = get_subject_role_assignments(non_existent_subject) |
| 882 | + self.assertEqual(len(assignments_after), 0) |
| 883 | + |
| 884 | + def test_unassign_subject_does_not_affect_other_subjects(self): |
| 885 | + """Test that unassigning one subject does not affect other subjects. |
| 886 | +
|
| 887 | + Expected result: |
| 888 | + - When unassigning roles from one subject, other subjects retain their roles |
| 889 | + - Other subjects with the same roles in the same scopes are unaffected |
| 890 | + """ |
| 891 | + # Use subjects that share the same scope |
| 892 | + subject_to_unassign = SubjectData(external_key="grace") |
| 893 | + other_subject = SubjectData(external_key="heidi") |
| 894 | + shared_scope = ScopeData(external_key="lib:Org1:math_advanced") |
| 895 | + |
| 896 | + # Verify both subjects have roles in the shared scope before |
| 897 | + grace_assignments_before = get_subject_role_assignments_in_scope( |
| 898 | + subject_to_unassign, shared_scope |
| 899 | + ) |
| 900 | + heidi_assignments_before = get_subject_role_assignments_in_scope( |
| 901 | + other_subject, shared_scope |
| 902 | + ) |
| 903 | + |
| 904 | + self.assertGreater(len(grace_assignments_before), 0) |
| 905 | + self.assertGreater(len(heidi_assignments_before), 0) |
| 906 | + |
| 907 | + # Unassign all roles from grace |
| 908 | + result = unassign_subjects_from_all_roles(subject_to_unassign) |
| 909 | + self.assertTrue(result) |
| 910 | + |
| 911 | + # Verify grace has no assignments after unassignment |
| 912 | + grace_assignments_after = get_subject_role_assignments(subject_to_unassign) |
| 913 | + self.assertEqual(len(grace_assignments_after), 0) |
| 914 | + |
| 915 | + # Verify heidi still has her assignments |
| 916 | + heidi_assignments_after = get_subject_role_assignments_in_scope( |
| 917 | + other_subject, shared_scope |
| 918 | + ) |
| 919 | + self.assertEqual(len(heidi_assignments_after), len(heidi_assignments_before)) |
| 920 | + |
| 921 | + # Verify heidi still has the library_contributor role |
| 922 | + heidi_roles = { |
| 923 | + r.external_key |
| 924 | + for assignment in heidi_assignments_after |
| 925 | + for r in assignment.roles |
| 926 | + } |
| 927 | + self.assertIn("library_contributor", heidi_roles) |
| 928 | + |
| 929 | + def test_unassign_and_reassign_subject(self): |
| 930 | + """Test that a subject can be reassigned roles after being unassigned. |
| 931 | +
|
| 932 | + Expected result: |
| 933 | + - Subject has roles initially |
| 934 | + - After unassignment, subject has no roles |
| 935 | + - Subject can be assigned new roles |
| 936 | + - Newly assigned roles work correctly |
| 937 | + """ |
| 938 | + subject = SubjectData(external_key="bob") |
| 939 | + original_scope = ScopeData(external_key="lib:Org1:history_201") |
| 940 | + new_scope = ScopeData(external_key="lib:Org1:new_library") |
| 941 | + new_role = RoleData(external_key="library_admin") |
| 942 | + |
| 943 | + # Verify bob has roles initially |
| 944 | + assignments_before = get_subject_role_assignments(subject) |
| 945 | + self.assertGreater(len(assignments_before), 0) |
| 946 | + |
| 947 | + # Unassign all roles |
| 948 | + result = unassign_subjects_from_all_roles(subject) |
| 949 | + self.assertTrue(result) |
| 950 | + |
| 951 | + # Verify no roles after unassignment |
| 952 | + assignments_after_unassign = get_subject_role_assignments(subject) |
| 953 | + self.assertEqual(len(assignments_after_unassign), 0) |
| 954 | + |
| 955 | + # Assign a new role in a new scope |
| 956 | + assign_result = assign_role_to_subject_in_scope(subject, new_role, new_scope) |
| 957 | + self.assertTrue(assign_result) |
| 958 | + |
| 959 | + # Verify the new assignment works |
| 960 | + new_assignments = get_subject_role_assignments_in_scope(subject, new_scope) |
| 961 | + self.assertEqual(len(new_assignments), 1) |
| 962 | + |
| 963 | + new_roles = { |
| 964 | + r.external_key for assignment in new_assignments for r in assignment.roles |
| 965 | + } |
| 966 | + self.assertIn("library_admin", new_roles) |
0 commit comments