Files
JQC_multi_tenant/app/tenancy/resolver.py
T
2026-06-26 16:37:44 -04:00

52 lines
1.5 KiB
Python

"""
app/tenancy/resolver.py
-----------------------
Resolve an incoming Host header to a tenant by querying the control plane.
The `control` package is imported lazily inside the function so that the data
plane carries no import-time dependency on the control plane when
multi-tenancy is disabled.
Resolution rules:
* exact match on tenant_domains.domain (host, lowercased, port stripped)
* tenant must be status='active'
* custom domains must be verified; subdomains we issue are trusted
Returns a detached TenantContext or None.
"""
from app.tenancy.context import TenantContext
def resolve_tenant(host):
if not host:
return None
# Lazy import — keeps control plane optional when MT is disabled.
from control.base import control_session
from control.models import TenantDomain
with control_session() as s:
domain = (
s.query(TenantDomain)
.filter(TenantDomain.domain == host)
.first()
)
if domain is None:
return None
if domain.kind == 'custom' and not domain.verified:
return None
tenant = domain.tenant
if tenant is None or tenant.status != 'active':
return None
# Materialise everything needed while the session is still open
# (db_uri decrypts the stored credential).
return TenantContext(
id=tenant.id,
slug=tenant.slug,
name=tenant.name,
plan_id=tenant.plan_id,
db_uri=tenant.db_uri,
)