# Usuários — Rotas e Controllers

> Todas as rotas vivem no grupo `cms` de `v6/routes/basepro.php`, carregado
> com o middleware `web` (`v6/app/Providers/RouteServiceProvider.php:76-81`).
> O prefixo `cms` é o que separa o painel do portal público.

---

## 1. Rotas do módulo (CMS)

### 1.1 Usuários

| Método | URI | Nome | Controller@action | Middleware | Linha |
|---|---|---|---|---|---|
| GET | `/cms/usuario/inativos` | `usuario.inactive` | `Admin\UserController@inactive` | `web` | 44 |
| GET | `/cms/usuario/ativos` | `usuario.active` | `Admin\UserController@active` | `web` | 45 |
| GET | `/cms/usuario/todos` | `usuario.all` | `Admin\UserController@all` | `web` | 46 |
| POST | `/cms/usuario/{id}/status` | `usuario.status` | `Admin\UserController@set_status` | `web` | 47 |
| DELETE | `/cms/usuario/{id}/inactivate` | `usuario.inactivate` | `Admin\UserController@inactivate` | `web` | 48 |
| GET | `/cms/usuario/{id}/restore` | `usuario.restore` | `Admin\UserController@restore` | `web` | 49 |
| GET | `/cms/usuario/{user}/add/roles` | `usuario.addRoles` | `Admin\UserController@addRules` | `web` | 50 |
| GET | `/cms/usuario/add/roles` | `usuario.addRoles` (duplicado) | `Admin\UserController@addRules` | `web` | 52 |
| GET | `/cms/usuario/save/rules` | `usuario.saveRules` | `Admin\RoleController@store` | `web` | 54 |
| GET | `/cms/usuario` | `usuario.index` | `Admin\UserController@index` | `web` | 58 (`Route::resource`) |
| GET | `/cms/usuario/create` | `usuario.create` | `Admin\UserController@create` | `web` | 58 |
| POST | `/cms/usuario` | `usuario.store` | `Admin\UserController@store` | `web` | 58 |
| GET | `/cms/usuario/{usuario}` | `usuario.show` | `Admin\UserController@show` | `web` | 58 |
| GET | `/cms/usuario/{usuario}/edit` | `usuario.edit` | `Admin\UserController@edit` | `web` | 58 |
| PUT/PATCH | `/cms/usuario/{usuario}` | `usuario.update` | `Admin\UserController@update` | `web` | 58 |
| DELETE | `/cms/usuario/{usuario}` | `usuario.destroy` | `Admin\UserController@destroy` | `web` | 58 |

> As rotas explícitas (44-54) são **declaradas antes** do `Route::resource`
> (58), por isso `/cms/usuario/inativos` não colide com
> `usuario.show` (parâmetro `{usuario}`).
>
> `usuario.addRoles` é declarado **duas vezes** (linhas 50 e 52) com o mesmo
> nome. A segunda sobrescreve a primeira no dicionário de rotas: `route('usuario.addRoles')`
> resolve para `/cms/usuario/add/roles`, que **não tem parâmetro** — e
> `UserController@addRules($id)` exige um argumento. Ver
> `08-GAPS-E-DIVIDAS.md`.

### 1.2 Perfis (roles)

| Método | URI | Nome | Controller@action | Middleware | Linha |
|---|---|---|---|---|---|
| GET | `/cms/perfil` | `perfil.index` | `Admin\RoleController@index` | `web` + `check.route.permission` | 59 |
| GET | `/cms/perfil/create` | `perfil.create` | `Admin\RoleController@create` | idem | 59 |
| POST | `/cms/perfil` | `perfil.store` | `Admin\RoleController@store` | idem | 59 |
| GET | `/cms/perfil/{perfil}` | `perfil.show` | `Admin\RoleController@show` | idem | 59 |
| GET | `/cms/perfil/{perfil}/edit` | `perfil.edit` | `Admin\RoleController@edit` | idem | 59 |
| PUT/PATCH | `/cms/perfil/{perfil}` | `perfil.update` | `Admin\RoleController@update` | idem | 59 |
| DELETE | `/cms/perfil/{perfil}` | `perfil.destroy` | `Admin\RoleController@destroy` | idem | 59 |
| GET | `/cms/role/list/all` | `role.listAll` | `Admin\RoleController@getAll` | `web` | 107 |

> `check.route.permission` vem do construtor de `RoleController`
> (`v6/app/Http/Controllers/Admin/RoleController.php:20-24`).
> `RoleController@show` e `@getAll` têm **corpo vazio** (retornam `null`).

### 1.3 Permissões

