# Gestão de Permissões — Arquitetura

> Este documento descreve como uma requisição chega ao módulo, quem decide o
> acesso e quais classes participam. Caminhos relativos a
> `/home/one/projects/maxima/v7/lotep`.

## 1. Fluxo da requisição

### 1.1 Entrada no CMS

```
GET /cms/permissoes/roles
  │
  ├─ stack global do grupo CMS (V2ServiceProvider::mapModuleRoutes)
  │    web
  │    module.active:GestaoPermissoes  →  ModuleAccessMiddleware
  │         └─ ModuleFlagService::isActive('GestaoPermissoes')
  │             (tabela v2_module_flags; ausência de linha = inativo)
  │
  ├─ middleware do grupo de rotas (Routes/cms.php:10)
  │    CmsAuthenticate::class
  │         ├─ Sentinel::check()            → 302 /cms/login se anônimo
  │         ├─ $user->roles()->count() === 0 → logout + 302
  │         └─ app(V2PermissionMiddleware)->handle($request, $next)
  │
  ├─ v2.permission:gestao-permissoes
  │    V2PermissionMiddleware::handle($request, Closure $next)
  │         ├─ Sentinel::check()
  │         ├─ scan de v2.modules.configs por active_pattern
  │         ├─ bloqueio do grupo SUPPORT
  │         ├─ bypass: e-mail de suporte | inRole('super-admin')
  │         ├─ fail-open se nenhum módulo casou
  │         └─ resolvePermission(permissions_v2, 'GestaoPermissoes')
  │
  ├─ GestaoPermissoesRolesController@index
  │    ├─ não chama v2_can() (listagem é liberada para read)
  │    ├─ V2Role::query()->with search/order/paginate(15)
  │    ├─ PermissionService::getAssignedUsersCount() por perfil
  │    └─ view('v2-gestaopermissoes::roles.index')
  │
  └─ HTML (layout v2-cms::layouts.app)
```

### 1.2 Escrita (exemplo: criar perfil)

```
POST /cms/permissoes/roles
  │
  ├─ web + module.active:GestaoPermissoes
  ├─ CmsAuthenticate (que já executa o V2PermissionMiddleware)
  ├─ v2.permission:gestao-permissoes  (executa o middleware pela segunda vez)
  │
  ├─ GestaoPermissoesRolesController@store
  │    ├─ v2_can('GestaoPermissoes', 'write')
  │    │     └─ PermissionService::hasPermission()
  │    │           ├─ bypass suporte/super-admin
  │    │           ├─ getUserPermissions($user) → V2Role::find(v2_role_id)
  │    │           └─ resolvePermission(permissions, 'GestaoPermissoes')
  │    ├─ validate(name, slug unique)
  │    ├─ bloqueio de slug 'super-admin'
  │    ├─ remoção de módulos com restricted=email
  │    ├─ PermissionService::createRole()
  │    │     ├─ sanitizePermissions()
  │    │     ├─ V2Role::create() (UUID v4 no evento creating)
  │    │     └─ V2AuditLogger::activity($role, 'created', props)
  │    └─ redirect route('v2.gestaopermissoes.roles.index')
  │
  └─ 302
```

### 1.3 Diagrama de camadas

```
┌─────────────────────────────────────────────────────────────────────┐
│ Rotas do módulo (Routes/cms.php)                                    │
│ CmsAuthenticate + v2.permission:gestao-permissoes                   │
└──────────────────────────────┬──────────────────────────────────────┘
                               │
┌──────────────────────────────▼──────────────────────────────────────┐
│ V2PermissionMiddleware                                              │
│  active_pattern → moduleKey → nível (none|read|write)               │
└───────────────┬───────────────────────────────┬─────────────────────┘
                │                               │
┌───────────────▼──────────────┐  ┌─────────────▼─────────────────────┐
│ PermissionService            │  │ v2_can() — helper global          │
│  aliases + normalização      │  │  (helpers.php:4-29)               │
│  getUserPermissions()        │  └─────────────┬─────────────────────┘
└───────────────┬──────────────┘                │
                │                               │
┌───────────────▼───────────────────────────────▼─────────────────────┐
│ V2Role (v2_roles.permissions_v2 jsonb)                              │
│ User (App\Admin\User — users.v2_role_id, Sentinel V1 em paralelo)   │
└─────────────────────────────────────────────────────────────────────┘
```

