Aller au contenu

RBAC & scopes

Logmis utilise un RBAC (Role-Based Access Control) avec deny-overrides et vérification de scope par préfixe d’OrgPath.

L’organisation est représentée comme un arbre de codes séparés par / :

/UAT-GROUP ← racine groupe
/UAT-GROUP/UAT-LE ← entité légale
/UAT-GROUP/UAT-LE/UAT-HOTEL ← établissement hôtel
/UAT-GROUP/UAT-LE/UAT-CAMP ← établissement camping

Un rôle assigné à /UAT-GROUP couvre tous les sous-chemins (préfixe). Un rôle assigné à /UAT-GROUP/UAT-LE/UAT-HOTEL ne couvre que l’hôtel.

Chaque utilisateur a un home_scope OrgPath fixé à la création, borné au scope du créateur. Il ne peut pas être élargi après création.

Toute opération de gestion d’utilisateur (assign/revoke rôle, liste, détail, désactivation) exige que l’acteur couvre le home_scope de l’utilisateur cible (préfixe OrgPath, inclusif). En dehors du périmètre : 403/404.

Un rôle assigné ne peut pas sortir l’utilisateur de son home_scope (le scope du rôle doit être ⊆ home_scope).

CodePermissions principales
receptionistRéservations CRUD, check-in/out, folio lecture, board arrivées/départs
housekeeperBoard HK, tâches ménage, statuts unités
managerDashboard, rapports, réservations (lecture étendue), tarifs, allotments
maintenance-technicianWork-orders, asset maintenance, compteurs
accountantFolio complet, FEC, fiscal, settlement
tenant-adminAdministration tenant, users/rôles (dans son home_scope)
directorMêmes permissions que tenant-admin — rôle à sémantique métier (direction d’un groupe d’établissements), scopé sur un sous-arbre OrgPath couvrant 1..N établissements

Les rôles builtin sont seedés via seedBuiltinRoles (idempotent). Ils ne peuvent pas être supprimés.

Rôle director — scoping multi-établissements (ADR-00D86)

Section intitulée « Rôle director — scoping multi-établissements (ADR-00D86) »

Un directeur supervise plusieurs établissements d’un même groupe. Son périmètre est défini par le scope de l’assignation dans role_assignments (OrgPath, ex. /UAT-GROUP), pas par l’appartenance à la racine tenant. La matrice de permissions est identique à tenant-admin en V1.0.

Différences sémantiques avec tenant-admin :

tenant-admindirector
Scope typiqueRacine tenant /Sous-arbre org (ex. /UAT-GROUP)
SémantiqueAdministration de l’abonnement SaaSDirection opérationnelle d’un groupe d’établissements
MFAExigéeExigée (même niveau de sensibilité)

Le ScopeAuthorizer (ADR-00D82) applique au rôle director les mêmes gardes qu’aux autres rôles : toute opération de gestion (org.manage, apikey.manage) est bornée au périmètre de l’acteur. Un directeur scopé /grp-a ne peut pas créer une org-unit sous /grp-b, ni émettre une clé API avec un orgScope plus large que son propre scope.

apps/api/src/modules/authorization/domain/
PermissionResolver.resolve(
assignments: RoleAssignment[],
requiredPermission: Permission,
resourceScope: OrgPath,
): boolean
  • Deny-overrides : si un rôle deny couvre la ressource, l’accès est refusé même si un autre rôle l’autorise
  • Scope check : le scope du rôle doit être un préfixe (ou égal) au scope de la ressource
# Assigner un rôle
POST /v1/users/:userId/roles
{ "roleCode": "receptionist", "scope": "/UAT-GROUP/UAT-LE/UAT-HOTEL" }
# Révoquer
DELETE /v1/users/:userId/roles/:roleCode

Gardes :

  1. L’acteur JWT doit avoir la permission users:assign-role
  2. Le scope du rôle à assigner doit être ⊆ home_scope de l’utilisateur cible
  3. L’acteur doit couvrir le home_scope de l’utilisateur cible (préfixe OrgPath)

Les permissions effectives sont cachées dans Redis avec un TTL configurable. La permission-epoch est incrémentée à chaque assign/revoke, invalide le cache.

SurfaceGuardPool DBRôles
/v1/users (tenant)AuthGuard + RBACpms_appRôles builtin tenant
/admin/* (control-plane)PlatformAdminGuardpms_control_planeOpérateurs Osmozis uniquement

Un opérateur Logmis n’a aucun accès aux données tenant via /v1/. Un utilisateur tenant n’a aucun accès au control-plane.

Le JWT porte :

  • sub — userId
  • tid — tenantId
  • scope — home_scope OrgPath de l’utilisateur

Le scope du JWT est utilisé par le ScopeAuthorizer pour le re-check IDOR sur chaque requête.

La création d’un utilisateur ne peut pas lui attribuer un home_scope plus large que celui du créateur. Exemple :

  • Un manager avec scope /UAT-GROUP/UAT-LE/UAT-HOTEL ne peut créer que des utilisateurs avec scope ⊆ /UAT-GROUP/UAT-LE/UAT-HOTEL
  • Il ne peut pas créer un utilisateur avec scope /UAT-GROUP (plus large)

Cette règle est vérifiée dans create-user.use-case.ts et dans le guard assign-role.