bae705aa97
- Add NFC ePassport roadmap (ICAO 9303, eIDAS) - Add TensorFlow.js edge face detection (BlazeFace) - Add structured audit logger (GDPR-compliant) - Risk scoring support Part of KYC Apple Native UX v1.1.0
254 lines
7.8 KiB
Python
254 lines
7.8 KiB
Python
"""
|
|
Multi-tenancy Support
|
|
Support for multiple organizations on the same platform
|
|
"""
|
|
|
|
from typing import Dict, List, Optional
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
|
|
|
|
class TenantTier(str, Enum):
|
|
"""Tenant subscription tiers"""
|
|
FREE = "free"
|
|
BASIC = "basic"
|
|
PROFESSIONAL = "professional"
|
|
ENTERPRISE = "enterprise"
|
|
|
|
|
|
@dataclass
|
|
class Tenant:
|
|
"""Organization tenant"""
|
|
id: str
|
|
name: str
|
|
tier: TenantTier
|
|
created_at: str
|
|
settings: Dict
|
|
limits: Dict
|
|
features: List[str]
|
|
active: bool
|
|
|
|
def to_dict(self) -> Dict:
|
|
return {
|
|
"id": self.id,
|
|
"name": self.name,
|
|
"tier": self.tier.value,
|
|
"created_at": self.created_at,
|
|
"settings": self.settings,
|
|
"limits": self.limits,
|
|
"features": self.features,
|
|
"active": self.active
|
|
}
|
|
|
|
|
|
class TenantManager:
|
|
"""
|
|
Multi-tenancy manager
|
|
|
|
Features:
|
|
- Tenant isolation
|
|
- Resource limits
|
|
- Feature flags
|
|
- Custom branding
|
|
- Data segregation
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.tenants: Dict[str, Tenant] = {}
|
|
self.default_limits = {
|
|
TenantTier.FREE: {
|
|
"max_observations": 100,
|
|
"max_users": 1,
|
|
"max_objects": 50,
|
|
"max_storage_mb": 100,
|
|
"api_calls_per_day": 1000,
|
|
"features": ["basic_observations", "basic_reports"]
|
|
},
|
|
TenantTier.BASIC: {
|
|
"max_observations": 1000,
|
|
"max_users": 5,
|
|
"max_objects": 500,
|
|
"max_storage_mb": 1000,
|
|
"api_calls_per_day": 10000,
|
|
"features": ["basic_observations", "basic_reports", "api_access", "email_support"]
|
|
},
|
|
TenantTier.PROFESSIONAL: {
|
|
"max_observations": 10000,
|
|
"max_users": 25,
|
|
"max_objects": 5000,
|
|
"max_storage_mb": 10000,
|
|
"api_calls_per_day": 100000,
|
|
"features": ["advanced_observations", "advanced_reports", "api_access", "priority_support", "custom_branding", "webhooks"]
|
|
},
|
|
TenantTier.ENTERPRISE: {
|
|
"max_observations": -1, # Unlimited
|
|
"max_users": -1,
|
|
"max_objects": -1,
|
|
"max_storage_mb": -1,
|
|
"api_calls_per_day": -1,
|
|
"features": ["all_features", "dedicated_support", "sla", "custom_development", "on_premise"]
|
|
}
|
|
}
|
|
|
|
def create_tenant(
|
|
self,
|
|
name: str,
|
|
tier: TenantTier = TenantTier.FREE,
|
|
settings: Optional[Dict] = None
|
|
) -> Tenant:
|
|
"""Create new tenant"""
|
|
tenant_id = f"TEN-{datetime.utcnow().strftime('%Y%m%d%H%M%S')}"
|
|
|
|
limits = self.default_limits[tier].copy()
|
|
features = limits.pop("features", [])
|
|
|
|
tenant = Tenant(
|
|
id=tenant_id,
|
|
name=name,
|
|
tier=tier,
|
|
created_at=datetime.utcnow().isoformat(),
|
|
settings=settings or {},
|
|
limits=limits,
|
|
features=features,
|
|
active=True
|
|
)
|
|
|
|
self.tenants[tenant_id] = tenant
|
|
return tenant
|
|
|
|
def get_tenant(self, tenant_id: str) -> Optional[Tenant]:
|
|
"""Get tenant by ID"""
|
|
return self.tenants.get(tenant_id)
|
|
|
|
def update_tenant(self, tenant_id: str, updates: Dict) -> Optional[Tenant]:
|
|
"""Update tenant"""
|
|
tenant = self.tenants.get(tenant_id)
|
|
if not tenant:
|
|
return None
|
|
|
|
if "name" in updates:
|
|
tenant.name = updates["name"]
|
|
if "tier" in updates:
|
|
tenant.tier = TenantTier(updates["tier"])
|
|
# Update limits and features
|
|
limits = self.default_limits[tenant.tier].copy()
|
|
tenant.features = limits.pop("features", [])
|
|
tenant.limits.update(limits)
|
|
if "settings" in updates:
|
|
tenant.settings.update(updates["settings"])
|
|
if "active" in updates:
|
|
tenant.active = updates["active"]
|
|
|
|
return tenant
|
|
|
|
def delete_tenant(self, tenant_id: str) -> bool:
|
|
"""Delete tenant (soft delete)"""
|
|
tenant = self.tenants.get(tenant_id)
|
|
if tenant:
|
|
tenant.active = False
|
|
return True
|
|
return False
|
|
|
|
def check_limit(self, tenant_id: str, limit_type: str, current_value: int) -> bool:
|
|
"""Check if tenant is within limits"""
|
|
tenant = self.tenants.get(tenant_id)
|
|
if not tenant:
|
|
return False
|
|
|
|
limit = tenant.limits.get(limit_type, 0)
|
|
if limit == -1: # Unlimited
|
|
return True
|
|
|
|
return current_value < limit
|
|
|
|
def has_feature(self, tenant_id: str, feature: str) -> bool:
|
|
"""Check if tenant has feature"""
|
|
tenant = self.tenants.get(tenant_id)
|
|
if not tenant:
|
|
return False
|
|
|
|
return feature in tenant.features or "all_features" in tenant.features
|
|
|
|
def get_tenant_stats(self, tenant_id: str) -> Dict:
|
|
"""Get tenant statistics"""
|
|
tenant = self.tenants.get(tenant_id)
|
|
if not tenant:
|
|
return {}
|
|
|
|
return {
|
|
"tenant": tenant.to_dict(),
|
|
"usage": {
|
|
"observations": 0, # In production, query database
|
|
"users": 0,
|
|
"objects": 0,
|
|
"storage_mb": 0
|
|
},
|
|
"limits_remaining": {
|
|
k: v - 0 if v > 0 else "unlimited"
|
|
for k, v in tenant.limits.items()
|
|
}
|
|
}
|
|
|
|
def list_tenants(self, active_only: bool = True) -> List[Dict]:
|
|
"""List all tenants"""
|
|
tenants = self.tenants.values()
|
|
if active_only:
|
|
tenants = [t for t in tenants if t.active]
|
|
return [t.to_dict() for t in tenants]
|
|
|
|
|
|
# Example usage
|
|
def example_multi_tenancy():
|
|
"""Example: Multi-tenancy"""
|
|
print("=== Multi-Tenancy Demo ===\n")
|
|
|
|
manager = TenantManager()
|
|
|
|
# Create tenants
|
|
print("Creating tenants...")
|
|
|
|
free_tenant = manager.create_tenant(
|
|
name="Small Municipality",
|
|
tier=TenantTier.FREE
|
|
)
|
|
print(f"Free tenant: {free_tenant.name} ({free_tenant.id})")
|
|
print(f" Limits: {free_tenant.limits}")
|
|
|
|
pro_tenant = manager.create_tenant(
|
|
name="Large City",
|
|
tier=TenantTier.PROFESSIONAL,
|
|
settings={"custom_domain": "city.iom.app"}
|
|
)
|
|
print(f"Pro tenant: {pro_tenant.name} ({pro_tenant.id})")
|
|
print(f" Features: {pro_tenant.features}")
|
|
|
|
enterprise_tenant = manager.create_tenant(
|
|
name="National Agency",
|
|
tier=TenantTier.ENTERPRISE
|
|
)
|
|
print(f"Enterprise tenant: {enterprise_tenant.name} ({enterprise_tenant.id})")
|
|
|
|
# Check limits
|
|
print("\nChecking limits...")
|
|
print(f"Free tenant can create observation (0/100): {manager.check_limit(free_tenant.id, 'max_observations', 0)}")
|
|
print(f"Free tenant can create observation (100/100): {manager.check_limit(free_tenant.id, 'max_observations', 100)}")
|
|
print(f"Enterprise tenant unlimited: {manager.check_limit(enterprise_tenant.id, 'max_observations', 999999)}")
|
|
|
|
# Check features
|
|
print("\nChecking features...")
|
|
print(f"Free has API access: {manager.has_feature(free_tenant.id, 'api_access')}")
|
|
print(f"Pro has API access: {manager.has_feature(pro_tenant.id, 'api_access')}")
|
|
print(f"Pro has custom branding: {manager.has_feature(pro_tenant.id, 'custom_branding')}")
|
|
|
|
# List tenants
|
|
print("\nAll tenants:")
|
|
for tenant in manager.list_tenants():
|
|
print(f" {tenant['name']} ({tenant['tier']})")
|
|
|
|
return manager
|
|
|
|
|
|
if __name__ == '__main__':
|
|
example_multi_tenancy()
|