diff --git a/src/lib/longevity/anagrafica.ts b/src/lib/longevity/anagrafica.ts index 5d60a0e..9bf8eb3 100644 --- a/src/lib/longevity/anagrafica.ts +++ b/src/lib/longevity/anagrafica.ts @@ -4,6 +4,7 @@ // risalga alla persona (richiesta del cliente, 21/08): un secondo punto di giunzione // la annullerebbe in silenzio. Un test in tests/longevity/anagrafica.test.ts lo verifica. import type Database from 'better-sqlite3'; +import { getIdentityDb } from './db'; /** * Il prossimo codice libero: il massimo fra quelli già usati in identity.clienti E in @@ -62,6 +63,17 @@ export function codicePerUtente(identity: Database.Database, userId: number): st return r?.client_code ?? null; } +/** + * Il codice cliente a partire dall'utente di sessione del sito: apre lei stessa la + * connessione a identity (getIdentityDb), perché QUESTO è il modulo autorizzato a farlo. + * Un endpoint che deve solo sapere "chi è" chiama questa funzione e non ha mai bisogno + * di aprire identity da sé — riceve una stringa, mai una connessione che potrebbe + * incrociarsi con quella di longevity nello stesso file. + */ +export function codicePerUtenteLoggato(userId: number): string | null { + return codicePerUtente(getIdentityDb(), userId); +} + /** L'eta' al momento della misura: è ciò che serve al motore, e non è un quasi-identificatore. */ export function etaAllaData(dataNascita: string, alla: string): number { const n = new Date(dataNascita); diff --git a/src/pages/api/longevity/questionario.ts b/src/pages/api/longevity/questionario.ts index aa19eb8..5f0909e 100644 --- a/src/pages/api/longevity/questionario.ts +++ b/src/pages/api/longevity/questionario.ts @@ -1,6 +1,6 @@ import type { APIRoute } from 'astro'; -import { getIdentityDb, getLongevityDb } from '../../../lib/longevity/db'; -import { codicePerUtente } from '../../../lib/longevity/anagrafica'; +import { getLongevityDb } from '../../../lib/longevity/db'; +import { codicePerUtenteLoggato } from '../../../lib/longevity/anagrafica'; import { salvaDalForm } from '../../../lib/longevity/vista'; import { CAMPI_LIBERI } from '../../../lib/longevity/questionario'; @@ -11,8 +11,10 @@ const json = (status: number, body: object) => export const POST: APIRoute = async ({ request, locals }) => { // Il codice cliente si ricava SEMPRE dalla sessione dell'utente loggato (spec §8), mai dal - // corpo della richiesta: qui infatti il corpo non viene mai letto per questo campo. - const codice = codicePerUtente(getIdentityDb(), locals.user!.id); + // corpo della richiesta: qui infatti il corpo non viene mai letto per questo campo. La + // risoluzione passa da anagrafica.ts (l'unico modulo autorizzato a toccare quella + // connessione): questo file apre solo longevity, per salvare. + const codice = codicePerUtenteLoggato(locals.user!.id); if (!codice) return json(403, { error: 'Nessun fascicolo cliente associato a questo utente.' }); let data: Record; diff --git a/tests/longevity/anagrafica.test.ts b/tests/longevity/anagrafica.test.ts index bf232be..4dfcd0f 100644 --- a/tests/longevity/anagrafica.test.ts +++ b/tests/longevity/anagrafica.test.ts @@ -88,7 +88,15 @@ describe('anagrafica pseudonimizzata', () => { expect(id.prepare(`SELECT * FROM clienti WHERE nome = 'Errato'`).get()).toBeUndefined(); }); - it('nessun file in src/ (escluso anagrafica.ts e db.ts) apre entrambe le connessioni', () => { + it('nessun file in src/ (escluso anagrafica.ts e db.ts) fa riferimento sia a identity sia a longevity', () => { + // Non cerchiamo due nomi di funzione precisi (createIdentityDb/createLongevityDb): un + // domani qualcuno aggiunge getIdentityDb, o un terzo accessore con un nome diverso, e + // l'elenco invecchia senza che nessuno se ne accorga (è già successo: getIdentityDb e + // getLongevityDb sono nati DOPO questo test e sono passati indisturbati). Il verso + // giusto è strutturale: qualunque accesso alle due connessioni passa comunque per una + // funzione esportata da db.ts il cui nome contiene "Identity" o "Longevity" — quindi + // basta cercare quei due riferimenti letterali, in QUALUNQUE forma compaiano, e + // segnalare i file che li hanno tutti e due. const srcDir = join(process.cwd(), 'src'); const colpevoli: string[] = []; @@ -106,7 +114,7 @@ describe('anagrafica pseudonimizzata', () => { if (f === 'lib/longevity/anagrafica.ts' || f === 'lib/longevity/db.ts') continue; const src = readFileSync(path, 'utf8'); - if (src.includes('createIdentityDb') && src.includes('createLongevityDb')) { + if (src.includes('Identity') && src.includes('Longevity')) { colpevoli.push(f); } }