## 2. Descoberta e registro do módulo

O `V2ServiceProvider` varre os diretórios de grupos e módulos sob
`V2/Modules`
(`V2/Core/Providers/V2ServiceProvider.php:84-131`). Para
`Infraestrutura/GestaoPermissoes`:

| Recurso | Regra | Resultado |
|---|---|---|
| Provider | `V2\Modules\Infraestrutura\GestaoPermissoes\ModuleProvider` | injeta a config em `v2.modules.configs['GestaoPermissoes']` |
| Grupo | nome do diretório pai | `group = 'Infraestrutura'` |
| Views | `loadViewsFrom(diretório do módulo/Views, 'v2-gestaopermissoes')` | namespace `v2-gestaopermissoes::` |
| Migrations | `loadMigrationsFrom` | o módulo **não** possui migrations próprias |
| Rotas CMS | `Routes/cms.php` + `web` + `module.active:GestaoPermissoes` | prefixo `/cms/permissoes` |
| Rotas web/API | ausentes | — |

A chave canônica do módulo é o **nome do diretório**: `GestaoPermissoes`. É a
chave gravada na matriz de permissões e a chave usada por `v2_can()`.

## 3. Classes envolvidas

| Classe | Caminho | Papel |
|---|---|---|
| `ModuleProvider` | `V2/Modules/Infraestrutura/GestaoPermissoes/ModuleProvider.php` | título, ícone, posição 100, `active_pattern` e dois submenus |
| `GestaoPermissoesRolesController` | `V2/Modules/Infraestrutura/GestaoPermissoes/Controllers/GestaoPermissoesRolesController.php` | CRUD de perfis e matriz de permissões |
| `GestaoPermissoesPermissionsController` | `V2/Modules/Infraestrutura/GestaoPermissoes/Controllers/GestaoPermissoesPermissionsController.php` | listagem/edição de usuários, troca de senha e papel V1 |
| `BaseController` | `V2/Core/Controllers/BaseController.php` | classe-base dos dois controllers |
| `V2Role` | `V2/Core/Models/V2Role.php` | model de `v2_roles`, cast `permissions_v2 => array`, UUID v4, soft delete |
| `PermissionService` | `V2/Core/Services/PermissionService.php` | resolução canônica/alias, leitura das permissões, CRUD de perfis, auditoria |
| `V2PermissionMiddleware` | `V2/Core/Middleware/V2PermissionMiddleware.php` | autorização por rota e nível |
| `CmsAuthenticate` | `V2/Core/Middleware/CmsAuthenticate.php` | autenticação Sentinel + delegação ao middleware de permissão |
| `ModuleAccessMiddleware` | `V2/Core/Middleware/ModuleAccessMiddleware.php` | ativação por flag (`v2_module_flags`) |
| `SidebarComposer` | `V2/Core/ViewComposers/SidebarComposer.php` | oculta módulos sem permissão e grupos restritos |
| `V2AuditLogger` | `V2/Core/Services/V2AuditLogger.php` | auditoria persistida (`activity()` na linha 635) |
| `v2_can()` | `V2/Core/Helpers/helpers.php:4-29` | fachada global de verificação |
| `User` | `app/Http/Model/Admin/User.php` | usuário Sentinel; recebe `v2_role_id` por atribuição direta |

## 4. Dependências externas

