# RBAC: mapa completo, o que estava aberto e o que foi fechado

Data: 2026-07-27. Origem: ordem do dono ("arrume todo o RBAC") mais o chamado
GLPI **#0000134** do cliente. Tudo aqui foi medido contra o catálogo **vivo de
produção**, não contra o seed.

---

## 1. O método, porque um número errado já apareceu no caminho

Extraí as features exigidas pelo backend (`grep` de `feature: "..."` nos plugs
`RequirePermission`), as exigidas pelo front (`requiredRbac` no router e no
menu), e as existentes no catálogo (`SELECT code FROM features` no nó vivo de
PRD). Comparei os três conjuntos.

**A primeira comparação, feita com `comm`, deu 45 features "faltando" no backend
e 62 no front. Era lixo de ordenação**: os arquivos não estavam na mesma
collation. Refeito com comparação de conjuntos em Python, o resultado real é
outro, e muito menor. Fica registrado para ninguém repetir o método errado.

---

## 2. O resultado real

| | quantidade |
|---|---|
| features no catálogo vivo (PRD e HML) | **97** |
| exigidas pelo backend | 45 |
| exigidas pelo front | 62 |
| **exigidas pelo backend e AUSENTES do catálogo** | **1** |
| exigidas pelo front e ausentes | **0** |
| no catálogo e que ninguém exige | 27 |

A única ausente era **`admin.parceiros`**.

### 2.1. `admin.parceiros`: o defeito que travava o cliente

`Admin.PartnersController` exige `admin.parceiros` em view/create/edit/delete.
A feature **nunca foi semeada**, então nenhum grupo podia concedê-la e o
`RequirePermission` só liberava por `is_super_admin?`.

Efeito prático: o operador do cliente **via o item "Parceiros" no menu e tomava
403** ao tentar cunhar credencial da Partner API. O menu aparecia porque o front
pedia `admin.provedores.view`, feature **diferente** da que o backend cobra.

Corrigido em três pontos:

- feature `admin.parceiros` acrescentada ao `rbac_seed.exs`, com os flags
  `can_view`, `can_create`, `can_edit`, `can_delete`;
- `core/apps/admin/src/router/index.ts`: rota `/parceiros` passa a pedir
  `admin.parceiros.view`;
- `core/apps/admin/src/layouts/AdminLayout.vue`: item de menu, idem.

Conferido depois da correção: **o front pede 63 features e nenhuma está fora do
catálogo.**

### 2.2. As 27 sem consumidor

Existem no catálogo e nenhuma rota as exige hoje. **Não são defeito**: são
telas previstas e ainda não ligadas (`clients.historico`, `compliance.pgv`,
`compliance.provisionamento`, `comunicacao.sms`, `conta_corrente.abertura`,
`contas.bloqueios`, `contas.extratos`, `contas.saldos`, `credito.alcada`,
`integracoes.bacen`, `integracoes.nats`, `integracoes.nuclea`, entre outras).
Ficam registradas para não serem confundidas com sobra.

---

## 3. Chamado #0000134: criar perfil de acesso

**Sintoma:** "Erro ao criar perfil. Tente novamente." em
`coreadmin.monetarie.internal/dashboard/profiles/create`.

**Causa, provada no nó de produção com o payload exato da tela:**

```
Group.changeset(%Group{}, %{"name" => "Analista de Compliance",
                            "description" => "teste"})
  ->  valido? false   %{code: ["can't be blank"]}

com "code" => "ANALISTA_COMPLIANCE"
  ->  valido? true
```

`@required ~w(code name)a` exige `code`; `ProfileCreateView.vue:112` manda só
`name` e `description`. **Nenhum perfil novo jamais poderia ser criado pela
tela.** Não era intermitente: era determinístico.

**Correção 1:** o `code` passa a ser derivado do `name` no changeset. A
convenção já existia nos dados de produção (`ADMINISTRADOR`, `AUDITOR`,
`OPERADOR`, `SUPER_ADMIN`, `SUPORTE` são o slug do nome). Derivar conserta a
tela **e** qualquer cliente de API. `code` explícito continua vencendo, para não
quebrar seed nem import. Nome que não produz caractere útil segue inválido.

