feat: aggiornamenti alla documentazione e miglioramenti UI/UX
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# 📄 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.
|
||||
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
|
||||
@@ -10,12 +11,15 @@ Questo documento descrive le specifiche che il backend reale deve implementare p
|
||||
## 🌐 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.
|
||||
|
||||
@@ -24,15 +28,18 @@ 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
|
||||
|
||||
@@ -45,6 +52,7 @@ Questo garantisce che:
|
||||
Restituisce informazioni sulla sala/meeting corrente e lo stato del server.
|
||||
|
||||
#### Response `200 OK`
|
||||
|
||||
```json
|
||||
{
|
||||
"room_name": "Sala Assemblea",
|
||||
@@ -53,19 +61,21 @@ Restituisce informazioni sulla sala/meeting corrente e lo stato del server.
|
||||
}
|
||||
```
|
||||
|
||||
| Campo | Tipo | Descrizione |
|
||||
|-------|------|-------------|
|
||||
| `room_name` | string | Nome della sala visualizzato nell'header |
|
||||
| `meeting_id` | string | Identificativo del meeting/votazione |
|
||||
| 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.
|
||||
Verifica le credenziali del validatore. **IMPORTANTE:** Qualsiasi badge può diventare un badge validatore se la password
|
||||
è corretta.
|
||||
|
||||
#### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"badge": "0007399575",
|
||||
@@ -73,12 +83,20 @@ Verifica le credenziali del validatore. **IMPORTANTE:** Qualsiasi badge può div
|
||||
}
|
||||
```
|
||||
|
||||
| Campo | Tipo | Descrizione |
|
||||
|-------|------|-------------|
|
||||
| `badge` | string | Codice badge scansionato (numerico, senza sentinel) |
|
||||
| `password` | string | Password inserita dall'utente |
|
||||
| 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,
|
||||
@@ -87,14 +105,26 @@ Verifica le credenziali del validatore. **IMPORTANTE:** Qualsiasi badge può div
|
||||
}
|
||||
```
|
||||
|
||||
#### Response `401 Unauthorized` (Errore)
|
||||
#### 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`.
|
||||
**Note:**
|
||||
|
||||
- La password è l'unico fattore di autenticazione
|
||||
- Il badge viene memorizzato dal frontend come "badge validatore" per conferme successive
|
||||
- Il token è opzionale (per future implementazioni di autenticazione JWT)
|
||||
@@ -106,13 +136,23 @@ Verifica le credenziali del validatore. **IMPORTANTE:** Qualsiasi badge può div
|
||||
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",
|
||||
@@ -120,11 +160,18 @@ Gli zeri iniziali sono significativi: `"0008988288"` e `"8988288"` sono badge **
|
||||
"cognome": "Bianchi",
|
||||
"url_foto": "https://example.com/foto.jpg",
|
||||
"ruolo": "Votante",
|
||||
"ammesso": true # <-- utente ammesso all'ingresso
|
||||
"ammesso": true
|
||||
#
|
||||
<--
|
||||
utente
|
||||
ammesso
|
||||
all
|
||||
'ingresso
|
||||
}
|
||||
```
|
||||
|
||||
#### Response `200 OK` (Utente trovato ma NON ammesso)
|
||||
|
||||
```json
|
||||
{
|
||||
"badge_code": "0000514162",
|
||||
@@ -132,27 +179,35 @@ Gli zeri iniziali sono significativi: `"0008988288"` e `"8988288"` sono badge **
|
||||
"cognome": "Verdi",
|
||||
"url_foto": "https://example.com/foto.jpg",
|
||||
"ruolo": "Tecnico",
|
||||
"ammesso": false, #<-- utente NON ammesso all'ingresso
|
||||
"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 (es. "Votante", "Tecnico", "Ospite") |
|
||||
| `ammesso` | boolean | `true` se autorizzato all'ingresso |
|
||||
| `warning` | string? | Opzionale, presente se `ammesso: false` |
|
||||
| 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 (es. "Votante", "Tecnico", "Ospite") |
|
||||
| `ammesso` | boolean | `true` se autorizzato all'ingresso |
|
||||
| `warning` | string? | Opzionale, presente se `ammesso: false` |
|
||||
|
||||
---
|
||||
|
||||
@@ -161,6 +216,7 @@ Gli zeri iniziali sono significativi: `"0008988288"` e `"8988288"` sono badge **
|
||||
Registra l'ingresso di un utente. Richiede conferma del validatore.
|
||||
|
||||
#### Request Body
|
||||
|
||||
```json
|
||||
{
|
||||
"user_badge": "0008988288",
|
||||
@@ -168,12 +224,13 @@ Registra l'ingresso di un utente. Richiede conferma del validatore.
|
||||
}
|
||||
```
|
||||
|
||||
| Campo | Tipo | Descrizione |
|
||||
|-------|------|-------------|
|
||||
| `user_badge` | string | Badge dell'utente che sta entrando |
|
||||
| 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,
|
||||
@@ -182,6 +239,7 @@ Registra l'ingresso di un utente. Richiede conferma del validatore.
|
||||
```
|
||||
|
||||
#### Response `401 Unauthorized` (Password errata)
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Password validatore non valida"
|
||||
@@ -189,6 +247,7 @@ Registra l'ingresso di un utente. Richiede conferma del validatore.
|
||||
```
|
||||
|
||||
#### Response `403 Forbidden` (Utente non ammesso)
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Utente non autorizzato all'ingresso"
|
||||
@@ -196,6 +255,7 @@ Registra l'ingresso di un utente. Richiede conferma del validatore.
|
||||
```
|
||||
|
||||
#### Response `404 Not Found` (Badge non trovato)
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Badge utente non trovato"
|
||||
@@ -203,7 +263,9 @@ Registra l'ingresso di un utente. Richiede conferma del validatore.
|
||||
```
|
||||
|
||||
**IMPORTANTE - Sicurezza:**
|
||||
Anche se il frontend non dovrebbe permettere di inviare entry-request per utenti non ammessi, il backend **DEVE** sempre verificare:
|
||||
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`)
|
||||
@@ -215,23 +277,28 @@ 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
|
||||
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
|
||||
|
||||
@@ -240,32 +307,36 @@ Implementare rate limiting su:
|
||||
## 📊 Struttura Dati Suggerita
|
||||
|
||||
### Database Utenti
|
||||
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
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,
|
||||
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);
|
||||
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,
|
||||
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)
|
||||
room_id VARCHAR(50),
|
||||
action VARCHAR(20) NOT NULL, -- 'entry', 'denied', 'not_found'
|
||||
timestamp TIMESTAMP DEFAULT NOW(),
|
||||
ip_address VARCHAR(45)
|
||||
);
|
||||
```
|
||||
|
||||
@@ -286,9 +357,10 @@ CREATE TABLE access_log (
|
||||
9. **Entry badge inesistente** → 404 Not Found
|
||||
|
||||
### Badge di Test (Mock)
|
||||
|
||||
```
|
||||
0008988288 - Marco Bianchi (Votante, ammesso)
|
||||
0007399575 - Laura Rossi (Votante, ammessa)
|
||||
0008988288 - Marco Bianchi (Convocato, ammesso)
|
||||
0007399575 - Laura Rossi (Invitato, ammessa)
|
||||
0000514162 - Giuseppe Verdi (Tecnico, NON ammesso)
|
||||
0006478281 - NON nel database (per test 404)
|
||||
```
|
||||
@@ -298,9 +370,11 @@ CREATE TABLE access_log (
|
||||
## 📝 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)
|
||||
@@ -308,7 +382,9 @@ Tutte le richieste e risposte usano `application/json`.
|
||||
- `500`: Errore interno server
|
||||
|
||||
### Formato Errori
|
||||
|
||||
Tutti gli errori devono restituire un JSON con campo `detail`:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Messaggio di errore descrittivo"
|
||||
@@ -320,6 +396,7 @@ Tutti gli errori devono restituire un JSON con campo `detail`:
|
||||
## 🔄 Changelog
|
||||
|
||||
### v1.0 (Gennaio 2026)
|
||||
|
||||
- Specifica iniziale
|
||||
- Endpoint: `/info-room`, `/login-validate`, `/anagrafica/{badge}`, `/entry-request`
|
||||
- Meccanismo invalidazione sessioni con `server_start_time`
|
||||
|
||||
@@ -5,12 +5,20 @@
|
||||
"meeting_id": "VOT-2024"
|
||||
},
|
||||
"users": [
|
||||
{
|
||||
"badge_code": "0008988288",
|
||||
"nome": "Marco",
|
||||
"cognome": "Bianchi",
|
||||
"url_foto": "https://randomuser.me/api/portraits/men/1.jpg",
|
||||
"ruolo": "Convocato",
|
||||
"ammesso": true
|
||||
},
|
||||
{
|
||||
"badge_code": "0007399575",
|
||||
"nome": "Laura",
|
||||
"cognome": "Rossi",
|
||||
"url_foto": "https://randomuser.me/api/portraits/women/2.jpg",
|
||||
"ruolo": "Votante",
|
||||
"ruolo": "Invitato",
|
||||
"ammesso": true
|
||||
},
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"nome": "Test",
|
||||
"cognome": "Ammesso",
|
||||
"url_foto": "https://randomuser.me/api/portraits/lego/1.jpg",
|
||||
"ruolo": "Votante",
|
||||
"ruolo": "Convocato",
|
||||
"ammesso": true
|
||||
},
|
||||
{
|
||||
@@ -18,7 +18,7 @@
|
||||
"nome": "Test",
|
||||
"cognome": "NonAmmesso",
|
||||
"url_foto": "https://randomuser.me/api/portraits/lego/2.jpg",
|
||||
"ruolo": "Ospite",
|
||||
"ruolo": "Invitato",
|
||||
"ammesso": false
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user