mirror of
https://github.com/mcloh/Enterprise-MCP-Service-Bus.git
synced 2026-11-06 06:24:12 +00:00
M0 e M1 revisados e fixes implementados
This commit is contained in:
52
RI/src/emcp_bus/approval/models.py
Normal file
52
RI/src/emcp_bus/approval/models.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""Approval request contract (EP-14-T01, README.md §26).
|
||||
|
||||
Out-of-band, bound to the *exact* operation it was requested for (not just
|
||||
the tool name), with an expiry and single-use semantics -- so an approval
|
||||
granted for one call can never be replayed against a different one (same
|
||||
tool, different arguments) or reused twice (README.md §46, "approval
|
||||
replay").
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any, Literal
|
||||
from uuid import uuid4
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
def compute_operation_hash(*, client_profile: str, tool: str, arguments: dict[str, Any]) -> str:
|
||||
"""Binds an approval to one exact (client_profile, tool, arguments) triple.
|
||||
|
||||
Canonical JSON (sorted keys) so the same logical call always hashes the
|
||||
same way regardless of dict insertion order.
|
||||
"""
|
||||
canonical = json.dumps(
|
||||
{"client_profile": client_profile, "tool": tool, "arguments": arguments},
|
||||
sort_keys=True,
|
||||
default=str,
|
||||
)
|
||||
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
class ApprovalRequest(BaseModel):
|
||||
approval_id: str = Field(default_factory=lambda: str(uuid4()))
|
||||
operation_hash: str
|
||||
client_profile: str
|
||||
tool: str
|
||||
arguments_summary: dict[str, Any]
|
||||
"""A copy of the relevant arguments for an approver to review -- README.md
|
||||
§26 ("com resumo dos argumentos relevantes"). Not redacted in this RI;
|
||||
a production deployment would apply the same PII-minimization rules as
|
||||
audit logging (README.md §27)."""
|
||||
|
||||
reason_code: str
|
||||
requested_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
||||
expires_at: datetime
|
||||
status: Literal["pending", "granted", "denied", "consumed"] = "pending"
|
||||
|
||||
def is_expired(self, *, now: datetime | None = None) -> bool:
|
||||
return (now or datetime.now(UTC)) >= self.expires_at
|
||||
Reference in New Issue
Block a user