Initial curated ITWorx_MCP_Hub source from 368fba691ea8d23403b4d56b53b5ec670d7c5020

This commit is contained in:
Public Source Publisher committed 2026-10-01 01:36:21 +02:00
commit cb5be898ea
663 files changed
+82674

No files matched your search

+154
View File
@@ -0,0 +1,154 @@
from __future__ import annotations
from typing import Any
from uuid import UUID
from sqlalchemy import select
from packages.hub_policy import AuditEvent, AuditPage
from services.control_api.persistence.models import (
AuditEventRow,
EnvironmentRow,
ProjectRow,
WorkspaceRow,
)
class SqlAlchemyAuditLog:
"""Append-only production audit sink backed by the Hub-owned database."""
def __init__(self, sessions: Any, *, issuer: str) -> None:
self.sessions = sessions
self.issuer = issuer
@staticmethod
def _workspace_id(session: Any, workspace: str) -> UUID | None:
try:
identifier = UUID(workspace)
except ValueError:
identifier = None
if identifier is not None and session.get(WorkspaceRow, identifier) is not None:
return identifier
value = session.scalar(select(WorkspaceRow.id).where(WorkspaceRow.slug == workspace))
return value if isinstance(value, UUID) else None
def append(self, event: AuditEvent) -> None:
with self.sessions() as session, session.begin():
workspace_id = self._workspace_id(session, event.workspace)
if workspace_id is None:
raise RuntimeError("audit workspace is not registered")
project = session.scalar(
select(ProjectRow).where(
ProjectRow.workspace_id == workspace_id,
ProjectRow.slug == event.project,
)
)
if project is None:
raise RuntimeError("audit project is not registered")
environment_id = session.scalar(
select(EnvironmentRow.id).where(
EnvironmentRow.workspace_id == workspace_id,
EnvironmentRow.project_id == project.id,
EnvironmentRow.name == event.environment,
)
)
if environment_id is None:
raise RuntimeError("audit environment is not registered")
session.add(
AuditEventRow(
id=event.id,
occurred_at=event.occurred_at,
trace_id=event.trace_id,
workspace_id=workspace_id,
project_id=project.id,
environment_id=environment_id,
event_type=event.event_type,
principal_issuer=self.issuer,
principal_subject=event.principal,
client_id=event.client_id,
tool_name=event.tool,
policy_decision=event.policy_decision,
matched_rule_ids=list(event.matched_rule_ids),
policy_trace=list(event.policy_trace),
arguments_hash=event.arguments_hash,
sanitized_arguments=event.sanitized_arguments,
duration_ms=event.duration_ms,
outcome=event.outcome,
error_code=event.error_code,
retention_until=event.retention_until,
)
)
def search(
self, *, workspace: str, project: str, limit: int = 100, offset: int = 0
) -> AuditPage:
if not 1 <= limit <= 500 or offset < 0:
raise ValueError("invalid audit pagination")
with self.sessions() as session:
workspace_id = self._workspace_id(session, workspace)
if workspace_id is None:
return AuditPage(items=())
project_row = session.scalar(
select(ProjectRow).where(
ProjectRow.workspace_id == workspace_id,
ProjectRow.slug == project,
)
)
if project_row is None:
return AuditPage(items=())
rows = session.scalars(
select(AuditEventRow)
.where(
AuditEventRow.workspace_id == workspace_id,
AuditEventRow.project_id == project_row.id,
)
.order_by(AuditEventRow.occurred_at, AuditEventRow.id)
.offset(offset)
.limit(limit + 1)
).all()
selected = rows[:limit]
environments = {
row.id: row.name
for row in session.scalars(
select(EnvironmentRow).where(EnvironmentRow.workspace_id == workspace_id)
).all()
}
items = tuple(
AuditEvent(
id=row.id,
occurred_at=row.occurred_at,
trace_id=row.trace_id,
event_type=row.event_type,
workspace=workspace,
project=project,
environment=environments.get(row.environment_id, "unknown"),
principal=row.principal_subject or "unknown",
client_id=row.client_id,
tool=row.tool_name or "mcp.request",
policy_decision=row.policy_decision or "deny",
matched_rule_ids=tuple(row.matched_rule_ids),
policy_trace=tuple(row.policy_trace),
arguments_hash=row.arguments_hash or "0" * 64,
sanitized_arguments=dict(row.sanitized_arguments),
duration_ms=row.duration_ms or 0,
outcome=row.outcome,
error_code=row.error_code,
retention_until=row.retention_until,
)
for row in selected
)
return AuditPage(
items=items,
next_offset=offset + limit if len(rows) > limit else None,
)
def export_metadata(self, *, workspace: str, project: str) -> dict[str, Any]:
page = self.search(workspace=workspace, project=project, limit=500)
return {
"workspace": workspace,
"project": project,
"eventCount": len(page.items),
"lastEventAt": page.items[-1].occurred_at.isoformat() if page.items else None,
"format": "audit-event-v1",
"access": "policy-restricted",
}