| Método | URI | Nome | Controller@action | Middleware | Linha |
|---|---|---|---|---|---|
| GET | `/cms/permissao` | `permissao.index` | `Admin\PermissionController@index` | `web` + `check.route.permission` | 64 |
| GET | `/cms/permissao/create` | `permissao.create` | `Admin\PermissionController@create` | idem | 64 |
| POST | `/cms/permissao` | `permissao.store` | `Admin\PermissionController@store` | idem | 64 |
| GET | `/cms/permissao/{permissao}` | `permissao.show` | `Admin\PermissionController@show` | idem | 64 |
| GET | `/cms/permissao/{permissao}/edit` | `permissao.edit` | `Admin\PermissionController@edit` | idem | 64 |
| PUT/PATCH | `/cms/permissao/{permissao}` | `permissao.update` | `Admin\PermissionController@update` | idem | 64 |
| DELETE | `/cms/permissao/{permissao}` | `permissao.destroy` | `Admin\PermissionController@destroy` | idem | 64 |

> `PermissionController` restringe **todas** as ações a `super-admin` com
> `Sentinel::inRole('super-admin')` explícito no corpo de cada método
> (`v6/app/Http/Controllers/Admin/PermissionController.php:26-30,50-54,...`).

### 1.4 Recursos

| Método | URI | Nome | Controller@action | Middleware | Linha |
|---|---|---|---|---|---|
| GET | `/cms/recurso` | `recurso.index` | `Admin\ResourceController@index` | `web` | 63 |
| GET | `/cms/recurso/create` | `recurso.create` | `Admin\ResourceController@create` | idem | 63 |
| POST | `/cms/recurso` | `recurso.store` | `Admin\ResourceController@store` | idem | 63 |
| GET | `/cms/recurso/{recurso}` | `recurso.show` | `Admin\ResourceController@show` | idem | 63 |
| GET | `/cms/recurso/{recurso}/edit` | `recurso.edit` | `Admin\ResourceController@edit` | idem | 63 |
| PUT/PATCH | `/cms/recurso/{recurso}` | `recurso.update` | `Admin\ResourceController@update` | idem | 63 |
| DELETE | `/cms/recurso/{recurso}` | `recurso.destroy` | `Admin\ResourceController@destroy` | idem | 63 |

### 1.5 Endpoints AJAX (DataTables)

Prefixo `/cms/datatable` (grupo em `v6/routes/basepro.php:392-460`).

| Método | URI | Nome | Action | Consulta |
|---|---|---|---|---|
| GET | `/cms/datatable/listar_usuarios` | — | `Prefecture\DatatableController@get_listar_usuarios` | view `listar_usuarios` |
| GET | `/cms/datatable/listar_todos_usuarios` | — | `@get_listar_todos_usuarios` | view `listar_todos_usuarios` |
| GET | `/cms/datatable/listar_usuarios_inativos` | — | `@get_listar_usuarios_inativos` | view `listar_usuarios_inativos` |
| GET | `/cms/datatable/perfis` | `perfis` | `@get_perfis` | view `perfis` |
| GET | `/cms/datatable/permissoes` | — | `@get_permissoes` | view `permissoes` |
| GET | `/cms/datatable/recursos` | — | `@get_recursos` | view `recursos` |

### 1.6 Autenticação (relacionadas)

| Método | URI | Nome | Controller@action | Linha |
|---|---|---|---|---|
| GET | `/login` | `login` | `Admin\LoginController@login` | 13 |
| POST | `/login` | `postLogin` | `Admin\LoginController@postLogin` | 14 |
| POST | `/logout` | `logout` | `Admin\LoginController@logout` | 15 |
| GET | `/activate/{email}/{activation_code}` | `activate` | `Admin\ActivationController@activate` | 19 |
| GET | `password/reset` | `password.request` | `Admin\ForgotPasswordController@showLinkRequestForm` | 23 |
| POST | `password/email` | `password.email` | `Admin\ForgotPasswordController@sendResetLinkEmail` | 24 |
| GET | `password/reset/{token}` | `password.reset` | `Admin\ResetPasswordController@showResetForm` | 25 |
| POST | `password/reset` | — | `Admin\ResetPasswordController@reset` | 26 |

### 1.7 Rotas do portal

Nenhuma. O módulo é **exclusivamente CMS**; o cidadão não interage com ele.

---

## 2. Permissões exigidas por ação

| Ação | Permissão | Onde é verificada |
|---|---|---|
| Ver lista de usuários | `usuario.index` | `UserController@index` (linha 34) |
| Criar usuário | `usuario.create` | `UserController@create` (linha 56), `@store` (linha 91) |
| Ver detalhe | `usuario.show` | `UserController@show` (linha 162) |
| Editar usuário | `usuario.edit` **ou** ser o próprio usuário | `UserController@edit` (linha 184), `@update` (linha 224) |
| Inativar | `usuario.inactivate` | `UserController@inactivate` (linha 304) |
| Ativar | `usuario.restore` | `UserController@restore` (linha 431) |
| Ver/editar perfis | `perfil.*` | `check.route.permission` (não há checagem no corpo do controller) |
| Ver/editar permissões | `super-admin` | `PermissionController@index` (linha 29), `@create` (51), `@edit` (116) |
| Atribuir perfis a um usuário | `perfil.create` | `UserController@store` (linha 97), `@update` (linha 228) |
| Ver o item "Usuários" no menu | `usuario.*` | `MenuServiceProvider.php:337` |
| Ver o item "Perfis" no submenu | `perfil.index` | `MenuServiceProvider.php:343` |
| Ver o item "Recursos" no submenu | `recurso.index` | `MenuServiceProvider.php:346` |
| Ver o item "Permissões" no submenu | `permissao.index` | `MenuServiceProvider.php:349` |

