feat(ai): durcissement phase F (#79) - rate limit, redaction, OpenAPI/MCP, E2E
This commit is contained in:
@@ -102,6 +102,21 @@ class ToolConfirmationRequired(ToolError):
|
||||
return payload
|
||||
|
||||
|
||||
class ToolRateLimitError(ToolError):
|
||||
"""Raised when an identity exceeds its tool-call rate limit (Phase F)."""
|
||||
|
||||
code = "rate_limited"
|
||||
|
||||
def __init__(self, tool: str, retry_after: int = 0, message: str | None = None):
|
||||
super().__init__(
|
||||
message or f"Rate limit exceeded for tool '{tool}'",
|
||||
code="rate_limited",
|
||||
details={"tool": tool, "retry_after": retry_after},
|
||||
)
|
||||
self.tool = tool
|
||||
self.retry_after = retry_after
|
||||
|
||||
|
||||
@dataclass
|
||||
class ToolContext:
|
||||
"""Carries the caller identity and execution options for a tool call.
|
||||
@@ -126,6 +141,21 @@ class ToolContext:
|
||||
def username(self) -> str:
|
||||
return self.user.get("username", "unknown")
|
||||
|
||||
@property
|
||||
def rate_limit_identity(self) -> str:
|
||||
"""Identity used by the per-token/per-tool rate limiter.
|
||||
|
||||
Prefers the JWT id (``_token_jti``, attached by the auth middleware),
|
||||
then an explicit ``token_id`` in :attr:`metadata`, then the user id and
|
||||
finally the username. This yields per-token limiting when a token id is
|
||||
available and per-account limiting otherwise.
|
||||
"""
|
||||
token_id = self.metadata.get("token_id") or self.user.get("_token_jti")
|
||||
if token_id:
|
||||
return f"token:{token_id}"
|
||||
user_id = self.user.get("id") or self.user.get("username")
|
||||
return f"user:{user_id or 'anonymous'}"
|
||||
|
||||
def has_vault_access(self, vault: str) -> bool:
|
||||
"""Return True if the caller may access *vault*."""
|
||||
return bool(vault) and check_vault_access(vault, self.user)
|
||||
|
||||
Reference in New Issue
Block a user