388 lines
10 KiB
Markdown
388 lines
10 KiB
Markdown
# 📄 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 (es. "Votante", "Tecnico", "Ospite") |
|
|
| `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`
|