Architecture des permissions et des droits
Dernière mise à jour : 2026-03-03
Ce document décrit l’architecture du système de permissions (droits) : modèles de données, liste des permissions, limites par plan, et usage backend/frontend.
1. Vue d’ensemble
- Plans d’abonnement : FREE, BASIC, PRO, ADMIN (non souscriptible).
- Permissions : droits granulaires par fonctionnalité (ex.
CREATE_SESSION,UPLOAD_FILE). - Limites numériques : quotas par plan (ex.
max_contacts,max_storage_mb).-1= illimité. - Vérification : guards côté API, hooks/composants côté frontend ; les comptes ADMIN contournent les limites et ont toutes les permissions.
2. Liste des permissions (droits)
Les codes ci-dessous sont définis dans backend/src/permissions/permissions.enum.ts et doivent correspondre à la table permissions en base.
| Code | Catégorie | Description |
|---|---|---|
| Sessions | ||
CREATE_SESSION | SESSIONS | Créer une séance |
EDIT_SESSION | SESSIONS | Modifier une séance |
DELETE_SESSION | SESSIONS | Supprimer une séance |
VIEW_SESSION | SESSIONS | Voir une séance |
| Contacts | ||
CREATE_CONTACT | CONTACTS | Créer un contact |
EDIT_CONTACT | CONTACTS | Modifier un contact |
DELETE_CONTACT | CONTACTS | Supprimer un contact |
VIEW_CONTACT | CONTACTS | Voir un contact |
| Fichiers | ||
UPLOAD_FILE | FILES | Téléverser des fichiers |
DELETE_FILE | FILES | Supprimer des fichiers |
VIEW_FILE | FILES | Voir des fichiers |
| Extraction | ||
USE_EXTRACTION | EXTRACTION | Utiliser l’extraction IA (OCR/LLM) |
| Doublons | ||
CREATE_DUPLICATE | DUPLICATES | Créer un doublon |
EDIT_DUPLICATE | DUPLICATES | Modifier un doublon |
DELETE_DUPLICATE | DUPLICATES | Supprimer un doublon |
VIEW_DUPLICATE | DUPLICATES | Voir les doublons |
| Tags | ||
CREATE_TAG | TAGS | Créer un tag |
EDIT_TAG | TAGS | Modifier un tag |
DELETE_TAG | TAGS | Supprimer un tag |
| Analytics | ||
VIEW_ANALYTICS | ANALYTICS | Voir les analytiques / rapports |
| Admin | ||
ADMIN_ACCESS | ADMIN | Accès admin (toutes les permissions + contournement des limites) |
3. Limites par plan (plan_limits)
| Clé de limite | Unité | Description |
|---|---|---|
max_sessions | nombre | Nombre max de séances |
max_contacts | nombre | Nombre max de contacts |
max_storage_mb | Mo | Stockage max (en Mo). -1 = illimité |
Valeurs actuelles (après migration 0186) :
| Plan | max_sessions | max_contacts | max_storage_mb |
|---|---|---|---|
| FREE | -1 | 20 | 100 |
| BASIC | -1 | 200 | 1000 |
| PRO | -1 | -1 | 10000 |
| ADMIN | -1 | -1 | -1 (bypass) |
4. Répartition des droits par plan
- FREE : toutes les permissions sauf
ADMIN_ACCESS,USE_EXTRACTION,VIEW_ANALYTICS. - BASIC et PRO : toutes les permissions sauf
ADMIN_ACCESS. - ADMIN : toutes les permissions (dont
ADMIN_ACCESS), limites ignorées.
Les associations plan ↔ permission sont en base dans plan_permissions (voir migrations Liquibase, ex. 0186_realign_plans_permissions_and_pricing).
5. Tables principales
| Table | Rôle |
|---|---|
users | Utilisateurs (sans champ role ; les droits viennent du plan). |
subscription_plans | Plans (FREE, BASIC, PRO, ADMIN). |
user_subscriptions | Abonnement actif par utilisateur (plan_id, status: ACTIVE/TRIAL/CANCELLED/EXPIRED). |
permissions | Liste des permissions (code, name, description, category). |
plan_permissions | Liaison plan ↔ permission. |
plan_limits | Limites numériques par plan (limit_key, limit_value ; -1 = illimité). |
user_permission_overrides | Surcharges utilisateur (octroi ou révocation d’une permission). |
6. Diagrammes de relation
6.1 Modèle entité-association (tables et clés étrangères)
erDiagram
users {
uuid id PK
text email
text password_hash
text display_name
timestamptz created_at
timestamptz updated_at
}
subscription_plans {
uuid id PK
text code "FREE, BASIC, PRO, ADMIN"
text name
text description
decimal price_monthly
decimal price_yearly
boolean is_active
int display_order
}
permissions {
uuid id PK
text code "CREATE_SESSION, UPLOAD_FILE, ..."
text name
text description
text category
}
user_subscriptions {
uuid id PK
uuid user_id FK
uuid plan_id FK
text status "ACTIVE, TRIAL, CANCELLED, EXPIRED"
timestamptz started_at
timestamptz expires_at
timestamptz trial_ends_at
}
plan_permissions {
uuid id PK
uuid plan_id FK
uuid permission_id FK
}
plan_limits {
uuid id PK
uuid plan_id FK
text limit_key "max_sessions, max_contacts, max_storage_mb"
int limit_value "-1 = illimité"
}
user_permission_overrides {
uuid id PK
uuid user_id FK
uuid permission_id FK
boolean granted "true = octroi, false = révocation"
}
users ||--o{ user_subscriptions : "a un abonnement"
subscription_plans ||--o{ user_subscriptions : "attribué à"
subscription_plans ||--o{ plan_permissions : "définit"
permissions ||--o{ plan_permissions : "associée à"
subscription_plans ||--o{ plan_limits : "définit"
users ||--o{ user_permission_overrides : "surcharges"
permissions ||--o{ user_permission_overrides : "concernée par"
6.2 Flux de résolution des droits (utilisateur → permissions effectives)
flowchart LR
subgraph entrées
U[Utilisateur<br/>user_id]
end
subgraph résolution
US[user_subscriptions]
SP[subscription_plans]
PP[plan_permissions]
P[permissions]
UPO[user_permission_overrides]
end
subgraph sortie
E[Permissions effectives<br/>+ Limites]
end
U -->|"1 abonnement actif<br/>(ACTIVE ou TRIAL)"| US
US -->|plan_id| SP
SP -->|plan_id| PP
PP -->|permission_id| P
U -->|user_id| UPO
UPO -->|permission_id<br/>granted true/false| P
SP --> E
P --> E
En résumé : l’abonnement actif de l’utilisateur pointe vers un plan ; le plan donne des permissions (via plan_permissions) et des limites (via plan_limits). Les surcharges (user_permission_overrides) ajoutent ou retirent des permissions pour cet utilisateur, sans changer de plan.
6.3 Schéma simplifié : qui donne quoi
Un utilisateur a un abonnement actif (une ligne dans user_subscriptions) qui référence un plan. Ce plan détermine les permissions et les limites. Les overrides modifient les permissions à la marge.
flowchart TB
USER[Utilisateur]
US[user_subscriptions<br/>1 abonnement actif]
PLAN[Plan<br/>FREE / BASIC / PRO / ADMIN]
PP[plan_permissions]
PL[plan_limits]
PERMS[Permissions effectives]
LIMS[Limites effectives]
OVR[user_permission_overrides<br/>optionnel]
USER --> US
US --> PLAN
PLAN --> PP
PLAN --> PL
PP --> PERMS
PL --> LIMS
USER --> OVR
OVR -.->|"ajoute ou retire"| PERMS
7. Backend : vérification des droits
7.1 Guard + décorateur sur un endpoint
import { RequirePermission } from "../permissions/permission.decorator.js";
import { PermissionGuard } from "../permissions/permission.guard.js";
@Controller("sessions")
@UseGuards(AuthGuard("jwt"), ActiveUserGuard)
export class SessionsController {
@Post()
@UseGuards(PermissionGuard)
@RequirePermission("CREATE_SESSION")
async create(@Req() req: RequestWithUser, @Body() data: CreateSessionDto) {
return this.service.create(data, req.user.id);
}
}
7.2 Vérification dans un service
-
Permission :
verifyPermission(permissionsService, userId, permissionCode, logger?, action?)
LanceForbiddenExceptionsi l’utilisateur n’a pas la permission. -
Limite :
verifyLimit(permissionsService, userId, limitKey, currentCount, logger?, resourceName?)
LanceForbiddenExceptionsicurrentCount >= limit(sauf si limit = -1). -
Admin :
verifyAdmin(permissionsService, userId, logger?, action?)
Équivalent àverifyPermission(..., "ADMIN_ACCESS", ...).
Fichier : backend/src/common/permission.utils.ts.
7.3 Contrôleurs qui appliquent déjà des limites / permissions
sessions.controller.ts→ limitemax_sessionsà la création.contacts.controller.ts→ limitemax_contactsà la création.extraction.controller.ts→ permissionUSE_EXTRACTION.session-files.controller.ts→ permissionUPLOAD_FILE+ limitemax_storage_mb(presigned URL).
8. Frontend : utilisation des permissions
8.1 Hooks
useMyPermissions():{ planCode, limits, permissions }pour l’utilisateur connecté.useHasPermission(permissionCode):{ hasPermission, isLoading }.useHasAnyPermission(permissionCodes): l’utilisateur doit avoir au moins une des permissions.useHasAllPermissions(permissionCodes): l’utilisateur doit avoir toutes les permissions.
Fichiers : frontend/src/client/permissions/usePermissions.ts, frontend/src/hooks/useHasPermission.ts.
8.2 Composants
RequirePermission: affichechildrensi l’utilisateur a la permission, sinonfallback.RequireAnyPermission/RequireAllPermissions: idem avec liste de permissions.
Fichier : frontend/src/components/RequirePermission.tsx.
8.3 Bannières et portes premium
PlanLimitBanner: avertissement / blocage quand une limite est proche ou atteinte.PremiumFeatureGate: masque une fonctionnalité (blur + CTA upgrade) si une permission est absente.
9. Comportements particuliers
- Admin :
planCode === "ADMIN"ou permissionADMIN_ACCESS→ toutes les permissions et limites considérées comme illimitées dansPermissionsService. - Overrides :
user_permission_overridespermet d’ajouter ou retirer une permission pour un utilisateur sans changer de plan. - Cache : les permissions utilisateur sont mises en cache (ex. Redis) pour limiter les requêtes (TTL configurable, ex. 10 min).
10. Fichiers de référence
| Fichier | Rôle |
|---|---|
backend/src/permissions/permissions.enum.ts | Énumération des codes de permission |
backend/src/permissions/permissions.interface.ts | Interface du service de permissions |
backend/src/permissions/permission.guard.ts | Guard de vérification sur les routes |
backend/src/permissions/permission.decorator.ts | Décorateur @RequirePermission() |
backend/src/common/permission.utils.ts | verifyPermission, verifyLimit, verifyAdmin |
frontend/src/client/permissions/usePermissions.ts | API et hooks des permissions |
frontend/src/lib/plan-features.ts | Libellés des limites et permissions “notables” pour l’UI |
11. Ajouter une nouvelle permission
-
Base de données (migration Liquibase) :
INSERT INTO permissions (code, name, description, category) VALUES ('NOUVEAU_DROIT', 'Nom', 'Description', 'CATEGORIE'); -
Backend : ajouter le code dans
permissions.enum.ts. -
Associer aux plans :
INSERT INTO plan_permissions (plan_id, permission_id) SELECT sp.id, p.id FROM subscription_plans sp CROSS JOIN permissions p WHERE sp.code IN ('BASIC','PRO') AND p.code = 'NOUVEAU_DROIT'; -
Utilisation :
- API :
@RequirePermission('NOUVEAU_DROIT')ouverifyPermission(..., 'NOUVEAU_DROIT', ...). - Frontend :
useHasPermission('NOUVEAU_DROIT')ou<RequirePermission permission="NOUVEAU_DROIT">...</RequirePermission>.
- API :
12. Ajouter une nouvelle limite
-
Base de données :
INSERT INTO plan_limits (plan_id, limit_key, limit_value) SELECT id, 'max_nouvelle_ressource', 10 FROM subscription_plans WHERE code = 'BASIC'; -
Backend : avant l’action concernée, appeler
await verifyLimit(permissionsService, userId, 'max_nouvelle_ressource', currentCount, logger, 'nouvelle ressource'); -
Frontend (optionnel) : ajouter la clé dans
LIMIT_LABELSdansplan-features.tset utiliserPlanLimitBannersi besoin.