3939from openedx .core .djangolib .testing .utils import skip_unless_cms
4040from openedx_authz .constants .permissions import VIEW_LIBRARY
4141
42- from ..models import ContentLibrary
42+ from ..models import ContentLibrary , ContentLibraryPermission
4343from ..permissions import CAN_VIEW_THIS_CONTENT_LIBRARY , HasPermissionInContentLibraryScope
4444
4545
@@ -1220,14 +1220,36 @@ def test_uncaught_error_creates_error_log(self):
12201220
12211221 self .assertEqual (task_data , expected )
12221222
1223+
1224+ @skip_unless_cms
1225+ class ContentLibrariesAuthZTestCase (ContentLibrariesRestApiTest ):
1226+ """
1227+ Tests for Content Libraries AuthZ integration via openedx-authz.
1228+
1229+ These tests verify the HasPermissionInContentLibraryScope Bridgekeeper rule
1230+ integrates correctly with the openedx-authz authorization system (Casbin).
1231+ See: https://github.com/openedx/openedx-authz/
1232+
1233+ IMPORTANT: These tests explicitly remove legacy ContentLibraryPermission grants
1234+ to ensure ONLY the AuthZ system is being tested, not the legacy fallback.
1235+ """
1236+
1237+ def setUp (self ):
1238+ super ().setUp ()
1239+ # The parent class provides self.user (a staff user) and self.organization
1240+ # Set up admin_user as an alias to self.user for test readability
1241+ self .admin_user = self .user
1242+ # Set up org_short_name for convenience
1243+ self .org_short_name = self .organization .short_name
1244+
12231245 def test_authz_scope_filters_by_authorized_libraries (self ):
12241246 """
12251247 Test that HasPermissionInContentLibraryScope rule filters libraries
12261248 based on authorized org/slug combinations.
12271249
12281250 Given:
12291251 - 3 libraries: lib1 (org1), lib2 (org2), lib3 (org1)
1230- - User authorized for lib1 and lib2 only
1252+ - User authorized for lib1 and lib2 only via AuthZ (NO legacy permissions)
12311253
12321254 Expected:
12331255 - Filter returns exactly 2 libraries (lib1 and lib2)
@@ -1244,6 +1266,9 @@ def test_authz_scope_filters_by_authorized_libraries(self):
12441266 lib2 = self ._create_library (slug = "lib2" , org = "org2" , title = "Library 2" )
12451267 lib3 = self ._create_library (slug = "lib3" , org = "org1" , title = "Library 3" )
12461268
1269+ # CRITICAL: Ensure user has NO legacy permissions (test ONLY AuthZ filtering)
1270+ ContentLibraryPermission .objects .filter (user = user ).delete ()
1271+
12471272 with patch (
12481273 'openedx.core.djangoapps.content_libraries.permissions.get_scopes_for_user_and_permission'
12491274 ) as mock_get_scopes :
@@ -1279,6 +1304,7 @@ def test_authz_scope_individual_check_with_permission(self):
12791304 - Non-staff user
12801305 - Library exists
12811306 - Authorization system grants permission (mocked)
1307+ - NO legacy permissions
12821308
12831309 Expected:
12841310 - check() returns True
@@ -1290,6 +1316,9 @@ def test_authz_scope_individual_check_with_permission(self):
12901316
12911317 library_obj = ContentLibrary .objects .get_by_key (LibraryLocatorV2 .from_string (lib ["id" ]))
12921318
1319+ # CRITICAL: Ensure user has NO legacy permissions (test ONLY AuthZ)
1320+ ContentLibraryPermission .objects .filter (user = user ).delete ()
1321+
12931322 with patch ("openedx.core.djangoapps.content_libraries.permissions.is_user_allowed" , return_value = True ):
12941323 result = perms [CAN_VIEW_THIS_CONTENT_LIBRARY ].check (user , library_obj )
12951324
@@ -1304,6 +1333,7 @@ def test_authz_scope_individual_check_without_permission(self):
13041333 - Non-staff user
13051334 - Non-public library
13061335 - Authorization system denies permission (mocked)
1336+ - NO legacy permissions
13071337
13081338 Expected:
13091339 - check() returns False
@@ -1315,6 +1345,9 @@ def test_authz_scope_individual_check_without_permission(self):
13151345
13161346 library_obj = ContentLibrary .objects .get_by_key (LibraryLocatorV2 .from_string (lib ['id' ]))
13171347
1348+ # CRITICAL: Ensure user has NO legacy permissions (test ONLY AuthZ)
1349+ ContentLibraryPermission .objects .filter (user = user ).delete ()
1350+
13181351 with patch ('openedx.core.djangoapps.content_libraries.permissions.is_user_allowed' , return_value = False ):
13191352 result = perms [CAN_VIEW_THIS_CONTENT_LIBRARY ].check (user , library_obj )
13201353
@@ -1332,6 +1365,7 @@ def test_authz_scope_handles_empty_scopes(self):
13321365 - Non-staff user
13331366 - Library exists in database
13341367 - Authorization system returns empty scope list (mocked)
1368+ - NO legacy permissions
13351369
13361370 Expected:
13371371 - Filter returns 0 libraries
@@ -1342,6 +1376,9 @@ def test_authz_scope_handles_empty_scopes(self):
13421376 with self .as_user (self .admin_user ):
13431377 lib = self ._create_library (slug = "empty-lib" , title = "Empty Scopes Test" )
13441378
1379+ # CRITICAL: Ensure user has NO legacy permissions (test ONLY AuthZ)
1380+ ContentLibraryPermission .objects .filter (user = user ).delete ()
1381+
13451382 with patch (
13461383 'openedx.core.djangoapps.content_libraries.permissions.get_scopes_for_user_and_permission' ,
13471384 return_value = []
@@ -1369,6 +1406,9 @@ def test_authz_scope_q_object_has_correct_structure(self):
13691406
13701407 Multiple scopes should be OR'd:
13711408 (Q(org__short_name='org1') & Q(slug='lib1')) | (Q(org__short_name='org2') & Q(slug='lib2'))
1409+
1410+ Note: This test focuses on Q object structure, not filtering behavior,
1411+ so legacy permissions don't affect the outcome.
13721412 """
13731413 user = UserFactory .create (username = "q_user" )
13741414 rule = HasPermissionInContentLibraryScope (VIEW_LIBRARY , filter_keys = ['org' , 'slug' ])
@@ -1467,6 +1507,9 @@ def test_authz_scope_q_object_matches_exact_org_slug_pairs(self):
14671507 lib3 = self ._create_library (slug = "pair-lib3" , org = "pair-org1" , title = "Pair Lib 3" ) # Same org as lib1
14681508 lib4 = self ._create_library (slug = "pair-lib1" , org = "pair-org3" , title = "Pair Lib 4" ) # Same slug as lib1
14691509
1510+ # CRITICAL: Ensure user has NO legacy permissions (test ONLY AuthZ filtering)
1511+ ContentLibraryPermission .objects .filter (user = user ).delete ()
1512+
14701513 with patch (
14711514 'openedx.core.djangoapps.content_libraries.permissions.get_scopes_for_user_and_permission'
14721515 ) as mock_get_scopes :
0 commit comments