# LGPD (Barra de Privacidade) — Gaps, Dívidas e Comparativo v5 → v6

> **Versão analisada:** `v6` (comparativo com `v5`)
> **Slug do módulo:** `lgpd` (tabela `modulos`, id 3)

---

## 1. Sobreposição com os outros três módulos de configuração

**A sobreposição é literal.** O módulo `lgpd` não tem controller, model, tabela,
rota de leitura nem endpoint de gravação próprios. Ele usa:

| Camada | LGPD | `../configuracoes-da-instituicao/` | `../temas-e-cores/` | `../outras-configuracoes/` |
|---|---|---|---|---|
| Tabela | `configurations` | `configurations` | `configurations` | `configurations` |
| Model | `App\Prefecture\Configuration` | idem | idem | idem |
| Controller | `Prefecture\ConfigurationController` | idem | idem | idem |
| Endpoint de gravação | `POST /cms/update/configuration` (`update.configurations.by.category`) | idem | idem | idem |
| Form Request | `ConfigurationRequest` | idem | idem | idem |
| View de edição | `prefecture.configurations.barra-privacidade.edit` | `prefecture.configurations.configuracoes.edit` | `prefecture.configurations.temas.edit` | `prefecture.configurations.index` (reaproveitada) |

**O que realmente diferencia o módulo LGPD dos demais:**

| Eixo | Valor do LGPD |
|---|---|
| Categoria de configuração | `barra-privacidade` (`category_configurations.id = 7`) |
| Recurso (`resources.slug`) | `barra-privacidade`, com `resources.modulo_id = 3` |
| Módulo (`modulos.slug`) | `lgpd` (id 3, `name = 'LGPD'`) |
| Slug de rota | `barra-privacidade` (usado em `edit.configuracoes.by.category`) |
| Permissão de leitura | `barra-privacidade.edit` |
| Efeito no portal | Renderização da faixa de privacidade (feature pública) |

Ou seja: **a separação é por categoria + recurso + módulo**; tudo o mais é
compartilhado. Consequências práticas:

- Uma correção no `ConfigurationController` (ex.: validação do
  `category_configurations_id`) afeta os quatro módulos.
- Um payload de `POST /cms/update/configuration` pode gravar chaves de
  qualquer categoria, desde que traga o `category_configurations_id`
  correspondente — a autorização é por `configuracoes.edit`, não por
  categoria.
- O `modulos.status` só tem efeito no **portal** (feature pública). No CMS,
  o módulo `lgpd` é apenas mais uma linha de `modulos`.

## 2. Achados verificados (bugs e riscos)

