# ๐Ÿ“„ Specifiche API Backend - Focolari Voting System Questo documento descrive le specifiche che il backend reale deve implementare per funzionare correttamente con il frontend del sistema di controllo accessi. **Versione:** 1.0 **Data:** Gennaio 2026 --- ## ๐ŸŒ Configurazione Server ### CORS Il server deve abilitare CORS con le seguenti impostazioni: - **Origins:** `*` (o lista specifica di domini autorizzati) - **Methods:** `GET, POST, OPTIONS` - **Headers:** `Content-Type, Authorization` ### Serving Frontend Il backend dovrebbe servire il frontend buildato (file statici) dalla root `/`. Questo permette di avere un unico endpoint per frontend e API. --- ## ๐Ÿ”‘ Meccanismo di Invalidazione Sessioni ### Server Start Time All'avvio, il server deve generare un **timestamp univoco** (es. Unix timestamp in secondi). Questo valore viene restituito nell'endpoint `/info-room` e serve al frontend per invalidare sessioni obsolete. **Comportamento:** 1. Il frontend salva `server_start_time` nella sessione locale 2. Al caricamento successivo, confronta il valore salvato con quello attuale 3. Se differiscono, la sessione viene invalidata (il server รจ stato riavviato) Questo garantisce che: - Un riavvio del server forza il re-login di tutti i validatori - Sessioni zombie non rimangono attive dopo manutenzione --- ## ๐Ÿ“ก Endpoint API ### 1. `GET /info-room` Restituisce informazioni sulla sala/meeting corrente e lo stato del server. #### Response `200 OK` ```json { "room_name": "Sala Assemblea", "meeting_id": "VOT-2024", "server_start_time": 1737100800 } ``` | Campo | Tipo | Descrizione | |---------------------|---------|---------------------------------------------------------------| | `room_name` | string | Nome della sala visualizzato nell'header | | `meeting_id` | string | Identificativo del meeting/votazione | | `server_start_time` | integer | Unix timestamp dell'avvio server (per invalidazione sessioni) | --- ### 2. `POST /login-validate` Verifica le credenziali del validatore. **IMPORTANTE:** Qualsiasi badge puรฒ diventare un badge validatore se la password รจ corretta. #### Request Body ```json { "badge": "0007399575", "password": "focolari" } ``` | Campo | Tipo | Descrizione | |------------|--------|-------------------------------------------------------------------------------------| | `badge` | string | Codice badge scansionato (numerico, senza sentinel) - **opzionale per validazione** | | `password` | string | Password inserita dall'utente | **Logica di Validazione:** - Il backend **DEVE** verificare la password - Il backend **PUร’** opzionalmente verificare anche il badge (lista whitelist di badge abilitati come validatori) - Se verifica solo password: qualsiasi badge puรฒ diventare validatore con la password corretta - Se verifica anche badge: solo badge nella whitelist possono diventare validatori #### Response `200 OK` (Successo) ```json { "success": true, "message": "Login effettuato con successo" } ``` #### Response `401 Unauthorized` (Password errata) ```json { "detail": "Password non valida" } ``` #### Response `403 Forbidden` (Badge non autorizzato - se implementato controllo badge) ```json { "detail": "Badge non autorizzato come validatore" } ``` **Note:** Il frontend gestisce entrambi i codici di errore (401 e 403) mostrando il messaggio ricevuto nel campo `detail`. - La password รจ l'unico fattore di autenticazione - Il badge viene memorizzato dal frontend come "badge validatore" per conferme successive --- ### 3. `GET /anagrafica/{badge_code}` Recupera i dati anagrafici di un utente dato il suo codice badge. #### Path Parameters - `badge_code`: Codice badge (stringa, es. "0008988288") **IMPORTANTE - Confronto Badge:** Il badge รจ una **stringa**, non un numero. Va confrontato **esattamente** carattere per carattere. Gli zeri iniziali sono significativi: `"0008988288"` e `"8988288"` sono badge **diversi**. **Ruoli Ammessi:** | Ruolo | Descrizione | |-------------|-------------------------| | `Convocato` | Con diritto di voto | | `Invitato` | Senza diritto di voto | | `Tecnico` | Staff tecnico | | `Staff` | Personale organizzativo | #### Response `200 OK` (Utente trovato e ammesso) ```json { "badge_code": "0008988288", "nome": "Marco", "cognome": "Bianchi", "url_foto": "https://example.com/foto.jpg", "ruolo": "Votante", "ammesso": true # <-- utente ammesso all 'ingresso } ``` #### Response `200 OK` (Utente trovato ma NON ammesso) ```json { "badge_code": "0000514162", "nome": "Giuseppe", "cognome": "Verdi", "url_foto": "https://example.com/foto.jpg", "ruolo": "Tecnico", "ammesso": false, # <-- utente NON ammesso all 'ingresso "warning": "Utente non ammesso all'ingresso" } ``` #### Response `404 Not Found` (Utente non trovato) ```json { "detail": "Badge non trovato nel sistema" } ``` | Campo Response | Tipo | Descrizione | |----------------|---------|----------------------------------------------------------------| | `badge_code` | string | Codice badge | | `nome` | string | Nome dell'utente | | `cognome` | string | Cognome dell'utente | | `url_foto` | string | URL immagine profilo (puรฒ essere placeholder) | | `ruolo` | string | Ruolo dell'utente: `Convocato`, `Invitato`, `Tecnico`, `Staff` | | `ammesso` | boolean | `true` se autorizzato all'ingresso | | `warning` | string? | Opzionale, presente se `ammesso: false` | --- ### 4. `POST /entry-request` Registra l'ingresso di un utente. Richiede conferma del validatore. #### Request Body ```json { "user_badge": "0008988288", "validator_password": "focolari" } ``` | Campo | Tipo | Descrizione | |----------------------|--------|---------------------------------------| | `user_badge` | string | Badge dell'utente che sta entrando | | `validator_password` | string | Password del validatore (ri-verifica) | #### Response `200 OK` (Successo) ```json { "success": true, "message": "Ingresso registrato con successo" } ``` #### Response `401 Unauthorized` (Password errata) ```json { "detail": "Password validatore non valida" } ``` #### Response `403 Forbidden` (Utente non ammesso) ```json { "detail": "Utente non autorizzato all'ingresso" } ``` #### Response `404 Not Found` (Badge non trovato) ```json { "detail": "Badge utente non trovato" } ``` **IMPORTANTE - Sicurezza:** Anche se il frontend non dovrebbe permettere di inviare entry-request per utenti non ammessi, il backend **DEVE** sempre verificare: 1. Che la password validatore sia corretta 2. Che l'utente esista 3. Che l'utente sia ammesso (`ammesso: true`) Non fidarsi mai del frontend per la validazione! --- ## ๐Ÿ”’ Considerazioni di Sicurezza ### Validazione Lato Backend Il backend deve **sempre** eseguire tutte le validazioni, indipendentemente da cosa fa il frontend: 1. **Login:** Verificare che la password sia corretta 2. **Entry:** - Verificare password validatore - Verificare che utente esista - Verificare che utente sia ammesso - Loggare l'operazione per audit ### Logging e Audit Si raccomanda di loggare: - Ogni tentativo di login (successo/fallimento) - Ogni registrazione di ingresso - Badge non trovati (potenziale tentativo di accesso non autorizzato) ### Protezione Rate Limiting Implementare rate limiting su: - `/login-validate`: max 5 tentativi/minuto per IP - `/entry-request`: max 30 richieste/minuto per IP --- ## ๐Ÿ“Š Struttura Dati Suggerita ### Database Utenti ```sql CREATE TABLE users ( id SERIAL PRIMARY KEY, badge_code VARCHAR(20) UNIQUE NOT NULL, nome VARCHAR(100) NOT NULL, cognome VARCHAR(100) NOT NULL, url_foto VARCHAR(500), ruolo VARCHAR(50) NOT NULL, ammesso BOOLEAN DEFAULT true, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX idx_badge_code ON users (badge_code); ``` ### Tabella Accessi (Audit Log) ```sql CREATE TABLE access_log ( id SERIAL PRIMARY KEY, user_badge VARCHAR(20) NOT NULL, validator_badge VARCHAR(20) NOT NULL, room_id VARCHAR(50), action VARCHAR(20) NOT NULL, -- 'entry', 'denied', 'not_found' timestamp TIMESTAMP DEFAULT NOW(), ip_address VARCHAR(45) ); ``` --- ## ๐Ÿงช Test e Validazione ### Casi di Test Minimi 1. **Login con password corretta** โ†’ 200 OK 2. **Login con password errata** โ†’ 401 Unauthorized 3. **Anagrafica badge esistente ammesso** โ†’ 200 OK con `ammesso: true` 4. **Anagrafica badge esistente non ammesso** โ†’ 200 OK con `ammesso: false` + warning 5. **Anagrafica badge inesistente** โ†’ 404 Not Found 6. **Entry utente ammesso con password corretta** โ†’ 200 OK 7. **Entry utente ammesso con password errata** โ†’ 401 Unauthorized 8. **Entry utente NON ammesso** โ†’ 403 Forbidden (anche se password corretta!) 9. **Entry badge inesistente** โ†’ 404 Not Found ### Badge di Test (Mock) ``` 0008988288 - Marco Bianchi (Convocato, ammesso) 0007399575 - Laura Rossi (Invitato, ammessa) 0000514162 - Giuseppe Verdi (Tecnico, NON ammesso) 0006478281 - NON nel database (per test 404) ``` --- ## ๐Ÿ“ Note Implementative ### Content-Type Tutte le richieste e risposte usano `application/json`. ### Codici Errore HTTP - `200`: Successo - `401`: Non autorizzato (password errata) - `403`: Vietato (utente non ammesso) - `404`: Risorsa non trovata - `500`: Errore interno server ### Formato Errori Tutti gli errori devono restituire un JSON con campo `detail`: ```json { "detail": "Messaggio di errore descrittivo" } ``` --- ## ๐Ÿ”„ Changelog ### v1.0 (Gennaio 2026) - Specifica iniziale - Endpoint: `/info-room`, `/login-validate`, `/anagrafica/{badge}`, `/entry-request` - Meccanismo invalidazione sessioni con `server_start_time`