Vue d’ensemble — à quoi cela sert
Les rôles et les équipes sont des libellés informatifs servant à classer les personnes. La seule et unique chose qui ouvre l’accès aux écrans et aux actions est le niveau (user_level). Ce qu’un utilisateur peut voir, et ce qu’il a le droit d’enregistrer ou de supprimer, est décidé à 100 % par le niveau ; les rôles (« DBA », « DevOps », « Sécurité ») et les équipes (« Équipe Infrastructure », « Équipe Sécurité ») sont des étiquettes d’affichage et d’organisation qui n’accordent absolument aucune permission.
Le modèle de permissions reste ainsi simple. Pour donner à quelqu’un des pouvoirs d’administrateur, vous relevez son niveau — vous ne changez pas son rôle. Étiqueter un utilisateur « Sécurité » n’ouvre pas les écrans de sécurité ; son niveau doit être Personnel (5) ou supérieur.
Cet article couvre trois sujets :
- Les rôles (
infr_role) — six intitulés de poste (DBA, DevOps, Sécurité, Administrateur, Personnel, Développeur). - Les équipes (
infr_team) — des unités organisationnelles, utilisées pour l’appartenance des utilisateurs et la responsabilité des actifs. - Le sélecteur de responsable — une liste déroulante combinée qui permet d’affecter soit un utilisateur, soit une équipe, avec
App\Support\Ownerscomme source de vérité unique. Le champ « responsable » des Instances, du Matériel et du Stockage l’utilise.
Contrôle d’accès = le niveau (
user_level) uniquement. Les rôles et les équipes n’ouvrent aucune permission de manière programmatique. Voir Comprendre les niveaux (RBAC) pour le système de niveaux.
Quand l’utiliser
- À l’arrivée d’une nouvelle personne, pour étiqueter son poste (rôle) et son appartenance (équipe).
- Lors d’une réorganisation — créer une équipe ou changer son responsable.
- Pour affecter un responsable (une personne ou une équipe) aux actifs serveur, matériel ou stockage, afin que « qui en répond » soit explicite.
- Audit / passation : rechercher « qui est responsable de cet actif ».
Disposition de l’écran
/admin/users (Administration ▸ Utilisateurs et rôles) comporte deux onglets.
| Onglet | Contenu |
|---|---|
| Utilisateurs | Liste des utilisateurs (identifiant · nom · e-mail · rôle · équipe · niveau · statut · dernière connexion) + CRUD en modale |
| Équipes | Liste des équipes (nom · couleur · responsable · description · nombre de membres) + CRUD en modale |
La modale d’édition d’un utilisateur comporte des sélecteurs niveau / rôle / équipe, suivis de cette indication :
Le niveau régit l’accès — Démo (lecture), Personnel (exploitation), Administrateur (tout). Le rôle est un simple libellé.
Comment l’utiliser
Les rôles — des intitulés de poste
Le référentiel des rôles compte six entrées, insérées ou mises à jour de façon idempotente par key grâce à InfrRoleSeeder.
| key | Libellé | Jeton de couleur | seq |
|---|---|---|---|
dba |
DBA | info | 10 |
devops |
DevOps | accent | 20 |
security |
Sécurité | crit | 30 |
admin |
Administrateur | warn | 40 |
staff |
Personnel | muted | 50 |
developer |
Développeur | good | 60 |
- Un utilisateur possède au plus un rôle (
users.role_id, nullable). Il peut ne pas être renseigné. - Les rôles n’apparaissent que sous forme de libellé et de couleur dans la liste déroulante utilisateur de la barre supérieure et dans les badges des listes.
- Le libellé affiché est résolu par
App\Support\RolePolicy::roleLabel($roleId)avec un cache de 60 secondes (appelezRolePolicy::flush()après une modification).
Le rôle
admin(libellé « Administrateur ») n’a rien à voir avec le niveau administrateur (10). Un rôle n’est qu’un libellé : un utilisateur dont le rôle est « Administrateur » mais dont le niveau est Démo (1) ne peut rien enregistrer. À l’inverse, un administrateur de niveau 10 dispose de toute l’administration même avec un rôle vide.
Les équipes — des unités organisationnelles
Les équipes se gèrent librement en CRUD dans l’onglet Équipes. Les quatre équipes de la semence :
| Nom | Couleur | Description |
|---|---|---|
| Équipe Infrastructure | accent | Exploitation serveurs / réseau / stockage |
| Équipe Sécurité | crit | Détection · réponse · blocage |
| Équipe Développement | good | Développement de services · déploiement |
| Équipe DBA | info | Exploitation de bases de données · optimisation |
Champs d’édition d’une équipe :
| Champ | Signification |
|---|---|
Nom (name) |
Obligatoire. Nom de l’équipe |
Couleur (color) |
Teinte du badge (accent/crit/good/info/muted …) |
Responsable (lead_user_id) |
Un utilisateur (facultatif) |
Description (description) |
Texte libre |
Actif (is_active) |
Les équipes inactives sont masquées dans le sélecteur de responsable |
- Un utilisateur appartient à au plus une équipe (
users.team_id, nullable). - À la suppression d’une équipe : le
team_iddes membres de cette équipe passe ànull(les utilisateurs subsistent, désormais sans affectation). Les actifs qui référençaient l’équipe viaowner_type='team'relèvent d’un autre sujet : les actifs eux-mêmes ne sont donc pas supprimés.
Le sélecteur de responsable — utilisateurs et équipes réunis
Les actifs (instances serveur, matériel, stockage) possèdent un champ « responsable » qui peut désigner soit une personne, soit une équipe. Les règles de ce sélecteur combiné se trouvent dans App\Support\Owners (la SOT).
Forme de stockage = deux colonnes
La table de l’actif enregistre le responsable dans deux colonnes.
| Colonne | Signification |
|---|---|
owner_id |
Clé primaire entière d’un utilisateur ou d’une équipe |
owner_type |
'user' ou 'team' |
Forme d’échange côté front = une chaîne ref unique
Le navigateur (formulaires, filtres) échange owner_type et owner_id sous la forme d’une seule chaîne.
user:5 → user id 5
team:3 → team id 3
Les options d’amorçage (Owners::options()) renvoient les deux groupes pour le rendu en optgroup (cache de 20 secondes). Les utilisateurs clôturés (is_close=1) et les équipes inactives (is_active=0) sont exclus.
{
"users": [ { "ref": "user:5", "id": 5, "name": "Jane Doe" } ],
"teams": [ { "ref": "team:3", "id": 3, "name": "Infra Team" } ]
}
Découpage / reconstruction côté serveur
| Assistant | Comportement |
|---|---|
Owners::split($ref) |
"user:5" → ['user', 5]. Format incorrect → ['user', 0] (= sans affectation) |
Owners::ref($ownerId, $ownerType) |
(5, 'user') → "user:5". ownerId vide → null |
Owners::resolveName($ownerId, $ownerType) |
Nom affiché sur les cartes et les listes (null s’il n’y en a pas, cache de 20 secondes) |
Owners::flush() |
Invalide le cache après une modification d’utilisateur ou d’équipe |
Par exemple, le contrôleur d’enregistrement des instances procède ainsi :
// ref du responsable ("user:5"/"team:3") → owner_id + owner_type
[$ownerType, $ownerId] = Owners::split($request->input('owner_ref'));
$payload['owner_id'] = $ownerId ?: null;
$payload['owner_type'] = $ownerId ? $ownerType : 'user'; // 'user' par défaut si non renseigné
La même ref sert au filtrage par responsable : la requête de liste découpe le paramètre owner (user:5/team:3) avec split() et restreint à WHERE owner_id = ? AND owner_type = ?.
Le sélecteur de responsable est réutilisé à l’identique sur les écrans d’actifs Instances, Matériel et Stockage et fichiers. Affecter une équipe comme responsable place l’équipe entière sur la ligne de responsabilité (ce qui résiste au départ de n’importe quelle personne).
Avatar · initiales · couleur
L’avatar affiché à côté d’un libellé est généré à partir du nom par RolePolicy.
- Initiales (
initials) : CJC/coréen → premier caractère ; noms latins → première lettre du premier et du dernier mot (2 caractères). - Teinte (
hue) :crc32(name) % 360pour un fond HSL stable. - Teinte de niveau (
tierTone) : administrateur (≥10) = crit · personnel/exploitation (≥2) = accent · démo = muted.
Sources de données
| Table | Rôle |
|---|---|
infr_role |
Référentiel des rôles (6 libellés informatifs) — key·name·color·seq·is_active |
infr_team |
Référentiel des équipes — name·color·lead_user_id·description·is_active·seq |
users |
Utilisateurs — user_level (décide de l’accès), role_id (nullable), team_id (nullable) |
| tables d’actifs | Responsable enregistré dans les deux colonnes owner_id + owner_type (user/team) (Instances/Matériel/Stockage, etc.) |
Assistants SOT :
App\Support\Owners— sélecteur de responsable combiné (options/split/ref/resolveName/flush).App\Support\RolePolicy— libellés et teintes de niveau, libellés de rôle (cache de 60 secondes), initiales et couleur de l’avatar.
Accès
- L’entrée de tout l’écran est réservée aux administrateurs.
/admin/*est protégé par le middleware de groupeinfr.level:10, etUserController/TeamControllerrevérifient eux-mêmes leuser_level >= 10de la session (RolePolicy::ADMIN). Démo (1) et Personnel (5) obtiennent un 403. - Seuils de niveau (référence) : écriture ≥ 2 (
RolePolicy::WRITE, bloque la démo) · administration ≥ 10 (ADMIN) · développement/observabilité ≥ 9 (DEV, inclut l’administrateur 10). - À l’enregistrement d’un utilisateur,
user_levelest restreint à l’une des valeurs1,5,10. - Chaque modification d’utilisateur ou d’équipe est auditée dans
infr_audit_log(team.create/team.update/team.delete, enregistrement/suppression d’utilisateur, etc.).
Garde-fous (protection du dernier administrateur)
| Situation | Résultat |
|---|---|
| Rétrograder / désactiver / supprimer le dernier administrateur actif | Refusé — « Le dernier administrateur actif ne peut pas être rétrogradé, désactivé ou supprimé. » |
| Abaisser ou désactiver son propre niveau d’administrateur | Refusé — « Vous ne pouvez pas rétrograder ni désactiver votre propre accès administrateur. » |
| Se supprimer soi-même | Refusé — « Vous ne pouvez pas vous supprimer vous-même. » |
| Identifiant déjà utilisé | Refusé — « Cet identifiant est déjà utilisé. » |
Dépannage et FAQ
Q. J’ai donné à quelqu’un le rôle « Sécurité » mais les écrans de sécurité ne s’ouvrent pas. Les rôles sont des libellés ; ils n’ouvrent aucune permission. Les écrans de sécurité (essentiellement en lecture) exigent un niveau — faites-le passer à Personnel (5) ou au-delà. Voir Niveaux RBAC.
Q. Un utilisateur ou une équipe manque dans la liste déroulante des responsables.
Les utilisateurs clôturés (is_close=1) et les équipes inactives (is_active=0) sont exclus du sélecteur. Réactivez l’utilisateur ou l’équipe ; si la liste est périmée, attendez l’expiration du cache de 20 secondes (Owners::flush() l’invalide immédiatement).
Q. Si je supprime une équipe, ses membres disparaissent-ils ?
Non. Les utilisateurs subsistent ; seul leur team_id devient null (sans affectation). La référence owner_type='team' d’un actif est indépendante : l’actif n’est donc pas supprimé non plus (son responsable affiché devient simplement vide).
Q. J’ai enregistré un responsable mais aucun nom ne s’affiche.
resolveName() dispose d’un cache de 20 secondes. Si vous venez de renommer quelque chose, cela se rafraîchit sous peu. Vérifiez également que le format de la ref est bien user:5/team:3 — un format cassé est traité par split() comme sans affectation (user:0).
Q. Un utilisateur peut-il avoir deux rôles ?
Non. role_id est une valeur unique (nullable). S’il vous faut aussi un regroupement organisationnel, utilisez une équipe (également une valeur unique).
Voir aussi
- Comprendre les niveaux (RBAC) — le seul axe qui décide réellement de l’accès
- Console utilisateurs et équipes — détails du CRUD de
/admin/users - Instances — l’écran d’actifs phare qui utilise le sélecteur de responsable
- Matériel — responsables des actifs physiques
- Stockage et fichiers — responsables des actifs de stockage
- Journal d’audit — historique des modifications d’utilisateurs, d’équipes et de responsables
