Chiede le credenziali alle route che avvisano tutta la squadra (DD-024).
Le route in src/routes/api/public/ girano con la service role e saltano la RLS, quindi DD-023 non le copre. Nessuna 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 far suonare i telefoni della squadra, e 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" più il timestamp in base 36, compare negli URL che la squadra si scambia ed è elencabile da qualsiasi utente loggato. auth-route.server.ts porta i due controlli, diversi perché i chiamanti sono diversi. apri-sondaggio e sollecita-presenze usano richiediAdmin: token della sessione verificato con auth.getUser, poi ruolo admin da user_roles, la stessa fonte di ruoli.ts. Il controllo precede la validazione dell'input, così la risposta non rivela nemmeno se un evento esiste. promemoria-palloni usa richiediSegreto, perché la chiama un cron che una sessione non ce l'ha: se CRON_SEGRETO non è configurata la route resta chiusa con 503, perché una porta che si riapre da sola quando manca una variabile non se ne accorge nessuno. csi, push-config, push-subscribe e push-messaggio restano aperte: le chiamano il browser prima del login e il service worker, dove qualsiasi segreto finirebbe nel bundle. Lato client i due pulsanti admin mandano il token con intestazioniAutenticate(), letto al momento della chiamata e non da uno stato React. permessi-route.test.ts copre il giro intero — nessun token, giocatore, admin — avviando il server di sviluppo puntato al database locale, perché servono utenti veri. Il controllo positivo è il 404: l'admin supera l'accesso e arriva alla validazione. In api.test.ts restano i rifiuti che non richiedono un utente e sparisce la verifica della validazione di sollecita-presenze, che ora sta dietro all'accesso. I limiti noti di palloni.md sono aggiornati: il secret che il piano originale prevedeva ora c'è. Resta vero che nessun cron chiama la route, quindi il promemoria quotidiano non parte da solo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,21 @@ L'invio effettivo (`src/lib/webpush.server.ts`, funzione `inviaPush`) firma un J
|
||||
(ECDSA P-256) e fa una POST senza corpo all'endpoint push del browser; è riusato identico da
|
||||
`sollecita-presenze.ts` e `promemoria-palloni.ts`.
|
||||
|
||||
### Chi può farle partire (DD-024)
|
||||
|
||||
Queste route usano la service role e saltano la RLS, quindi il permesso deve stare nella
|
||||
route. `src/lib/auth-route.server.ts` fornisce i due controlli:
|
||||
|
||||
| Route | Controllo | Chi la chiama |
|
||||
| -------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| `apri-sondaggio`, `sollecita-presenze` | `richiediAdmin` — token della sessione Supabase, poi ruolo `admin` in `user_roles` | l'app, dal pulsante riservato agli admin |
|
||||
| `promemoria-palloni` | `richiediSegreto` — intestazione `x-cron-segreto` uguale alla variabile `CRON_SEGRETO` | un cron, senza sessione |
|
||||
| `csi`, `push-config`, `push-subscribe`, `push-messaggio` | nessuno | il browser prima del login e il service worker, che una sessione non ce l'hanno |
|
||||
|
||||
**`CRON_SEGRETO` va configurata negli ambienti**: se manca, `promemoria-palloni` risponde
|
||||
503 e il promemoria non parte. È voluto — una porta che si riapre da sola quando manca una
|
||||
configurazione non se ne accorge nessuno.
|
||||
|
||||
---
|
||||
|
||||
## Notifiche smart
|
||||
@@ -77,8 +92,11 @@ ripetersi — deduplica puramente locale al dispositivo, non sincronizzata.
|
||||
è iscritto o no. Separare i canali richiederebbe schema e UI dedicati.
|
||||
- `promemoria_push` è descritta altrove come "storico" ma nel codice è una coda che si
|
||||
autocancella alla lettura: non conserva nulla.
|
||||
- Nessuna verifica di autenticazione su `push-messaggio` (chiunque conosca un endpoint push
|
||||
valido può leggerne il messaggio) né su `promemoria-palloni`.
|
||||
- Nessuna verifica di autenticazione su `push-messaggio`: chiunque conosca un endpoint push
|
||||
valido può leggerne il messaggio. Non è chiudibile con un segreto, perché a chiamarla è il
|
||||
service worker, dove qualsiasi segreto sarebbe pubblico; di fatto la protegge il dover
|
||||
conoscere l'endpoint, che è un URL segreto per dispositivo. `promemoria-palloni` invece è
|
||||
chiusa da DD-024.
|
||||
- Compatibilità iOS/Safari non gestita esplicitamente nel codice (nessun branch dedicato):
|
||||
serve l'installazione da schermata Home per funzionare, ma l'app non lo segnala
|
||||
esplicitamente.
|
||||
|
||||
@@ -53,11 +53,12 @@ testo effettivo viene calcolato al volo dal service worker interrogando
|
||||
|
||||
## Limiti noti
|
||||
|
||||
- **Nessuna verifica di autenticazione/secret** sulla route `promemoria-palloni`: chiunque
|
||||
può invocarla via POST diretto, nonostante il piano originale prevedesse una protezione
|
||||
con secret.
|
||||
- **Nessun cron nel repository**: lo scheduling effettivo (se esiste) è configurato fuori dal
|
||||
codice versionato — da verificare lato Supabase/hosting.
|
||||
codice versionato — da verificare lato Supabase/hosting. Finché non esiste, il promemoria
|
||||
quotidiano non parte da solo.
|
||||
- La route è protetta dal segreto previsto dal piano originale (DD-024): chi la chiama deve
|
||||
mandare `x-cron-segreto` uguale alla variabile `CRON_SEGRETO`. Se la variabile non è
|
||||
configurata nell'ambiente la route risponde `503`.
|
||||
- Il conteggio dei turni include anche le proposte non confermate: badge e statistiche
|
||||
possono contare turni mai effettivamente convalidati da nessuno.
|
||||
- La rotazione non considera le assenze dichiarate: può proporre il turno a chi ha risposto
|
||||
@@ -67,7 +68,6 @@ testo effettivo viene calcolato al volo dal service worker interrogando
|
||||
|
||||
## Evoluzioni possibili
|
||||
|
||||
- Aggiungere un secret/header di autorizzazione alla route pubblica.
|
||||
- Versionare il cron (es. una migration con `cron.schedule`) invece di configurarlo solo
|
||||
lato dashboard.
|
||||
- Escludere dalla rotazione chi ha già dichiarato assenza per l'evento.
|
||||
|
||||
Reference in New Issue
Block a user