Paramétrage en masse — détails techniques
Description et usage : Paramétrer plusieurs établissements en une fois.
Propagation du jeu d'accès d'un groupe au groupe de même profil dans plusieurs établissements,
depuis Gestion des accès (/admin/access → « Étendre au périmètre »).
Route et écran
| Élément | Emplacement |
|---|---|
| Dialogue « Étendre ces accès à votre périmètre » | apps/dashboard/src/app/admin/access/page-view.tsx (ExtendAccessDialog) |
| API | apps/dashboard/src/pages/api/admin/access-bulk.ts (Pages Router) |
| Résolution du périmètre | apps/dashboard/src/pages/api/_perimeter.ts (resolveAdminPerimeter, splitBulkTargets) |
GET /api/admin/access-bulk→ les établissements réellement administrables par l'appelant, pour alimenter la liste à cocher.POST /api/admin/access-bulkavec{ structureIds, profile, levels }→ applique le jeu de niveaux et renvoie{ profile, total, applied, results }.
Les niveaux envoyés sont ceux du groupe affiché, recalculés côté client par le même code que
« Modifier les accès » (getGroupLevel). Aucune écriture Neo4j directe : tout passe par
appregistry et directory.
Périmètre : le client n'est jamais cru sur parole
resolveAdminPerimeter interroge GET /directory/structure/admin/list — tous les établissements
pour un SUPER_ADMIN, le scope de la fonction (héritage HAS_ATTACHMENT compris) pour un
ADMIN_LOCAL. La liste envoyée par le navigateur ne sert qu'à choisir dans ce périmètre.
splitBulkTargets sépare la sélection en targets et unauthorized plutôt que de rejeter tout le
lot : un référent territorial peut porter un périmètre ADML partiel, et les établissements hors
périmètre ressortent comme des lignes de compte rendu (status: 'unauthorized'), pas comme un 403
global. Seul le plafond est bloquant : MAX_STRUCTURES = 40 par requête (413 au-delà), chaque
établissement coûtant plusieurs appels — au-delà, la requête dépasserait le délai d'attente du
navigateur.
Le rôle métier de référent collectivité combine actuellement ADMIN_COLLECTIVITE et un
périmètre ADMIN_LOCAL. La première fonction ouvre notamment les commandes groupées de
structure-info ; la seconde détermine les établissements administrables par les API entcore.
Le serveur ne remplace pas ce périmètre par la portée de ADMIN_COLLECTIVITE : cela proposerait
des établissements sur lesquels les opérations locales pourraient être refusées.
Attribuer ADMIN_LOCAL sur la structure de tête avec héritage pour couvrir le réseau.
Voir Référent collectivité et
Établissements & structures.
Le bon groupe : celui de l'établissement, pas d'une de ses classes
Pour chaque établissement, quatre lectures en parallèle : appregistry/groups/roles,
directory/group/admin/list, appregistry/roles et appregistry/applications.
Deux pièges y sont traités explicitement :
groups/rolesne porte pas le champtype— sans lui,profileIdForGroupne peut rattacher aucun groupe à un profil. Le type est fusionné par id depuis l'annuaire (group/admin/list), comme le fait déjà l'écran « Gestion des accès ».- Un groupe de classe porte un libellé voisin du groupe d'établissement (« Enseignants du
groupe 3ème-1. » / « Enseignants du groupe Collège André Malraux-Dijon. »). Un simple
findaurait posé les droits sur une classe : le groupe retenu est celui dont le nom contient le nom de l'établissement (comparaison normalisée, accents retirés). Sans candidat, l'établissement ressortskippedavec le motif — jamais une écriture au hasard.
Écriture : retirer avant de reposer
Les rôles gérés sont tous les niveaux (- Lecture, - Gestion, - Administration, plus les
conventions historiques <app> et <app>- tous les droits, résolus par resolveRoleId) des
applications visées. Ils sont retirés de la liste courante, puis les rôles cibles sont ajoutés —
c'est ce qui fait qu'un passage à « Aucun accès » ou une rétrogradation prend effet, là où un
simple ajout n'aurait jamais rien enlevé. Les rôles étrangers aux applications visées sont
conservés : l'opération ne remet pas le groupe à plat. L'écriture se fait en un seul
POST /appregistry/authorize/group (groupId, roleIds).
Relecture post-écriture (verified)
Un HTTP 200 sur l'écriture ne prouve pas que l'annuaire a propagé le changement. Après chaque
établissement appliqué, appregistry/groups/roles est relu et comparé au paramétrage demandé :
tous les rôles cibles présents, tous les rôles retirés absents. Le résultat alimente le drapeau
verified de la ligne de compte rendu (« droits vérifiés » / « non vérifié » à l'écran) ; un échec
de la relecture elle-même est reporté en clair (Vérification impossible : …) plutôt que d'être
confondu avec un succès.
Chaque ligne de results porte structureId, structureName, status
(applied / skipped / error / unauthorized), le group effectivement modifié,
rolesApplied, verified et un message. L'opération n'est donc jamais un « tout ou rien » : une
application partielle se voit.
Couverture de tests
Pas de scénario e2e dédié à ce jour. L'écran /admin/access est visité par
apps/dashboard-e2e/src/modules/03_admin/05_configuration_openent.spec.ts (contrôle d'accès aux
pages d'administration) et par apps/dashboard-e2e/src/tests/10_app_access_grant.spec.ts
(attribution d'application sur l'établissement courant) ; ni l'un ni l'autre n'ouvre le dialogue
« Étendre au périmètre » ni ne vérifie la propagation. Les opérations de masse par
établissements — écran structure-info, qui partage _perimeter.ts — sont, elles, couvertes par
07_operations_masse.spec.ts (voir Établissements &
structures).
Séparation des fonctions collectivité
ADMIN_COLLECTIVITEconserve le rôle territorial et le nom de groupe historiqueCollectivite. Son libellé dans le Dashboard est Référent collectivité. L'attribution est réservée au super-administrateur, dans le proxy Dashboard et les filtresAddFunctionFilterdes backends.COLLECTIVITE_LOCALEest la fonction métier pour les réservations et le patrimoine. Son groupe fonctionnel porte le suffixeCollectiviteLocale. Elle n'est pas acceptée par les contrôles réservés àADMIN_COLLECTIVITE(actions groupées, registre, thème, sauvegardes).ADMIN_LOCALreste la source du périmètre administrable des opérations locales ; la nouvelle fonction métier ne la remplace pas et n'est jamais attribuée à sa place.
Mise en service
Dans le dépôt open-ent-mods, le script scripts/demo/creer_fonctions_metier.py crée notamment
COLLECTIVITE_LOCALE sous le profil PROFILE_PERSONNEL. Il accepte --dry-run pour vérifier
les créations prévues. Il ne réaffecte aucun utilisateur et peut être rejoué.
Déployer le Dashboard et les filtres d'attribution du backend utilisé, puis créer la nouvelle
fonction avant de l'attribuer. Les référents existants gardent ADMIN_COLLECTIVITE. Les comptes
métier doivent être réexaminés individuellement : attribuer la nouvelle fonction et retirer les
anciennes habilitations qui ne sont plus justifiées. Le simple changement de libellé ne retire
aucun droit existant.