""" Test script for Scope-Based Admin RBAC Service. Verifies that the RBACService correctly handles scope-based access control. """ import asyncio import sys from sqlalchemy import select, text from app.database import AsyncSessionLocal from app.models.identity.identity import User, UserRole from app.models.marketplace.organization import Organization from app.services.rbac_service import rbac_service, AdminAction async def test_rbac_service(): print("=" * 60) print("RBAC SERVICE TEST SUITE") print("=" * 60) async with AsyncSessionLocal() as db: # 1. Check that scope_type and scope_value columns exist on User print("\n[TEST 1] Checking User model has scope_type/scope_value columns...") result = await db.execute( text("SELECT column_name FROM information_schema.columns WHERE table_schema = 'identity' AND table_name = 'users' AND column_name IN ('scope_type', 'scope_value')") ) cols = result.scalars().all() assert "scope_type" in cols, "scope_type column missing!" assert "scope_value" in cols, "scope_value column missing!" print(f" ✅ Columns found: {cols}") # 2. Check that region and segment columns exist on Organization print("\n[TEST 2] Checking Organization model has region/segment columns...") result = await db.execute( text("SELECT column_name FROM information_schema.columns WHERE table_schema = 'fleet' AND table_name = 'organizations' AND column_name IN ('region', 'segment')") ) cols = result.scalars().all() assert "region" in cols, "region column missing!" assert "segment" in cols, "segment column missing!" print(f" ✅ Columns found: {cols}") # 3. Test RBACService.get_permitted_actions for each role print("\n[TEST 3] Testing get_permitted_actions for each role...") for role in UserRole: actions = rbac_service.get_permitted_actions(role) print(f" {role.value}: {len(actions)} permitted actions") assert isinstance(actions, (set, list)), f"Actions for {role} should be a set or list" print(" ✅ All roles return valid action lists") # 4. Test is_action_permitted print("\n[TEST 4] Testing is_action_permitted...") assert rbac_service.is_action_permitted(UserRole.SUPERADMIN, AdminAction.EDIT_DATA) is True assert rbac_service.is_action_permitted(UserRole.ADMIN, AdminAction.EDIT_DATA) is True assert rbac_service.is_action_permitted(UserRole.MODERATOR, AdminAction.EDIT_DATA) is False assert rbac_service.is_action_permitted(UserRole.SALES_REP, AdminAction.VIEW_DATA) is True assert rbac_service.is_action_permitted(UserRole.SERVICE_MGR, AdminAction.VIEW_DATA) is True print(" ✅ All action permission checks pass") # 5. Test SUPERADMIN bypasses all scope checks print("\n[TEST 5] Testing SUPERADMIN scope bypass...") # Create a mock user object class MockUser: role = UserRole.SUPERADMIN scope_type = None scope_value = None id = 999999 try: await rbac_service.check_admin_access(db, MockUser(), AdminAction.VIEW_DATA, target_org_id=1) print(" ✅ SUPERADMIN bypasses scope check - no PermissionError raised") except PermissionError as e: print(f" ❌ SUPERADMIN should bypass scope check but got: {e}") # 6. Test that a regular user without scope gets PermissionError print("\n[TEST 6] Testing regular user without scope...") class MockUserNoScope: role = UserRole.ADMIN scope_type = None scope_value = None id = 999998 try: await rbac_service.check_admin_access(db, MockUserNoScope(), AdminAction.VIEW_DATA, target_org_id=1) print(" ❌ Should have raised PermissionError for user without scope") except PermissionError as e: print(f" ✅ Correctly raised PermissionError: {e}") # 7. Test GLOBAL scope print("\n[TEST 7] Testing GLOBAL scope...") class MockUserGlobal: role = UserRole.ADMIN scope_type = "GLOBAL" scope_value = None id = 999997 try: await rbac_service.check_admin_access(db, MockUserGlobal(), AdminAction.VIEW_DATA, target_org_id=1) print(" ✅ GLOBAL scope allows access to any org") except PermissionError as e: print(f" ❌ GLOBAL scope should allow access but got: {e}") # 8. Test get_accessible_org_ids for GLOBAL scope print("\n[TEST 8] Testing get_accessible_org_ids for GLOBAL scope...") org_ids = await rbac_service.get_accessible_org_ids(db, MockUserGlobal()) assert isinstance(org_ids, list), "Should return a list" print(f" ✅ GLOBAL scope returns {len(org_ids)} accessible orgs") # 9. Test REGION scope matching print("\n[TEST 9] Testing REGION scope matching...") # First, set an org's region to a known value for testing await db.execute( text("UPDATE fleet.organizations SET region = 'Test_Region' WHERE id = 1") ) await db.commit() class MockUserRegion: role = UserRole.ADMIN scope_type = "REGION" scope_value = "Test_Region" id = 999996 try: await rbac_service.check_admin_access(db, MockUserRegion(), AdminAction.VIEW_DATA, target_org_id=1) print(" ✅ REGION scope matches org with same region") except PermissionError as e: print(f" ❌ REGION scope should match but got: {e}") # Test non-matching region class MockUserRegionWrong: role = UserRole.ADMIN scope_type = "REGION" scope_value = "Other_Region" id = 999995 try: await rbac_service.check_admin_access(db, MockUserRegionWrong(), AdminAction.VIEW_DATA, target_org_id=1) print(" ❌ Should have raised PermissionError for wrong region") except PermissionError as e: print(f" ✅ Correctly denied access for wrong region: {e}") # 10. Test SEGMENT scope matching print("\n[TEST 10] Testing SEGMENT scope matching...") await db.execute( text("UPDATE fleet.organizations SET segment = 'Test_Segment' WHERE id = 1") ) await db.commit() class MockUserSegment: role = UserRole.ADMIN scope_type = "SEGMENT" scope_value = "Test_Segment" id = 999994 try: await rbac_service.check_admin_access(db, MockUserSegment(), AdminAction.VIEW_DATA, target_org_id=1) print(" ✅ SEGMENT scope matches org with same segment") except PermissionError as e: print(f" ❌ SEGMENT scope should match but got: {e}") # 11. Test action not permitted for role print("\n[TEST 11] Testing action not permitted for role...") class MockUserModerator: role = UserRole.MODERATOR scope_type = "GLOBAL" scope_value = None id = 999993 try: await rbac_service.check_admin_access(db, MockUserModerator(), AdminAction.DELETE_DATA, target_org_id=1) print(" ❌ Should have raised PermissionError - MODERATOR cannot DELETE_DATA") except PermissionError as e: print(f" ✅ Correctly denied DELETE_DATA for MODERATOR: {e}") # Cleanup test data await db.execute( text("UPDATE fleet.organizations SET region = NULL, segment = NULL WHERE id = 1") ) await db.commit() print("\n" + "=" * 60) print("ALL TESTS PASSED ✅") print("=" * 60) return True if __name__ == "__main__": success = asyncio.run(test_rbac_service()) sys.exit(0 if success else 1)