**Correção 2:** o `catch` das telas de criar e editar era mudo e trocava o
motivo real por "Tente novamente". O backend devolvia 422 com
`code: can't be blank` e isso nunca chegava ao operador. Repetir jamais
resolveria, e o cliente ficou sem ter o que reportar. Agora a tela mostra o que
o backend respondeu.

Sete testes novos, quatro vermelhos antes da correção.

---

## 4. Dois controllers que qualquer `viewer` podia escrever

Estavam apenas atrás do pipeline `:admin_only`, que aceita **qualquer**
`platform_admin`, inclusive um perfil de leitura.

### 4.1. `EscrowController`, 10 ações

Quem só devia olhar podia **abrir acordo de escrow, liberar valor e encerrar
conta**. Agora, sobre `contas.lista`:

| Flag | Ações |
|---|---|
| `:view` | `index`, `show` |
| `:create` | `create`, `add_party`, `add_condition` |
| `:edit` | `activate`, `verify_condition` |
| `:approve` | `request_release`, `approve_release`, `close` |

Dinheiro e encerramento ficaram no flag mais restritivo. **10 de 10 ações com
plug.**

### 4.2. `CoreprovidersParityController`, 90 ações

Daqui se altera **permissão e whitelist de IP de chave da Partner API**, emite e
revoga **token de integração MED**, e se mexe em tarifa, limite global,
tesouraria e caixa institucional. Tudo isso estava aberto a `viewer`.

As 90 ações foram mapeadas uma a uma, sem sobra:

| Feature | `:view` | escrita |
|---|---|---|
| `admin.seguranca` | chaves de API, tokens MED, segurança de membro | status, permissões e whitelist de chave, reset de senha (`:edit`); emissão de token (`:create`); revogação (`:delete`) |
| `admin.usuarios` | vínculos usuário-merchant | criar (`:create`), desvincular (`:delete`) |
| `conta_corrente.tarifas` | overrides, splits, histórico, limites globais, resumos | criar (`:create`), atualizar e desativar (`:edit`), apagar split (`:delete`) |
| `compliance.infracoes` | configurações de infração e MED | atualizações (`:edit`) |
| `compliance.kyc` | quarentena e detalhe | decisões e onboarding manual (`:approve`) |
| `compliance.sisbajud` | config e teste do STA | atualizar config, subir certificado (`:edit`) |
| `compliance.cadoc` | CCS, exports de compliance e auditoria | |
| `tesouraria.movimentacoes` | recibos, conciliação, saldo de liquidação, espelho do caixa | provisionar caixa, atualizar saldo (`:edit`) |
| `operacoes.pix` | settings, analytics, devoluções, baldes do DICT | atualizar settings (`:edit`) |
| `integracoes.webhooks` | listagem | teste e replay (`:edit`) |
| `investimentos.aplicacoes` | | rollover (`:edit`) |
| `corporativo.seguros` | | análise de sinistro (`:edit`) |
| `contas.lista` | resumos, saldos, dashboard em tempo real | |
| `relatorios.geral` | exports e rankings | |

**Verificação automática depois de aplicar: 90 de 90 ações cobertas, 0 sem
plug.** O mesmo teste roda para o escrow: 10 de 10.

---

## 5. O que fica pendente

1. **Rodar o seed de RBAC nos dois ambientes** para a feature `admin.parceiros`
   passar a existir. Sem isso, a correção está no código e não no banco, e o
   cliente continua tomando 403.
2. **Conceder `admin.parceiros` ao perfil certo.** Criar a feature não dá acesso
   a ninguém: alguém precisa marcar o flag no grupo do operador do cliente.
   **Decisão do dono:** qual perfil recebe, e com quais flags.
3. **As 27 features sem consumidor** merecem uma passada: são telas previstas e
   não ligadas, ou resíduo? Não mexi em nenhuma.
