From a56368a83deeb25faba5dec4ed5257a89e92ef22 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Wed, 25 Mar 2026 17:03:11 -0500 Subject: [PATCH 1/5] feat: implement org retrieval for user roles in authz compatibility layer --- common/djangoapps/student/roles.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/djangoapps/student/roles.py b/common/djangoapps/student/roles.py index 6a26e0f4db70..75f565a56c57 100644 --- a/common/djangoapps/student/roles.py +++ b/common/djangoapps/student/roles.py @@ -533,9 +533,10 @@ def _authz_get_orgs_for_user(self, user) -> list[str]: Returns a list of org short names for the user with given role. AuthZ compatibility layer """ - # TODO: This will be implemented on Milestone 1 - # of the Authz for Course Authoring project - return [] + role = get_authz_role_from_legacy_role(self._role_name) + assignments = authz_api.get_user_role_assignments_for_role(user.username, role) + orgs = {assignment.scope.org for assignment in assignments if assignment.scope.org is not None} + return list(orgs) def _legacy_get_orgs_for_user(self, user) -> list[str]: """ From 95e0de51eb2ac4fe65d06056617e6f2178c07cab Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Fri, 27 Mar 2026 17:33:06 -0500 Subject: [PATCH 2/5] refactor: use new get_user_role_assignments_filtered api function --- common/djangoapps/student/roles.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/djangoapps/student/roles.py b/common/djangoapps/student/roles.py index 75f565a56c57..c8c0b5f09441 100644 --- a/common/djangoapps/student/roles.py +++ b/common/djangoapps/student/roles.py @@ -534,7 +534,10 @@ def _authz_get_orgs_for_user(self, user) -> list[str]: AuthZ compatibility layer """ role = get_authz_role_from_legacy_role(self._role_name) - assignments = authz_api.get_user_role_assignments_for_role(user.username, role) + assignments = authz_api.get_user_role_assignments_filtered( + user_external_key=user.username, + role_external_key=role, + ) orgs = {assignment.scope.org for assignment in assignments if assignment.scope.org is not None} return list(orgs) From c2903a385ffa3346a3f738dcb2632d9d3b11d8f8 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Mon, 30 Mar 2026 11:05:26 -0500 Subject: [PATCH 3/5] test: add authz test for get_orgs_for_user --- common/djangoapps/student/tests/test_roles.py | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/common/djangoapps/student/tests/test_roles.py b/common/djangoapps/student/tests/test_roles.py index c95d0c5af8a3..5ad793727c94 100644 --- a/common/djangoapps/student/tests/test_roles.py +++ b/common/djangoapps/student/tests/test_roles.py @@ -2,7 +2,6 @@ Tests of student.roles """ - from unittest.mock import patch import ddt @@ -11,7 +10,8 @@ from edx_toggles.toggles.testutils import override_waffle_flag from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.locator import LibraryLocator -from openedx_authz.api.data import ContentLibraryData, RoleAssignmentData, RoleData, UserData +from openedx_authz.api.data import ContentLibraryData, CourseOverviewData, RoleAssignmentData, RoleData, UserData +from openedx_authz.constants.roles import COURSE_ADMIN, COURSE_STAFF from openedx_authz.engine.enforcer import AuthzEnforcer from common.djangoapps.student.admin import CourseAccessRoleHistoryAdmin @@ -239,9 +239,51 @@ def test_get_orgs_for_user(self): role_second_org.add_users(self.student) assert len(role.get_orgs_for_user(self.student)) == 2 + @override_waffle_flag(AUTHZ_COURSE_AUTHORING_FLAG, active=True) + def test_get_orgs_for_user_authz(self): + """ + Test get_orgs_for_user using AuthZ compatibility layer + """ + role = CourseStaffRole(self.course_key) + + other_org = "MIT" + other_course_key = CourseKey.from_string(f"course-v1:{other_org}+Javascript+2026_T1") + another_course_key = CourseKey.from_string(f"course-v1:{other_org}+Python+2026_T1") + + staff_authz_role = RoleData(external_key=COURSE_STAFF) + instructor_authz_role = RoleData(external_key=COURSE_ADMIN) + + assignments = [ + RoleAssignmentData( + subject=UserData(external_key=self.student.username), + roles=[staff_authz_role], + scope=CourseOverviewData(external_key=str(self.course_key)), + ), + RoleAssignmentData( + subject=UserData(external_key=self.student.username), + roles=[staff_authz_role], + scope=CourseOverviewData(external_key=str(other_course_key)), + ), + RoleAssignmentData( + subject=UserData(external_key=self.student.username), + roles=[staff_authz_role], + scope=CourseOverviewData(external_key=str(another_course_key)), + ), + # Non-matching role should be ignored + RoleAssignmentData( + subject=UserData(external_key=self.student.username), + roles=[instructor_authz_role], + scope=CourseOverviewData(external_key=str(self.course_key)), + ), + ] + + with patch("openedx_authz.api.users.get_user_role_assignments_filtered", return_value=assignments): + result = role.get_orgs_for_user(self.student) + self.assertEqual(result, [self.course_key.org, other_org]) + def test_get_authz_compat_course_access_roles_for_user(self): """ - Thest that get_authz_compat_course_access_roles_for_user doesn't crash when the user + Test that get_authz_compat_course_access_roles_for_user doesn't crash when the user has Libraries V2 or other non-course roles in their assignments. """ lib_assignment = RoleAssignmentData( From 61c6efd4c05ad5a62a98401fdfd10c4360f8325c Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Mon, 30 Mar 2026 11:05:44 -0500 Subject: [PATCH 4/5] chore: update openedx-authz to 1.2.0 --- requirements/edx/base.txt | 3 ++- requirements/edx/development.txt | 3 ++- requirements/edx/doc.txt | 3 ++- requirements/edx/testing.txt | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 1f9df206f0ae..496211cab431 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -514,6 +514,7 @@ edx-opaque-keys[django]==3.1.0 edx-organizations==7.3.0 # via # -r requirements/edx/kernel.in + # openedx-authz # openedx-core edx-proctoring==5.2.0 # via -r requirements/edx/kernel.in @@ -823,7 +824,7 @@ openedx-atlas==0.7.0 # enterprise-integrated-channels # openedx-authz # openedx-forum -openedx-authz==1.0.0 +openedx-authz==1.2.0 # via -r requirements/edx/kernel.in openedx-calc==5.0.0 # via diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index f6f53b6041bb..4398f3686ca8 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -806,6 +806,7 @@ edx-organizations==7.3.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt + # openedx-authz # openedx-core edx-proctoring==5.2.0 # via @@ -1373,7 +1374,7 @@ openedx-atlas==0.7.0 # enterprise-integrated-channels # openedx-authz # openedx-forum -openedx-authz==1.0.0 +openedx-authz==1.2.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index 99606f68983e..02ae81a93b86 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -605,6 +605,7 @@ edx-opaque-keys[django]==3.1.0 edx-organizations==7.3.0 # via # -r requirements/edx/base.txt + # openedx-authz # openedx-core edx-proctoring==5.2.0 # via -r requirements/edx/base.txt @@ -1001,7 +1002,7 @@ openedx-atlas==0.7.0 # enterprise-integrated-channels # openedx-authz # openedx-forum -openedx-authz==1.0.0 +openedx-authz==1.2.0 # via -r requirements/edx/base.txt openedx-calc==5.0.0 # via diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 5ce8448d174b..a68b55f91287 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -627,6 +627,7 @@ edx-opaque-keys[django]==3.1.0 edx-organizations==7.3.0 # via # -r requirements/edx/base.txt + # openedx-authz # openedx-core edx-proctoring==5.2.0 # via -r requirements/edx/base.txt @@ -1048,7 +1049,7 @@ openedx-atlas==0.7.0 # enterprise-integrated-channels # openedx-authz # openedx-forum -openedx-authz==1.0.0 +openedx-authz==1.2.0 # via -r requirements/edx/base.txt openedx-calc==5.0.0 # via From ef4ab95b38a0989a7ea9ece5ac05b12340caf459 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Tue, 31 Mar 2026 11:18:43 -0500 Subject: [PATCH 5/5] fix: update assertion in get_orgs_for_user test to use assertCountEqual --- common/djangoapps/student/tests/test_roles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/djangoapps/student/tests/test_roles.py b/common/djangoapps/student/tests/test_roles.py index 5ad793727c94..c1e861ba6a4c 100644 --- a/common/djangoapps/student/tests/test_roles.py +++ b/common/djangoapps/student/tests/test_roles.py @@ -279,7 +279,7 @@ def test_get_orgs_for_user_authz(self): with patch("openedx_authz.api.users.get_user_role_assignments_filtered", return_value=assignments): result = role.get_orgs_for_user(self.student) - self.assertEqual(result, [self.course_key.org, other_org]) + self.assertCountEqual(result, [self.course_key.org, other_org]) def test_get_authz_compat_course_access_roles_for_user(self): """