Aller au contenu principal

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.

CodeCatégorieDescription
Sessions
CREATE_SESSIONSESSIONSCréer une séance
EDIT_SESSIONSESSIONSModifier une séance
DELETE_SESSIONSESSIONSSupprimer une séance
VIEW_SESSIONSESSIONSVoir une séance
Contacts
CREATE_CONTACTCONTACTSCréer un contact
EDIT_CONTACTCONTACTSModifier un contact
DELETE_CONTACTCONTACTSSupprimer un contact
VIEW_CONTACTCONTACTSVoir un contact
Fichiers
UPLOAD_FILEFILESTéléverser des fichiers
DELETE_FILEFILESSupprimer des fichiers
VIEW_FILEFILESVoir des fichiers
Extraction
USE_EXTRACTIONEXTRACTIONUtiliser l’extraction IA (OCR/LLM)
Doublons
CREATE_DUPLICATEDUPLICATESCréer un doublon
EDIT_DUPLICATEDUPLICATESModifier un doublon
DELETE_DUPLICATEDUPLICATESSupprimer un doublon
VIEW_DUPLICATEDUPLICATESVoir les doublons
Tags
CREATE_TAGTAGSCréer un tag
EDIT_TAGTAGSModifier un tag
DELETE_TAGTAGSSupprimer un tag
Analytics
VIEW_ANALYTICSANALYTICSVoir les analytiques / rapports
Admin
ADMIN_ACCESSADMINAccès admin (toutes les permissions + contournement des limites)

3. Limites par plan (plan_limits)

Clé de limiteUnitéDescription
max_sessionsnombreNombre max de séances
max_contactsnombreNombre max de contacts
max_storage_mbMoStockage max (en Mo). -1 = illimité

Valeurs actuelles (après migration 0186) :

Planmax_sessionsmax_contactsmax_storage_mb
FREE-120100
BASIC-12001000
PRO-1-110000
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

TableRôle
usersUtilisateurs (sans champ role ; les droits viennent du plan).
subscription_plansPlans (FREE, BASIC, PRO, ADMIN).
user_subscriptionsAbonnement actif par utilisateur (plan_id, status: ACTIVE/TRIAL/CANCELLED/EXPIRED).
permissionsListe des permissions (code, name, description, category).
plan_permissionsLiaison plan ↔ permission.
plan_limitsLimites numériques par plan (limit_key, limit_value ; -1 = illimité).
user_permission_overridesSurcharges 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?)
    Lance ForbiddenException si l’utilisateur n’a pas la permission.

  • Limite : verifyLimit(permissionsService, userId, limitKey, currentCount, logger?, resourceName?)
    Lance ForbiddenException si currentCount >= 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 → limite max_sessions à la création.
  • contacts.controller.ts → limite max_contacts à la création.
  • extraction.controller.ts → permission USE_EXTRACTION.
  • session-files.controller.ts → permission UPLOAD_FILE + limite max_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 : affiche children si l’utilisateur a la permission, sinon fallback.
  • 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 permission ADMIN_ACCESS → toutes les permissions et limites considérées comme illimitées dans PermissionsService.
  • Overrides : user_permission_overrides permet 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

FichierRôle
backend/src/permissions/permissions.enum.tsÉnumération des codes de permission
backend/src/permissions/permissions.interface.tsInterface du service de permissions
backend/src/permissions/permission.guard.tsGuard de vérification sur les routes
backend/src/permissions/permission.decorator.tsDécorateur @RequirePermission()
backend/src/common/permission.utils.tsverifyPermission, verifyLimit, verifyAdmin
frontend/src/client/permissions/usePermissions.tsAPI et hooks des permissions
frontend/src/lib/plan-features.tsLibellés des limites et permissions “notables” pour l’UI

11. Ajouter une nouvelle permission

  1. Base de données (migration Liquibase) :
    INSERT INTO permissions (code, name, description, category) VALUES ('NOUVEAU_DROIT', 'Nom', 'Description', 'CATEGORIE');

  2. Backend : ajouter le code dans permissions.enum.ts.

  3. 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';

  4. Utilisation :

    • API : @RequirePermission('NOUVEAU_DROIT') ou verifyPermission(..., 'NOUVEAU_DROIT', ...).
    • Frontend : useHasPermission('NOUVEAU_DROIT') ou <RequirePermission permission="NOUVEAU_DROIT">...</RequirePermission>.

12. Ajouter une nouvelle limite

  1. 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';

  2. Backend : avant l’action concernée, appeler
    await verifyLimit(permissionsService, userId, 'max_nouvelle_ressource', currentCount, logger, 'nouvelle ressource');

  3. Frontend (optionnel) : ajouter la clé dans LIMIT_LABELS dans plan-features.ts et utiliser PlanLimitBanner si besoin.