Controle de Acesso

Controle de acesso de nível empresarial para agentes de IA usando adk-auth.

Visão Geral

adk-auth fornece controle de acesso baseado em função (RBAC) com registro de auditoria e suporte a SSO para ADK agents. Ele permite controle seguro e granular sobre quais usuários podem acessar quais tools.

Arquitetura

┌─────────────────────────────────────────────────────────────────┐
│                        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)                          │
└─────────────────────────────────────────────────────────────────┘

Princípios de Design

1. Precedência de Negação

Quando uma função possui regras de permissão (allow) e negação (deny), a negação sempre prevalece:

let role = Role::new("limited")
    .allow(Permission::AllTools)      // Allow everything...
    .deny(Permission::Tool("admin")); // ...except admin

// Result: Can access any tool EXCEPT "admin"

2. União de Múltiplas Funções

Usuários com múltiplas funções obtêm a união das permissões, mas as regras de negação de qualquer função ainda se aplicam:

let ac = AccessControl::builder()
    .role(reader)    // allow: search
    .role(writer)    // allow: write
    .assign("alice", "reader")
    .assign("alice", "writer")
    .build()?;

// Alice can access both "search" AND "write"

3. Explícito Sobre Implícito

As permissões são explícitas - nenhum acesso é concedido por padrão:

let role = Role::new("empty");
// This role grants NO permissions

ac.check("user", &Permission::Tool("anything")); // → Denied

4. Separação de Autenticação e Autorização

  • Autenticação (SSO): Verifica QUEM é o usuário (via JWT)
  • Autorização (RBAC): Determina O QUE eles podem acessar
// Authentication: validate JWT, extract claims
let claims = provider.validate(token).await?;

// Authorization: check specific permission
ac.check(&claims.sub, &Permission::Tool("search"))?;

// Combined: SsoAccessControl does both
sso.check_token(token, &permission).await?;

Instalação

[dependencies]
adk-auth = "0.2.0"

# For SSO/OAuth support
adk-auth = { version = "0.2.0", features = ["sso"] }

Componentes Principais

Permission

pub enum Permission {
    Tool(String),     // Ferramenta específica por nome
    AllTools,         // Coringa: todas as ferramentas
    Agent(String),    // Agent específico por nome  
    AllAgents,        // Coringa: todos os agents
}

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()?;

// Verificar permissão
ac.check("bob@company.com", &Permission::Tool("search".into()))?;

ProtectedTool

Envolve uma ferramenta com verificação automática de permissões:

use adk_auth::ToolExt;

let protected = my_tool.with_access_control(Arc::new(ac));

// Quando executado, verifica a permissão antes de rodar
protected.execute(ctx, args).await?;

AuthMiddleware

Protege em lote múltiplas ferramentas:

let middleware = AuthMiddleware::new(ac);
let protected_tools = middleware.protect_all(tools);

Integração SSO

Provedores Suportados

ProvedorConstrutorEmissor
GoogleGoogleProvider::new(client_id)accounts.google.com
Azure ADAzureADProvider::new(tenant, client)login.microsoftonline.com
OktaOktaProvider::new(domain, client){domain}/oauth2/default
Auth0Auth0Provider::new(domain, audience){domain}/
GenéricoOidcProvider::from_discovery(issuer, client)Qualquer provedor OIDC

Declarações do Token

Declarações extraídas de JWTs validados:

pub struct TokenClaims {
    pub sub: String,              // Assunto (ID do usuário)
    pub email: Option<String>,    // E-mail
    pub name: Option<String>,     // Nome de exibição
    pub groups: Vec<String>,      // Grupos IdP
    pub roles: Vec<String>,       // Funções IdP
    pub hd: Option<String>,       // Domínio hospedado do Google
    pub tid: Option<String>,      // ID do inquilino do Azure
    // ... mais declarações OIDC padrão
}

Mapeador de Declarações

Mapeia grupos IdP para funções adk-auth:

let mapper = ClaimsMapper::builder()
    .map_group("AdminGroup", "admin")
    .map_group("Users", "viewer")
    .default_role("guest")
    .user_id_from_email()
    .build();

Controle de Acesso SSO

Combina validação SSO com RBAC em uma única chamada:

let sso = SsoAccessControl::builder()
    .validator(GoogleProvider::new("client-id"))
    .mapper(mapper)
    .access_control(ac)
    .audit_sink(audit)
    .build()?;

// Valida token + verifica permissão + log de auditoria
let claims = sso.check_token(token, &Permission::Tool("search".into())).await?;

Registro de Auditoria

Coletor de Auditoria de Arquivo

let audit = FileAuditSink::new("/var/log/adk/audit.jsonl")?;
let middleware = AuthMiddleware::with_audit(ac, audit);

Formato de Saída (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"}

Coletor de Auditoria Personalizado

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> {
        // Inserir no banco de dados
        sqlx::query("INSERT INTO audit_log ...")
            .bind(event.user)
            .bind(event.resource)
            .execute(&self.pool)
            .await?;
        Ok(())
    }
}

Exemplos

# RBAC Essencial
cargo run --example auth_basic          # Controle de acesso baseado em função
cargo run --example auth_audit          # Registro de auditoria

# SSO (requer --features sso)
cargo run --example auth_sso --features sso     # Fluxo SSO completo
cargo run --example auth_jwt --features sso     # Validação de JWT
cargo run --example auth_oidc --features sso    # Descoberta OIDC
cargo run --example auth_google --features sso  # Identidade Google

Melhores Práticas de Segurança

PráticaDescrição
Negar por padrãoConceda apenas as permissões explicitamente necessárias
Negações explícitasAdicione regras de negação para operações perigosas
Auditar tudoHabilite o registro para conformidade
Validar no lado do servidorSempre valide JWTs no servidor
Usar HTTPSEndpoints JWKS requerem conexões seguras
Rotacionar chavesO cache JWKS atualiza automaticamente a cada hora
Limitar tempo de vida do tokenUse tokens de acesso de curta duração

Tratamento de Erros

use adk_auth::{AccessDenied, AuthError};
use adk_auth::sso::TokenError;

// Erros de RBAC
match ac.check("user", &Permission::Tool("admin".into())) {
    Ok(()) => { /* acesso concedido */ }
    Err(AccessDenied { user, permission }) => {
        eprintln!("Acesso negado: {} não pode acessar {}", user, permission);
    }
}

// Erros de SSO
match provider.validate(token).await {
    Ok(claims) => { /* token válido */ }
    Err(TokenError::Expired) => { /* token expirado */ }
    Err(TokenError::InvalidSignature) => { /* assinatura inválida */ }
    Err(TokenError::InvalidIssuer { expected, actual }) => { /* emissor errado */ }
    Err(e) => { /* outro erro */ }
}

Anterior: ← Avaliação | Próximo: Diretrizes de Desenvolvimento →