Sistema de Gestio de Consentiment
Documentacio tecnica del sistema de consentiment legal de LDP.
Arquitectura
Taules
legal_documents: Versions dels documents legals (ToS, Privacy Policy, Cookie Policy)user_consents: Registre de cada acceptacio/retirada amb audit trail complet
Flux de registre
- Frontend envia
tosAccepted=true,tosVersion,privacyAccepted=true,privacyVersion - Backend valida edat >= 14 (LOPDGDD Art. 7)
- Backend valida que els documents existeixen i estan actius
- Es crea
UserConsentamb timestamp, IP, User-Agent per cada document - Es registra
ageVerifiedAt+ageVerificationMethod=SELF_DECLARED
Flux de re-consentiment (login)
ConsentService.hasRequiredConsents(userId)verifica si l'usuari te els consentiments vigents- Si un document ha canviat de versio des de l'ultima acceptacio, retorna
false - Frontend mostra
ConsentRenewalModalamb els documents pendents - L'usuari ha d'acceptar abans de poder continuar
Flux de retirada (GDPR Art. 7.3)
DELETE /api/users/me/consents/{documentType}- Es crea un registre
UserConsentambconsentType=WITHDRAWAL - La retirada es tan facil com l'acceptacio (requisit legal)
Flux d'esborrat (GDPR Art. 17)
POST /api/users/me/data-erasureDataErasureServiceanonimitza l'usuari (veure ADR-004 per detalls de cascada)UserConsentes manté com a prova legal
Endpoints
| Endpoint | Metode | Acces | Proposit |
|---|---|---|---|
/api/legal/documents/current | GET | Public | Documents vigents |
/api/legal/platform-info | GET | Public | Info LSSI-CE Art. 10 |
/api/users/me/consents | GET | Autenticat | Estat consentiments |
/api/users/me/consents | POST | Autenticat | Acceptar el CONJUNT de documents (T-515) |
/api/users/me/consents/{type} | DELETE | Autenticat | Retirar consentiment |
/api/users/me/data-erasure | POST | Autenticat | Sol·licitud esborrat |
Audit Trail
Cada UserConsent registra:
user_id: Usuarilegal_document_id: Document i versio acceptatsconsent_type: ACCEPTANCE o WITHDRAWALgiven_at: Timestamp exacteip_address: IP del client (X-Forwarded-For si proxy)user_agent: Navegador/clientsuperseded_at: Quan aquesta declaracio va quedar sense efecte per una de signe contrari POSTERIOR sobre el MATEIX document.NULL= declaracio vigent (T-515)metadata: JSONB per informacio addicional futura
Idempotencia i integritat del registre (T-515)
El registre de consentiments es l'evidencia que exigeix l'art. 7.1 del RGPD: «el responsable ha de poder DEMOSTRAR que l'interessat va consentir». Per aixo hi ha dues regles que el codi fa complir, i no depenen que ningu les recordi:
1. Una acceptacio, una fila. POST /api/users/me/consents es idempotent: si aquella
persona ja te una acceptacio vigent d'aquell document, tornar-la a enviar NO escriu res i NO
toca la fila que ja hi es. La data, la IP i el navegador que hi consten son els del moment en
que el consentiment es va donar de veritat, no els del reintent. La retirada tambe ho es.
2. El conjunt s'escriu sencer o no s'escriu. La peticio porta TOTS els documents d'una pantalla i el servidor els escriu en una sola transaccio. Abans el navegador n'enviava una per document i, si en fallava una pel mig, en quedaven de desades i d'altres no: un registre a mitges, que no demostra res, mentre l'usuari llegia que no s'havia desat res.
Qui ho garanteix:
uk_user_consent_effective(V151) — index PARCIALUNIQUE (user_id, legal_document_id, consent_type) WHERE superseded_at IS NULL. Substitueixuk_user_consent(V68), que eraUNIQUE (..., given_at)i no podia impedir cap duplicat:given_ates l'instant exacte, i dues files nomes hi xocaven si es desaven al mateix microsegon. Tenia nom de garantia i no en donava cap.ConsentIdempotencyIT— exerceix el reintent, el tot-o-res, la sequencia legitima «acceptar → retirar → tornar a acceptar» (que NO es un duplicat i s'ha de conservar sencera), i que la base de dades refusa un duplicat escrit sense passar pel servei.
Nota sobre el cos de la peticio. El POST accepta el conjunt
({"consents": [...]}) i, com a pont de desplegament, tambe la forma antiga d'un sol document.
El pont existeix perque la web es redesplega sola a cada fusio a main i l'API es desplega a
ma: sense ell, l'ordre dels dos desplegaments obriria una finestra en que acceptar els termes
respondria 400. Es pot retirar quan cap client enviï la forma antiga.