Em quase todo painel administrativo existe uma propriedade que significa, ao mesmo tempo, usar o produto, cadastrar pessoas, conceder privilégios e atravessar fronteiras. Geralmente ela se chama admin. É conveniente até o dia em que alguém pergunta: um administrador do cliente pode criar outro administrador igual a ele?
Separação de deveres parte da ideia de que certas autoridades não deveriam vir juntas só por conveniência. No catálogo, isso vira permissions distintas:
users.write -- cadastrar pessoas
authz.roles.assign_tenant -- atribuir roles do tenant
authz.grants.direct -- conceder exceções temporárias
platform.tenant.bypass -- atravessar tenantsPor que “só concede o que possui” falha
A regra mais comum é: um usuário só pode conceder permissions que ele mesmo tem. Parece segura e tem dois defeitos.
O primeiro é a auto-replicação. Se o administrador possui todas as permissions de admin, o subset check também permite que ele conceda admin a qualquer um. A política “o admin do tenant cadastra pessoas e atribui papéis operacionais, mas não cria outro admin” simplesmente não é expressável.
O segundo é o inverso: a regra obriga quem só precisa conceder uma capacidade a possuí-la. Um gestor que distribui acesso a microdados passa a ler microdados, mesmo sem nenhuma razão para isso.
A matriz de autoridade
A saída é modelar a autoridade de concessão diretamente:
role_grantable(grantor_role, target_role)
tenant_admin -> viewer
tenant_admin -> analyst
platform_admin -> tenant_admin
platform_admin -> viewer
platform_admin -> analystRepare no que não está ali: tenant_admin -> tenant_admin. A regra de negócio deixa de ser efeito colateral de uma comparação de conjuntos e vira uma linha inspecionável. Para grants diretos vale o mesmo raciocínio, com outra matriz:
permission_grantable(grantor_role, permission_code)
tenant_admin -> analysis.run
tenant_admin -> data.query
tenant_admin -> content.shareE acima das duas matrizes fica um hard deny: nenhuma autoridade de tenant concede permission de plataforma, independentemente do que a matriz disser. Se alguém configurar errado, a fronteira continua de pé.
O predicado, com os parênteses no lugar
can_assign(actor, target, role) :=
(
tenant_branch(actor, target)
OR platform_branch(actor)
)
AND role_is_grantable(actor, role)
AND target_role_content_is_allowed(actor, role)Parece pedantismo, mas aqui precedência é segurança. Sem os parênteses, A OR B AND C AND D vira A OR (B AND C AND D), e qualquer ator que satisfaça o ramo de tenant pula as checagens de matriz. É um bypass completo escondido num operador. A implementação deve espelhar a fórmula, ou usar early returns equivalentes que não deixem dúvida.
O último termo, target_role_content_is_allowed, importa quando o tenant pode criar roles próprias: a validação olha o conteúdo da role, não o nome. Uma role custom chamada “Leitor” que contém authz.roles.assign_tenant não é leitora.
Cadastro não é concessão
POST /users -- cria a identidade
POST /users/{id}/roles -- concede autoridadeDois endpoints, dois serviços, duas trilhas. Juntar os dois num formulário de “novo usuário” com um dropdown de role faz de todo cadastro uma concessão, e a auditoria passa a ter que separar o que a arquitetura juntou.
Bloquear autoatribuição, actor == target, é uma salvaguarda razoável. Mas é só isso: o controle real é a matriz. Se a matriz permite a escalada, impedir que alguém a faça em si mesmo só obriga a pedir para um colega.
Grants diretos com prazo
Exceções temporárias existem, e é melhor modelá-las do que fingir que não. Mas grant direto não pode virar uma segunda implementação descontrolada de RBAC. As regras que eu sigo:
expires_atobrigatório. Não existe grant eterno.- Justificativa obrigatória, gravada junto.
- Concessão auditada na mesma transação.
- O vencimento entra no frescor da sessão.
- Recorrência monitorada: o mesmo grant renovado todo mês é uma role que ninguém quis criar.
- Permissions de plataforma indelegáveis por qualquer mecanismo de tenant.
Proveniência
Para cada permission efetiva, o sistema deve conseguir dizer de onde ela veio: role de sistema, role custom ou grant direto, quem concedeu, quando e até quando. Isso transforma “por que essa pessoa consegue fazer isso?” numa consulta, e não numa investigação manual de logs.
Este texto faz parte de uma série que começa em Autorização em quatro perguntas.