PT EN
Back to site

Roles and Permissions — Access Control

Controlling who can see and do what usually ends up as a permissions spreadsheet nobody maintains — an incident waiting to happen. In DATTA every capability declares a named permission, permissions are grouped into roles you manage from the interface, and anything not granted is denied by default. You see the access map on a single screen, adjust it in a few clicks, and get an audit trail of every denial.

Concepts

  • Permission — a named, specific capability (e.g. CHAT_SEND, RULES_EDIT, PLATFORM_ADMIN). It is the smallest unit of control. The catalog is central: every permission in the platform lives in it, and creating a permission outside the catalog is a bug — nobody can grant what is not there.
  • Role — a manageable grouping of permissions. You assign roles to users; the platform checks permissions. That gives you granularity without multiplying management work.
  • Sensitive permission — permissions flagged as sensitive (revealing secrets, destructive operations) require a reason recorded in the audit trail, a stricter usage limit, and a double confirmation in the interface.
  • Deny by default — if the user lacks the permission, the action is blocked with a friendly message in Portuguese (HTTP 403) and the attempt is recorded in the audit trail.

Built-in profiles (roles)

The platform ships with four profiles that cover most teams:

RoleFor whomWhat it covers
ADMINAdministratorsEvery permission in the catalog, including the sensitive ones.
ADVANCED_USERData curators and engineersAnalytical operations and curation: editing the catalog, ontology, glossary, lineage, pipelines, DATTABI, DATTAX, and source discovery.
ANALISTAAnalystsData reading + running notebooks, DATTAX, and DATTABI dashboards.
READ_ONLYConsumersConsuming dashboards, search, chat, and catalog reading.

The built-in profiles are synchronized into the system database every time the platform starts, idempotently. Your own roles are preserved by that synchronization — nothing you created is overwritten.

Where it is administered

The management screen is SistemaSegurançaRoles e Permissões, where the catalog appears grouped by category — the same grouping used throughout the interface. To grant access to a person, use SistemaSegurançaUsuários: each user gets a base role and can receive additional direct permissions for edge cases. Account details are covered in the users guide.

Permission changes take effect on the user's next sign-in. Server-side blocking, however, is immediate: even if a stale screen still shows a button, the action is denied.

Catalog categories

Every permission belongs to a category. That grouping is what organizes the roles screen:

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           SIQL

Cross-cutting permissions

These cover shared capabilities that many screens use, and therefore behave differently from the fine-grained functional permissions:

PermissionCategoryWhat it unlocksFine granularity
LLM_INVOKELLMInvoking language inference in the platform (chat, completion, and embedding generation).Per-model limit/quota is a separate control.
MCP_INVOKEMCPAccess to the MCP channel: listing tools, opening a session, and invoking.Each individual tool requires its own MCP_TOOL_*.
PRESENCE_VIEWCHATReal-time presence channel (who is online).
PROMPT_VIEWPROMPTViewing prompt templates (system, entity extraction, data extraction, investigation).
PROMPT_EDITPROMPTEditing, creating, and deleting templates — sensitive, because it affects AI behavior across the whole platform.
PLATFORM_VIEWPLATFORMReading infrastructure resources and metrics in the platform console.
PLATFORM_ADMINPLATFORMNon-destructive infrastructure changes (scale, restart, edit non-sensitive configuration).
PLATFORM_SENSITIVEPLATFORMRevealing secrets, isolating or draining a node, force-removing a resource — always requires a reason.

Default distribution per profile

A cross-section of the cross-cutting and most used permissions, and how the built-in profiles distribute them. The complete list is always visible on the roles screen itself — that is the up-to-date source, category by category.

PermissionADMINADVANCED_USERANALISTAREAD_ONLY
CHAT_SEND
LLM_INVOKE
MCP_INVOKE
PRESENCE_VIEW
PROMPT_VIEW
PROMPT_EDIT
SEARCH_EXECUTE
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

Layered security

  • The interface hides what the user cannot do — nobody clicks a button just to receive an error.
  • The platform blocks on the server, regardless of the interface. That is the real barrier: it never depends on the browser.
  • The audit trail records every denial with the actor, the required permission, the resource, and the outcome. With the interface hiding the actions, a recorded denial now genuinely signals an anomalous attempt.

Every access denial generates the AUTHZ.PERMISSION_DENIED event, with actor, permission, resource, method, and outcome=denied. The event covers both denial paths: the route-level block — in which the permission comes through as (path-gate) and the resource is the route itself — and the block by the permission declared on the endpoint, in which the required permission appears by name and the resource identifies class and method.

