fix(iwooos): scope control plane database read
This commit is contained in:
@@ -616,6 +616,7 @@ def build_iwooos_security_asset_control_plane(
|
||||
ai_trace_row: Any,
|
||||
siem_row: Any,
|
||||
audit_row: Any,
|
||||
source_health: Iterable[dict[str, Any]] | None = None,
|
||||
generated_at: datetime | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Build a public-safe aggregate without exposing raw asset or event identities."""
|
||||
@@ -624,6 +625,20 @@ def build_iwooos_security_asset_control_plane(
|
||||
coverage_source = list(coverage_rows)
|
||||
compliance_source = list(compliance_rows)
|
||||
automation_source = list(automation_rows)
|
||||
source_health_rows = list(source_health or [])
|
||||
unavailable_sources = [
|
||||
row for row in source_health_rows if row.get("status") != "ready"
|
||||
]
|
||||
core_source_unavailable = any(
|
||||
row.get("source_id") in {"asset_discovery", "asset_inventory"}
|
||||
for row in unavailable_sources
|
||||
)
|
||||
if core_source_unavailable:
|
||||
source_status = "live_database_unavailable"
|
||||
elif unavailable_sources:
|
||||
source_status = "live_database_partial"
|
||||
else:
|
||||
source_status = "live_database_aggregate"
|
||||
|
||||
by_type = {
|
||||
str(_value(row, "asset_type", "unknown")): int(_value(row, "cnt", 0) or 0)
|
||||
@@ -831,6 +846,17 @@ def build_iwooos_security_asset_control_plane(
|
||||
)
|
||||
|
||||
work_items: list[dict[str, Any]] = []
|
||||
if unavailable_sources:
|
||||
work_items.append(
|
||||
_work_item(
|
||||
"AIA-P0-006-00",
|
||||
"P0",
|
||||
"source_health",
|
||||
"修復控制平面資料源",
|
||||
len(unavailable_sources),
|
||||
"依 source_health reason code 修復 schema、權限或資料源相容性。",
|
||||
)
|
||||
)
|
||||
if asset_total == 0 or not discovery_fresh:
|
||||
work_items.append(
|
||||
_work_item(
|
||||
@@ -947,9 +973,16 @@ def build_iwooos_security_asset_control_plane(
|
||||
return {
|
||||
"schema_version": _SCHEMA_VERSION,
|
||||
"generated_at": now.isoformat(),
|
||||
"status": "ready" if asset_total > 0 and discovery_fresh else "degraded",
|
||||
"source_status": "live_database_aggregate",
|
||||
"status": (
|
||||
"ready"
|
||||
if asset_total > 0 and discovery_fresh and not unavailable_sources
|
||||
else "degraded"
|
||||
),
|
||||
"source_status": source_status,
|
||||
"summary": {
|
||||
"source_count": len(source_health_rows),
|
||||
"ready_source_count": len(source_health_rows) - len(unavailable_sources),
|
||||
"unavailable_source_count": len(unavailable_sources),
|
||||
"managed_asset_count": asset_total,
|
||||
"expected_asset_type_count": len(_ASSET_TYPES),
|
||||
"present_asset_type_count": len(present_types),
|
||||
@@ -1010,6 +1043,7 @@ def build_iwooos_security_asset_control_plane(
|
||||
"same_run_closed_loop_count": 0,
|
||||
},
|
||||
"security_audit": audit,
|
||||
"source_health": source_health_rows,
|
||||
"work_items": work_items,
|
||||
"boundaries": {
|
||||
"aggregate_only": True,
|
||||
@@ -1239,7 +1273,7 @@ class IwoooSSecurityAssetControlPlaneService:
|
||||
)
|
||||
|
||||
async def _load_snapshot(self) -> dict[str, Any]:
|
||||
async with get_db_context() as db:
|
||||
async with get_db_context("awoooi") as db:
|
||||
discovery_result = await db.execute(
|
||||
_sql(
|
||||
"""
|
||||
|
||||
@@ -230,6 +230,32 @@ def test_service_redacts_database_exception_details(monkeypatch) -> None:
|
||||
assert "private-host" not in text
|
||||
|
||||
|
||||
def test_service_uses_canonical_awoooi_project_scope(monkeypatch) -> None:
|
||||
service = IwoooSSecurityAssetControlPlaneService()
|
||||
requested_projects: list[str] = []
|
||||
|
||||
class FailingContext:
|
||||
async def __aenter__(self):
|
||||
raise RuntimeError("stop after project scope capture")
|
||||
|
||||
async def __aexit__(self, exc_type, exc, traceback):
|
||||
return False
|
||||
|
||||
def fake_db_context(project_id: str):
|
||||
requested_projects.append(project_id)
|
||||
return FailingContext()
|
||||
|
||||
monkeypatch.setattr(
|
||||
"src.services.iwooos_security_asset_control_plane.get_db_context",
|
||||
fake_db_context,
|
||||
)
|
||||
payload = asyncio.run(service.get_snapshot())
|
||||
|
||||
assert requested_projects == ["awoooi"]
|
||||
assert payload["status"] == "degraded"
|
||||
assert payload["reason_code"] == "database_query_failed"
|
||||
|
||||
|
||||
def test_public_api_returns_live_aggregate(monkeypatch) -> None:
|
||||
payload = _ready_payload()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user