Séparation des entrepreneurs : deux niveaux au lieu d'un seul
Lorsque plusieurs parties partagent une même plateforme — personnel, sous-traitants, voire des entrepreneurs concurrents —, l'authentification ne suffit pas à elle seule à les isoler. Voici comment nous envisageons la mise en place d'une séparation qui soit réellement efficace.
La situation
De plus en plus d'organisations avec lesquelles nous travaillons utilisent des plateformes partagées où plusieurs parties interviennent sur les mêmes données de base relatives aux infrastructures. Une entreprise de services publics peut ainsi compter sur ses équipes internes, un maître d'œuvre principal et deux ou trois sous-traitants, tous connectés au même réseau. Une municipalité peut quant à elle faire appel à son propre personnel ainsi qu'à des bureaux d'études externes. Il est courant — et de plus en plus inévitable — que des parties en concurrence finissent par travailler au sein de la même application.
La question qui se pose naturellement est de savoir comment empêcher ces parties de voir le travail de l'autre. La première réponse qui vient à l'esprit est « leur attribuer à chacun un identifiant de connexion ». Cela permet l'authentification, qui est nécessaire mais pas suffisante. L'authentification indique au système qui vous êtes. Elle ne lui indique pas ce que vous êtes autorisé à voir. Sans un deuxième niveau de sécurité, toute personne qui se connecte a accès à tout.
Les approches qui ne tiennent pas tout à fait la route
Il existe quatre approches courantes, et chacune a sa place. Elles ont également chacune leurs limites, qu'il convient de reconnaître en toute honnêteté.
Déploiements par locataires distincts. Fournissez à chaque entrepreneur sa propre copie de l'application, sa propre base de données, son propre environnement. Il s'agit là d'une véritable isolation, et c'est parfois la solution idéale — généralement lorsque les parties ne partagent rien et ne collaboreront jamais. Cela devient rapidement coûteux, et cela va à l'encontre de la raison d'être de la plateforme si l'objectif premier était la collaboration, le reporting agrégé ou une source unique de vérité. Si la plupart des données sont partagées et que seule une partie est sensible, la séparation des locataires est excessive.
Masquage au niveau de l'interface utilisateur uniquement. C'est la solution la plus courante. Masquer le bouton, ignorer l'élément de menu, filtrer l'affichage de la liste. Il s'agit là d'une mesure de sécurité purement symbolique. Quiconque dispose d'outils de développement pour navigateur, d'une requête API directe ou d'une exportation bien ciblée peut récupérer les données « masquées ». L'enregistrement existe toujours, circule toujours sur le réseau et apparaît toujours dans les exportations en masse. Cela empêche les utilisateurs honnêtes de tomber par hasard sur ces informations ; cela n'arrête pas ceux qui cherchent réellement à les obtenir.
Filtrage au niveau de la couche application, point par point. Une véritable avancée : appliquer le filtre dans le code partout où des données sont interrogées. Cela fonctionne… jusqu’à ce que ça ne fonctionne plus. La sécurité est désormais répartie sur des dizaines d'emplacements dans le code où les données sont interrogées. Chaque nouvelle fonctionnalité est une fuite potentielle. Chaque refactorisation est une occasion d'en oublier un. Cette approche a tendance à être correcte au moment de sa mise en production, mais perd de son efficacité à mesure que la base de code s’étoffe.
Exportation vers des espaces de travail distincts. Copiez les données dont chaque sous-traitant a besoin dans un espace de travail qu'il est le seul à pouvoir consulter. Cette méthode garantit une véritable isolation, mais la copie devient obsolète dès sa création. Vous vous retrouvez alors face à un problème de synchronisation plutôt qu'à un problème de visibilité, et les collaborateurs sur le terrain finissent par consulter une version d'hier du travail d'aujourd'hui.
Aucune de ces solutions n'est mauvaise en toutes circonstances. Elles ne conviennent toutefois pas comme réponse universelle à la question de l'accès multipartite sur une plateforme partagée.
Le modèle à deux niveaux
Notre approche divise l'autorisation en deux couches indépendantes qui se complètent.
Les droits d'administrateur sont de nature permissive. Ils définissent ce qu'un utilisateur est autorisé à faire : créer un bon de travail, modifier un rapport, supprimer un article en stock. Sans droit d'administrateur, par défaut, vous pouvez consulter les données mais ne pouvez pas les modifier.
Les restrictions de rôle sont restrictives. Elles définissent ce qu'un utilisateur ne peut en aucun cas voir ou modifier. Chaque restriction spécifie un modèle (points, rapports, affectations), un champ de ce modèle (propriétaire, statut, catégorie), une condition et une valeur. Les enregistrements correspondant à ces critères sont masqués aux membres du rôle pour les opérations spécifiées — lecture, modification, création, suppression — de manière indépendante.
Ces niveaux n'interfèrent pas les uns avec les autres. Un agent de terrain peut disposer du droit de modifier les rapports, tandis qu'une restriction de rôle masque tous les rapports dont il n'est pas l'auteur — il peut donc modifier des rapports, mais uniquement les siens. Le droit d'administrateur accorde cette capacité ; la restriction de rôle en limite la portée. Aucun de ces niveaux n'a besoin de connaître l'existence de l'autre, et c'est principalement grâce à cette indépendance que le modèle résiste si bien à l'épreuve du temps.
Pourquoi l'application côté serveur au niveau des champs est-elle importante ?
Les restrictions liées aux rôles sont appliquées sur le serveur, avant que les données ne quittent la base de données. C'est cet aspect qui importe dans la pratique :
- Le filtre s'applique à tous les chemins d'accès : pages de détail, vues de liste, appels API, exportations en masse, tuiles de carte.
- Un utilisateur qui saisit dans l'URL un identifiant d'enregistrement qu'il connaît par hasard obtient une erreur 404 Not Found, car pour lui, cet enregistrement n'existe réellement pas.
- Une capture d'écran provenant de l'écran d'un autre utilisateur n'est d'aucune utilité ; les données ne s'afficheront pas lorsque l'utilisateur soumis à la restriction tentera d'y accéder.
- Les nouvelles fonctionnalités héritent automatiquement de cette restriction, car elles passent par la même couche de données du serveur.
Le masquage au niveau de l'interface utilisateur échoue à chacun de ces tests. Le filtrage au niveau de l'application ne les réussit que si le développeur pense à appliquer le filtre à chaque nouvelle requête. Les restrictions au niveau des champs appliquées côté serveur transforment la question « avons-nous pensé à le faire ? » en « les données correspondaient-elles à la règle ? » — ce qui est précisément la question à laquelle nous voulons que le système réponde.
Un exemple détaillé
Deux entrepreneurs travaillent sur le même projet de déploiement de fibre optique. Tous deux utilisent la même carte, exploitent les mêmes couches de base partagées et établissent des rapports sur leurs propres travaux.
Créez un rôle nommé Entrepreneur A avec une seule restriction :
- Modèle : Point
- Champ : propriétaire
- Opérateur : =
- Valeur du filtre : Entrepreneur B
- Autorisations bloquées : lecture, modification, création, suppression
Ajoutez les utilisateurs de l'entrepreneur A comme membres. Procédez de la même façon pour l'entrepreneur B. Voilà, la configuration est terminée.
Les équipes de l'entrepreneur A voient désormais leurs propres points, les couches partagées, mais rien de ce qui concerne l'entrepreneur B. Si elles ouvrent une liste de points, les enregistrements de l'entrepreneur B n'y figurent pas. Si elles exportent au format CSV, l'exportation est filtrée. Si elles devinent l'identifiant numérique d'un point de l'entrepreneur B et le collent dans l'URL, elles obtiennent une erreur 404 Not Found. L'entrepreneur B vit exactement la même situation. Aucune des deux parties ne sait quelle quantité de travail l'autre a effectuée, où elle se trouve ni quand elle a été mise à jour.
Les infrastructures de base communes — chemins de câbles, poteaux, réseaux de gaines — restent visibles pour les deux parties, car aucune restriction ne s'applique à celles-ci. La collaboration est préservée là où elle est souhaitée ; l'isolation est imposée là où elle est nécessaire. La plateforme n'a pas à choisir entre les deux.
Quand le recours à des locataires distincts reste la meilleure solution
Le modèle à deux niveaux n'est pas une solution universelle. Si deux parties ne partagent rien, ne collaborent jamais, ne produisent jamais de rapport conjoint et ont des raisons réglementaires de fonctionner sur des infrastructures physiquement distinctes, alors la séparation des locataires est le choix le plus judicieux. Ce que le modèle à deux niveaux remplace, c'est le cas bien plus courant où les parties partagent la majeure partie de la plateforme et ont besoin de masquer une partie spécifique. C'est dans ce cas que la séparation des locataires entraîne un gaspillage d'argent, perturbe le reporting et ralentit le travail, et que le masquage limité à l'interface utilisateur présente des failles.
Le critère que nous appliquons est simple : si les parties ont intérêt à consulter les mêmes couches de base, à générer les mêmes rapports et à travailler sur la même carte, elles ont leur place sur la même plateforme — à condition qu'une séparation effective soit garantie au niveau où elle est réellement applicable.
Résumé
- Le travail multipartite sur des plateformes partagées est de plus en plus courant dans les services publics, les télécommunications et les services municipaux, et implique de plus en plus souvent des acteurs qui se font concurrence.
- L'authentification à elle seule n'isole pas les utilisateurs ; elle se contente de les identifier. Toute personne qui se connecte continue de tout voir, à moins qu'un deuxième niveau de sécurité ne soit ajouté.
- Les déploiements de locataires distincts offrent une véritable isolation, mais vont à l'encontre de l'intérêt d'une plateforme partagée lorsque les parties doivent collaborer sur la plupart des données.
- Le masquage au niveau de l'interface utilisateur n'est qu'une façade de sécurité : les données sont toujours présentes et accessibles via les outils de développement, l'API ou des exportations.
- Le filtrage au niveau de la couche applicative est plus efficace, mais a tendance à perdre de sa pertinence à mesure que la base de code s'étoffe et que de nouvelles fonctionnalités sont ajoutées.
- Le modèle à deux couches sépare l'autorisation en droits d'administration permissifs (ce que vous pouvez faire) et en restrictions de rôle restrictives (ce que vous ne pouvez pas voir), appliquées au niveau du serveur champ par champ.
- L'application côté serveur au niveau des champs signifie que les données restreintes n'existent véritablement pas du point de vue de l'utilisateur restreint — ni dans l'interface utilisateur, ni dans l'API, ni dans les exportations, ni à une URL devinée.
- La configuration retenue consiste en un rôle unique par entrepreneur, ce qui permet d'obtenir un isolement là où il est nécessaire tout en préservant les couches partagées là où la collaboration est souhaitée.
- Les locataires distincts ont toujours leur place lorsque les parties ne partagent rien ; le modèle à deux couches est la meilleure solution pour le cas bien plus courant où les parties partagent la plupart des éléments et ont besoin d'en masquer une partie.