Skip to content

Commit 24730a2

Browse files
test: add test case for combined frameworks
1 parent 795d36b commit 24730a2

1 file changed

Lines changed: 103 additions & 0 deletions

File tree

openedx/core/djangoapps/content_libraries/tests/test_content_libraries.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,6 +1573,109 @@ def test_authz_scope_q_object_matches_exact_org_slug_pairs(self):
15731573
f"Result must contain exactly {expected_pairs}, got {result_pairs}",
15741574
)
15751575

1576+
def test_authz_scope_with_combined_authz_and_legacy_permissions(self):
1577+
"""
1578+
Test that the filter returns libraries when user has BOTH AuthZ AND legacy permissions.
1579+
1580+
The CAN_VIEW_THIS_CONTENT_LIBRARY permission uses OR logic:
1581+
is_user_active & (
1582+
is_global_staff |
1583+
(allow_public_read & is_course_creator) |
1584+
HasPermissionInContentLibraryScope(VIEW_LIBRARY) | # AuthZ
1585+
has_explicit_read_permission_for_library # Legacy
1586+
)
1587+
1588+
This means a user with BOTH types of permissions should get access through EITHER system.
1589+
1590+
Test scenario:
1591+
- lib1: User has AuthZ permission only
1592+
- lib2: User has legacy permission only
1593+
- lib3: User has BOTH AuthZ AND legacy permissions
1594+
- lib4: User has NO permissions
1595+
1596+
Expected behavior:
1597+
- Filter returns lib1, lib2, and lib3 (NOT lib4)
1598+
- Having both permission types doesn't break filtering
1599+
- Each permission system contributes its authorized libraries
1600+
"""
1601+
user = UserFactory.create(username="combined_perm_user", is_staff=False)
1602+
1603+
Organization.objects.get_or_create(short_name="comb-org", defaults={"name": "Combined Org"})
1604+
1605+
with self.as_user(self.admin_user):
1606+
lib1 = self._create_library(slug="comb-lib1", org="comb-org", title="AuthZ Only Library")
1607+
lib2 = self._create_library(slug="comb-lib2", org="comb-org", title="Legacy Only Library")
1608+
lib3 = self._create_library(slug="comb-lib3", org="comb-org", title="Both AuthZ and Legacy Library")
1609+
lib4 = self._create_library(slug="comb-lib4", org="comb-org", title="No Permissions Library")
1610+
1611+
# Retrieve library objects for permission assignment
1612+
lib1_obj = ContentLibrary.objects.get_by_key(LibraryLocatorV2.from_string(lib1['id']))
1613+
lib2_obj = ContentLibrary.objects.get_by_key(LibraryLocatorV2.from_string(lib2['id']))
1614+
lib3_obj = ContentLibrary.objects.get_by_key(LibraryLocatorV2.from_string(lib3['id']))
1615+
1616+
# Set up legacy permissions: lib2 (legacy only), lib3 (both)
1617+
ContentLibraryPermission.objects.create(
1618+
library=lib2_obj,
1619+
user=user,
1620+
access_level=ContentLibraryPermission.READ_LEVEL,
1621+
)
1622+
ContentLibraryPermission.objects.create(
1623+
library=lib3_obj,
1624+
user=user,
1625+
access_level=ContentLibraryPermission.READ_LEVEL,
1626+
)
1627+
1628+
with patch(
1629+
'openedx.core.djangoapps.content_libraries.permissions.get_scopes_for_user_and_permission'
1630+
) as mock_get_scopes:
1631+
# Set up AuthZ permissions: lib1 (AuthZ only), lib3 (both)
1632+
lib1_key = LibraryLocatorV2.from_string(lib1['id'])
1633+
lib3_key = LibraryLocatorV2.from_string(lib3['id'])
1634+
1635+
mock_get_scopes.return_value = [
1636+
type('Scope', (), {'library_key': lib1_key})(),
1637+
type('Scope', (), {'library_key': lib3_key})(),
1638+
]
1639+
1640+
all_libs = ContentLibrary.objects.filter(slug__in=['comb-lib1', 'comb-lib2', 'comb-lib3', 'comb-lib4'])
1641+
filtered = perms[CAN_VIEW_THIS_CONTENT_LIBRARY].filter(user, all_libs).distinct()
1642+
1643+
# TEST: Verify exactly 3 libraries returned (lib1, lib2, lib3 - NOT lib4)
1644+
self.assertEqual(
1645+
filtered.count(),
1646+
3,
1647+
"Should return exactly 3 libraries: AuthZ-only, legacy-only, and both",
1648+
)
1649+
1650+
# TEST: Verify correct libraries are included
1651+
slugs = set(filtered.values_list('slug', flat=True))
1652+
self.assertIn('comb-lib1', slugs, "lib1 should be accessible via AuthZ permission")
1653+
self.assertIn('comb-lib2', slugs, "lib2 should be accessible via legacy permission")
1654+
self.assertIn('comb-lib3', slugs, "lib3 should be accessible via BOTH AuthZ and legacy permissions")
1655+
self.assertNotIn('comb-lib4', slugs, "lib4 should NOT be accessible (no permissions)")
1656+
1657+
# TEST: Verify lib3 doesn't get duplicated despite having both permission types
1658+
lib3_results = filtered.filter(slug='comb-lib3')
1659+
self.assertEqual(
1660+
lib3_results.count(),
1661+
1,
1662+
"lib3 should appear exactly once despite having both AuthZ and legacy permissions",
1663+
)
1664+
1665+
# TEST: Verify the permission sources work independently
1666+
# This demonstrates the OR logic: user gets access if EITHER permission type grants it
1667+
result_pairs = set(filtered.values_list('org__short_name', 'slug'))
1668+
expected_pairs = {
1669+
('comb-org', 'comb-lib1'), # AuthZ only
1670+
('comb-org', 'comb-lib2'), # Legacy only
1671+
('comb-org', 'comb-lib3'), # Both
1672+
}
1673+
self.assertEqual(
1674+
result_pairs,
1675+
expected_pairs,
1676+
f"Should get exactly the 3 authorized libraries via OR logic, got {result_pairs}",
1677+
)
1678+
15761679

15771680
@ddt.ddt
15781681
class ContentLibraryXBlockValidationTest(APITestCase):

0 commit comments

Comments
 (0)