# Gestão de Permissões — Gaps, Dívidas e Riscos

> Severidade: **crítica** (segurança/perda de função), **alta** (função
> quebrada com contorno), **média** (degradação), **baixa** (code smell).

## 1. Riscos e bugs conhecidos

| ID | Severidade | Descrição | Evidência | Impacto |
|---|---|---|---|---|
| R-01 | **crítica** | O `V2PermissionMiddleware` ignora o parâmetro `v2.permission:Operators,write`. A ação efetiva é inferida do método HTTP. | `V2/Core/Middleware/V2PermissionMiddleware.php:22`; 208 rotas declaram `,read`/`,write` | Um `read` pode executar qualquer GET, inclusive rotas que mutam estado; a declaração de rota dá falsa sensação de proteção |
| R-02 | **crítica** | Rota que não casa nenhum `active_pattern` é liberada (fail-open). | `V2PermissionMiddleware.php:74-77` | Rotas autenticadas fora do mapa ficam sem RBAC: dashboard, busca, organizar e qualquer módulo com padrão errado |
| R-03 | **alta** | Com nível `read`, todo GET/HEAD passa — a distinção “ler vs escrever” é o verbo, não a intenção. | `V2PermissionMiddleware.php:102-105` | Ação de leitura que gera/exporta/muta estado escapa do RBAC |
| R-04 | **alta** | Bypass por e-mail fixo em três pontos independentes. | `helpers.php:21`; `PermissionService.php:72`; `V2PermissionMiddleware.php:70` | Qualquer conta cadastrada com o e-mail de suporte recebe acesso total, inclusive aos módulos do grupo `Support` |
| R-05 | **alta** | `update()` de usuário não verifica se o alvo é suporte, super-admin ou o próprio usuário logado. | `GestaoPermissoesPermissionsController.php:108-179` | POST direto altera senha/e-mail/perfil de qualquer conta |
| R-06 | **alta** | `updateRole()`/`deleteRole()` não protegem um perfil cujo slug seja `super-admin`; a checagem só impede atribuir esse slug. | `GestaoPermissoesRolesController.php:218-226`; `PermissionService.php:308-378` | Se a linha reservada existir, pode ser editada ou soft-deletada por qualquer perfil com `write` no módulo |
| R-07 | **alta** | Dois RBACs coexistem: Sentinel V1 e `v2_roles`. | `CmsAuthenticate.php:26-38`; `GestaoPermissoesPermissionsController.php:207-253` | Usuário com perfil V2 `write` e sem papel V1 não entra; usuário com papel V1 e sem perfil V2 entra sem módulos. A tela exige os dois conceitos |
| R-08 | **média** | Aliases de módulo são uma lista hardcoded de 13 entradas. | `PermissionService.php:26-40` | Módulo novo com slug divergente retorna 403 mesmo com permissão gravada; correção exige deploy |
| R-09 | **média** | `sanitizePermissions()` valida valores, mas não chaves contra o catálogo. | `PermissionService.php:391-399` | Chaves órfãs são gravadas e ficam inertes; relatórios e auditoria ficam sujos |
| R-10 | **média** | `getUserPermissions()` engole exceção e devolve `[]`. | `PermissionService.php:245-249` | Falha de banco vira “sem permissão” sem alerta; se a tabela cair, todos perdem acesso silenciosamente |
| R-11 | **média** | Middleware executa duas vezes por request nas rotas do módulo. | `CmsAuthenticate.php:40` + `Routes/cms.php:10` | Custo dobrado, dois scans, dois logs; divergência futura entre caminhos |
| R-12 | **média** | `create()` de usuário cria e ativa no Sentinel antes de validar o papel V1; não há transação. | `GestaoPermissoesPermissionsController.php:228-253` | Tentativa bloqueada de `super-admin` deixa usuário criado e possivelmente com `v2_role_id` |
| R-13 | **média** | Módulos `restricted` são removidos da matriz, mas o middleware não checa `restricted`. | `GestaoPermissoesRolesController.php:136-146`; `V2PermissionMiddleware.php:51` | Chave gravada por SQL em módulo restrito libera acesso se o módulo não estiver no grupo `Support` |
| R-14 | **baixa** | Auditoria de exclusão é gravada antes do `delete()`. | `PermissionService.php:366-378` | Trilha registra exclusão que pode não ter ocorrido |
| R-15 | **baixa** | Texto de erro diz “Apenas super-administradores”, mas qualquer perfil com `write` executa. | `GestaoPermissoesRolesController.php:91-92` | Mensagem enganosa para o operador |
| R-16 | **baixa** | `mapModuleIcon()` é definida dentro de view, com `function_exists`. | `V2/Modules/Infraestrutura/GestaoPermissoes/Views/roles/create.blade.php:8-27`; `V2/Modules/Infraestrutura/GestaoPermissoes/Views/roles/edit.blade.php:8-27` | Lógica de apresentação em Blade; dependente da ordem de carregamento |
| R-17 | **baixa** | `confirm()` inline e script inline de slug. | `V2/Modules/Infraestrutura/GestaoPermissoes/Views/roles/index.blade.php:116`; `V2/Modules/Infraestrutura/GestaoPermissoes/Views/roles/create.blade.php:159-186` | Bloqueia uma futura CSP sem `unsafe-inline` |
| R-18 | **baixa** | `dd()` em dois testes de feature. | `tests/Feature/V2/PermissionsTest.php` | Falha de teste interrompe a suíte em vez de reportar assert |