| # | Achado | Evidência | Impacto |
|---|---|---|---|
| G1 | **`modulos.status = 0` para o módulo `lgpd` na migration.** | `v6/database/migrations/2024_11_01_150206_create_modulos_table.php`: `'slug' => 'lgpd', 'status' => 0` (os outros três recebem `1`). | Em instalação nova, a barra **nunca** é exibida no portal, mesmo com `barra-privacidade-exibir = 1`. O item "LGPD" também não aparece no menu do CMS. É preciso ativar o módulo em `/cms/modulos`. |
| G2 | **CSS referenciado e inexistente.** | `barra-privacidade.blade.php` referencia `assets/css/portal/privacybar/style.css`; o arquivo não existe em `v6/public/assets/` nem na v5 (listagem de `v6/public/assets/css/portal/` e `find` por `*privacybar*` sem resultado). | 404 em toda página que renderiza a barra. O estilo efetivo vem do `<style>` inline de `head.blade.php`. |
| G3 | **`ConfigurationsComposer` é código morto.** | `v6/app/Http/Views/Composers/ConfigurationsComposer.php` carrega exatamente as 3 chaves da barra, mas não é registrado em nenhum provider (grep em `v6/app/Providers/` sem resultado). | Confusão de manter código que parece ativo; a leitura real é feita pelo `ConfigHeaderServiceProvider`. |
| G4 | **Permissão assimétrica.** | Leitura: `has_access(["$acess.edit"])` → `barra-privacidade.edit`. Gravação: `has_access(['configuracoes.edit'])`. | Quem possui `configuracoes.edit` grava a LGPD sem nunca ter lido a tela; e quem tem `barra-privacidade.edit` vê a tela mas não salva. |
| G5 | **Ausência de consentimento granular e de prova de consentimento.** | O único registro do aceite é o cookie `acceptCookies` do navegador (`v6/public/assets/js/custom/portal/barra-privacidade.js`). Não existe tabela de consentimento, nem log de aceite, nem versionamento do texto aceito. | Não é possível comprovar quem aceitou, quando, qual texto e sob qual IP/UA. Risco jurídico direto (LGPD art. 8). |
| G6 | **Cookie sem `Secure`/`SameSite`, escrito por JS.** | `document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"`. O middleware `v6/app/Http/Middleware/EncryptCookies.php` existe, mas não participa (o cookie é criado no cliente). | O cookie acompanha requisições cross-site; não é `HttpOnly`; em HTTP puro trafega em claro. |
| G7 | **CSS do botão aplicado por cor hexadecimal sem validação.** | `barra-privacidade-botao` é `type = text` com `max_input = 999`/`min_input = 1`; o `Form Request` não gera regra porque `notnull = false`. | Um valor inválido (ex.: `azul`) quebra apenas a cor (CSS inválido é ignorado); nada impede. |
| G8 | **`barra-privacidade-botao` não está na lista do `ConfigHeaderServiceProvider`.** | Lista literal do provider inclui `barra-privacidade-exibir`, `barra-privacidade` e `barra-privacidade-expiracao` — mas não `-botao`. | Leitura extra via `@inject` + query por renderização; qualquer nova chave da barra não chegará ao portal. |
| G9 | **HTML bruto do Summernote renderizado sem escape.** | `{!! $configurations['barra-privacidade'] !!}` (`barra-privacidade.blade.php:14`). | XSS Stored por operador com acesso à configuração. |
| G10 | **`head.blade.php` faz `$config->where('key','cor_1')->first()->value` sem guarda.** | `head.blade.php:2-5`. | Se `cor_1`..`cor_4` não existirem (categoria `site`), erro fatal em todas as páginas do portal. 5 queries por renderização. |
| G11 | **`configurations.key` sem índice único.** | `updateByCategory` usa `Configuration::where('key',$key)->first()`. | Chaves duplicadas gravam apenas a primeira linha; não há garantia de unicidade no banco. |
| G12 | **N+1 no Form Request.** | `ConfigurationRequest::rules()` e `::messages()` executam uma query por campo enviado. | 4+ queries por submit; piora com categorias maiores (a categoria `site` tem 57 configurações ativas). |
| G13 | **`category_configurations_id` não é validado contra as chaves enviadas.** | O controller usa `CategoryConfiguration::find($request->get('category_configurations_id'))` e grava tudo que veio no POST. | POST forjado grava chaves de outra categoria com o redirecionamento da LGPD. |
| G14 | **`cache 'modulos'` com `rememberForever`.** | `ModulosServiceProvider`: `Cache::rememberForever('modulos', fn () => Modulos::all()->pluck('status','slug'))`. | Alterações diretas no banco (ou em outra instância) não surtem efeito até o `Cache::pull('modulos')` feito pelo `Admin\ModulosController`. |
| G15 | **Endpoint público `/getPrivacyBar` sem cache.** | `Portal\HelperController@getPrivacyBar()` consulta o banco a cada clique. | Volume baixo, mas é uma query por aceite. |
| G16 | **`myFetch` engole exceções e retorna `undefined`.** | `v6/public/assets/js/custom/utilities/functions.js:64` (`catch` só faz `console.log`). | `bar_config.value` lança `TypeError` → o cookie não é gravado e o cidadão vê a barra novamente. |
| G17 | **`maxlength="999"` no campo de dias.** | `form.blade.php` aplica `maxlength="{{$configuration->max_input}}"` em todo input `text`. | Prazo máximo de 999 dias; e `onkeyup="somenteNumeros(this);"` impede valores fracionários. |
| G18 | **A view SQL `configuracoes` não filtra por categoria.** | `v6/database/migrations/2023_04_17_144615_create_view_to_configuracoes.php` + `DatatableController@get_configuracoes` (linha 384). | O datatable do CMS lista as 4 chaves da LGPD junto das 87 configurações ativas de todas as categorias. |
| G19 | **`echo` dentro do controller.** | `ConfigurationController@updateByCategory` imprime "JSON file created successfully..." quando a categoria é `site`. | Não afeta a LGPD (categoria ≠ `site`); no container com `output_buffering = 0` emite saída antes do redirect. Registrado aqui por completude. |
| G20 | **Menu do CMS depende de `$modulos['lgpd']`.** | `v6/resources/views/vendor/adminlte/page.blade.php:244`. | Com `status = 0`, o item "LGPD" não aparece — o operador não descobre a tela pelo menu. |

