feat(dev_tools): add tenant isolation audit to diagnostics page
Add a new "Tenant Isolation" diagnostic tool that scans all stores and reports where configuration values come from — flagging silent inheritance, missing data, and potential data commingling. Also fix merchant dashboard and onboarding integration tests that were missing require_platform dependency override. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -77,7 +77,8 @@ def trace_platform_resolution(
|
||||
))
|
||||
|
||||
# ── Step 1b: Referer fallback (storefront API on localhost) ──
|
||||
host_without_port = host.split(":")[0] if ":" in host else host
|
||||
from middleware.platform_context import _strip_port
|
||||
host_without_port = _strip_port(host)
|
||||
if (
|
||||
host_without_port in _LOCAL_HOSTS
|
||||
and path.startswith("/api/v1/storefront/")
|
||||
@@ -275,6 +276,91 @@ def trace_platform_resolution(
|
||||
|
||||
|
||||
|
||||
# ── Isolation Audit models ──
|
||||
|
||||
|
||||
class IsolationFinding(BaseModel):
|
||||
check: str
|
||||
check_label: str
|
||||
risk: str
|
||||
resolved_value: str | None = None
|
||||
source: str
|
||||
source_label: str
|
||||
is_explicit: bool
|
||||
note: str = ""
|
||||
|
||||
|
||||
class StoreIsolationResult(BaseModel):
|
||||
store_code: str
|
||||
store_name: str
|
||||
merchant_name: str | None = None
|
||||
finding_counts: dict[str, int]
|
||||
findings: list[IsolationFinding]
|
||||
|
||||
|
||||
class IsolationAuditResponse(BaseModel):
|
||||
total_stores: int
|
||||
stores_with_findings: int
|
||||
summary: dict[str, int]
|
||||
results: list[StoreIsolationResult]
|
||||
|
||||
|
||||
@router.get("/isolation-audit", response_model=IsolationAuditResponse)
|
||||
def isolation_audit(
|
||||
store_code: str | None = Query(None, description="Filter to a single store by store_code"),
|
||||
risk: str | None = Query(None, description="Filter by risk level: critical, high, medium"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: UserContext = Depends(get_current_super_admin_api),
|
||||
):
|
||||
"""
|
||||
Audit tenant isolation across all stores.
|
||||
|
||||
Scans every active store and reports where configuration values come from,
|
||||
flagging silent inheritance, missing data, and potential data commingling.
|
||||
"""
|
||||
from app.modules.dev_tools.services.isolation_audit_service import (
|
||||
run_isolation_audit,
|
||||
)
|
||||
|
||||
return run_isolation_audit(db, store_code=store_code, risk_filter=risk)
|
||||
|
||||
|
||||
class DomainHealthEntry(BaseModel):
|
||||
domain: str
|
||||
type: str # "subdomain" or "custom_domain"
|
||||
platform_code: str | None = None
|
||||
expected_store: str | None = None
|
||||
resolved_store: str | None = None
|
||||
status: str # "pass" or "fail"
|
||||
note: str = ""
|
||||
|
||||
|
||||
class DomainHealthResponse(BaseModel):
|
||||
total: int
|
||||
passed: int
|
||||
failed: int
|
||||
details: list[DomainHealthEntry]
|
||||
|
||||
|
||||
@router.get("/domain-health", response_model=DomainHealthResponse)
|
||||
def domain_health_check(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: UserContext = Depends(get_current_super_admin_api),
|
||||
):
|
||||
"""
|
||||
Simulate the middleware resolution pipeline for every active
|
||||
StorePlatform custom subdomain and StoreDomain custom domain.
|
||||
|
||||
Returns a pass/fail report showing whether each domain resolves
|
||||
to the expected store.
|
||||
"""
|
||||
from app.modules.dev_tools.services.domain_health_service import (
|
||||
run_domain_health_check,
|
||||
)
|
||||
|
||||
return run_domain_health_check(db)
|
||||
|
||||
|
||||
def _sanitize(d: dict | None) -> dict | None:
|
||||
"""Remove non-serializable objects from dict."""
|
||||
if d is None:
|
||||
|
||||
Reference in New Issue
Block a user