187 lines
7.8 KiB
Python
187 lines
7.8 KiB
Python
"""
|
|
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)
|