---

## 3. Form Requests e validações

### 3.1 `UserRequest` — `v6/app/Http/Requests/UserRequest.php`

| Campo | Regra | Observação |
|---|---|---|
| `name` | `required` | |
| `email` | `required` | a unicidade é validada só no `store` (ver abaixo) |
| `password` | `required\|min:6` | só é exigida se o campo foi enviado |
| `password_confirmation` | `required\|same:password` | idem |
| `selectProfiles` | `required` | só quando `Sentinel::hasAnyAccess('perfil.create','perfil.edit')` **ou** a rota atual é `usuario.store` |

Mensagens customizadas para `password.*` e `selectProfiles.required`.

### 3.2 Validação do `UserController@store` (dupla validação)

O `store` **não** usa o `UserRequest`; ele usa um validador próprio
(`v6/app/Http/Controllers/Admin/UserController.php:343-372`):

```php
private function getRules()
{
    $unique = (Route::getCurrentRoute()->getName() == 'usuario.store') ? '|unique:users' : '';
    $rules = [
        "email" => "required|email|max:255$unique",
        "password" => "required|min:6|confirmed",
        "password_confirmation" => "required|min:6",
    ];
    if (Sentinel::hasAnyAccess('perfil.create', 'perfil.edit')) {
        $rules["selectProfiles"] = "required";
    }
    return $rules;
}
```

> Ou seja: o `create` valida com o validador do controller e o `update` valida
> com o `UserRequest`. As regras divergem: o `store` exige `email` válido e
> único; o `UserRequest` só exige `email` preenchido.

### 3.3 `RoleController` / `PermissionController` / `ResourceController`

Validam no próprio controller:

| Controller | Regra | Linha |
|---|---|---|
| `RoleController` | `name` → `required\|string\|max:255` (+ `unique:roles` só no `perfil.store`) | `v6/app/Http/Controllers/Admin/RoleController.php:203-209` |
| `PermissionController` | `name` → `required\|max:255`; `description` → `required\|max:255` | `v6/app/Http/Controllers/Admin/PermissionController.php:193-201` |

---

## 4. API Resources / Presenters

Nenhum. O módulo não expõe API JSON; a listagem usa Yajra DataTables com
serialização manual (`struct_views()` em
`v6/app/Http/Controllers/Prefecture/DatatableController.php:504-520`):

```php
$data[$i]->edit       = has_access("$permission.edit");
$data[$i]->delete     = has_access("$permission.delete");
$data[$i]->active     = has_access("$permission.restore");
$data[$i]->inactivate = has_access("$permission.inactivate");
```

Essas flags são consumidas no JS para habilitar/desabilitar botões de ação
linha a linha.

---

## 5. Mapa de decisão do `check.route.permission`

O middleware infere a permissão a partir do **nome da rota** e dos
**segmentos da URL** (`v6/app/Http/Middleware/CheckRoutePermission.php:47-113`):

| Entrada | Permissão avaliada |
|---|---|
| Nome da rota `usuario.index` | `usuario.index` |
| Nome da rota `perfil.create` | `perfil.create` |
| Segmento 3 da URL = `cadastrar` | `<segmento 4>.create` |
| Segmento 3 da URL = `editar` | `<segmento 4>.edit` |
| Segmento 3 da URL = `ativos` / `inativos` | `<segmento 2>.index` |
| Qualquer outro segmento 3 | `<segmento 3>.index` |

Rotas liberadas sem checagem (`$openRoutes`): `login`, `logout`, `datatable`,
`dashboard`, `error`, `ajax`, `info-request-status-update`.

Exceções explícitas no mesmo middleware: `responder_chamado`/`enviar_resposta`
→ `ouvidoria.edit`; `diario-config.*` → `diario-oficial.create`;
`diario.generation-status` e afins → `diario-oficial.edit`.

> **Falha relevante:** `$resourceRoutes = ['edit' => 'update', 'create' =>
> 'store', 'delete' => 'destroy']` e a checagem é
> `in_array($value, $resourceRoutes)`, que compara com os **valores** do array
> (`update`, `store`, `destroy`), não com as chaves. Como
> `array_where` (`v6/vendor/laravel/framework/src/Illuminate/Support/helpers.php:315`)
> filtra o array de segmentos do nome da rota, **qualquer rota cujo nome
> contenha `store`, `update` ou `destroy` é liberada incondicionalmente**.
> Ver `08-GAPS-E-DIVIDAS.md`.
