Meilleures pratiques RLS pour les sources de données et les classeurs
La sécurité au niveau des lignes (RLS, pour Row-Level Security) dans Tableau fait référence à la restriction des lignes de données qu’un utilisateur donné peut voir dans un classeur. Cela diffère des autorisations Tableau, qui contrôlent l’accès au contenu et aux fonctionnalités. Par exemple, les autorisations contrôlent si un utilisateur peut commenter ou modifier un classeur, tandis que la sécurité au niveau des lignes permet à deux utilisateurs affichant le même tableau de bord de ne voir que les données que chaque utilisateur est autorisé à voir.
Il existe plusieurs façons d’implémenter RLS dans Tableau. Par exemple, vous pouvez définir RLS au niveau de la source de données ou du classeur, ou vous pouvez définir RLS au niveau de la connexion à l’aide d’une connexion virtuelle avec une stratégie de données (nécessite Gestion des données). Consultez Présentation des options de sécurité au niveau des lignes dans Tableau pour plus de détails sur les solutions de rechange.
Remarque : cette rubrique se concentre sur les meilleures pratiques RLS pour les sources de données et les classeurs. Pour des exemples plus détaillés des concepts décrits dans cette rubrique, consultez le document technique Meilleures pratiques pour la sécurité au niveau des lignes avec les tables de droits(Le lien s’ouvre dans une nouvelle fenêtre) ou Comment configurer votre base de données pour la sécurité au niveau des lignes dans Tableau(Le lien s’ouvre dans une nouvelle fenêtre) sur le blogue Tableau and Behold.
Flux de travail RLS
Pour des connexions en direct et des extraits multi-tables, le flux de travail RLS (sécurité au niveau des lignes) de base est le suivant :
- L’utilisateur est identifié en se connectant à Tableau Server ou Tableau Cloud
- Cette opération nécessite un nom d’utilisateur distinct par utilisateur et une authentification unique sécurisée (SSO)
- Vous pouvez utiliser Active Directory, LDAP ou l’API REST de Tableau pour synchroniser les noms d’utilisateur et établir les autorisations
- L’ensemble des droits de l’utilisateur sur les données est extrait de tous les droits de données possibles
- Ceci nécessite une structure de données capable de lier les droits au nom d’utilisateur Tableau
- Les données sont filtrées en fonction des droits de cet utilisateur
- Ceci nécessite souvent l’utilisation de fonctions utilisateur dans un champ calculé
- Les données publiées et filtrées sont utilisées pour créer le contenu
- L’utilisation d’une source de données publiée (plutôt qu’intégrée) avec un filtre de source de données permet de s’assurer que la sécurité au niveau des lignes ne peut pas être modifiée en téléchargeant le classeur ou en effectuant une édition Web
La façon dont les jointures, les champs calculés et les filtres sont configurés dépend de la structure des données et du mode de gestion des utilisateurs.
Tables de droits
Toute combinaison unique d’attributs sur laquelle les données peuvent être filtrées constitue un droit. Le plus souvent, il existe des tables séparées pour spécifier les droits eux-mêmes et les associer avec les utilisateurs ou les rôles des utilisateurs. La dénormalisation est recommandée du point de vue des performances car les jointures sont des opérations coûteuses.
La vue des droits, constituée des droits mappés à des utilisateurs ou à des rôles, est liée aux données. Un filtre de source de données basé sur l’utilisateur est ensuite appliqué, agissant comme une clause WHERE qui n’apporte que les droits (et donc les lignes de données appropriées) pour l’utilisateur concerné. (Pour optimiser les requêtes, il faut veiller à ce que le filtrage ait lieu avant la jointure au moment du traitement de la requête afin de minimiser la duplication des données. Pour plus d’informations, consultez Performances et ordre de traitement des opérations pour plus d’informations.)
Modèles de tables de droits
En général, il existe deux modèles pour représenter les droits :
Mappage complet au niveau de granularité le plus profond
- Les droits sont définis intégralement pour chaque colonne.
- La table de mappage comporte une ligne pour chaque droit possible de l’utilisateur.
- Ce modèle nécessite moins de clauses de jointure.
Droits clairsemés
- Les droits sont définis pour chaque niveau de la hiérarchie, NULL étant utilisé pour représenter un état « tous ».
- La table de mappage comporte une ligne unique pour un niveau particulier de la hiérarchie des droits, ce qui réduit considérablement le nombre de lignes de droits pour les utilisateurs aux niveaux supérieurs dans une hiérarchie.
- Ce modèle nécessite des jointures et des filtres plus complexes.
Utilisateurs et rôles
Les combinaisons de droits sont généralement représentées sous forme de rôles, qui sont ensuite liés aux utilisateurs dans une table de mappage de plusieurs à plusieurs. Cela permet de modifier ou de supprimer facilement un utilisateur du rôle, tout en conservant un enregistrement du rôle et de ses droits.
Il est également possible de créer une table de mappage de plusieurs à plusieurs qui affecte les utilisateurs directement aux droits plutôt que de lier une table de rôles. Dans ce cas, les valeurs seront gérées plus directement dans la table, mais une jointure ne sera pas nécessaire.
Remarque : les valeurs utilisateur associées à un rôle ou à un droit doivent correspondre au nom d’utilisateur ou au nom complet sur le site Tableau afin de profiter des fonctions utilisateur de Tableau Desktop.
Jointures
Quel que soit le modèle utilisé pour représenter les droits, il est recommandé de lier ensemble tous les droits et toutes les tables de mappage en une seule vue dénormalisée des droits. Si, dans un premier temps, il en résultera une version « agrandie » (hautement redondante) des droits, le filtre de la source de données sur l’utilisateur réduira ce nombre. Cette vue vous sera en outre utile si vous prévoyez d’utiliser un extrait.
La méthode de granularité la plus profonde peut présenter un avantage en termes de performances lorsque tout est hiérarchique. Vous avez alors besoin d’effectuer une seule jointure au niveau le plus profond de la hiérarchie. Ceci ne fonctionne que si tous les attributs du niveau le plus bas sont distincts. S’il y a un risque de duplication (par exemple, une sous-région Centre dans plus d’une région), vous devrez appliquer la jointure sur toutes les colonnes pour obtenir l’effet d’une valeur clé distincte.
Les détails réels et leurs caractéristiques de performance dépendent du système de données et doivent être testés. Par exemple, l’utilisation d’une seule clé pourrait potentiellement améliorer les performances parce que la jointure ne s’exécute alors que sur une seule colonne. Par contre, l’indexation correcte de toutes les colonnes peut offrir des performances égales lorsque d’autres facteurs sont pris en compte.
Mise en œuvre de la sécurité au niveau des lignes
Granularité la plus profonde
Une fois que la vue dénormalisée des droits mappés est créée, une jointure interne est configurée entre la vue et les données dans la boîte de dialogue de connexion de données Tableau. Les données peuvent rester dans un schéma en étoile traditionnel. Alternativement, les tables de dimensions et de faits peuvent être matérialisées ensemble en deux vues. Les extraits multi-tables créeront des tables d’extraits pour correspondre aux jointures, si bien que la création des deux vues simplifiera l’extrait résultant. Les instructions SQL suivent ce modèle de base :
SELECT * FROM data d INNER JOIN entitlements e ON d.attribute_a = e.attribute_a AND d.attribute_b = e.attribute_b AND ... WHERE e.username = USERNAME()
Droits clairsemés
Si vos droits ressemblent plus étroitement au modèle de droits clairsemés, les instructions SQL personnalisé pour lier les données aux droits sont un peu plus complexes en raison des valeurs nulles. Théoriquement, elles se présenteraient ainsi :
SELECT * FROM data d INNER JOIN entitlements e ON (e.region_id = d.region_id OR ISNULL(e.region_id) AND (e.sub_region_id = d.sub_region_id OR ISNULL(e.sub_region_id) AND (e.country_id = d.country_id OR ISNULL(e.country_id)
Si vous n’utilisez pas SQL personnalisé, vous pouvez utiliser une jointure croisée et des filtres supplémentaires dans Tableau Desktop. Créez un calcul de jointure des deux côtés de la boîte de dialogue de jointure qui consiste simplement en l’entier 1 et mettez-les égaux. Cette opération lie chaque ligne de la table de données à chaque ligne de la table des droits.
Ensuite, vous avez besoin d’un calcul (ou de calculs individuels) pour prendre en compte les niveaux dans la hiérarchie. Par exemple, vous pourriez avoir plusieurs calculs qui suivent ce format : [region_id] = [region_id (Entitlements View)] OR ISNULL([region_id (Entitlements View)]
Ou vous pourriez avoir un calcul combiné pour tous les niveaux en un :
([region_id] = [region_id (Entitlements View)] OR ISNULL([region_id (Entitlements View)]) AND ([sub_region_id] = [sub_region_id (Entitlements View)] OR ISNULL([sub_region_id (Entitlements View)]) AND ([country_id] = [country_id (Entitlements View)] OR ISNULL([country_id (Entitlements View)])
La fonction ISNULL associe n’importe quelle colonne de droits à tous les postes de l’autre colonne. Comme toujours avec la sécurité au niveau des lignes, ces calculs doivent être ajoutés comme filtres de source de données.
Filtre de source de données
Dans les deux approches, une fois que les droits sont correctement liés aux données, un filtre doit être mis en place pour limiter les données pour un utilisateur spécifique. Il vous faut créer un champ calculé avec une fonction utilisateur. Par exemple, une simple comparaison booléenne pour savoir si l’utilisateur inscrit dans le champ Nom d’utilisateur est le même que le nom d’utilisateur de la personne connectée au site Tableau : [Username] = USERNAME()
Ce calcul doit être utilisé comme un filtre de source de données (en sélectionnant TRUE).
Si la source de données est intégrée et qu’un utilisateur dispose du droit d’édition Web ou de téléchargement de classeurs, alors la sécurité au niveau des lignes est inexistante puisque les filtres qui l’appliquent peuvent être facilement supprimés. La source de données Tableau doit être publiée séparément plutôt que d’être intégrée au classeur.
Accès complet avec la granularité la plus profonde
Dans un scénario courant, l’entreprise peut comporter deux niveaux d’accès : les personnes qui peuvent tout voir (« tout accès ») ou les personnes ayant un sous-ensemble raisonnablement définissable de droits. C’est le cas le plus souvent pour les applications embarquées : l’entreprise qui héberge les données peut tout voir, mais chaque client ne peut voir que ses propres données. Dans ce cas, vous avez besoin d’un moyen de retourner les données complètes pour les utilisateurs « tout accès », tout en conservant les jointures de granularité la plus profonde pour tous les autres utilisateurs.
Pour cette technique, vous utiliserez les groupes Tableau de manière à créer une dérogation en utilisant un calcul dans la condition de jointure.
- Créez un groupe pour les utilisateurs qui devraient voir toutes les données (ici appelé All Access)
- Depuis la vue des faits, créez une jointure gauche avec deux conditions de jointure
- La première condition de jointure doit se trouver sur la colonne qui représente le niveau de granularité le plus profond
- La deuxième condition de jointure doit être deux calculs :
- Sur le côté gauche (la vue des faits), pour le calcul, saisissez
True
- Sur le côté droit (la vue des droits), le calcul doit être :
IF ISMEMBEROF('All Access') THEN False ELSE True END
- Sur le côté gauche (la vue des faits), pour le calcul, saisissez
- Sur une feuille, créez un calcul structuré de ce type :
[Username] = USERNAME() OR ISMEMBEROF(['All Access'] ([Entitlements View)])
- Créer un filtre de source de données sur le calcul du nom d’utilisateur
Si un utilisateur appartient au groupe Tout accès, la jointure devient une jointure gauche sur True = False
. Cela signifie qu’il n’y a aucune correspondance dans la vue des droits. Toute la vue des faits est donc retournée avec des valeurs NULL pour les colonnes de la vue des droits (aucune duplication). Dans le cas où l’utilisateur n’appartient pas au groupe Tout accès, la condition de jointure True = True
ne change rien et la jointure fonctionne comme prévu.
Le calcul utilisateur utilisé comme filtre de source de données est vrai pour toutes les lignes lorsque la dérogation de groupe fonctionne, ou il sera filtré uniquement jusqu’à la granularité la plus profonde de l’utilisateur dans la hiérarchie.
Performances et ordre de traitement des opérations
Lorsqu’une visualisation est consultée dans Tableau Desktop, Tableau Server ou Tableau Cloud, Tableau envoie une requête optimisée au RDBMS qui traite la requête et renvoie les résultats à Tableau pour créer la visualisation avec les données résultantes. L’ordre des opérations d’exécution des jointures, calculs et filtres dépend de l’optimiseur de requêtes et de la façon dont la requête est exécutée.
Connexions en direct
Lors de l’utilisation d’une connexion en direct à une source de données dans Tableau, les performances d’exécution de la requête dépendent de l’optimiseur de requêtes qui traduit les instructions SQL entrantes en un plan efficace de récupération des données.
La requête peut être traitée de deux façons :
- Filtrer les lignes de droit sur l’utilisateur, puis lier à la table de faits
- Lier les droits à la table de faits puis filtrer sur les lignes de l’utilisateur
Dans une situation idéale, l’optimiseur de requêtes veille à ce que la base de données traite la requête par filtrage puis par jointure. Si un utilisateur a droit à tout, cela signifie que le nombre maximum de lignes traitées sera le nombre de lignes de la table de données.
Si la base de données traite la requête par jointure puis par filtrage, il peut y avoir duplication de données. Le nombre maximum de lignes traitées sera le nombre d’utilisateurs autorisés à voir cette ligne particulière multipliée par chaque ligne dans la table de données.
Si le second scénario se produit : l’exécution des requêtes demande beaucoup de temps, des erreurs se produisent, ou des problèmes de performances sont signalés dans la base de données. Le volume total de vos données augmente de façon exponentielle, ce qui pourrait entraîner une surcharge du système au niveau du backend.
Extraits
Lorsque la source de données dans Tableau est une connexion en direct, Tableau envoie toutes les requêtes nécessaires au rendu d’une visualisation ou d’un tableau de bord spécifique à RDBMS. Lorsque la source de données est un extrait, le processus d’interrogation des données de la source de données sous-jacentes ne se produit qu’au moment de la création et de l’actualisation de l’extrait. Le moteur d’extraction utilise le fichier d’extrait pour répondre à toutes les requêtes individuelles de visualisations.
Le même problème d’ordre des opérations est présent lors de la création d’extraits de table individuels. Cependant, l’augmentation exponentielle des donnée se produira à la fois sur la source de données sous-jacentes et à l’intérieur de l’extrait résultant lui-même.
Points à prendre en compte pour les extraits
À partir de la version 2018.3 de Tableau, le moteur de données peut créer un extrait multi-tables et la sécurité au niveau des lignes peut être implémentée comme décrit ci-dessus. L’utilisation d’extraits multi-tables réduit le temps nécessaire à la création d’un extrait avec des relations plusieurs à plusieurs grâce à la non-matérialisation de la jointure.
L’extrait doit être construit avec un objet de données et un objet de droits. Il s’agit du stockage le plus simple dans l’extrait et il permet d’obtenir les meilleures performances.
- L’objet de données est la table, la vue ou la requête SQL personnalisée qui représente la combinaison dénormalisée des tables de faits et de dimensions nécessaires
- L’objet droits est une table dénormalisée, une vue ou une requête SQL personnalisée de tous les droits nécessaires pour filtrer les données au niveau le plus granulaire, ce qui requiert :
- Une colonne pour le nom d’utilisateur correspondant exactement aux noms d’utilisateur dans Tableau Server ou Tableau Cloud
- Une ligne pour chacun des droits les plus granulaires sur l’objet de données
Ce format est présenté selon la méthode de granularité la plus profonde décrite ci-dessus. Les extraits multi-tables utilisent la même méthode, à condition que seulement deux objets de données soient liés et que tout filtrage spécifique au champ soit déjà appliqué dans l’objet.
Étant donné que les filtres d’extrait sont désactivés pour les extraits multi-tables, vous pouvez les filtrer dans les vues ou les tables auxquelles vous vous connectez dans la source de données, ou définir les filtres dans les objets SQL personnalisés dans la boîte de dialogue Connexion de données Tableau.
Remarque : Comme pour les connexions en direct, si la source de données est intégrée et qu’un utilisateur dispose du droit d’édition Web ou de téléchargement des classeurs, la sécurité au niveau des lignes est inexistante puisque les filtres qui l’appliquent peuvent être facilement supprimés. Vous devez publier l’extrait séparément plutôt que de le laisser intégré au classeur.
Extraits de table unique
La méthode suivante n’est recommandée que pour les versions de Tableau antérieures à 2018.3. Il est préférable d’utiliser des extraits multi-tables si possible.
Les extraits de table unique matérialisent toutes les jointures que vous créez lors de la construction de la source de données Tableau et stockent le tout sous la forme d’une seule table à travers une seule requête, dont les résultats sont transformés en une table unique dans le fichier d’extrait. Cette dénormalisation risque d’entraîner une duplication massive des données, car chaque ligne attribuée à plus d’un droit ou à plus d’un utilisateur serait dupliquée en raison de la relation plusieurs à plusieurs.
Pour éviter cette duplication :
- Créez un champ Utilisateurs de sécurité qui contient les noms d’utilisateur pour ce droit
- par exemple, une valeur peut être « bhowell|mosterheld|rdugger »
- Utilisez la fonction CONTAINS() dans Tableau pour identifier correctement les utilisateurs individuels
- Par exemple,
CONTAINS([Security Users Field], USERNAME())
- Par exemple,
Cette méthode comporte évidemment quelques limitations. Elle exige que vous passiez de vos droits en lignes à une seule colonne séparée correctement à l’aide de SQL, et cette colonne ne peut contenir qu’un nombre limité de caractères. Les correspondances partielles peuvent poser problème et vous devez utiliser des séparateurs qui ne seront jamais valides dans les ID eux-mêmes. Bien qu’il soit performant dans le moteur de données Tableau, en tant que calcul de chaîne de caractères, il sera très lent pour la plupart des bases de données, ce qui limite votre capacité à revenir à une connexion en direct.
Alternativement, vous pouvez prendre différents extraits par « rôle » ou niveau de droit, de sorte que l’extrait ne contienne que les données adaptées à cette personne ou à ce niveau. Ceci nécessitera toutefois des processus pour autoriser et exploiter de manière appropriée la publication de modèles dans Tableau Server, généralement via les API.
Utiliser la sécurité intégrée au niveau des lignes dans une base de données
De nombreuses bases de données intègrent des mécanismes pour RLS. Si votre entreprise a déjà déployé des efforts pour créer une sécurité au niveau des lignes dans une base de données, vous pourrez peut-être tirer parti de votre RLS existant. Il n’est pas nécessairement plus facile ou préférable de mettre en œuvre un modèle RLS intégré plutôt que de le construire spécifiquement pour Tableau. Ces techniques sont généralement mises à profit lorsqu’une entreprise a déjà investi dans ces technologies et qu’elle veut tirer parti de l’investissement. Le principal avantage de l’utilisation de RLS intégré est que les administrateurs peuvent mettre en place et contrôler leur stratégie de sécurité des données en un seul emplacement : leurs bases de données. Pour plus d’informations, consultez Sécurité au niveau des lignes dans la base de données.