Skip to content

Commit 0b43e07

Browse files
authored
fix: authz compat layer was failing on libraries v2 keys (openedx#38131)
1 parent 110ec0c commit 0b43e07

2 files changed

Lines changed: 46 additions & 13 deletions

File tree

common/djangoapps/student/roles.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from opaque_keys.edx.keys import CourseKey
1717
from opaque_keys.edx.locator import CourseLocator
1818
from openedx_authz.api import users as authz_api
19+
from openedx_authz.api.data import RoleAssignmentData, CourseOverviewData
1920
from openedx_authz.constants import roles as authz_roles
2021

2122
from common.djangoapps.student.models import CourseAccessRole
@@ -73,6 +74,24 @@ def authz_add_role(user: User, authz_role: str, course_key: str):
7374
legacy_role = get_legacy_role_from_authz_role(authz_role)
7475
emit_course_access_role_added(user, course_locator, course_locator.org, legacy_role)
7576

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
88+
89+
def get_org_from_key(key: str) -> str:
90+
"""
91+
Get the org from a course key.
92+
"""
93+
parsed_key = CourseKey.from_string(key)
94+
return parsed_key.org
7695

7796
def register_access_role(cls):
7897
"""
@@ -136,13 +155,12 @@ def get_authz_compat_course_access_roles_for_user(user: User) -> set[AuthzCompat
136155
Retrieve all CourseAccessRole objects for a given user and convert them to AuthzCompatCourseAccessRole objects.
137156
"""
138157
compat_role_assignments = set()
139-
assignments = authz_api.get_user_role_assignments(user_external_key=user.username)
158+
assignments = authz_get_all_course_assignments_for_user(user)
140159
for assignment in assignments:
141160
for role in assignment.roles:
142161
legacy_role = get_legacy_role_from_authz_role(authz_role=role.external_key)
143162
course_key = assignment.scope.external_key
144-
parsed_key = CourseKey.from_string(course_key)
145-
org = parsed_key.org
163+
org = get_org_from_key(course_key)
146164
compat_role = AuthzCompatCourseAccessRole(
147165
user_id=user.id,
148166
username=user.username,
@@ -825,9 +843,7 @@ def courses_with_role(self) -> set[AuthzCompatCourseAccessRole]:
825843

826844
# Get all assignments for a user to a role
827845
new_authz_roles = [get_authz_role_from_legacy_role(role) for role in roles]
828-
all_authz_user_assignments = authz_api.get_user_role_assignments(
829-
user_external_key=self.user.username
830-
)
846+
all_authz_user_assignments = authz_get_all_course_assignments_for_user(self.user)
831847

832848
all_assignments = set()
833849

@@ -847,8 +863,7 @@ def courses_with_role(self) -> set[AuthzCompatCourseAccessRole]:
847863
continue
848864
legacy_role = get_legacy_role_from_authz_role(authz_role=role.external_key)
849865
course_key = assignment.scope.external_key
850-
parsed_key = CourseKey.from_string(course_key)
851-
org = parsed_key.org
866+
org = get_org_from_key(course_key)
852867
all_assignments.add(AuthzCompatCourseAccessRole(
853868
user_id=self.user.id,
854869
username=self.user.username,
@@ -880,9 +895,7 @@ def has_courses_with_role(self, org: str | None = None) -> bool:
880895

881896
# Then check for authz assignments
882897
new_authz_roles = [get_authz_role_from_legacy_role(role) for role in roles]
883-
all_authz_user_assignments = authz_api.get_user_role_assignments(
884-
user_external_key=self.user.username
885-
)
898+
all_authz_user_assignments = authz_get_all_course_assignments_for_user(self.user)
886899

887900
for assignment in all_authz_user_assignments:
888901
for role in assignment.roles:
@@ -892,7 +905,7 @@ def has_courses_with_role(self, org: str | None = None) -> bool:
892905
# There is at least one assignment, short circuit
893906
return True
894907
course_key = assignment.scope.external_key
895-
parsed_key = CourseKey.from_string(course_key)
896-
if org == parsed_key.org:
908+
parsed_org = get_org_from_key(course_key)
909+
if org == parsed_org:
897910
return True
898911
return False

common/djangoapps/student/tests/test_roles.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44

55

66
import ddt
7+
from unittest.mock import patch
78
from django.contrib.auth.models import Permission
89
from django.test import TestCase
910
from edx_toggles.toggles.testutils import override_waffle_flag
1011
from opaque_keys.edx.keys import CourseKey
1112
from opaque_keys.edx.locator import LibraryLocator
1213

1314
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
15+
from openedx_authz.api.data import ContentLibraryData, RoleAssignmentData, RoleData, UserData
1416
from openedx_authz.engine.enforcer import AuthzEnforcer
1517

1618
from common.djangoapps.student.admin import CourseAccessRoleHistoryAdmin
@@ -32,6 +34,7 @@
3234
OrgInstructorRole,
3335
OrgStaffRole,
3436
RoleCache,
37+
get_authz_compat_course_access_roles_for_user,
3538
get_role_cache_key_for_course,
3639
ROLE_CACHE_UNGROUPED_ROLES__KEY
3740
)
@@ -236,6 +239,23 @@ def test_get_orgs_for_user(self):
236239
role_second_org.add_users(self.student)
237240
assert len(role.get_orgs_for_user(self.student)) == 2
238241

242+
def test_get_authz_compat_course_access_roles_for_user(self):
243+
"""
244+
Thest that get_authz_compat_course_access_roles_for_user doesn't crash when the user
245+
has Libraries V2 or other non-course roles in their assignments.
246+
"""
247+
lib_assignment = RoleAssignmentData(
248+
subject=UserData(external_key=self.student.username),
249+
roles=[RoleData(external_key='test-role')],
250+
scope=ContentLibraryData(external_key='lib:edX:test-lib'),
251+
)
252+
with patch(
253+
'openedx_authz.api.users.get_subject_role_assignments',
254+
return_value=[lib_assignment],
255+
):
256+
result = get_authz_compat_course_access_roles_for_user(self.student)
257+
assert result == set()
258+
239259

240260
@ddt.ddt
241261
class RoleCacheTestCase(TestCase): # lint-amnesty, pylint: disable=missing-class-docstring

0 commit comments

Comments
 (0)