## 3. Divergências v5 → v6

| Item | v5 | v6 | Observação |
|---|---|---|---|
| Tabela `modulos` | inexistente | `v6/database/migrations/2024_11_01_150206_create_modulos_table.php` | `lgpd` inserido com `status = 0` (os outros três com `1`). |
| `resources.modulo_id` | inexistente | `v6/database/migrations/2024_11_05_104026_add_modulo_id_to_resources_table.php` | Recurso `barra-privacidade` → `modulo_id = 3`. |
| Condição de renderização | `isset($configurations['barra-privacidade-exibir']) && == true` e `isset($configurations['barra-privacidade'])` | idem **+** `isset($modulos['lgpd']) && $modulos['lgpd']` | A barra passou a depender do módulo ativo. |
| CSS inline da barra | dentro de `barra-privacidade.blade.php` | movido para `head.blade.php` | v6 acrescenta o bloco `.barradeprivacidade` no head. |
| Link de CSS externo | inexistente | `assets/css/portal/privacybar/style.css` | **Arquivo inexistente** em ambas as versões → refactor incompleto na v6. |
| `ConfigurationsComposer` | inexistente | criado (não registrado) | Código morto na v6. |
| Seeder da barra | `v5/database/seeds/PrivacyBarSeeder.php` (criava categoria + 4 chaves) | **removido**; as 4 chaves migraram para `v6/database/seeds/ConfigurationsTableSeeder.php:1047-1105` | `PrivacyBarSeeder` não existe na v6 (confirmado por busca no diretório de seeds). |
| `DefaultSettingSeeder` | `v5/database/seeds/DefaultSettingSeeder.php:94-97` com os mesmos 4 defaults | idem em `v6/database/seeds/DefaultSettingSeeder.php:92-97` | Mantido. |
| `order` das chaves | 1..4 (exibir=1, texto=2, expiração=3, botão=4) | 64..67 (texto=64, expiração=65, exibir=66, botão=67) | Reordenação global. |
| Descrição do campo texto | "Texto Barra de privacidade" | "Ao continuar navegando no nosso portal, você concorda com a nossa Política de Privacidade." | Ajuste de conteúdo; o valor default também mudou. |
| Tipo do campo texto | `richtext` | `richtext` | Sem mudança. |
| Valor default de `barra-privacidade-exibir` | `0` | `0` | Sem mudança. |
| JS da barra | `v5/public/assets/js/custom/portal/barra-privacidade.js` | idêntico | Sem mudança funcional. |
| Views do CMS | `v5/resources/views/prefecture/configurations/barra-privacidade/edit.blade.php` e `form.blade.php` | **idênticas** (diff vazio) | Nenhuma evolução no CMS. |
| `Portal\HelperController@getPrivacyBar` | `v5/app/Http/Controllers/Portal/HelperController.php:15` | idem | Sem mudança. |
| Rota `GET /getPrivacyBar` | `v5/routes/web.php:186` | `v6/routes/web.php:208` | Sem mudança funcional. |
| Mascaramento de dados (LGPD ampla) | ausente | `v6/app/Http/Traits/DataMaskTrait.php`, seeds de backfill, `mask_request` | **Ganho da v6**, fora do módulo `lgpd` estrito. |
| Manual/Cadastro da LGPD | — | `v6/database/migrations/2023_02_01_083626_add_itens_to_archives.php:512` e `:722` | Ganho da v6. |
| Item de menu "LGPD" | sem checagem de módulo | `&& $modulos['lgpd']` | Ganho/Perda: melhor consistência, mas oculta o módulo por padrão. |

## 4. Itens da v5 que foram removidos na v6

