Roles e Permissões — Controle de Acesso
Controlar quem pode ver e fazer o quê costuma virar uma planilha de permissões que ninguém mantém — e um incidente esperando para acontecer. No DATTA cada funcionalidade declara uma permissão nomeada, as permissões são agrupadas em roles administráveis pela interface, e tudo que não foi concedido é negado por padrão. Você enxerga o mapa de acessos em uma tela, ajusta com poucos cliques e ganha rastro de auditoria de cada negação.
Conceitos
- Permissão — uma capacidade nomeada e específica (ex.:
CHAT_SEND,RULES_EDIT,PLATFORM_ADMIN). É a menor unidade de controle. O catálogo é central: toda permissão da plataforma vive nele, e criar permissão fora do catálogo é bug — ninguém consegue conceder o que não está lá. - Role — um agrupamento administrável de permissões. Você atribui roles a usuários; a plataforma verifica permissões. Isso dá granularidade sem multiplicar o trabalho de gestão.
- Permissão sensível — permissões marcadas como sensíveis (revelar segredos, operações destrutivas) exigem motivo registrado em auditoria, limite de uso reforçado e confirmação dupla na interface.
- Negar por padrão — se o usuário não tem a permissão, a ação é bloqueada com mensagem amigável em português (HTTP 403) e a tentativa fica registrada na trilha de auditoria.
Perfis prontos (roles built-in)
A plataforma já vem com quatro perfis que cobrem a maioria das equipes:
| Role | Para quem | O que cobre |
|---|---|---|
ADMIN | Administradores | Todas as permissões do catálogo, inclusive as sensíveis. |
ADVANCED_USER | Curadores e engenheiros de dados | Operações analíticas e curadoria: edição de catálogo, ontologia, glossário, linhagem, pipelines, DATTABI, DATTAX e descoberta de fontes. |
ANALISTA | Analistas | Leitura de dados + execução de notebooks, DATTAX e dashboards DATTABI. |
READ_ONLY | Consumidores | Consumo de dashboards, busca, chat e leitura de catálogo. |
Os perfis prontos são sincronizados na base de sistema a cada início da plataforma, de forma idempotente. Roles próprias, criadas por você, são preservadas nessa sincronização — nada que você criou é sobrescrito.
Onde se administra
O ponto de gestão é , onde o catálogo aparece agrupado por categoria — o mesmo agrupamento usado no resto da interface. Para conceder acesso a uma pessoa, use : cada usuário recebe uma role base e pode ganhar permissões diretas adicionais para casos pontuais. Detalhes do cadastro estão no guia de usuários.
Mudanças de permissão valem a partir do próximo login do usuário. O bloqueio no servidor, porém, é imediato: mesmo que uma tela antiga ainda mostre um botão, a ação é negada.
Criar uma role própria
Os quatro perfis prontos cobrem o uso comum; quando nenhum serve, Nova role cria um perfil próprio. Informe um nome (2 a 40 caracteres, letras, números ou underscore — ele vira maiúscula e não muda depois, porque usuários e regras passam a apontar para ele), uma descrição que diga para que serve, e marque as permissões. O campo de filtro busca por nome, descrição e categoria; o contador mostra quantas estão marcadas, que é a medida do que aquele perfil concede.
Uma role sem permissão nenhuma não concede nada — a tela recusa antes de salvar, para não criar um perfil que parece existir e não faz nada.
O nome INTERNAL_SERVICE, em qualquer grafia, é reservado à credencial com que os serviços da plataforma falam entre si: o servidor recusa criar, editar ou atribuir a usuário uma role com esse nome, porque ela daria a quem a recebesse a identidade de serviço interno. Se uma role com esse nome já existir, ela pode ser excluída.
Roles próprias podem ser editadas e excluídas pelos botões do cartão. Os perfis prontos não: eles trazem o selo Built-in e não oferecem ação, porque o servidor recusa alterá-los — são a base sobre a qual o resto é construído. Se precisar de algo parecido com um perfil pronto, crie uma role própria e marque as permissões que quiser.
Categorias do catálogo
Cada permissão pertence a uma categoria. É esse agrupamento que organiza a tela de roles:
CHAT GRAPH DOCUMENT RULES PROCESSOS
USERS CONFIG MODELS AUDIT BACKUP
EMBEDDING SEARCH MASKING CATALOG ONTOLOGY
GLOSSARY LINEAGE PROFILING PIPELINE TWIN
JDBC DRIVER DISCOVERY DATTAX DATTABI
MCP PROMPT STATS NOTEBOOK SYSTEM
PLATFORM NEO4J OPENSEARCH VALKEY KAFKA
LLM SIQLPermissões transversais
Estas cobrem capacidades compartilhadas que muitas telas usam, e por isso se comportam de forma diferente das permissões funcionais finas:
| Permissão | Categoria | O que libera | Granularidade fina |
|---|---|---|---|
LLM_INVOKE | LLM | Invocar inferência de linguagem na plataforma (chat, completion e geração de embeddings). | Limite por modelo/cota é controle separado. |
MCP_INVOKE | MCP | Acesso ao canal MCP: listar ferramentas, abrir sessão e invocar. | Cada ferramenta individual exige sua própria MCP_TOOL_*. |
PRESENCE_VIEW | CHAT | Canal de presença em tempo real (quem está online). | — |
PROMPT_VIEW | PROMPT | Visualizar templates de prompt (sistema, extração de entidades, extração de dados, investigação). | — |
PROMPT_EDIT | PROMPT | Editar, criar e apagar templates e as personas do chat — sensível, porque afeta o comportamento da IA em toda a plataforma. | — |
CAPTAIN_ADMIN | CHAT | Ver e cancelar sessões do Captain de outros usuários, inclusive o diário (objetivo e passos) pela ferramenta MCP. | Sem ela, cada usuário só alcança o próprio. |
PLATFORM_VIEW | PLATFORM | Leitura dos recursos e das métricas de infraestrutura no console da plataforma. | — |
PLATFORM_ADMIN | PLATFORM | Mutações não destrutivas de infraestrutura (escalar, reiniciar, editar configuração não sensível). | — |
PLATFORM_SENSITIVE | PLATFORM | Revelar segredos, isolar ou esvaziar um nó, forçar remoção de recurso — sempre exige motivo. | — |
Distribuição padrão por perfil
Recorte das permissões transversais e das mais usadas, e como os perfis prontos as distribuem. A lista completa está sempre visível na própria tela de roles — ela é a fonte atualizada, categoria por categoria.
| Permissão | ADMIN | ADVANCED_USER | ANALISTA | READ_ONLY |
|---|---|---|---|---|
CHAT_SEND | ✓ | ✓ | ✓ | ✓ |
LLM_INVOKE | ✓ | ✓ | ✓ | ✓ |
MCP_INVOKE | ✓ | ✓ | ✓ | ✓ |
MCP_TOOL_* de descoberta (grafo do código, capacidades, diário do Captain) | ✓ | ✓ | ✓ | ✓ |
MCP_TOOL_* de leitura e análise (consultas Cypher e JDBC, buscas, modelo de dados, GDS/DATTAX, R, estatística, listagens do DATTABI, laudo de triagem) | ✓ | ✓ | ✓ | ✗ |
MCP_TOOL_* de criação (workspaces, dashboards, charts, datasets, modelos e medidas do DATTABI, conexões, classificação no catálogo, compose) | ✓ | ✓ | ✗ | ✗ |
DATTABI_R_EXECUTE | ✓ | ✓ | ✓ | ✗ |
PRESENCE_VIEW | ✓ | ✓ | ✓ | ✓ |
PROMPT_VIEW | ✓ | ✓ | ✓ | ✗ |
PROMPT_EDIT | ✓ | ✗ | ✗ | ✗ |
CAPTAIN_USE | ✓ | ✓ | ✓ | ✓ |
CAPTAIN_ADMIN | ✓ | ✗ | ✗ | ✗ |
SEARCH_EXECUTE | ✓ | ✓ | ✓ | ✓ |
SEARCH_INDEX_WRITE | ✓ | ✗ | ✗ | ✗ |
CATALOG_VIEW | ✓ | ✓ | ✓ | ✓ |
CATALOG_EDIT | ✓ | ✓ | ✗ | ✗ |
RULES_VIEW | ✓ | ✓ | ✗ | ✓ |
RULES_EDIT | ✓ | ✓ | ✗ | ✗ |
RULES_DELETE | ✓ | ✓ | ✗ | ✗ |
AUDIT_VIEW | ✓ | ✓ | ✓ | ✓ |
PLATFORM_VIEW | ✓ | ✓ | ✓ | ✗ |
PLATFORM_ADMIN | ✓ | ✗ | ✗ | ✗ |
PLATFORM_SENSITIVE | ✓ | ✗ | ✗ | ✗ |
USERS_MANAGE_ROLES | ✓ | ✗ | ✗ | ✗ |
BACKUP_RESTORE | ✓ | ✗ | ✗ | ✗ |
DRIVER_MANAGE | ✓ | ✗ | ✗ | ✗ |
MASKING_CONFIGURE | ✓ | ✗ | ✗ | ✗ |
MODELS_MANAGE | ✓ | ✗ | ✗ | ✗ |
EMBEDDING_GENERATE | ✓ | ✓ | ✓ | ✓ |
EMBEDDING_GENERATE é a permissão de pedir o vetor de um texto e de acompanhar a geração de embeddings em curso. Disparar, verificar e cancelar essa geração — um trabalho único na plataforma, que ocupa o motor de embeddings por horas — exige MODELS_MANAGE (issue #1912); até ali, qualquer perfil cancelava a geração que o administrador tinha disparado.
Pelo mesmo motivo, regerar os vetores de todas as regras da plataforma (a reindexação dos trechos de regra) e abrir a troca do modelo de embedding exigem MODELS_MANAGE, ou o canal entre serviços da plataforma (issue #1935). A reindexação das regras pedia RULES_EDIT, que o perfil avançado tem; nenhuma tela a chama.
Permissão de uso não serve de permissão de escrita
Duas rotas cobravam a permissão de usar o recurso e, por isso, não cobravam nada: ambas eram alcançáveis pelo perfil somente-leitura. Corrigidas em 2026-09-26; ficam aqui porque mudam o contrato das rotas.
| Ação | Cobrava | Passa a exigir | Por quê |
|---|---|---|---|
| Criar, editar e excluir persona do chat (Sistema → Agentes) | CHAT_SEND | PROMPT_EDIT | A instrução de sistema da persona entra no prompt de todo usuário que a escolher, e a permissão que ela exige também era campo gravável — dava para clonar uma persona restrita afrouxando o controle. Ver e listar continuam com CHAT_SEND. |
Consultar o diário do Captain de outra pessoa (ferramenta captain_logs_query) | MCP_TOOL_CAPTAIN_LOGS | CAPTAIN_ADMIN | O diário traz e-mail, objetivo e passos de cada sessão. O recorte por usuário passou a ser imposto pelo servidor a partir da credencial: sem CAPTAIN_ADMIN a consulta fica presa ao próprio, e pedir a de outro é recusado. |
Também nesta revisão: executar uma persona pela ficha dela passou a exigir a permissão que a própria persona declara — o mesmo julgamento que a conversa no chat já fazia —, e o sinônimo de propriedade gravado pelo chat passou a registrar o autor e a recusar termo com quebra de linha ou acima de 60 caracteres — ele entra na instrução de todos os usuários do contexto.
Segurança em camadas
- A interface esconde o que o usuário não pode fazer — ninguém clica num botão só para receber um erro.
- A plataforma bloqueia no servidor, independentemente da interface. Essa é a barreira real: ela nunca depende do navegador.
- A auditoria registra toda negação com autor, permissão exigida, recurso e desfecho. Com a interface escondendo as ações, uma negação registrada passa a sinalizar de fato uma tentativa anômala.
Qual das camadas do servidor cobra a permissão de uma rota — o aspecto, o matcher de caminho ou uma guarda no corpo do handler — está documentado em Permissão declarada e gate efetivo, na seção de arquitetura. Ali também está a medição que precede qualquer aperto: antes de uma rota passar a negar, mede-se quais papéis perderiam acesso.
Toda negação de acesso gera o evento AUTHZ.PERMISSION_DENIED, com actor, permission, resource, method e outcome=denied. O evento cobre os dois caminhos de negação: o bloqueio por rota — nesse caso a permissão vem como (path-gate) e o recurso é a própria rota — e o bloqueio pela permissão declarada no endpoint, em que a permissão exigida aparece pelo nome e o recurso identifica classe e método.
O evento é gravado como log estruturado (logger datta.audit.authz, nível WARN, prefixo AUDIT event=AUTHZ.PERMISSION_DENIED) e levado pela telemetria da plataforma ao índice otel-logs-* do OpenSearch, consultável no Trace Analytics e no Discover do OpenSearch Dashboards, filtrando por AUTHZ.PERMISSION_DENIED. Não há tela de auditoria de acesso na plataforma, e Sistema › Logs não mostra esse evento: aquela tela lê os logs de sistema, não os otel-logs-*. A escolha pelo log estruturado é deliberada: a plataforma não expõe hoje um endpoint de ingestão de auditoria por HTTP, então qualquer publicação por esse caminho cairia em erro silencioso — é o log que de fato garante o rastro. Criar esse endpoint de ingestão, ou migrar os demais publicadores para o mesmo canal de log, é trabalho separado.
Trilha das mudanças de papéis e permissões
Toda mudança feita na gestão de papéis — criar, editar e apagar uma role própria, criar um usuário já com a role inicial, trocar a role de um usuário e definir as permissões diretas dele — gera um evento de auditoria depois do desfecho, dizendo quem fez, sobre o quê, o que mudou e como terminou:
| Evento | Quando | target | detail |
|---|---|---|---|
AUTHZ.ROLE_CREATED | criar uma role | nome da role | permissões concedidas: +X,+Y |
AUTHZ.ROLE_UPDATED | editar uma role | nome da role | o que entrou e o que saiu: +X,-Y |
AUTHZ.ROLE_DELETED | apagar uma role | nome da role | permissões que ela tinha: -X,-Y |
AUTHZ.USER_ROLE_ASSIGNED | trocar a role de um usuário | id do usuário | role anterior e nova: ANALISTA->AUDITOR_FISCAL |
AUTHZ.USER_DIRECT_PERMISSIONS_SET | definir as permissões diretas | id do usuário | +X,-Y sobre as diretas que o usuário tinha |
AUTHZ.USER_CREATED | criar um usuário (Novo Usuário ou pela API) | e-mail do usuário | role inicial e id da conta criada: papel=ANALISTA id=... |
O detail lista as permissões em ordem alfabética, com + para o que entrou e - para o que saiu. O outcome segue o que a tela recebeu:
success— a mudança foi gravada;failed— a mudança foi recusada (nome ou permissão inválida, role built-in, role inexistente, role em uso, usuário inexistente, e-mail ou login já em uso, dado de cadastro inválido) ou falhou; oreasontraz o status e a mensagem que a tela mostrou, por exemploHTTP 409: ...;cancelled— a conexão foi fechada antes da resposta. A mudança pode ter sido gravada: confira o estado atual da role ou do usuário.
Uma linha típica:
AUDIT event=AUTHZ.ROLE_UPDATED outcome=success actor=admin@exemplo.gov.br target=AUDITOR_FISCAL detail=+USERS_EDIT,-AUDIT_VIEW reason=O evento nunca leva senha nem hash. Quebra de linha e demais caracteres de controle que cheguem num valor (um nome de role digitado com quebra de linha, por exemplo) aparecem escapados como \n, \r ou \uXXXX: cada mudança é sempre uma linha só, e um valor não consegue forjar outra.
Estes eventos saem no logger datta.audit.data-access, em nível INFO — o mesmo canal dos eventos de contas de serviço —, e não no datta.audit.authz em WARN do AUTHZ.PERMISSION_DENIED. Para ver a trilha completa de acesso, filtre pelos dois loggers.
Onde o evento fica hoje. Enquanto a telemetria de logs (otel-logs-*) estiver parada, o evento fica só no log do serviço de autenticação: não chega ao OpenSearch nem ao console de auditoria e se perde quando esse log é rotacionado ou o serviço é recriado. Quando a telemetria voltar, os mesmos eventos passam a chegar ao otel-logs-* com o logger datta.audit.data-access.
O "antes" do diff é lido imediatamente antes da gravação, fora da mesma transação: duas edições simultâneas do mesmo alvo podem deixar o detail impreciso. Isso afeta só a trilha, nunca a mudança gravada.
Exemplo prático — modo somente leitura nas Regras de Triagem
O controle de acesso não só bloqueia: ele adapta a interface. A tela Regras de Triagem (/regras_triagem) é o caso canônico.
Antes, a tela mostrava os botões de escrita para qualquer usuário autenticado. Quem não tinha permissão clicava, recebia o erro traduzido em um aviso vermelho e ficava sem entender por quê — falha de experiência e de feedback de permissão. Hoje a tela lê a permissão do usuário logado e se adapta: as ações de escrita somem, um selo Somente leitura aparece no cabeçalho ao lado da contagem de regras, e as regras abrem em modo de visualização. O usuário enxerga e busca todo o conteúdo, sem botões que levariam a uma negação.
O gatilho é a presença de RULES_EDIT (ou da role ADMIN) — não a presença de RULES_VIEW. Se a leitura da permissão falhar por qualquer motivo, o modo somente leitura é o estado assumido: negar por padrão.
O que muda na tela
| Elemento | Com RULES_EDIT | Sem RULES_EDIT |
|---|---|---|
| Selo no cabeçalho | — | Somente leitura (title="Voce nao tem permissao para alterar regras") |
| + Nova Regra | visível | oculto |
| Co-pilot (gerar com IA) | visível | oculto |
| Campos do detalhe (título, bloco, ordem, texto, contextos) | editáveis | travados |
| Salvar / Descartar | aparecem ao editar | nunca aparecem |
| Ativar/Desativar e Excluir | visíveis | ocultos |
| Mover (regra de IA) | visível | oculto |
| Texto do detalhe vazio | "← Selecione uma regra para editar" | "← Selecione uma regra para visualizar" |
O selo só aparece para usuário logado sem permissão de edição. A engrenagem de Manutenção (apagar todas as regras) é separada e exige ADMIN estrito — RULES_EDIT não basta.
Regras geradas por IA (bloco "Regras Automáticas") ficam travadas para todos, inclusive quem tem RULES_EDIT, até serem movidas para um bloco personalizado. Esse bloqueio é independente do modo somente leitura por permissão e se soma a ele: com qualquer um dos dois ativo, os campos não aceitam edição e a barra Salvar / Descartar nunca surge.
O que o servidor exige
Toda mutação de regra — criar, atualizar, mover, excluir, ativar/desativar, testar referências normativas e gerar com IA, inclusive cancelar a geração — exige RULES_EDIT ou ROLE_ADMIN. O bloqueio é por rota, na configuração de segurança do próprio serviço de regras; os caminhos exatos estão na referência de API. A exceção é regerar os vetores de todas as regras, que é manutenção de modelo e exige MODELS_MANAGE (issue #1935).
A leitura das regras, hoje, exige apenas token válido: qualquer usuário autenticado lista as regras. RULES_VIEW existe no catálogo e governa a distribuição por role e a intenção de governança, mas ainda não é enforçada na leitura. Endurecer isso exigiria acrescentar um bloqueio por rota também nas leituras — trabalho em aberto.
Um detalhe importante para quem implementa: nos serviços reativos, a permissão declarada em classe não é interceptada em métodos que retornam Mono/Flux — é por isso que o bloqueio das mutações é feito por rota. Os nomes das permissões do usuário chegam ao servidor pelo token e viram authorities; ROLE_ADMIN nunca é barrado. Há ainda um canal servidor-a-servidor (cabeçalho X-Internal-Token, que concede ROLE_INTERNAL_SERVICE mais RULES_VIEW e RULES_EDIT) para que a triagem em lote — assíncrona, sem token de usuário — consiga ler as regras sem cair em 401.
Quem cai no modo somente leitura
| Permissão | ADMIN | ADVANCED_USER | ANALISTA | READ_ONLY |
|---|---|---|---|---|
RULES_VIEW | ✓ | ✓ | ✗ | ✓ |
RULES_EDIT | ✓ | ✓ | ✗ | ✗ |
Na prática: usuários READ_ONLY (têm RULES_VIEW, não têm RULES_EDIT) e qualquer role própria sem RULES_EDIT. ADMIN e ADVANCED_USER editam normalmente.
ANALISTA não tem RULES_VIEW na distribuição padrão, mas isso não esconde a tela: o item Regras do menu (seção "Processar") não é filtrado por permissão, e a leitura está liberada para todo token válido. Um ANALISTA que abra a tela a vê em modo somente leitura, por não ter RULES_EDIT. O RULES_VIEW ausente reflete a intenção de governança, que hoje não é aplicada nem no menu nem no servidor.
Passo a passo do usuário
- Abra Regras de Triagem. Se você só tem permissão de visualização, o selo Somente leitura aparece ao lado da contagem de regras.
- Navegue, filtre por bloco/status e busque por número, título ou texto normalmente — a leitura é livre.
- Selecione uma regra: o painel de detalhe abre com os campos travados, sem os botões Salvar, Excluir ou Ativar/Desativar.
- Para ganhar edição, peça ao administrador a permissão
RULES_EDIT(ou uma role que a contenha, comoADVANCED_USER).
Passo a passo do administrador
- Para conceder edição: atribua
RULES_EDITao usuário — por role ou como permissão direta — em . A mudança vale no próximo login da pessoa. - Para deixar alguém só consultando: garanta
RULES_VIEWsemRULES_EDIT. O perfilREAD_ONLYjá atende. - A operação destrutiva "Apagar todas as regras" (engrenagem de Manutenção) permanece exclusiva de
ADMINestrito e exige motivo com no mínimo 10 caracteres, registrado em auditoria — independentemente deRULES_EDIT.
Limitações conhecidas
- A permissão é lida quando a tela monta; reduzir a permissão no meio da sessão não atualiza o selo em tempo real. O servidor, porém, já bloqueia qualquer mutação independentemente do estado da tela.
- A interface é dica visual; a barreira é o servidor. Mesmo que a tela exibisse as ações por engano, a mutação seria negada.
Como uma permissão nova entra no catálogo
- Declarar a permissão no catálogo central, na categoria adequada, com descrição em pt-BR e a marcação de sensível. Se a categoria ainda não existe, criá-la na lista de categorias.
- Distribuí-la nos perfis prontos apropriados — lembrando que
ADMINcobre tudo automaticamente. - Anotar o(s) endpoint(s) com
@RequirePermission("NOVA_PERMISSAO"). - Documentar nesta página quando a permissão for transversal.
- Atualizar os testes que validam o catálogo, se houver.
A anotação na prática
Em método:
@GetMapping("/datasets")
@RequirePermission("CATALOG_VIEW")
public Flux<DatasetDto> listDatasets() { ... }Em classe, cobrindo todos os métodos:
// declarada na classe do controlador — vale para todos os seus métodos
@RequirePermission("AUDIT_VIEW")A anotação funciona nos dois níveis, e o método sobrescreve a classe — não acumula: cada método tem exatamente uma exigência. Permissões terminadas em _ADMIN/:ADMIN ou _VIEW/:VIEW são herdadas automaticamente por ROLE_ADMIN, o que evita listar cada uma na role. Alternativa equivalente: @PreAuthorize("hasAuthority('X')").
Onde a permissão é aplicada: aspecto ou filtro
A anotação declara o que o endpoint exige; quem aplica depende do tipo de retorno do método, e a diferença já custou caro:
| Retorno do método | Serviço WebFlux (todos os da plataforma) |
|---|---|
Mono/Flux | O aspecto de permissão aplica, desde que o módulo registre o bean. |
ResponseEntity, List, Map, tipo simples (blocking) | O aspecto não aplica: a autoridade vive no contexto reativo e o thread-local que ele lê está vazio. A anotação é documentação — a barreira real é o matcher de path na configuração de segurança do módulo — hasAnyAuthority(...) por verbo e caminho. |
Regras que decorrem disso:
- Endpoint blocking anotado sem matcher é anotação que mente: qualquer usuário autenticado passa. Foi assim que 47 leituras de governança (linhagem, ownership, knowledge, ficha, ontologia, twins) ficaram abertas a quem não tinha
CATALOG_VIEW/ONTOLOGY_VIEW/TWIN_VIEWaté 2026-09-15. - Quando o gate mora no filtro, a anotação declara isso —
@RequirePermission(value = "X", enforcedBy = RequirePermission.Enforcement.FILTER)— e o codegraph confere que existe matcher cobrindo o caminho (permissao_declara_filtro_sem_matcher, gate). DeclararFILTERsem matcher reprova. - Endpoint novo anotado que nada aplica reprova o
make codegraph-check(permissao_sem_gate_efetivo_novo, ratchet contrascripts/codegraph-permissao-sem-gate-legado.txt). Os legados nomeados ali estão listados na issue de acompanhamento e só saem da lista com gate. - O matcher não herda o atalho do aspecto para
ROLE_ADMIN: liste-o explicitamente, ou um JWT emitido antes da última atualização do serviço de autenticação (só a role, sem a lista expandida) toma 403 na própria tela do administrador. - Leitura segue a hierarquia
VIEW < EDIT < ADMINno matcher (quem edita lê), como as escritas já fazem (EDITaceitaADMIN). É também o que deixa passar o token interno de serviço, que concede a autoridade de edição do módulo.
Onde o catálogo fica armazenado
O catálogo é materializado no banco de sistema datta a cada início da plataforma, de forma idempotente:
(:Role {name, description, builtIn, createdAt, updatedAt})
-[:TEM_PERMISSAO]-> (:Permissao {name})
(:Usuario)-[:TEM_ROLE]->(:Role)
(:Usuario)-[:TEM_PERMISSAO_DIRETA]->(:Permissao) // grants individuaisA fonte da verdade é o código; o grafo alimenta a tela de roles, guarda as roles próprias e os grants diretos e é também de onde o login e a renovação da sessão tiram as permissões do token. Por isso a sincronização dos perfis prontos é diferencial: uma permissão retirada do catálogo sai da role no boot seguinte (e do token no próximo login ou renovação), uma acrescentada entra, e as demais ficam como estão.
A sincronização roda sequencialmente e é precedida por uma rotina de higiene: deduplica nós :Permissao pelo nome, remove nós sem nenhuma aresta (permissões que saíram do catálogo) e cria as restrições de unicidade de Permissao.name e Role.name.
Solução de problemas — entradas repetidas ou permissões legadas na tela de roles. Basta reiniciar o componente de autenticação: a higiene roda no início. Sem reimplantar, dá para rodar o script
scripts/neo4j-permissoes-dedup.cypher— as instruções estão no cabeçalho do próprio arquivo. Histórico: até 2026-06-10 a sincronização era concorrente e sem restrição de unicidade, e a criação simultânea gerava até 4 cópias por nome; o rebrand SPAT→DATTA deixou 24 entradas órfãs.
Cobertura atual (2026-05-09)
Uma varredura arquitetural em 2026-05-09 garantiu que 209 de 209 controladores REST tenham a permissão declarada, em classe ou em método. Os 10 endpoints intencionalmente sem controle de acesso são:
| Endpoint | Justificativa |
|---|---|
| Login | público por definição |
| Download do instalador | distribuição pública do instalador |
| Verificações de saúde (4 arquivos) | sondas de disponibilidade da plataforma |
| Tratadores de erro (4 arquivos) | não expõem endpoints próprios |
Os dois interceptadores de permissão — um para os serviços reativos, outro para os tradicionais — suportam:
- Anotação em classe, cobrindo todos os métodos sem repetição.
- Sobrescrita por método, quando um método precisa de permissão mais alta que a da classe. Exemplo: a área de busca exige
SEARCH_EXECUTEna classe inteira, mas a exclusão de documentos por consulta exigeSEARCH_INDEX_DELETE, e as gravações genéricas no índice de busca (documentos, trechos, vetores, regras) exigemSEARCH_INDEX_WRITE— só do ADMIN; o perfil somente-leitura busca, mas não grava nem apaga. A exclusão por consulta aceita só o nome exato de um índice de contexto: padrão, lista e índice interno da plataforma são recusados mesmo para o ADMIN (issue #1912). Na busca, um padrão (datta_*) nunca alcança os índices de sistema da plataforma — histórico, prompts, telemetria, logs, glossário, catálogo — e pedir um deles pelo nome exato é recusado (issue #1935). - Herança por
ROLE_ADMINdas permissões terminadas em_ADMIN/:ADMINou_VIEW/:VIEW.
Anti-patterns rejeitados em revisão
- Endpoint sem permissão declarada.
- Permissão declarada com um nome que não existe no catálogo — impossível de conceder, então sempre nega.
- Checagem de role fixa no código (
hasRole('ADMIN')) em vez da permissão específica, perdendo granularidade. - Permissão garantida só na interface (esconder o botão) sem bloqueio no servidor.
- Permissão sensível sem evento de auditoria ou sem limite de uso.
Veja também: Usuários · Visão geral da API · Referência de API