6301f521fd
Campus, Stress Index e Longevity escono dal sito e diventano apps.insanitylab.it, con utenti, sessioni, login e database propri. Deciso da Adriano il 03/09/2026: una repo sola, due applicazioni autosufficienti, cosi' che ognuna resti estraibile. Misura che ha guidato il taglio: le tre aree condividevano col sito SOLO l'identita' - ~170 righe fra auth.ts, middleware.ts e safe-next.ts. Layout, librerie e dati erano gia' disgiunti. Lato piattaforme: - ruoli piattaforme/cliente/trainer/admin, senza quelli redazionali - canAccessPath con default CHIUSO (nel sito era aperto: li' le rotte non elencate erano di chiunque fosse loggato, qui ogni sezione e' di qualcuno) - tutta l'app protetta, si elencano le eccezioni invece dei prefissi protetti - /api risponde 401/403, non un redirect verso una pagina di login - tre database distinti: utenti, dati clinici senza nomi, mappatura coi nomi Lato sito: - ROLES tornano i tre redazionali; via le regole e il login pubblico - il login del pannello RESPINGE un account emigrato invece di aprirgli una sessione: senza, landingFor lo mandava su /admin, il middleware glielo negava, e si formava un ciclo di redirect che sembra un guasto del sito - l'header non mostra piu' un menu delle piattaforme: un link che esce Verificato: sito 20 file / 146 test, piattaforme 26 file / 197 test, entrambi i build passano, e l'app girata davvero in locale - il ruolo piattaforme entra in Campus e viene rimbalzato da Longevity, il cliente il contrario. Non ancora in produzione: manca il record DNS di apps.insanitylab.it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EndnceRA5WnA6rvA5iV9WL
106 lines
5.5 KiB
TypeScript
106 lines
5.5 KiB
TypeScript
// Chi entra dove, in QUEST'app. Sostituisce tests/auth-piattaforme.test.ts del repo unico:
|
|
// là si verificava che il ruolo `piattaforme` non sfondasse dentro il pannello del sito,
|
|
// qui il pannello non esiste — le rotte del sito non sono raggiungibili nemmeno sbagliando
|
|
// indirizzo, perché stanno su un'altra applicazione, su un altro host, con un altro database.
|
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
import type Database from 'better-sqlite3';
|
|
import { createDb } from '../src/lib/db';
|
|
import { createUser, login, getSessionUser, canAccessPath, landingFor, isRole, isProtectedPath, ROLES } from '../src/lib/auth';
|
|
|
|
let db: Database.Database;
|
|
beforeEach(() => { db = createDb(':memory:'); });
|
|
|
|
describe('utenti e sessioni di quest\'app', () => {
|
|
it('createUser accetta i ruoli dell\'app e getSessionUser li restituisce', () => {
|
|
for (const ruolo of ['piattaforme', 'cliente', 'trainer', 'admin'] as const) {
|
|
createUser(db, `u-${ruolo}`, 'segreta123', ruolo);
|
|
expect(getSessionUser(db, login(db, `u-${ruolo}`, 'segreta123')!)).toMatchObject({ role: ruolo });
|
|
}
|
|
});
|
|
|
|
it('i ruoli redazionali del sito qui non esistono', () => {
|
|
expect(isRole('superuser')).toBe(false);
|
|
expect(isRole('user')).toBe(false);
|
|
expect(isRole('campus')).toBe(false);
|
|
expect(ROLES).not.toContain('superuser');
|
|
expect(ROLES).not.toContain('user');
|
|
});
|
|
|
|
it('isRole accetta esattamente i ruoli di ROLES, nessuno in più o in meno', () => {
|
|
// Ereditata da tests/longevity/ruoli.test.ts del repo unico: quel file verificava
|
|
// sei ruoli e la tendina di admin/users.astro, che qui non esistono piu'. Questa e'
|
|
// la sua asserzione che vale ancora — le due liste devono restare la stessa cosa.
|
|
for (const ruolo of ROLES) expect(isRole(ruolo)).toBe(true);
|
|
for (const finto of ['campus', 'superuser', 'user', 'Admin', '', 'cliente ']) expect(isRole(finto)).toBe(false);
|
|
});
|
|
|
|
it('landingFor manda ognuno a casa propria, e nessuno su /admin', () => {
|
|
expect(landingFor('cliente')).toBe('/longevity/io');
|
|
expect(landingFor('trainer')).toBe('/longevity/gestionale');
|
|
expect(landingFor('piattaforme')).toBe('/piattaforme');
|
|
expect(landingFor('admin')).toBe('/piattaforme');
|
|
for (const ruolo of ROLES) expect(landingFor(ruolo)).not.toMatch(/^\/admin/);
|
|
});
|
|
});
|
|
|
|
describe('canAccessPath — le tre sezioni', () => {
|
|
it('piattaforme: Campus e Stress Index sì, Longevity no', () => {
|
|
expect(canAccessPath('piattaforme', '/campus')).toBe(true);
|
|
expect(canAccessPath('piattaforme', '/campus/corso-base/lezione-01')).toBe(true);
|
|
expect(canAccessPath('piattaforme', '/campus/assets/slide/x.png')).toBe(true);
|
|
expect(canAccessPath('piattaforme', '/piattaforme')).toBe(true);
|
|
expect(canAccessPath('piattaforme', '/piattaforme/stress-index/clienti')).toBe(true);
|
|
// Il ruolo storico non eredita i dati sanitari: sono arrivati dopo, e per altri.
|
|
expect(canAccessPath('piattaforme', '/longevity')).toBe(false);
|
|
expect(canAccessPath('piattaforme', '/longevity/io')).toBe(false);
|
|
expect(canAccessPath('piattaforme', '/api/longevity/questionario')).toBe(false);
|
|
});
|
|
|
|
it('cliente: il proprio spazio sì, il gestionale e le altre piattaforme no', () => {
|
|
expect(canAccessPath('cliente', '/longevity')).toBe(true);
|
|
expect(canAccessPath('cliente', '/longevity/io')).toBe(true);
|
|
expect(canAccessPath('cliente', '/longevity/questionario')).toBe(true);
|
|
expect(canAccessPath('cliente', '/api/longevity/questionario')).toBe(true);
|
|
expect(canAccessPath('cliente', '/longevity/gestionale')).toBe(false);
|
|
expect(canAccessPath('cliente', '/api/longevity/gestionale/misure')).toBe(false);
|
|
expect(canAccessPath('cliente', '/campus')).toBe(false);
|
|
expect(canAccessPath('cliente', '/piattaforme/stress-index')).toBe(false);
|
|
});
|
|
|
|
it('trainer: entrambe le parti di Longevity, niente Campus né Stress Index', () => {
|
|
expect(canAccessPath('trainer', '/longevity/gestionale')).toBe(true);
|
|
expect(canAccessPath('trainer', '/longevity/io')).toBe(true);
|
|
expect(canAccessPath('trainer', '/api/longevity/gestionale/misure')).toBe(true);
|
|
expect(canAccessPath('trainer', '/campus')).toBe(false);
|
|
expect(canAccessPath('trainer', '/piattaforme/stress-index')).toBe(false);
|
|
});
|
|
|
|
it('admin passa sempre', () => {
|
|
for (const p of ['/campus', '/piattaforme', '/piattaforme/stress-index', '/longevity', '/longevity/gestionale'])
|
|
expect(canAccessPath('admin', p)).toBe(true);
|
|
});
|
|
|
|
it('⚠️ il default è CHIUSO: una rotta senza regola non è di nessuno', () => {
|
|
// Differenza sostanziale dal sito, dove il default finale era `return true` perché
|
|
// blog e upload erano di chiunque fosse loggato. Qui una sezione nuova nasce vietata:
|
|
// il difetto si vede provandola, non leggendo un log di accessi.
|
|
for (const ruolo of ['piattaforme', 'cliente', 'trainer'])
|
|
expect(canAccessPath(ruolo, '/sezione-nuova-senza-regola')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('isProtectedPath — cosa si apre senza sessione', () => {
|
|
it('solo login, logout, 404 e la favicon', () => {
|
|
expect(isProtectedPath('/login')).toBe(false);
|
|
expect(isProtectedPath('/logout')).toBe(false);
|
|
expect(isProtectedPath('/404')).toBe(false);
|
|
expect(isProtectedPath('/favicon.svg')).toBe(false);
|
|
});
|
|
|
|
it('tutto il resto è protetto, comprese le pagine che ancora non esistono', () => {
|
|
for (const p of ['/', '/campus', '/piattaforme', '/longevity/io', '/api/longevity/questionario',
|
|
'/longevity/gestionale', '/qualcosa-che-non-esiste'])
|
|
expect(isProtectedPath(p)).toBe(true);
|
|
});
|
|
});
|