| Item | Consequência |
|---|---|
| `v5/database/seeds/PrivacyBarSeeder.php` (seeder isolado da barra) | Quem dependia dele (`db:seed --class=PrivacyBarSeeder`) não tem mais equivalente; o seeding virou parte do `ConfigurationsTableSeeder`. |
| Renderização da barra sem depender de módulo | Comportamento observável alterado: instalações migradas que não ativarem o módulo `lgpd` **perdem** a barra que antes aparecia. |

## 5. Riscos de dados (migração de schema)

| Risco | Descrição | Mitigação |
|---|---|---|
| R1 | Migração que recrie `configurations` sem as 4 chaves da LGPD | O seeding da v6 (`ConfigurationsTableSeeder`) recria as chaves; mas sistemas que não rodam o seeder perderão a barra. |
| R2 | `configurations.key` sem unicidade | Antes de criar índice único na v7, auditar duplicidades (ver `../configuracoes-da-instituicao/08-GAPS-E-DIVIDAS.md`). |
| R3 | Exclusão da categoria `barra-privacidade` (soft delete) | A view `configuracoes` e o `getConfiguration()` filtram por `deleted_at`/slug — a barra some silenciosamente. |
| R4 | Migração de `modulos.status` | Ao migrar para a v7, o `status = 0` do `lgpd` pode ser interpretado como "desativado" e gerar perda silenciosa da barra em municípios onde ela já estava no ar (o status só foi introduzido na v6). |
| R5 | Texto HTML antigo sem sanitização | Migrar o HTML para a nova tabela exige sanitização; o texto pode conter tags antigas do Summernote (`<p>`, `<span style>`). |

## 6. Funcionalidades incompletas ou mortas

| Item | Situação |
|---|---|
| `v6/app/Http/Views/Composers/ConfigurationsComposer.php` | Morto (não registrado). Deveria ser removido ou registrado no lugar do `ConfigHeaderServiceProvider` para as chaves da barra. |
| `assets/css/portal/privacybar/style.css` | Arquivo inexistente referenciado pela view. |
| Consentimento granular | Não implementado — há apenas um cookie booleano. |
| Registro/prova de consentimento | Não implementado — nenhuma tabela, nenhum log. |
| Botão "Política de Privacidade" na faixa | Não existe link estruturado; depende do HTML digitado pelo operador. |
| Central de preferências do cidadão | Não existe (o cidadão não pode revogar o aceite além de apagar cookies). |
| Endpoint de revogação | Não existe. |

## 7. Perguntas em aberto para o negócio

1. O módulo `lgpd` deve nascer **ativo** ou **inativo** na v7? A v6 entrega inativo.
2. O aceite da barra precisa ser comprovado em banco (prova de consentimento) ou o cookie basta? Com que dados (IP, UA, versão do texto)?
3. O texto da política de privacidade deve ser gerenciado no CMS ou vir de um documento versionado pelo encarregado de dados (DPO)?
4. É preciso consentimento granular (cookies de analytics, mapas, mídias sociais)? A chave `dashboard_enhanced_enabled` e o `jivo` da categoria `site` sugerem trackers usados **antes** do aceite — isso é uma inconsistência a resolver.
5. Qual o prazo padrão (7 dias) e há limite máximo aceitável (o formulário permite 999 dias)?
6. O texto deve poder conter link para a política completa? Quem mantém o destino?
7. Em caso de mudança do texto, o aceite anterior deve ser invalidado (pedir novo aceite)? Today: não.
8. Quem audita os consentimentos registrados?

## 8. Recomendações prioritárias para a v7

| Prioridade | Ação |
|---|---|
| P1 | Criar `privacy_bars` + `privacy_bar_consents` e registrar o aceite com IP, UA, hash do texto e data. |
| P1 | Migrar o conteúdo das 4 chaves e **decidir** conscientemente o default de exibição (hoje: inativo). |
| P2 | Corrigir a assimetria de permissões (uma policy só para leitura e escrita). |
| P2 | Sanitizar o HTML do texto e validar a cor (hex) e o prazo (inteiro 1..365). |
| P2 | Gravar o cookie no servidor, com `Secure`, `HttpOnly` e `SameSite=Lax`. |
| P3 | Remover o `ConfigurationsComposer` morto e implementar o CSS que falta (ou eliminar o link). |
| P3 | Trocar a configuração genérica por uma entidade dedicada; migrar as demais categorias com o mesmo padrão (ver `../configuracoes-da-instituicao/06-MIGRACAO-MODERNA.md`). |
