Multi-tenance¶
Ce que ça signifie pour l'utilisateur¶
Chaque société cliente de Jigi (un « tenant ») a l'impression d'utiliser une application qui lui est dédiée : ses employés, ses congés, ses factures, ses documents. En réalité, toutes les sociétés partagent la même instance de l'application et la même base de données — mais aucune donnée d'une société n'est jamais visible par une autre, sauf exception documentée ci-dessous pour les opérateurs de la plateforme elle-même.
Comment ça fonctionne¶
Chaque requête envoyée au backend doit porter un en-tête X-Tenant-ID (un UUID identifiant la société). Ce mécanisme est géré en trois temps :
- Résolution — un filtre Servlet (
TenantFilter) lit l'en-têteX-Tenant-IDsur chaque requête HTTP entrante et le valide. - Propagation — l'identifiant est stocké dans un
ThreadLocal(app.jigi.tenant.context.TenantContext) pendant toute la durée du traitement de la requête, puis nettoyé (TenantContext.clear()) une fois la réponse envoyée — y compris en cas d'erreur, pour éviter toute fuite entre requêtes traitées par le même thread. - Filtrage — chaque table métier porte une colonne
tenant_id UUID NOT NULL, et chaque requête de dépôt de données (repository) filtre systématiquement sur cetenant_id.
public final class TenantContext {
private static final ThreadLocal<UUID> CONTEXT = new ThreadLocal<>();
public static UUID getTenantId() { return CONTEXT.get(); }
public static void setTenantId(UUID tenantId) { CONTEXT.set(tenantId); }
public static void clear() { CONTEXT.remove(); }
}
L'exception : les opérateurs de la plateforme¶
Les comptes JIGI_OPERATOR et JIGI_ADMIN (voir Rôles & permissions) opèrent au niveau plateforme, pas au niveau d'un tenant particulier. Ils ont accès à une vue cross-tenant (liste de tous les tenants, catalogue d'abonnements, paiements, etc.) via l'application séparée admin-jigi, documentée dans le Guide Administrateur plateforme Jigi. Chacune de leurs actions sensibles est journalisée dans une table d'audit dédiée (subscription.operator_audit_log) — voir Opérateurs & audit.
Rôles globaux vs rôles par tenant¶
Le modèle de rôles de Jigi distingue :
- des rôles système globaux (
tenant_id IS NULL) : des modèles partagés par toutes les sociétés (ex.ORG_ADMIN,HR_MANAGER,MANAGER) ; - des rôles personnalisés propres à un tenant (
tenant_idrenseigné) : une société peut créer ses propres rôles (ex.ACCOUNTANT,CFO) avec ses propres combinaisons de permissions.
Ce détail est développé dans Rôles & permissions (RBAC).
Détails techniques
- Table
tenants(schémapublic) : identifie chaque société cliente. - Toute entité métier porte
tenant_id UUID NOT NULLavec un index sur cette colonne pour les performances. TenantFilters'exécute avant que la requête n'atteigne les contrôleurs — unX-Tenant-IDabsent ou invalide est rejeté avant toute logique métier.- Les tables au sein du schéma
workflow(voir Moteur de workflow) suivent la même conventiontenant_id. - Les rôles globaux utilisent un index unique partiel (
uq_roles_global_name ON roles (name) WHERE tenant_id IS NULL) car PostgreSQL ne considère pas deux valeursNULLcomme égales dans une contrainteUNIQUEclassique.
FAQ¶
Un employé peut-il, par erreur de configuration, voir les données d'une autre société ? Non — l'isolation est appliquée au niveau des requêtes de données elles-mêmes (pas seulement dans l'interface), donc même un bug d'affichage ne peut pas exposer les données d'un autre tenant.
Que se passe-t-il si l'en-tête X-Tenant-ID est manquant ?
La requête est rejetée avant d'atteindre la logique métier — aucune donnée n'est renvoyée.
Les opérateurs Jigi voient-ils le contenu métier détaillé d'un tenant (ex. le contenu des fiches de paie) ? Non, leur accès porte sur les informations de gestion de la plateforme (abonnement, paiement, statut du tenant), pas sur le contenu métier détaillé des modules RH/comptabilité d'un tenant, sauf action explicite et journalisée (ex. support ponctuel).