Multi-tenancy sicura in Lino
Multi-tenancy consente a una singola applicazione di servire più clienti, aziende o organizzazioni mantenendo dati e permessi isolati. In Lino, Tenant è trattato come parte del flusso dell'applicazione: la funzionalità genera la base di tenancy, risolve il Tenant da subdominio, host o slug tramite ITenantContext ed espone l'utente autenticato dal JWT tramite IUserContext.
Il JWT porta anche il TenantId per cui l'utente è stato autenticato. Lino confronta quel Tenant con il Tenant risolto per la richiesta, quindi questa pagina si concentra su ciò che devi decidere dopo che questa base esiste: modellazione delle entità, permessi per organizzazione e uso corretto delle astrazioni generate.
Aggiunta del supporto Tenant
Usa la funzionalità Tenant quando il sistema è SaaS o quando dati di clienti diversi devono convivere nella stessa applicazione senza leak tra contesti.
lino feature tenant add
L'assistente genera la base per registrazione dei Tenants, associazione degli utenti, risoluzione del Tenant corrente, propagazione nel contesto dell'applicazione e integrazione con l'autenticazione. ITenantContext rappresenta il Tenant risolto da subdominio, host o slug, mentre IUserContext rappresenta l'utente autenticato dal JWT, incluso il TenantId per cui è stato autenticato.
Dopo la generazione, rivedi quali entità appartengono davvero a un Tenant e quali sono globali. Questa revisione fa parte della modellazione del prodotto: multi-tenancy non significa solo creare una tabella Tenants, ma decidere quali regole, schermate, permessi, dati e operazioni dipendono dal Tenant corrente validato da Lino.
- Tenant-scoped: ordini, prodotti di un cliente, impostazioni aziendali, clienti di un CRM o qualsiasi dato che appartenga a una specifica organizzazione.
- Global: catalogo pubblico della piattaforma, piani, risorse interne di operation, impostazioni amministrative o dati usati prima di scegliere un Tenant.
- Ibrido: dati globali con personalizzazione per Tenant, che richiedono modellazione esplicita per evitare duplicazione errata o leak di regole.
In un monolite modulare, di solito il modulo di tenancy è proprietario dell'entità Tenant originale. Altri moduli, come catalogo o CRM, non devono accedere direttamente a quell'entità. Quando devono conoscere Tenant, possono mantenere shadow entities con i campi minimi necessari, alimentate da eventi di integrazione come creazione o aggiornamento di Tenant.
Tenant context
Con la funzionalità Tenant, Lino centralizza la risoluzione del Tenant corrente ed espone quel valore tramite ITenantContext. Questo contesto rappresenta il Tenant risolto da subdominio, host o slug della richiesta, incluse informazioni come TenantId e slug quando il Tenant è stato trovato.
Nei flussi autenticati, IUserContext legge l'utente dal JWT. Quel JWT contiene anche il TenantId per cui l'utente è stato autenticato. Lino confronta il Tenant di IUserContext con il Tenant di ITenantContext, evitando che la regola di business si fidi di contesti divergenti.
Nelle funzionalità generate o aggiunte dopo, preferisci sempre queste astrazioni invece di rileggere claims manualmente o ricevere un TenantId arbitrario in ogni handler. Questo mantiene commands, queries, pagine e integrazioni sulla stessa source of truth e preserva i meccanismi di sicurezza già implementati da Lino.
Background Jobs, consumatori di eventi e integrazioni hanno bisogno di contesto. Un job che elabora prodotti, clienti o utenti tenant-scoped deve ricevere esplicitamente quale Tenant viene elaborato, perché non esiste una richiesta HTTP attiva da cui ITenantContext e IUserContext possano essere inferiti automaticamente.
Entità tenant-scoped
Le entità tenant-scoped portano l'identificatore del Tenant e partecipano ai filtri di query. Così il flusso comune non deve ricordarsi manualmente di applicare TenantId in tutte le queries; la funzionalità deve usare il contesto validato e i filtri generati da Lino.
- Includi Tenant negli aggregates che appartengono a una specifica organizzazione.
- Configura gli indici considerando Tenant e campi consultati di frequente.
- Usa
ITenantContexteIUserContextin commands e queries invece di ricevere Tenant come autorità isolata. - In imports e jobs, definisci esplicitamente quale Tenant viene elaborato.
Un'entità tenant-scoped appartiene a un Tenant. Se un'entità sembra appartenere a più Tenants, trattalo come una decisione di modellazione: forse è globale, forse deve esistere una copia per Tenant, o forse manca un'entità intermedia che rappresenti la relazione. Un prodotto, cliente o utente può anche avere significato diverso in moduli diversi; in questi casi, una shadow entity con pochi campi può essere più corretta rispetto alla condivisione dell'entità originale.
Distingui anche riferimento da ownership. Un prodotto nel catalogo può referenziare il Tenant, ma questo non significa che il catalogo controlli il ciclo di vita del Tenant. Il modulo di tenancy resta proprietario di creazione, attivazione, dominio, branding e modalità di isolamento; il catalogo mantiene solo i dati necessari per filtrare e validare il proprio flusso.
- Indici: combina
TenantIdcon campi usati in ricerca, ordinamento e unicità per Tenant. - Commands: applica l'operazione nel Tenant corrente validato quando l'entità è tenant-scoped.
- Queries: mantieni paginazione, filtri e contatori nello stesso contesto validato da Lino.
- Eventi: includi contesto sufficiente affinché i consumatori aggiornino proiezioni senza consultare impropriamente il producer.
Isolamento dei dati
L'isolamento può essere fatto per colonna, schema o database separato. La scelta dipende da costo, volume, requisiti contrattuali e capacità operativa.
| Strategia | Vantaggi | Quando usare |
|---|---|---|
| Colonna TenantId | Più semplice ed economica | Buona scelta iniziale per molti SaaS, combinata con filtri e contesto di Lino. |
| Schema per Tenant | Isolamento intermedio | Utile quando ci sono esigenze maggiori di separazione senza arrivare a database per Tenant. |
| Database per Tenant | Isolamento forte | Indicato quando contratto, volume o operation giustificano costo e automazione maggiori. |
Per la maggior parte dei SaaS in fase iniziale, una colonna per Tenant con filtri globali e contesto centralizzato è un punto di partenza pragmatico. Lino aiuta con questa base; la tua revisione deve confermare che gli aggregates tenant-scoped portino il Tenant corretto e che le queries nuove continuino a usare il Tenant corrente validato dalle astrazioni generate.
Nei servizi modulari, isolamento di Tenant e isolamento di modulo sono preoccupazioni diverse. Il modulo Catalog può avere una tabella prodotti con TenantId; il modulo CRM può avere clienti con TenantId; e il modulo Tenancy resta proprietario della registrazione originale dei Tenants. La presenza dello stesso identificatore non autorizza accesso diretto tra moduli.
Roles e permessi per Tenant
In SaaS, un utente può essere amministratore in un Tenant e operatore in un altro. Lino genera la base per lavorare con il legame utente-Tenant, e i permessi delle funzionalità devono essere valutati nel contesto attivo validato dal framework.
Esistono anche permessi amministrativi fuori da Tenant. Una schermata di operation della piattaforma può essere accessibile solo nel contesto di sistema, mentre pagine come prodotti e clienti possono richiedere Tenant attivo. Questa separazione rende chiaro quando l'azione è globale e quando dipende dal Tenant risolto in ITenantContext e dall'utente autenticato in IUserContext.
- Modella roles per Tenant quando i permessi variano per organizzazione.
- Usa il contesto corrente nelle verifiche di autorizzazione di pagine, commands e queries.
- Mantieni claims compatte, portando nel token solo ciò che deve partecipare al flusso autenticato.
- Revoca o aggiorna tokens quando cambiano permessi critici.
| Contesto | Esempio | Come trattare |
|---|---|---|
| Sistema | Amministrazione della piattaforma, piani, Tenants e impostazioni globali. | Eseguire fuori dallo scope di uno specifico Tenant. |
| Tenant | Prodotti, clienti, ordini, utenti e roles di quel Tenant. | Usare il Tenant corrente validato da Lino. |
| Entrambi | Alcune funzioni di identità o gestione che esistono sia nel sistema sia nel Tenant. | Dichiarare nel caso d'uso quale contesto viene usato. |
JWT e sicurezza di Tenant
Quando la funzionalità Tenant è attiva, Lino usa il JWT come fonte del contesto dell'utente autenticato. IUserContext legge le claims dell'utente e il TenantId dal token, senza trasformare il JWT in una copia del database.
- Mantieni issuer, audience, firma e scadenza configurati correttamente.
- Usa il Tenant validato da Lino invece di accettare un cambio manuale basato solo su header, route o payload della schermata.
- Evita claims troppo grandi; tokens gonfi complicano operation e rinnovo.
- Registra logs di accesso con utente, Tenant e operazione quando il flusso richiede audit.
Nelle applicazioni con subdomini, come acme.dev.localhost e globex.dev.localhost, ITenantContext rappresenta il Tenant risolto per la richiesta. IUserContext rappresenta l'utente autenticato e il Tenant presente nel JWT. Lino confronta questi due contesti per evitare che un token emesso per un Tenant venga usato in un altro contesto di Tenant.
Anche l'autorizzazione resta contestuale. Il permesso di visualizzare prodotti, ad esempio, vale nel Tenant in cui è stato concesso e per cui l'utente è stato autenticato. Quando crei nuove funzionalità, usa i permessi e i contesti già disponibili invece di creare scorciatoie che ricevono un TenantId libero e ignorano il flusso autenticato.
