feat(datasources): enforce policy-aware data visibility
Module Package Release / publish-packages (push) Successful in 11s

This commit is contained in:
2026-08-21 20:31:52 +02:00
parent 97ca670bfe
commit b54d1919e4
18 changed files with 1675 additions and 72 deletions
+24 -1
View File
@@ -1,5 +1,7 @@
from __future__ import annotations
import hashlib
import json
from collections.abc import Mapping
from fastapi import APIRouter, Depends, HTTPException, Query, status
@@ -370,7 +372,17 @@ def api_update_datasource_governance(
action="datasources.governance.updated",
object_type="datasource",
object_id=item.ref,
details={"governance": item.governance.to_dict()},
details={
"classification": item.governance.classification,
"publication_state": item.governance.publication_state,
"access_policy_ref": item.governance.access_policy_ref,
"visibility_policy_configured": bool(
item.governance.visibility_policy
),
"visibility_policy_hash": _mapping_hash(
item.governance.visibility_policy
),
},
)
session.commit()
return _datasource_response(item)
@@ -580,6 +592,7 @@ def _datasource_response(item: DatasourceDescriptor) -> DatasourceResponse:
name=field.name,
data_type=field.data_type,
nullable=field.nullable,
classification=field.classification,
)
for field in item.schema
],
@@ -610,6 +623,7 @@ def _materialization_response(
name=field.name,
data_type=field.data_type,
nullable=field.nullable,
classification=field.classification,
)
for field in item.schema
],
@@ -643,6 +657,7 @@ def _stage_response(item: DatasourceStage) -> DatasourceStageResponse:
name=field.name,
data_type=field.data_type,
nullable=field.nullable,
classification=field.classification,
)
for field in item.schema
],
@@ -673,6 +688,7 @@ def _origin_response(item: DatasourceOrigin) -> DatasourceOriginResponse:
name=field.name,
data_type=field.data_type,
nullable=field.nullable,
classification=field.classification,
)
for field in item.schema
],
@@ -715,6 +731,13 @@ def _safe_mapping(value: object) -> Mapping[str, object]:
return value if isinstance(value, Mapping) else {}
def _mapping_hash(value: Mapping[str, object]) -> str | None:
if not value:
return None
encoded = json.dumps(value, sort_keys=True, separators=(",", ":"), default=str)
return hashlib.sha256(encoded.encode("utf-8")).hexdigest()
def _audit(
session: Session,
principal: ApiPrincipal,