++ fix import e permessi
This commit is contained in:
69
HANDOFF.md
69
HANDOFF.md
@@ -4,7 +4,7 @@ Stato del progetto per chi ci mette mano dopo. Per installazione, deploy e uso
|
||||
quotidiano vedi [README.md](README.md); qui c'è il contesto che il README non
|
||||
può dare: perché le cose sono come sono, e cosa resta aperto.
|
||||
|
||||
Ultimo aggiornamento: **18 luglio 2026**.
|
||||
Ultimo aggiornamento: **25 luglio 2026**.
|
||||
|
||||
---
|
||||
|
||||
@@ -89,6 +89,66 @@ messaggio d'errore è più chiaro).
|
||||
Deliberato: un errore immediato è meglio di un database in produzione con
|
||||
password `secret`.
|
||||
|
||||
### Modifica/cancellazione delle assegnazioni: solo amministratore
|
||||
|
||||
L'unico punto in cui un'assegnazione **esistente** si modifica o si cancella è
|
||||
il CRUD del Registro (`Livewire/Registro`: `openCreate`/`openEdit`/`save`/
|
||||
`askDelete`/`deleteConfirmed`), blindato su `settings.manage` a due livelli —
|
||||
`abort_if(..., 403)` nel componente e `@can('settings.manage')` in
|
||||
`registro.blade.php`, che nasconde pulsanti e modali. `settings.manage` ce l'ha
|
||||
solo l'`amministratore`.
|
||||
|
||||
Di conseguenza gli altri ruoli **non** possono alterare le assegnazioni: la
|
||||
pagina Assegna solo ne crea (`territori.assign`), la pagina Rientra solo le
|
||||
chiude (`territori.return`). Nello specifico l'`assistente` (`registro.view` +
|
||||
`territori.assign` + `territori.return`) vede il Registro, crea nuove
|
||||
assegnazioni e le fa rientrare, ma non le modifica né le elimina.
|
||||
|
||||
Poiché il seeder concede in modo additivo e non revoca mai (vedi la nota su
|
||||
`syncPermissions()` più sotto), un permesso `settings.manage` concesso a mano
|
||||
all'`assistente` in passato sopravviverebbe a ogni re-seed. La migration
|
||||
`2026_07_25_000001_revoke_settings_manage_from_assistente_role` chiude questo
|
||||
drift per i database esistenti: revoca `settings.manage` dal ruolo `assistente`
|
||||
se presente. È idempotente (no-op sul baseline) e volutamente irreversibile —
|
||||
il `down()` non ripristina il permesso.
|
||||
|
||||
### Export/Import XML: backup completo e round-trip
|
||||
|
||||
`Livewire/Settings/XmlExchange` esporta e reimporta **tutti** i dati gestionali,
|
||||
non più un sottoinsieme. L'export (`currentDataset` + `datasetToXml`) include
|
||||
ora: utenti (con hash password, ruoli, `email_verified_at`, `remember_token`),
|
||||
i campi settings prima omessi (`public_base_url`, `logo_path`,
|
||||
`pdf_viewer_show_download`, `setup_completed`), lo stato soft-delete
|
||||
(`deleted_at`) di proclamatori/territori, i path file dei territori
|
||||
(`pdf_path`, `thumbnail_path`), le FK di audit delle assegnazioni
|
||||
(`created_by`/`returned_by`, esportate come `legacy_*_id`), `link_sent`,
|
||||
`pdf_access_code`, e i timestamp `created_at`/`updated_at` di ogni entità.
|
||||
L'import (`importXmlFromContent`) legge simmetricamente tutti questi campi, così
|
||||
un export si reimporta senza perdite.
|
||||
|
||||
Tre scelte non ovvie:
|
||||
|
||||
- **`legacy_id` derivato dall'`id` reale.** Il rimappaggio delle FK in import si
|
||||
basa sull'attributo `legacy_id`. Prima veniva scritto solo dalla conversione
|
||||
SQL legacy: l'export dell'app corrente ne era privo, quindi un suo re-import
|
||||
**perdeva territori e assegnazioni** (riferimenti non risolti). Ora
|
||||
`addLegacyIdAttribute()` usa `legacy_id` se presente, altrimenti l'`id` reale.
|
||||
|
||||
- **Gli utenti sono UPSERT per email, non azzerati.** Tutte le altre entità
|
||||
vengono cancellate e ricreate; gli utenti no. Azzerare la tabella `users`
|
||||
durante un restore rischierebbe di **estromettere l'amministratore** che sta
|
||||
importando (backup che precede il suo account, o file parziale). Gli account
|
||||
esistenti vengono aggiornati, quelli nel file (ri)creati; nessuno viene
|
||||
eliminato. Conseguenza: non è un mirror perfetto (utenti extra restano), ma è
|
||||
senza perdite e non blocca fuori nessuno.
|
||||
|
||||
- **Hash password e timestamp scritti col query builder.** Dopo il `save()` del
|
||||
modello, password/`remember_token`/`email_verified_at`/`created_at`/
|
||||
`updated_at` vengono scritti con `DB::table()->update()`: preserva l'hash
|
||||
esatto (il cast `hashed` rivaliderebbe il costo bcrypt e potrebbe rifiutare un
|
||||
hash generato con `BCRYPT_ROUNDS` diverso) ed evita che Eloquent sovrascriva i
|
||||
timestamp importati. Stessa tecnica in `applyRowMeta()` per le altre entità.
|
||||
|
||||
### Un solo container esegue le migrazioni
|
||||
|
||||
Solo `app` migra. `queue-worker` e `scheduler` usano `queue-entrypoint.sh`, che
|
||||
@@ -120,6 +180,7 @@ Contesto utile se ti imbatti in comportamenti che sembrano regressioni.
|
||||
| `pdftoppm` senza timeout | Un PDF malformato bloccava il worker **per sempre** | `TerritorioThumbnailService` |
|
||||
| Collisione nomi file PDF | `1 A` e `1-A` scrivevano sullo stesso file → dati sovrascritti | `Jobs/ImportTerritoryPdfFolder` |
|
||||
| Zip senza limiti | Zip bomb → esaurimento memoria | `TerritorioPdfImportDispatcher` |
|
||||
| Export XML senza `legacy_id` | Il re-import dell'export dell'app corrente perdeva territori e assegnazioni (FK non risolte) | `Livewire/Settings/XmlExchange` (`addLegacyIdAttribute`) |
|
||||
| Migrazioni thumbnail duplicate | `migrate:rollback` falliva a metà | rimosse 2 migrazioni ridondanti |
|
||||
|
||||
---
|
||||
@@ -191,6 +252,12 @@ miniature e la chiave di fallback. Non va trattato come rigenerabile.
|
||||
**Modifiche a dipendenze o asset richiedono un rebuild.** Non è più sufficiente
|
||||
riavviare il container: `vendor/` e `public/build` vivono dentro l'immagine.
|
||||
|
||||
**L'export XML contiene dati sensibili.** Il file di export include gli hash
|
||||
delle password di tutti gli utenti e i nomi/cognomi dei proclamatori in chiaro.
|
||||
Va trattato come un segreto: chi lo ottiene può tentare il crack offline degli
|
||||
hash. La pagina di export lo segnala, ma il controllo resta umano — la
|
||||
funzione è già riservata all'amministratore (`settings.manage`).
|
||||
|
||||
---
|
||||
|
||||
## Come verificare che tutto funzioni
|
||||
|
||||
Reference in New Issue
Block a user