Control de Acceso
Control de acceso de nivel empresarial para agentes de IA utilizando adk-auth.
Resumen
adk-auth proporciona control de acceso basado en roles (RBAC) con registro de auditoría y soporte SSO para agentes ADK. Permite un control seguro y granular sobre qué usuarios pueden acceder a qué herramientas.
Arquitectura
┌─────────────────────────────────────────────────────────────────┐
│ Agent Request │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ SSO Token Validation │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Google │ │ Azure AD │ │ OIDC Discovery │ │
│ │ Provider │ │ Provider │ │ (Okta, Auth0, etc) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ JWKS Cache │ ← Auto-refresh keys │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼ TokenClaims
┌─────────────────────────────────────────────────────────────────┐
│ Claims Mapper │
│ │
│ IdP Groups → adk-auth Roles │
│ ───────────────────────────────────────── │
│ "AdminGroup" → "admin" │
│ "DataAnalysts" → "analyst" │
│ (default) → "viewer" │
└─────────────────────────────────────────────────────────────────┘
│
▼ Roles
┌─────────────────────────────────────────────────────────────────┐
│ Access Control │
│ │
│ Role: admin │
│ ├── allow: AllTools │
│ └── allow: AllAgents │
│ │
│ Role: analyst │
│ ├── allow: Tool("search") │
│ ├── allow: Tool("summarize") │
│ └── deny: Tool("code_exec") ← Deny takes precedence │
└─────────────────────────────────────────────────────────────────┘
│
▼ Check Result
┌─────────────────────────────────────────────────────────────────┐
│ Audit Logging │
│ │
│ {"user":"alice","resource":"search","outcome":"allowed"} │
│ {"user":"bob","resource":"exec","outcome":"denied"} │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Tool Execution │
│ (only if access granted) │
└─────────────────────────────────────────────────────────────────┘
Principios de Diseño
1. Precedencia de Denegación
Cuando un rol tiene reglas tanto de permitir como de denegar, denegar siempre prevalece:
let role = Role::new("limited")
.allow(Permission::AllTools) // Permitir todo...
.deny(Permission::Tool("admin")); // ...excepto admin
// Resultado: Puede acceder a cualquier herramienta EXCEPTO "admin"
2. Unión de Múltiples Roles
Los usuarios con múltiples roles obtienen la unión de permisos, pero las reglas de denegación de cualquier rol siguen aplicándose:
let ac = AccessControl::builder()
.role(reader) // permitir: search
.role(writer) // permitir: write
.assign("alice", "reader")
.assign("alice", "writer")
.build()?;
// Alice puede acceder tanto a "search" COMO a "write"
3. Explícito sobre Implícito
Los permisos son explícitos; no se concede acceso por defecto:
let role = Role::new("empty");
// Este rol NO concede permisos
ac.check("user", &Permission::Tool("anything")); // → Denegado
4. Separación de Authentication y Authorization
- Authentication (SSO): Verifica QUIÉN es el usuario (mediante JWT)
- Authorization (RBAC): Determina A QUÉ pueden acceder
// Authentication: validar JWT, extraer claims
let claims = provider.validate(token).await?;
// Authorization: comprobar permiso específico
ac.check(&claims.sub, &Permission::Tool("search"))?;
// Combinado: SsoAccessControl hace ambas cosas
sso.check_token(token, &permission).await?;
Instalación
[dependencies]
adk-auth = "0.2.0"
# Para soporte de SSO/OAuth
adk-auth = { version = "0.2.0", features = ["sso"] }
Componentes Principales
Permission
pub enum Permission {
Tool(String), // Herramienta específica por nombre
AllTools, // Comodín: todas las herramientas
Agent(String), // Agente específico por nombre
AllAgents, // Comodín: todos los agentes
}
Role
let analyst = Role::new("analyst")
.allow(Permission::Tool("search".into()))
.allow(Permission::Tool("summarize".into()))
.deny(Permission::Tool("code_exec".into()));
AccessControl
let ac = AccessControl::builder()
.role(admin)
.role(analyst)
.assign("alice@company.com", "admin")
.assign("bob@company.com", "analyst")
.build()?;
// Comprobar permiso
ac.check("bob@company.com", &Permission::Tool("search".into()))?;
ProtectedTool
Envuelve una Tool con verificación automática de permisos:
use adk_auth::ToolExt;
let protected = my_tool.with_access_control(Arc::new(ac));
// Cuando se ejecuta, comprueba el permiso antes de funcionar
protected.execute(ctx, args).await?;
AuthMiddleware
Protege por lotes múltiples Tools:
let middleware = AuthMiddleware::new(ac);
let protected_tools = middleware.protect_all(tools);
Integración SSO
Proveedores compatibles
| Proveedor | Constructor | Emisor |
|---|---|---|
GoogleProvider::new(client_id) | accounts.google.com | |
| Azure AD | AzureADProvider::new(tenant, client) | login.microsoftonline.com |
| Okta | OktaProvider::new(domain, client) | {domain}/oauth2/default |
| Auth0 | Auth0Provider::new(domain, audience) | {domain}/ |
| Genérico | OidcProvider::from_discovery(issuer, client) | Cualquier proveedor OIDC |
TokenClaims
Claims extraídos de JWTs validados:
pub struct TokenClaims {
pub sub: String, // Sujeto (ID de usuario)
pub email: Option<String>, // Correo electrónico
pub name: Option<String>, // Nombre visible
pub groups: Vec<String>, // Grupos IdP
pub roles: Vec<String>, // Roles IdP
pub hd: Option<String>, // Dominio alojado de Google
pub tid: Option<String>, // ID de inquilino de Azure
// ... más claims OIDC estándar
}
ClaimsMapper
Mapea grupos IdP a roles adk-auth:
let mapper = ClaimsMapper::builder()
.map_group("AdminGroup", "admin")
.map_group("Users", "viewer")
.default_role("guest")
.user_id_from_email()
.build();
SsoAccessControl
Combina la validación SSO con RBAC en una sola llamada:
let sso = SsoAccessControl::builder()
.validator(GoogleProvider::new("client-id"))
.mapper(mapper)
.access_control(ac)
.audit_sink(audit)
.build()?;
// Validar token + verificar permiso + registro de auditoría
let claims = sso.check_token(token, &Permission::Tool("search".into())).await?;
Registro de Auditoría
FileAuditSink
let audit = FileAuditSink::new("/var/log/adk/audit.jsonl")?;
let middleware = AuthMiddleware::with_audit(ac, audit);
Formato de Salida (JSONL)
{"timestamp":"2025-01-01T10:30:00Z","user":"bob","session_id":"sess-123","event_type":"tool_access","resource":"search","outcome":"allowed"}
{"timestamp":"2025-01-01T10:30:01Z","user":"bob","session_id":"sess-123","event_type":"tool_access","resource":"code_exec","outcome":"denied"}
Custom Audit Sink
use adk_auth::{AuditSink, AuditEvent, AuthError};
use async_trait::async_trait;
pub struct DatabaseAuditSink { /* ... */ }
#[async_trait]
impl AuditSink for DatabaseAuditSink {
async fn log(&self, event: AuditEvent) -> Result<(), AuthError> {
// Insertar en la base de datos
sqlx::query("INSERT INTO audit_log ...")
.bind(event.user)
.bind(event.resource)
.execute(&self.pool)
.await?;
Ok(())
}
}
Ejemplos
# RBAC Básico
cargo run --example auth_basic # Control de acceso basado en roles
cargo run --example auth_audit # Registro de auditoría
# SSO (requiere --features sso)
cargo run --example auth_sso --features sso # Flujo SSO completo
cargo run --example auth_jwt --features sso # Validación JWT
cargo run --example auth_oidc --features sso # Descubrimiento OIDC
cargo run --example auth_google --features sso # Identidad de Google
Mejores Prácticas de Seguridad
| Práctica | Descripción |
|---|---|
| Denegar por defecto | Solo conceder los permisos explícitamente necesarios |
| Denegaciones explícitas | Añadir reglas de denegación para operaciones peligrosas |
| Auditar todo | Habilitar el registro para cumplimiento |
| Validar en el servidor | Siempre validar los JWTs en el servidor |
| Usar HTTPS | Los endpoints JWKS requieren conexiones seguras |
| Rotar claves | La caché de JWKS se auto-refresca cada hora |
| Limitar la vida útil del token | Usar tokens de acceso de corta duración |
Manejo de Errores
use adk_auth::{AccessDenied, AuthError};
use adk_auth::sso::TokenError;
// Errores de RBAC
match ac.check("user", &Permission::Tool("admin".into())) {
Ok(()) => { /* acceso concedido */ }
Err(AccessDenied { user, permission }) => {
eprintln!("Denegado: {} no puede acceder a {}", user, permission);
}
}
// Errores de SSO
match provider.validate(token).await {
Ok(claims) => { /* token válido */ }
Err(TokenError::Expired) => { /* token expirado */ }
Err(TokenError::InvalidSignature) => { /* firma inválida */ }
Err(TokenError::InvalidIssuer { expected, actual }) => { /* emisor incorrecto */ }
Err(e) => { /* otro error */ }
}
Anterior: ← Evaluación | Siguiente: Directrices de Desarrollo →