diff --git a/common/djangoapps/student/roles.py b/common/djangoapps/student/roles.py index 3ae766dffc89..b2f4bd5b20fe 100644 --- a/common/djangoapps/student/roles.py +++ b/common/djangoapps/student/roles.py @@ -532,9 +532,13 @@ 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_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) def _legacy_get_orgs_for_user(self, user) -> list[str]: """ diff --git a/common/djangoapps/student/tests/test_roles.py b/common/djangoapps/student/tests/test_roles.py index 9485a7ca84d1..018f4220f294 100644 --- a/common/djangoapps/student/tests/test_roles.py +++ b/common/djangoapps/student/tests/test_roles.py @@ -2,44 +2,44 @@ Tests of student.roles """ +from unittest.mock import patch import ddt -from unittest.mock import patch from django.contrib.auth.models import Permission from django.test import TestCase 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.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory -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 from common.djangoapps.student.models import CourseAccessRoleHistory, User +from common.djangoapps.student.role_helpers import get_course_roles, has_staff_roles from common.djangoapps.student.roles import ( + ROLE_CACHE_UNGROUPED_ROLES__KEY, AuthzCompatCourseAccessRole, CourseAccessRole, CourseBetaTesterRole, + CourseDataResearcherRole, + CourseFinanceAdminRole, CourseInstructorRole, - CourseRole, CourseLimitedStaffRole, - CourseStaffRole, - CourseFinanceAdminRole, + CourseRole, CourseSalesAdminRole, - LibraryUserRole, - CourseDataResearcherRole, + CourseStaffRole, GlobalStaff, + LibraryUserRole, OrgContentCreatorRole, OrgInstructorRole, OrgStaffRole, RoleCache, get_authz_compat_course_access_roles_for_user, get_role_cache_key_for_course, - ROLE_CACHE_UNGROUPED_ROLES__KEY ) -from common.djangoapps.student.role_helpers import get_course_roles, has_staff_roles from common.djangoapps.student.tests.factories import AnonymousUserFactory, InstructorFactory, StaffFactory, UserFactory +from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory from openedx.core.toggles import AUTHZ_COURSE_AUTHORING_FLAG @@ -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.assertCountEqual(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( diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index d3d35e15878a..6724fd1f0700 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 750f3376f86a..f8f0837b098f 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 91f4061d6e53..5a1a4fc2c19b 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 49cb7b98c49b..50a7b142d294 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 @@ -1050,7 +1051,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