Escolher o modelo de controle de acesso
Objetivo
Escolher o modelo mais simples que represente corretamente o domínio e aplicá-lo como autorização centralizada, negada por padrão e testável. Separar autenticação — quem é o usuário — de autorização — o que ele pode fazer sobre um recurso concreto.
Começar pelo domínio
Inspecionar requisitos, modelos, endpoints, serviços, banco, autenticação e integrações. Identificar:
- atores humanos e não humanos;
- organizações, workspaces e fronteiras de tenant;
- recursos protegidos e dados sensíveis;
- ações de negócio, inclusive leitura, busca, exportação e operações em lote;
- ownership, membership, atribuição, hierarquia e compartilhamento;
- condições de sessão, recurso ou ambiente;
- jobs, WebSockets, webhooks, Agentes e ferramentas que acessam dados.
Não criar roles em sistema realmente local, de usuário único e sem compartilhamento. Registrar a decisão e reavaliar quando surgir uma fronteira de acesso.
Distinguir os modelos
RBAC — Role-Based Access Control
Escolher RBAC quando a pergunta principal for: “qual função organizacional este usuário exerce?”
Usar para capacidades estáveis e compartilhadas, como administrador, gestor, colaborador, auditor ou suporte. Modelar usuário -> role -> permissões, com roles limitadas ao tenant quando aplicável.
Não usar RBAC sozinho quando usuários com a mesma role puderem acessar recursos diferentes. Não criar uma role por projeto, usuário ou exceção; isso indica explosão de roles e necessidade de ReBAC ou ABAC.
ReBAC — Relationship-Based Access Control
Escolher ReBAC quando a pergunta principal for: “qual é a relação deste usuário com este recurso?”
Usar para relações como owner, member, manager, assignee, participant, reviewer, parent ou viewer. Modelar relações por IDs imutáveis e permitir que o acesso seja derivado por caminhos claros, por exemplo: membro do projeto → leitor das tarefas do projeto.
Não confundir relação com texto livre ou comparação de e-mail. Persistir vínculos por user_id, resource_id e tipo de relação.
ABAC — Attribute-Based Access Control
Escolher ABAC quando a pergunta principal for: “quais atributos e condições precisam ser verdadeiros agora?”
Usar atributos do sujeito, recurso, ação e ambiente, como tenant, departamento, classificação, status, MFA, horário, localização, risco ou estado do recurso.
Evitar políticas opacas, atributos sem fonte confiável e expressões dinâmicas difíceis de testar. Não usar ABAC para substituir roles e relações simples apenas por flexibilidade teórica.
Aplicar a árvore de decisão
Responder nesta ordem:
- O acesso muda conforme função ou responsabilidade organizacional? Adicionar RBAC.
- Pessoas com a mesma role acessam recursos diferentes por ownership, membership ou atribuição? Adicionar ReBAC.
- A decisão depende de atributos, estado ou contexto dinâmico? Adicionar ABAC.
- Mais de uma resposta foi positiva? Usar um modelo híbrido.
- A concessão é uma exceção explícita e pontual? Considerar ACL limitada, sem substituir o modelo principal.
Usar como padrão para sistemas colaborativos multi-tenant:
allow = same_tenant
AND role_allows(action)
AND relationship_allows(actor, resource)
AND contextual_conditions_hold(context)
Omitir apenas as parcelas que o domínio comprovadamente não necessita. Não transformar essa expressão em regra universal de que toda ação exige uma relação: ações como criar projeto podem depender somente da role e do tenant.
Formalizar antes de implementar
Produzir:
- catálogo de ações no formato
resource.action, usando verbos de negócio; - matriz
ator/role × ação × recurso × relação × condição × decisão; - definições precisas para termos como “próprio”, “relacionado” e “da equipe”;
- hierarquia e escopo das roles;
- grafo ou tabelas de relações;
- atributos, fonte de verdade e momento de avaliação;
- precedência entre allow, deny e restrições de tenant;
- política de ausência de regra: deny.
Exemplos de ações:
project.read
project.archive
task.assign
task.acceptance.check
finance.transaction.approve
member.role.change
report.export
Não modelar somente telas ou métodos HTTP. Uma mesma rota pode executar ações de negócio diferentes, e a mesma ação pode existir em API, job e ferramenta de Agente.
Projetar a arquitetura
Centralizar a decisão em contrato equivalente a:
authorize(actor, action, resource, context) -> allow | deny + reason
Separar:
- Policy Decision Point: calcular a decisão.
- Policy Enforcement Point: impedir a operação em controller, middleware, dependency, serviço, repositório, gateway ou worker.
- Policy Information Point: fornecer roles, relações e atributos confiáveis.
- Policy Administration Point: administrar e versionar políticas e atribuições.
Evitar condicionais de role espalhadas. Aplicar autorização no limite da requisição e preservar invariantes no serviço de domínio. Para mutações sensíveis, revalidar o recurso e suas condições dentro da transação quando houver risco de mudança concorrente.
Aplicar regras irredutíveis
- Negar por padrão e falhar fechado.
- Aplicar menor privilégio e separação de responsabilidades.
- Derivar actor, tenant e sessão de credenciais verificadas; não confiar no corpo da requisição.
- Verificar toda operação, inclusive rotas alternativas, busca, contagens, exportações e lotes.
- Validar primeiro a fronteira de tenant.
- Filtrar coleções na consulta ao banco; não carregar dados proibidos para filtrar em memória.
- Usar UUID/ID imutável para relações; não usar e-mail como chave de autorização.
- Tratar UI oculta como experiência, não segurança.
- Planejar revogação e invalidação de cache quando roles ou relações mudarem.
- Auditar actor, tenant, ação, recurso, decisão, motivo e correlation ID sem registrar segredos.
- Exigir confirmação e, quando apropriado, MFA para exclusões, finanças, roles e grandes exportações.
- Retornar 401 para ausência de autenticação; usar 403 ou 404 conforme a política de não revelar existência.
Adaptar à linguagem
Ao encontrar Python, ler references/python.md antes de planejar ou implementar. Escolher a integração adequada para Django, FastAPI, Flask ou camada independente.
Ao encontrar Java, ler references/java.md antes de planejar ou implementar. Preferir Spring Security AuthorizationManager e method security quando Spring estiver presente; adaptar a Jakarta Security ou outra stack sem perder a camada de domínio.
Não adicionar biblioteca externa automaticamente. Usar implementação própria pequena e centralizada quando as políticas forem estáveis e locais. Considerar uma policy engine quando houver políticas configuráveis, muitos tenants, múltiplos serviços, relações profundas ou necessidade de administração central.
Proteger Agentes e automações
Tratar Agentes, jobs e integrações como canais sujeitos às mesmas políticas. Propagar a identidade do usuário originador quando agirem em nome dele. Autorizar cada tool call e filtrar RAG antes de inserir dados no contexto. Não usar o prompt como controle de segurança nem conceder identidade administrativa genérica ao Agente.
Testar a política
Criar testes orientados por tabela cobrindo:
- allow e deny de cada ação relevante;
- mesma role com recurso relacionado e não relacionado;
- acesso entre tenants;
- usuário inativo ou revogado;
- atributo presente, ausente, inválido e alterado;
- IDs adulterados e endpoints alternativos;
- coleções, busca, exportações e lotes;
- chamadas por job, WebSocket e Agente;
- indisponibilidade da política e comportamento fail-closed.
Testar a decisão isoladamente e a aplicação real nas rotas, serviços e consultas. Não aceitar apenas testes de visibilidade da interface.
Entregar a decisão
Ao planejar, apresentar nesta ordem:
- resumo do domínio e fronteiras de confiança;
- escolha justificada entre RBAC, ReBAC, ABAC ou híbrido;
- recursos, ações, roles, relações e atributos;
- matriz de autorização;
- modelo de dados;
- arquitetura e pontos de aplicação;
- plano específico para a linguagem/framework;
- migração, auditoria e revogação;
- testes e critérios de aceite;
- premissas, riscos e perguntas bloqueantes.
Ao implementar, preservar o modelo aprovado, manter mudanças pequenas e verificáveis e não inventar permissões amplas para contornar um bloqueio.