1515from opaque_keys .edx .keys import CourseKey
1616from opaque_keys .edx .locator import CourseLocator
1717from openedx_authz .api import users as authz_api
18- from openedx_authz .api .data import CourseOverviewData , RoleAssignmentData
18+ from openedx_authz .api .data import CourseOverviewData , OrgCourseOverviewGlobData , RoleAssignmentData
1919from openedx_authz .constants import roles as authz_roles
2020
2121from common .djangoapps .student .models import CourseAccessRole
@@ -74,17 +74,6 @@ def authz_add_role(user: User, authz_role: str, course_key: str):
7474 legacy_role = get_legacy_role_from_authz_role (authz_role )
7575 emit_course_access_role_added (user , course_locator , course_locator .org , legacy_role )
7676
77- def authz_get_all_course_assignments_for_user (user : User ) -> list [RoleAssignmentData ]:
78- """
79- Get all course assignments for a user.
80- """
81- assignments = authz_api .get_user_role_assignments (user_external_key = user .username )
82- # filter courses only
83- filtered_assignments = [
84- assignment for assignment in assignments
85- if isinstance (assignment .scope , CourseOverviewData )
86- ]
87- return filtered_assignments
8877
8978def get_org_from_key (key : str ) -> str :
9079 """
@@ -93,6 +82,7 @@ def get_org_from_key(key: str) -> str:
9382 parsed_key = CourseKey .from_string (key )
9483 return parsed_key .org
9584
85+
9686def register_access_role (cls ):
9787 """
9888 Decorator that allows access roles to be registered within the roles module and referenced by their
@@ -141,34 +131,108 @@ class AuthzCompatCourseAccessRole:
141131 """
142132 Generic data class for storing CourseAccessRole-compatible data
143133 to be used inside BulkRoleCache and RoleCache.
134+
144135 This allows the cache to store both legacy and openedx-authz compatible roles
145136 """
146137 user_id : int
147138 username : str
148139 org : str
149- course_id : str # Course key
140+ course_id : str | None
150141 role : str
151142
152143
153- def get_authz_compat_course_access_roles_for_user (user : User ) -> set [AuthzCompatCourseAccessRole ]:
144+ def authz_get_all_course_assignments_for_user (user : User ) -> list [RoleAssignmentData ]:
145+ """
146+ Return AuthZ role assignments for a user that apply to courses.
147+
148+ Includes assignments scoped to a specific course (``CourseOverviewData``) and
149+ assignments scoped to all courses in an organization (``OrgCourseOverviewGlobData``).
150+ Assignments for other resource types, such as content libraries, are excluded.
151+
152+ Args:
153+ user (User): The user whose AuthZ role assignments should be retrieved.
154+
155+ Returns:
156+ list[RoleAssignmentData]: Role assignments whose scope is course-level or org-wide
154157 """
155- Retrieve all CourseAccessRole objects for a given user and convert them to AuthzCompatCourseAccessRole objects.
158+ assignments = authz_api .get_user_role_assignments (user_external_key = user .username )
159+ return [
160+ assignment
161+ for assignment in assignments
162+ if isinstance (assignment .scope , CourseOverviewData | OrgCourseOverviewGlobData )
163+ ]
164+
165+
166+ def _compat_roles_from_authz_assignment (
167+ user : User ,
168+ assignment : RoleAssignmentData ,
169+ ) -> set [AuthzCompatCourseAccessRole ]:
156170 """
157- compat_role_assignments = set ()
158- assignments = authz_get_all_course_assignments_for_user (user )
159- for assignment in assignments :
160- for role in assignment .roles :
161- legacy_role = get_legacy_role_from_authz_role (authz_role = role .external_key )
162- course_key = assignment .scope .external_key
163- org = get_org_from_key (course_key )
164- compat_role = AuthzCompatCourseAccessRole (
171+ Convert an AuthZ role assignment into legacy-compatible course access roles.
172+
173+ Course-scoped assignments produce roles tied to a specific course key.
174+ Org-wide assignments produce org-level roles with no course key (``course_id``
175+ is ``None``), matching legacy ``OrgStaffRole`` / ``OrgInstructorRole`` behavior.
176+ AuthZ roles without a legacy mapping are skipped.
177+
178+ Args:
179+ user (User): The user associated with the assignment.
180+ assignment (RoleAssignmentData): A single AuthZ role assignment, including
181+ its scope and assigned roles.
182+
183+ Returns:
184+ set[AuthzCompatCourseAccessRole]: Legacy-compatible role records for the
185+ assignment. Returns an empty set if the scope is unsupported, the org
186+ is missing for an org-wide assignment, or no roles could be mapped.
187+ """
188+ scope = assignment .scope
189+ if isinstance (scope , CourseOverviewData ):
190+ course_id = scope .external_key
191+ org = get_org_from_key (course_id )
192+ elif isinstance (scope , OrgCourseOverviewGlobData ):
193+ org = scope .org
194+ if not org :
195+ return set ()
196+ course_id = None
197+ else :
198+ return set ()
199+
200+ compat_roles = set ()
201+ for role in assignment .roles :
202+ legacy_role = get_legacy_role_from_authz_role (authz_role = role .external_key )
203+ if legacy_role is None :
204+ continue
205+ compat_roles .add (
206+ AuthzCompatCourseAccessRole (
165207 user_id = user .id ,
166208 username = user .username ,
167209 org = org ,
168- course_id = course_key ,
169- role = legacy_role
210+ course_id = course_id ,
211+ role = legacy_role ,
170212 )
171- compat_role_assignments .add (compat_role )
213+ )
214+ return compat_roles
215+
216+
217+ def get_authz_compat_course_access_roles_for_user (user : User ) -> set [AuthzCompatCourseAccessRole ]:
218+ """
219+ Retrieve AuthZ course and org role assignments for a user in legacy format.
220+
221+ Fetches all course-level and org-wide AuthZ assignments for the user and
222+ converts each one into ``AuthzCompatCourseAccessRole`` records suitable for
223+ ``RoleCache`` and other legacy permission checks.
224+
225+ Args:
226+ user (User): The user whose AuthZ role assignments should be converted.
227+
228+ Returns:
229+ set[AuthzCompatCourseAccessRole]: Legacy-compatible role records derived
230+ from the user's AuthZ assignments. Returns an empty set if the user
231+ has no applicable assignments.
232+ """
233+ compat_role_assignments = set ()
234+ for assignment in authz_get_all_course_assignments_for_user (user ):
235+ compat_role_assignments .update (_compat_roles_from_authz_assignment (user , assignment ))
172236 return compat_role_assignments
173237
174238
@@ -843,11 +907,9 @@ def courses_with_role(self) -> set[AuthzCompatCourseAccessRole]:
843907 """
844908 Return a set of AuthzCompatCourseAccessRole for all of the courses with this user x (or derived from x) role.
845909 """
910+ # Get all assignments for a user to a role
846911 roles = RoleCache .get_roles (self .role )
847912 legacy_assignments = CourseAccessRole .objects .filter (role__in = roles , user = self .user )
848-
849- # Get all assignments for a user to a role
850- new_authz_roles = [get_authz_role_from_legacy_role (role ) for role in roles ]
851913 all_authz_user_assignments = authz_get_all_course_assignments_for_user (self .user )
852914
853915 all_assignments = set ()
@@ -863,19 +925,9 @@ def courses_with_role(self) -> set[AuthzCompatCourseAccessRole]:
863925 ))
864926
865927 for assignment in all_authz_user_assignments :
866- for role in assignment .roles :
867- if role .external_key not in new_authz_roles :
868- continue
869- legacy_role = get_legacy_role_from_authz_role (authz_role = role .external_key )
870- course_key = assignment .scope .external_key
871- org = get_org_from_key (course_key )
872- all_assignments .add (AuthzCompatCourseAccessRole (
873- user_id = self .user .id ,
874- username = self .user .username ,
875- org = org ,
876- course_id = course_key ,
877- role = legacy_role
878- ))
928+ for compat_role in _compat_roles_from_authz_assignment (self .user , assignment ):
929+ if compat_role .role in roles :
930+ all_assignments .add (compat_role )
879931
880932 return all_assignments
881933
@@ -899,18 +951,12 @@ def has_courses_with_role(self, org: str | None = None) -> bool:
899951 return True
900952
901953 # Then check for authz assignments
902- new_authz_roles = [get_authz_role_from_legacy_role (role ) for role in roles ]
903954 all_authz_user_assignments = authz_get_all_course_assignments_for_user (self .user )
904955
905956 for assignment in all_authz_user_assignments :
906- for role in assignment . roles :
907- if role . external_key not in new_authz_roles :
957+ for compat_role in _compat_roles_from_authz_assignment ( self . user , assignment ) :
958+ if compat_role . role not in roles :
908959 continue
909- if org is None :
910- # There is at least one assignment, short circuit
911- return True
912- course_key = assignment .scope .external_key
913- parsed_org = get_org_from_key (course_key )
914- if org == parsed_org :
960+ if org is None or org == compat_role .org :
915961 return True
916962 return False
0 commit comments