| Pacote | Uso no módulo |
|---|---|
| `cartalyst/sentinel` | autenticação, papéis V1 (`super-admin`, `operador`, `administrador`), `registerAndActivate`, `findRoleById` |
| `ramsey/uuid` | geração do UUID v4 em `V2Role::boot()` |
| Laravel 5.5 (`illuminate/*`) | validação, paginação, Blade, redirect, Eloquent |
| PostgreSQL | coluna `jsonb` para `permissions_v2` |

Não há dependência de Livewire, Vue, jQuery ou biblioteca de componentes: as
views usam Bootstrap 5 por classes e pequenos scripts inline.

## 5. Configuração declarada

```php
// V2/Modules/Infraestrutura/GestaoPermissoes/ModuleProvider.php:13-38
public function getConfig(): array
{
    return [
        'title' => 'Gestão de Permissões',
        'description' => 'Permissões, perfis de acesso e segurança do CMS.',
        'icon' => 'bi bi-shield-lock-fill',
        'position' => 100,
        'active_pattern' => ['cms/permissoes/permissions*', 'cms/permissoes/roles*'],
        'subModulesLevel1' => [
            [
                'title' => 'Permissões e Usuários',
                'icon' => 'bi bi-people',
                'route' => 'v2.gestaopermissoes.permissions.index',
                'position' => 1
            ],
            [
                'title' => 'Perfis de Acesso V2',
                'icon' => 'bi bi-tags',
                'route' => 'v2.gestaopermissoes.roles.index',
                'position' => 2
            ]
        ]
    ];
}
```

O provider **não** declara `restricted` nem `route`; a entrada no menu é feita
pelos submenus. A sidebar mostra o grupo apenas se o usuário tiver ao menos um
nível diferente de `none` para `GestaoPermissoes`
(`V2/Core/ViewComposers/SidebarComposer.php:61-66`).

## 6. Configuração de suporte

`config/v2.php` centraliza o e-mail de bypass:

```php
'support' => [
    'email' => env('V2_SUPPORT_EMAIL', 'suporte@maxima.inf.br'),
],
```

(`config/v2.php:25-27`)

Esse valor é lido em três pontos independentes: `v2_can()`
(`helpers.php:21`), `PermissionService::hasPermission()`
(`PermissionService.php:72`) e `V2PermissionMiddleware` (`:51,70`). Não há
cache nem tabela: trocar o e-mail exige alterar `V2_SUPPORT_EMAIL` e limpar a
configuração da aplicação.

## 7. Ordem de avaliação e curto-circuito

| # | Condição | Resultado |
|---|---|---|
| 1 | `Sentinel::check()` falso | 401 (AJAX/JSON) ou redirect para login |
| 2 | rota casa um módulo do grupo `Support` e o usuário não é o e-mail de suporte | 403 / dashboard |
| 3 | e-mail de suporte ou `inRole('super-admin')` | libera |
| 4 | nenhum `active_pattern` casou | libera (fail-open) |
| 5 | nível `none` | 403 / dashboard |
| 6 | nível `read` e método GET/HEAD | libera |
| 7 | nível `read` e método de escrita | 403 / `back()` |
| 8 | nível `write` | libera |
| 9 | nível desconhecido | 403 “Configuração de permissões inválida” |

## 8. Acoplamentos estruturais

- O módulo **depende de todos os outros módulos** para montar a matriz: a lista
  de linhas é `app('v2.modules.configs')`, filtrada apenas por `restricted`.
- O `PermissionService` é um **singleton do Core** registrado em
  `V2ServiceProvider.php:562-566`, com `V2AuditLogger` injetado.
- O `V2Role` aponta para `App\Admin\User` por `hasMany`
  (`V2/Core/Models/V2Role.php:99-102`), mas a FK inversa não existe no model
  `User` (não há `belongsTo`).
- O `CmsAuthenticate` chama o `V2PermissionMiddleware` diretamente
  (`CmsAuthenticate.php:40`), o que duplica a execução nas rotas que também
  declaram `v2.permission`.