The event is written as a structured log (logger datta.audit.authz, WARN level, prefix AUDIT event=AUTHZ.PERMISSION_DENIED) and carried by the platform's telemetry into the OpenSearch otel-logs-* index, queryable in Trace Analytics and in the audit console (/configurar/audit?type=AUTHZ). The choice of a structured log is deliberate: the platform does not expose an HTTP audit ingest endpoint today, so any publication through that path would fall into a silent error — the log is what actually guarantees the trail. Creating that ingest endpoint, or migrating the other publishers to the same log channel, is separate work.

Hands-on example — read-only mode in Triage Rules

Access control does not just block: it adapts the interface. The Regras de Triagem (Triage Rules) page (/regras_triagem) is the canonical case.

Previously, the page showed the write buttons to any authenticated user. Anyone without permission clicked, got the error translated into a red notice, and was left not understanding why — a failure of both experience and permission feedback. Today the page reads the logged-in user's permission and adapts: the write actions disappear, a Somente leitura (read-only) badge appears in the header next to the rule count, and rules open in view mode. The user sees and searches all the content, with no buttons that would lead to a denial.

The trigger is the presence of RULES_EDIT (or the ADMIN role) — not the presence of RULES_VIEW. If reading the permission fails for any reason, read-only is the assumed state: deny by default.

What changes on screen

ElementWith RULES_EDITWithout RULES_EDIT
Header badgeSomente leitura (title="Voce nao tem permissao para alterar regras")
+ Nova Regravisiblehidden
Co-pilot (generate with AI)visiblehidden
Detail fields (title, block, order, text, contexts)editablelocked
Salvar / Descartarappear while editingnever appear
Ativar/Desativar (enable/disable) and Excluir (delete)visiblehidden
Mover (move, AI rule)visiblehidden
Empty-detail text"← Selecione uma regra para editar""← Selecione uma regra para visualizar"

The badge only appears for a logged-in user without edit permission. The Manutenção (Maintenance) gear — delete all rules — is separate and requires strict ADMIN; RULES_EDIT is not enough.

AI-generated rules (the "Regras Automáticas" block) stay locked for everyone, including those with RULES_EDIT, until they are moved to a custom block. That lock is independent of the permission-based read-only mode and stacks with it: with either one active, the fields do not accept edits and the Salvar / Descartar bar never shows up.

What the server requires

Every rule mutation — create, update, move, delete, enable/disable, test normative references, and generate with AI, including cancelling the generation — requires RULES_EDIT or ROLE_ADMIN. The block is per route, in the security configuration of the rules service itself; the exact paths are in the API reference.

Reading the rules, today, requires only a valid token: any authenticated user lists the rules. RULES_VIEW exists in the catalog and governs the per-role distribution and the governance intent, but it is not yet enforced on reads. Hardening this would require adding a route-level block on the reads as well — open work.

One detail that matters to implementers: in the reactive services, a permission declared on the class is not intercepted on methods that return Mono/Flux — which is why the mutation block is done per route. The user's permission names reach the server through the token and become authorities; ROLE_ADMIN is never blocked. There is also a server-to-server channel (the X-Internal-Token header, which grants ROLE_INTERNAL_SERVICE plus RULES_VIEW and RULES_EDIT) so that batch triage — asynchronous, without a user token — can read the rules without hitting a 401.

Who lands in read-only mode

PermissionADMINADVANCED_USERANALISTAREAD_ONLY
RULES_VIEW
RULES_EDIT

In practice: READ_ONLY users (they have RULES_VIEW, not RULES_EDIT) and any custom role without RULES_EDIT. ADMIN and ADVANCED_USER edit normally.

ANALISTA does not have RULES_VIEW in the default distribution, but that does not hide the page: the Regras menu item (the "Processar" section) is not filtered by permission, and reading is open to every valid token. An ANALISTA who opens the page sees it in read-only mode, for lack of RULES_EDIT. The missing RULES_VIEW reflects the governance intent, which today is enforced neither in the menu nor on the server.

Step by step for the user

  1. Open Regras de Triagem. If you only have view permission, the Somente leitura badge appears next to the rule count.
  2. Browse, filter by block/status, and search by number, title, or text normally — reading is unrestricted.
  3. Select a rule: the detail panel opens with the fields locked, without the Save, Delete, or Enable/Disable buttons.
  4. To gain editing, ask the administrator for the RULES_EDIT permission (or a role that contains it, such as ADVANCED_USER).

