Contrôle d'Accès
Contrôle d'accès de niveau entreprise pour les agents d'IA utilisant adk-auth.
Vue d'ensemble
adk-auth fournit un contrôle d'accès basé sur les rôles (RBAC) avec journalisation d'audit et prise en charge du SSO pour les agents ADK. Il permet un contrôle sécurisé et granulaire sur quels utilisateurs peuvent accéder à quels outils.
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ 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) │
└─────────────────────────────────────────────────────────────────┘
Principes de conception
1. Prépondérance du refus
Lorsqu'un Role a à la fois des règles d'autorisation (allow) et de refus (deny), le refus l'emporte toujours :
let role = Role::new("limited")
.allow(Permission::AllTools) // Allow everything...
.deny(Permission::Tool("admin")); // ...except admin
// Résultat : Peut accéder à n'importe quel Tool SAUF "admin"
2. Union de plusieurs Roles
Les utilisateurs avec plusieurs Role obtiennent l'union des Permission, mais les règles de refus (deny) de n'importe quel Role s'appliquent toujours :
let ac = AccessControl::builder()
.role(reader) // allow: search
.role(writer) // allow: write
.assign("alice", "reader")
.assign("alice", "writer")
.build()?;
// Alice peut accéder à la fois à "search" ET à "write"
3. Explicite plutôt qu'Implicite
Les Permission sont explicites - aucun accès n'est accordé par défaut :
let role = Role::new("empty");
// Ce Role n'accorde AUCUNE permission
ac.check("user", &Permission::Tool("anything")); // → Refusé
4. Séparation de l'authentification et de l'autorisation
- Authentification (SSO) : Vérifie QUI est l'utilisateur (via JWT)
- Autorisation (RBAC) : Détermine À QUOI ils peuvent accéder
// Authentification : valider le JWT, extraire les claims
let claims = provider.validate(token).await?;
// Autorisation : vérifier une permission spécifique
ac.check(&claims.sub, &Permission::Tool("search"))?;
// Combiné : SsoAccessControl fait les deux
sso.check_token(token, &permission).await?;
Installation
[dependencies]
adk-auth = "0.2.0"
# Pour le support SSO/OAuth
adk-auth = { version = "0.2.0", features = ["sso"] }
Composants principaux
Permission
pub enum Permission {
Tool(String), // Tool spécifique par nom
AllTools, // Joker : tous les Tool
Agent(String), // Agent spécifique par nom
AllAgents, // Joker : tous les Agent
}
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()?;
// Vérifier la permission
ac.check("bob@company.com", &Permission::Tool("search".into()))?;
ProtectedTool
Enveloppe un Tool avec une vérification de Permission automatique :
use adk_auth::ToolExt;
let protected = my_tool.with_access_control(Arc::new(ac));
// Lors de l'exécution, vérifie la permission avant de s'exécuter
protected.execute(ctx, args).await?;
AuthMiddleware
Protège en lot plusieurs Tool :
let middleware = AuthMiddleware::new(ac);
let protected_tools = middleware.protect_all(tools);
Intégration SSO
Fournisseurs pris en charge
| Fournisseur | Constructeur | Émetteur |
|---|---|---|
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}/ |
| Générique | OidcProvider::from_discovery(issuer, client) | Tout fournisseur OIDC |
TokenClaims
Revendications extraites des JWT validés :
pub struct TokenClaims {
pub sub: String, // Sujet (ID utilisateur)
pub email: Option<String>, // E-mail
pub name: Option<String>, // Nom d'affichage
pub groups: Vec<String>, // Groupes IdP
pub roles: Vec<String>, // Rôles IdP
pub hd: Option<String>, // Domaine hébergé Google
pub tid: Option<String>, // ID de locataire Azure
// ... plus de revendications OIDC standard
}
ClaimsMapper
Associe les groupes IdP aux rôles adk-auth :
let mapper = ClaimsMapper::builder()
.map_group("AdminGroup", "admin")
.map_group("Users", "viewer")
.default_role("guest")
.user_id_from_email()
.build();
SsoAccessControl
Combine la validation SSO avec le RBAC en un seul appel :
let sso = SsoAccessControl::builder()
.validator(GoogleProvider::new("client-id"))
.mapper(mapper)
.access_control(ac)
.audit_sink(audit)
.build()?;
// Valider le jeton + vérifier la permission + journal d'audit
let claims = sso.check_token(token, &Permission::Tool("search".into())).await?;
Journalisation d'Audit
FileAuditSink
let audit = FileAuditSink::new("/var/log/adk/audit.jsonl")?;
let middleware = AuthMiddleware::with_audit(ac, audit);
Format de Sortie (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"}
Récepteur d'Audit Personnalisé
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> {
// Insérer dans la base de données
sqlx::query("INSERT INTO audit_log ...")
.bind(event.user)
.bind(event.resource)
.execute(&self.pool)
.await?;
Ok(())
}
}
Exemples
# Core RBAC
cargo run --example auth_basic # Role-based access control
cargo run --example auth_audit # Audit logging
# SSO (requires --features sso)
cargo run --example auth_sso --features sso # Complete SSO flow
cargo run --example auth_jwt --features sso # JWT validation
cargo run --example auth_oidc --features sso # OIDC discovery
cargo run --example auth_google --features sso # Google Identity
Bonnes Pratiques de Sécurité
| Pratique | Description |
|---|---|
| Refuser par défaut | N'accorder que les permissions explicitement nécessaires |
| Refus explicites | Ajouter des règles de refus pour les opérations dangereuses |
| Auditer tout | Activer la journalisation pour la conformité |
| Valider côté serveur | Toujours valider les JWT sur le serveur |
| Utiliser HTTPS | Les points d'accès JWKS nécessitent des connexions sécurisées |
| Faire pivoter les clés | Le cache JWKS se rafraîchit automatiquement toutes les heures |
| Limiter la durée de vie du jeton | Utiliser des jetons d'accès de courte durée |
Gestion des Erreurs
use adk_auth::{AccessDenied, AuthError};
use adk_auth::sso::TokenError;
// RBAC errors
match ac.check("user", &Permission::Tool("admin".into())) {
Ok(()) => { /* access granted */ }
Err(AccessDenied { user, permission }) => {
eprintln!("Denied: {} cannot access {}", user, permission);
}
}
// SSO errors
match provider.validate(token).await {
Ok(claims) => { /* token valid */ }
Err(TokenError::Expired) => { /* token expired */ }
Err(TokenError::InvalidSignature) => { /* signature invalid */ }
Err(TokenError::InvalidIssuer { expected, actual }) => { /* wrong issuer */ }
Err(e) => { /* other error */ }
}
Précédent: ← Évaluation | Suivant: Lignes directrices de développement →