Files
Focolari-Voting-System/backend-mock/API_SPECIFICATION.md
2026-01-24 16:35:30 +01:00

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: `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`