Extracts agent identity registration behind AgentRegistrar protocol. Three implementations: KeycloakRegistrar — Keycloak Admin REST API (existing, refactored) EntraRegistrar — Microsoft Entra Agent ID platform (NEW) StubRegistrar — dev mode without real IdP Driver selection via AGENT_REGISTRAR env var: auto — prefers Entra if configured, Keycloak fallback, stub default keycloak — explicit Keycloak entra — explicit Entra Agent ID Entra integration: Registers agent as Entra app + service principal via Graph API Tags with delegation metadata (agent_type, delegator, governed:true) Client secret TTL matches delegation expiry Deletes application on revocation (cascades to SP) Uses msal for token acquisition Future: native Agent ID Blueprint API when GA Files: registrar.py — AgentRegistrar protocol + AgentCredentials dataclass registrar_keycloak.py — refactored from keycloak.py registrar_entra.py — NEW Entra Graph API driver registrar_stub.py — dev mode stub registrar_factory.py — driver selection factory delegation.py — updated to use registrar abstraction settings.py — added Entra config + agent_registrar field All 7 smoke tests pass with stub registrar. Implements GCAP-SPEC-LLM-PRINCIPAL-BROKER-0001 §4.1-§4.3. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
36 lines
1 KiB
Python
36 lines
1 KiB
Python
"""AgentRegistrar protocol — abstract interface for agent identity registration.
|
|
|
|
Implementations:
|
|
KeycloakRegistrar — Keycloak Admin REST API (§4.1)
|
|
EntraRegistrar — Microsoft Entra Agent ID platform (§4.2)
|
|
StubRegistrar — dev mode without a real IdP
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Protocol, runtime_checkable
|
|
|
|
|
|
@dataclass
|
|
class AgentCredentials:
|
|
"""Credentials returned after registering an agent identity."""
|
|
client_id: str
|
|
client_secret: str
|
|
agent_display_name: str
|
|
idp_backend: str # "keycloak" | "entra" | "stub"
|
|
|
|
|
|
@runtime_checkable
|
|
class AgentRegistrar(Protocol):
|
|
async def register_agent(
|
|
self,
|
|
delegation_id: str,
|
|
agent_type: str,
|
|
delegator_id: str,
|
|
display_name: str,
|
|
expires_at: str,
|
|
metadata: dict | None = None,
|
|
) -> AgentCredentials: ...
|
|
|
|
async def delete_agent(self, client_id: str) -> bool: ...
|
|
|
|
async def get_agent_token(self, client_id: str) -> str | None: ...
|