## 2. Funcionalidades incompletas ou mortas

| Item | Evidência | Consequência |
|---|---|---|
| `users.permissions_v2` (jsonb) | criada em `2026_06_01_000004_add_permissions_v2_to_users_table.php`; nenhuma leitura no código | Coluna órfã; risco de perda de dados se descartada sem backfill |
| Parâmetro de ação do middleware | `handle($request, Closure $next)` não recebe `$action` | 208 declarações de `,read`/`,write` são decorativas |
| `users.permissions_v2` no model `User` | ausente de `$fillable` e de casts | Nunca é gravada pela aplicação |
| Relação `User → V2Role` | não existe `belongsTo` em `app/Http/Model/Admin/User.php` | Toda leitura é `V2Role::find()` manual |
| Rotas `PUT/PATCH/DELETE` | todas as mutações usam `POST` | Semântica REST incompleta; CSRF e formulários funcionam |
| Tela de “show” de perfil | não existe | Não há visão somente leitura da matriz |
| Exportação/relatório de permissões | não existe | Auditoria depende de consulta SQL |

## 3. Mapa entre versões (evolução interna)

O projeto analisado contém apenas a V2. O mapa abaixo registra a evolução
interna observável, não uma comparação V5/V6:

| Item | Estado anterior | Estado atual | Efeito |
|---|---|---|---|
| Modelo de permissão | `users.permissions_v2` (por usuário) | `v2_roles.permissions_v2` (por perfil) | migration `000004` ficou órfã; `000005` criou o perfil |
| Chave de módulo | slug de rota/controller | nome canônico do diretório + aliases | correção de 403 em perfis antigos (`PermissionService.php:18-24`) |
| Edição de perfil | lia `permissions_v2` cru | resolve com `resolvePermission()` antes de renderizar | evita sobrescrever slug legado com `none` (`GestaoPermissoesRolesController.php:187-191`) |
| Grupo restrito | apenas sidebar | middleware bloqueia `group = SUPPORT` | fecha acesso direto por URL |
| Identificador | — | UUID v4 gerado no model | `v2_uuid()` também é v4, apesar do nome |

## 4. Riscos de dados na migração

1. **Perfil sem conversão** → usuário perde todo o acesso (`[]` = `none`).
2. **`write` convertido em quatro ações** → ampliação indevida de privilégio.
3. **Aliases não resolvidos** → chaves como `operadores` e `transparencia`
   precisam virar `Operators`/`TransparenciaFiscal` antes do corte.
4. **Perfil soft-deletado** → usuários ainda apontam para a linha; no V7 sem
   soft delete, a FK `nullOnDelete` zeraria o vínculo. Decidir se o perfil é
   recriado ou se o usuário fica sem perfil.
5. **Conta de suporte** → o e-mail reservado precisa ser recriado como conta
   com perfil `suporte`; não migrar o bypass.
6. **Senhas** → `Hash::make` (bcrypt) no legado; o V7 usa Argon2id (ADR-020).
   Migração exige rehash no primeiro login ou reset.
7. **Papéis V1** → `role_users` não tem equivalente direto; mapear apenas o
   necessário (super-admin, operador) para o perfil V7.

## 5. Perguntas em aberto

