Initial curated ITWorx_MCP_Hub source from 368fba691ea8d23403b4d56b53b5ec670d7c5020
This commit is contained in:
commit
cb5be898ea
663 files changed
+82674
No files matched your search
@@ -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",
|
||||
}
|
||||
Reference in new issue
Block a user