Files
boc/iom/multi_tenancy/tenant.py
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- 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
2026-06-29 16:24:48 +00:00

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()