Step by step for the administrator

  • To grant editing: assign RULES_EDIT to the user — via a role or as a direct permission — in SistemaSegurançaUsuários. The change takes effect at the person's next sign-in.
  • To keep someone view-only: ensure RULES_VIEW without RULES_EDIT. The READ_ONLY profile already covers it.
  • The destructive "Apagar todas as regras" (delete all rules) operation — the Maintenance gear — remains exclusive to strict ADMIN and requires a reason of at least 10 characters, recorded in the audit trail, regardless of RULES_EDIT.

Known limitations

  • The permission is read when the page mounts; reducing a permission mid-session does not update the badge in real time. The server, however, already blocks any mutation regardless of the screen state.
  • The interface is a visual hint; the barrier is the server. Even if the screen showed the actions by mistake, the mutation would be denied.

How a new permission enters the catalog

  1. Declare the permission in the central catalog, in the right category, with a pt-BR description and the sensitive flag. If the category does not exist yet, create it in the category list.
  2. Distribute it to the appropriate built-in profiles — remembering that ADMIN covers everything automatically.
  3. Annotate the endpoint(s) with @RequirePermission("NOVA_PERMISSAO").
  4. Document it on this page when the permission is cross-cutting.
  5. Update the tests that validate the catalog, if any.

The annotation in practice

On a method:

java
@GetMapping("/datasets")
@RequirePermission("CATALOG_VIEW")
public Flux<DatasetDto> listDatasets() { ... }

Declared on the controller class, covering all of its methods:

java
// declared on the controller class — applies to all of its methods
@RequirePermission("AUDIT_VIEW")

The annotation works at both levels, and the method overrides the class — it does not stack: each method has exactly one requirement. Permissions ending in _ADMIN/:ADMIN or _VIEW/:VIEW are automatically inherited by ROLE_ADMIN, which avoids listing each one in the role. Equivalent alternative: @PreAuthorize("hasAuthority('X')").

Where the catalog is stored

The catalog is materialized in the datta system database every time the platform starts, idempotently:

(:Role {name, description, builtIn, createdAt, updatedAt})
   -[:TEM_PERMISSAO]-> (:Permissao {name})
(:Usuario)-[:TEM_ROLE]->(:Role)
(:Usuario)-[:TEM_PERMISSAO_DIRETA]->(:Permissao)   // grants individuais

The source of truth is the code; the graph exists to feed the roles screen and to store your own roles and the direct grants.

The synchronization runs sequentially and is preceded by a hygiene routine: it deduplicates :Permissao nodes by name, removes nodes with no edges at all (permissions that left the catalog), and creates the uniqueness constraints on Permissao.name and Role.name.

Troubleshooting — repeated entries or legacy permissions on the roles screen. Just restart the authentication component: the hygiene runs at startup. Without redeploying, you can run the scripts/neo4j-permissoes-dedup.cypher script — the instructions are in the file's own header. History: until 2026-06-10 the synchronization was concurrent and had no uniqueness constraint, and simultaneous creation produced up to 4 copies per name; the SPAT→DATTA rebrand left 24 orphaned entries.

Current coverage (2026-05-09)

An architectural sweep on 2026-05-09 ensured that 209 out of 209 REST controllers have their permission declared, on the class or on the method. The 10 endpoints intentionally without access control are:

EndpointJustification
Loginpublic by definition
Installer downloadpublic distribution of the installer
Health checks (4 files)availability probes for the platform
Error handlers (4 files)expose no endpoints of their own

The two permission interceptors — one for the reactive services, one for the traditional ones — support:

  • Class-level annotation, covering all methods without repetition.
  • Method-level override, when a method needs a higher permission than the class. Example: the search area requires SEARCH_EXECUTE for the whole class, but deleting documents by query requires SEARCH_INDEX_DELETE.
  • ROLE_ADMIN inheritance of permissions ending in _ADMIN/:ADMIN or _VIEW/:VIEW.

Anti-patterns rejected in review

  • An endpoint without a declared permission.
  • A permission declared with a name that does not exist in the catalog — impossible to grant, so it always denies.
  • A role check hardcoded in the code (hasRole('ADMIN')) instead of the specific permission, losing granularity.
  • A permission granted only in the interface (hiding the button) without a server-side block.
  • A sensitive permission without an audit event or without a usage limit.

See also: Users · API overview · API reference