60 lines
3.2 KiB
Markdown
60 lines
3.2 KiB
Markdown
# DD-024 — Le route che avvisano la squadra chiedono le credenziali
|
|||
|
|
|
||
|
|
**Data:** 6 settembre 2026
|
||
|
|
**Stato:** Accettata
|
||
|
|
|
||
|
|
**Contesto**
|
||
|
|
Le route in `src/routes/api/public/` girano con la service role e saltano la RLS: DD-023 non
|
||
|
|
le tocca. Nessuna di loro faceva un controllo di accesso — cercando `authorization` in quella
|
||
|
|
cartella l'unico header era lo User-Agent con cui `csi.ts` chiama il portale CSI. Chiunque
|
||
|
|
conoscesse l'URL poteva quindi far suonare i telefoni di tutta la squadra:
|
||
|
|
`promemoria-palloni` accetta perfino una POST con il corpo vuoto.
|
||
|
|
|
||
|
|
La difesa apparente delle altre due — «serve un id evento valido» — non è una difesa: l'id è
|
||
|
|
`e` + il timestamp in base 36 (`nuovoIdEvento()`), compare negli URL che la squadra si
|
||
|
|
scambia, ed è elencabile da qualsiasi utente loggato.
|
||
|
|
|
||
|
|
Il danno non è furto di dati: i testi delle notifiche li costruisce il server. È molestia e
|
||
|
|
consumo della quota push. Non è però una ragione per lasciare la porta aperta.
|
||
|
|
|
||
|
|
**Decisione**
|
||
|
|
Le tre route che inviano notifiche chiedono le credenziali, con due controlli diversi perché
|
||
|
|
i chiamanti sono diversi (`src/lib/auth-route.server.ts`):
|
||
|
|
|
||
|
|
- `apri-sondaggio` e `sollecita-presenze` → `richiediAdmin`: token della sessione Supabase
|
||
|
|
verificato con `auth.getUser`, poi ruolo `admin` letto da `user_roles`, la stessa fonte di
|
||
|
|
`src/lib/ruoli.ts` (DD-011). `401` senza token valido, `403` con token ma senza ruolo. Il
|
||
|
|
controllo viene **prima** della validazione dell'input, così la risposta non rivela
|
||
|
|
nemmeno se un evento esiste.
|
||
|
|
- `promemoria-palloni` → all'inizio `richiediSegreto`, con un segreto da cron; sostituito
|
||
|
|
subito dopo da `richiediAdmin` quando la route è diventata manuale (DD-025).
|
||
|
|
|
||
|
|
`csi`, `push-config` e `push-subscribe` restano aperte: le chiama il browser prima del login,
|
||
|
|
dove qualsiasi segreto sarebbe pubblico. (`push-messaggio` esisteva per lo stesso motivo ed è
|
||
|
|
sparita con DD-026.)
|
||
|
|
|
||
|
|
**Alternative scartate**
|
||
|
|
|
||
|
|
- Un segreto condiviso anche per le due route dell'app → finirebbe nel bundle JavaScript,
|
||
|
|
cioè pubblico.
|
||
|
|
- Il middleware `requireSupabaseAuth` già presente nel repository → è
|
||
|
|
`createMiddleware({ type: "function" })`, protegge le server function di TanStack Start. In
|
||
|
|
CrAPP `createServerFn` non compare da nessuna parte: quel file non è mai stato eseguito,
|
||
|
|
e non si applica comunque alle route in `src/routes/api/`.
|
||
|
|
- Fidarsi dell'id evento come credenziale → è un timestamp in un URL condiviso.
|
||
|
|
|
||
|
|
**Conseguenze**
|
||
|
|
|
||
|
|
- Nessuna variabile d'ambiente da configurare: dopo DD-025 tutte e tre le route usano lo
|
||
|
|
stesso controllo sul ruolo.
|
||
|
|
- I due pulsanti dell'app mandano ora il token con `intestazioniAutenticate()`
|
||
|
|
(`src/lib/auth.ts`), letto al momento della chiamata e non da uno stato React, così non si
|
||
|
|
spedisce un token scaduto.
|
||
|
|
- `api.test.ts` non può più verificare la validazione dell'input di `sollecita-presenze`,
|
||
|
|
che ora sta dietro all'accesso: quel pezzo si è spostato in
|
||
|
|
`test/integration/permessi-route.test.ts`, che gira sullo stack locale perché ha bisogno
|
||
|
|
di utenti veri.
|
||
|
|
|
||
|
|
**Riesame**
|
||
|
|
Se un giorno l'app userà `createServerFn`, il middleware già presente diventa la strada
|
||
|
|
naturale e questi controlli vanno riletti alla sua luce.
|