| # | Pergunta | Por que decide arquitetura |
|---|---|---|
| 1 | O V7 deve permitir que um usuário tenha mais de um perfil? | ADR-033 fixa **um** perfil; múltiplos exigem novo ADR |
| 2 | Quais módulos precisam de `aprovar` e `arquivar`? | Define a matriz e a conversão de `write` |
| 3 | O perfil `suporte` pode ser visto (somente leitura) por outro perfil? | ADR-033 diz “não editado, visualizado nem atribuído” |
| 4 | Quem pode criar contas administrativas além do suporte? | Hoje qualquer `write` em `GestaoPermissoes` |
| 5 | O e-mail de suporte continua sendo um endereço fixo ou uma conta reservada? | ADR-033 substitui o bypass por perfil protegido |
| 6 | O V7 precisa de trilha de leitura para listagem de usuários? | ADR-035 pede leitura sensível no CMS |
| 7 | O que fazer com perfis sem nenhum usuário no corte? | Migrar, arquivar ou descartar |
| 8 | O módulo de permissões deve viver no Core ou em `modules/`? | ADR-013/033: o Core fornece o vocabulário; a tela pode ser módulo |
| 9 | A ativação de módulo (`v2_module_flags`) continua separada do RBAC? | ADR-034 trata ativação como dado; não confundir com permissão |
| 10 | Quem audita mudanças de perfil — o módulo ou o subscriber global? | ADR-035 exige via única (`AuditWriter`) |

## 6. Dívidas técnicas priorizadas

| Prioridade | Dívida | Ação recomendada |
|---|---|---|
| P0 | R-01/R-02/R-03 | No V7, abandonar o middleware genérico e autorizar por ação com `Permission::authorize`; negar por padrão |
| P0 | R-04 | Eliminar bypass por e-mail; perfil `suporte` protegido por código |
| P0 | R-05 | Policy de usuário com regra explícita para alvo suporte/super-admin/próprio |
| P1 | R-06 | Bloquear edição/exclusão do perfil reservado por ID/slug |
| P1 | R-07 | Remover Sentinel no V7 e unificar autenticação + perfil (ADR-037/033) |
| P1 | R-08/R-09 | Token canônico declarado; validar tokens contra o manifesto na escrita |
| P2 | R-10 | Tratar falha de leitura como erro explícito + auditoria |
| P2 | R-11 | Remover a chamada interna do `CmsAuthenticate` ao middleware de permissão |
| P2 | R-12 | Envolver criação de usuário em transação ou criar primeiro no banco local |
| P3 | R-13–R-18 | Limpeza de UI, CSS inline e testes |

## 7. Cenários de falha encadeada

| Cenário | Cadeia | Resultado observável |
|---|---|---|
| Tabela `v2_roles` indisponível | `getUserPermissions()` lança → `catch` devolve `[]` → `resolvePermission` devolve `none` | todo usuário comum perde acesso a todos os módulos; suporte e `super-admin` continuam entrando |
| Perfil soft-deletado com usuários | `V2Role::find()` respeita SoftDeletes → `null` → `[]` | usuários ficam órfãos de permissão sem aviso na tela |
| Módulo novo sem `active_pattern` | middleware não casa → fail-open | módulo inteiro fica acessível a qualquer usuário autenticado |
| Dois `active_pattern` casando o mesmo path | `break` no primeiro da iteração | a permissão aplicada pode variar com a ordem do filesystem |
| Alias esquecido após renomear módulo | `v2_can` usa slug, matriz usa diretório | 403 com permissão gravada; sidebar pode mostrar o módulo (ela resolve pelo canônico) |
| Conta criada com o e-mail de suporte | bypass em três camadas | acesso total, inclusive ao grupo `Support`, sem auditoria diferenciada |
| POST direto em usuário suporte | `update()` não valida o alvo | troca de senha/e-mail/perfil da conta reservada |
| `module.active` desligado | `ModuleAccessMiddleware` responde redirect/404 antes do RBAC | nem suporte nem `super-admin` acessam; a chave de permissão permanece intacta |

## 8. Sinais para monitorar na operação

- Quantidade de `403`/redirects por módulo no log
  (`[V2 > MIDDLEWARE > Permission]`): picos indicam alias ou matriz
  desatualizada.
- Perfis com `permissions_v2` contendo chaves que não existem em
  `v2.modules.configs`: catálogo órfão.
- Usuários com `v2_role_id` apontando para perfil soft-deletado: consulta
  periódica com `withTrashed()`.
- Diferença entre o que a sidebar mostra e o que o middleware libera: indica
  divergência de chave ou de `active_pattern`.
- Falhas de auditoria (`[PermissionService] Falha ao registrar auditoria`):
  perda de evidência, mesmo que a operação tenha sucesso.
