Pular para o conteúdo

Arcane.Politica

Motor de autorização: RBAC com herança, ABAC por atributo, ACL por objeto, grupos, isolamento por inquilino, negação explícita que vence o papel e delegação com prazo. O padrão é negar, e toda decisão diz qual regra a tomou — um motor que responde só sim/não é impossível de auditar.

Autorização escrita como given usuario["papel"] is "admin": espalha a decisão por cinquenta arquivos. Quando a regra muda — e ela sempre muda — não há onde olhar, e o que fica para trás não dá erro: fica permitindo.

O que este módulo acrescenta não é conveniência. São quatro garantias que um given não tem.

GarantiaSem ela
o padrão é negaruma ação nova nasce permitida para todo mundo
negar vence permitira exceção “este usuário não” é apagada por um papel
ninguém delega o que não tema cadeia de delegações cria autoridade do nada
a delegação vencedelegar vira conceder, com passos a mais — e ninguém lembra de revogar
RBAC com herança
adopt Arcane.Politica as P

pol := P.motor("loja")
pol.papel("leitor", ["pedido:ler"])
pol.papel("editor", ["pedido:escrever"], herda := ["leitor"])
pol.papel("admin", ["pedido:apagar", "usuario:*"], herda := ["editor"])

ana := {"id": "ana", "papel": "admin"}

assert pol.pode(ana, "pedido:ler")["permitido"]

// A decisao explica QUEM decidiu — e nomeia o papel que realmente
// tem a permissao, e nao o que foi consultado.
out pol.pode(ana, "pedido:ler")["motivo"]
// o papel 'leitor' permite 'pedido:ler' (por 'admin')

As sete camadas, e por que a ordem é contrato#

pode() percorre as camadas nesta ordem e para na primeira que decide. A ordem não é detalhe de implementação: a ACL existe para dizer “neste objeto, não”, e se o papel viesse antes ela nunca seria alcançada.

#CamadaO que ela decide
1tenantum sujeito de outro inquilino não existe para esta política
2negacaoa negação explícita — vence tudo
3acla lista daquele objeto; estar nela e não ter a ação é um não
4regraABAC: a condição olha os valores. void é não opino
5papelRBAC, com herança e curinga explícito
6delegacaoemprestada, e ainda no prazo
7padraonegar
ABAC
adopt Arcane.Politica as P

pol := P.motor("docs")
pol.papel("usuario", ["doc:ler"])

// ABAC: a regra decide olhando os valores. 'void' e "nao opino" —
// uma regra que so soubesse dizer nao bloquearia tudo que ela nao
// entende.
pol.regra("somente-dono",
    lambda s, a, r => ((r["dono"] is s["id"]) given r isnt void otherwise void),
    acoes := ["doc:escrever"])

carlos := {"id": "carlos", "papel": "usuario"}

assert pol.pode(carlos, "doc:escrever", {"id": 1, "dono": "carlos"})["permitido"]
assert pol.pode(carlos, "doc:escrever", {"id": 2, "dono": "dani"})["permitido"] is no

// A regra nao opina sobre ler: a decisao cai no papel, e passa.
assert pol.pode(carlos, "doc:ler", {"id": 2, "dono": "dani"})["permitido"]

Delegação#

delegar
adopt Arcane.Politica as P

pol := P.motor("fin")
pol.papel("gerente", ["pagamento:aprovar"])
pol.papel("analista", ["pagamento:ler"])

chefe := {"id": "chefe", "papel": "gerente"}
sub := {"id": "sub", "papel": "analista"}

pol.delegar(chefe, sub, ["pagamento:aprovar"], prazo := 3600.0)
assert pol.pode(sub, "pagamento:aprovar")["permitido"]

// Ninguem delega o que nao tem: sem essa conferencia, A delega a B
// algo que A nao pode, B delega a C, e C passa a poder.
monitor:
    pol.delegar(sub, chefe, ["pagamento:apagar"])
handle DelegationError as e:
    out "recusado: autoridade nao se cria do nada"

O que ele não faz#

Não fazPorque
não busca o sujeito nem o recursoum motor que consulta o banco decidiria com dados que quem chama não viu, e o teste dele passaria a precisar de banco
não substitui a autorização na consultaconferir depois de carregar o pedido ainda carrega o pedido de outra pessoa. A defesa que funciona é o WHERE que já filtra pelo dono — isto é a segunda camada
não é distribuídoas políticas vivem no processo. O que precisa ser comum é a fonte (o banco de papéis), e não o motor

Guia com contexto: Autorização e capacidades.

Funções (6)#

Assinatura
camadas()
casa(padrao, concreto)
de_vault(nome, definicao)
identidade(alvo)
motor(nome, inquilino='')
separacao_de_funcoes(politica, acao, solicitante, aprovador)