Revenir au contenu principal
Produit

Au-delà de l'intégration : comment sécuriser les tableaux de bord IA/BI pour chaque lecteur

Un seul tableau de bord publié, sécurité au niveau des lignes par lecteur. Une unique table d'autorisations régit l'accès, permettant aux partenaires externes et aux équipes internes de partager en toute sécurité le même tableau de bord intégré.

par Sonakshi Pandey

  • Un seul tableau de bord s'adresse à tous les lecteurs. Une unique table d'autorisations et la valeur __aibi_external_value du jeton d'intégration signé déterminent les lignes auxquelles chaque lecteur peut accéder, sans avoir à créer un tableau de bord pour chaque client ni à répéter les filtres dans les requêtes.
  • L'accès est accordé via des groupes de fournisseurs d'identité, et non via des listes d'utilisateurs gérées manuellement. L'application résout les groupes d'un lecteur interne avec un jeton de délégation avant de générer le jeton d'intégration.
  • Refus par défaut et défense en profondeur. Ce modèle masque les colonnes sensibles, refuse les jetons pour les lecteurs non autorisés et utilise les filtres de ligne de Unity Catalog pour protéger l'accès SQL direct.

Le défi

Intégrer un tableau de bord Databricks AI/BI dans une application destinée aux clients est relativement simple : activez l'intégration, générez un jeton d'accès restreint dans le backend et affichez le tableau de bord avec le SDK client. Le guide de base, Comment intégrer les tableaux de bord Databricks AI/BI dans des applications destinées aux clients, détaille ce processus de bout en bout.

La question la plus difficile est celle de l'autorisation : une fois qu'un tableau de bord est intégré, quelles lignes chaque utilisateur doit-il voir ? Un partenaire ne devrait voir que ses propres données, tandis qu'une équipe interne ne devrait voir que sa région. Ce guide explique comment appliquer ces règles.

Ce modèle de référence combine plusieurs fonctionnalités Databricks : __aibi_external_value, les filtres de lignes et les masques de colonnes de Unity Catalog, ainsi que des groupes synchronisés à partir d'un fournisseur d'identité (IdP). Il s'agit d'un modèle de conception, et non d'une fonctionnalité unique à activer.

Un seul ensemble de règles, deux voies d'application

image1.png

La même table d'autorisations régit deux voies : les tableaux de bord intégrés accessibles via l'application, et les requêtes SQL directes exécutées par les utilisateurs de Databricks.

Un scénario concret

Prenons l'exemple d'une entreprise qui utilise un tableau de bord partagé « Tâches de comptes clients (AR) ouvertes » pour les données de trois régions : West, East et Central. Le tableau de bord s'adresse à deux publics.

  • Les partenaires opérationnels externes, tels que Acme Ops, Bolt Partners et Core Logistics, n'ont pas d'identifiants de connexion Databricks et accèdent au tableau de bord via un portail en marque blanche. Chaque partenaire ne doit voir que sa propre région, avec les adresses e-mail de contact masquées.
  • Les équipes internes sont les employés de l'entreprise, qui se connectent à Databricks. L'équipe Finance a besoin d'accéder à toutes les régions, tandis qu'une équipe opérationnelle régionale ne voit que la sienne. Leur accès provient de groupes de fournisseurs d'identité tels qu'Okta ou Entra ID, et non de listes d'utilisateurs gérées manuellement.

Cinq utilisateurs partagent un même ensemble de données, chacun voyant une partie différente : Acme Ops, Bolt Partners, Core Logistics, Finance et une équipe opérationnelle régionale. Les exemples ci-dessous se concentrent sur Acme et Finance ; les identifiants partner_acme, finance_all et West représentent ces exemples. Acme voit la région West avec les e-mails masqués, tandis que Finance voit les trois régions en totalité, le tout à partir du même tableau de bord publié.

Une table, une vue, un tableau de bord

Les règles d'accès résident dans un seul endroit plutôt que d'être dispersées dans des tableaux de bord ou des requêtes. Créer un tableau de bord par client génère des copies qui peuvent se désynchroniser, tandis que répéter les filtres dans chaque requête augmente le risque d'erreurs.

Le modèle se compose de trois objets :

  • La table de base `open_ar_tasks` contient une ligne par tâche AR, associée à une région et à un e-mail de contact.
task_idmarketoperating_partneramount_opencontact_email
T-1001WestAcme Ops$12,400jane@acme.com
T-1002EastBolt Partners$8,900raj@bolt.com
T-1003CentralCore Logistics$15,200mia@core.com
  • La table des autorisations (entitlements) est la source unique de vérité pour l'accès. Chaque ligne identifie la région à laquelle une portée (scope) peut accéder et si les valeurs sensibles doivent être masquées. La colonne viewer_scope stocke à la fois les identifiants des partenaires externes, comme partner_acme, et les noms des groupes internes, comme finance_all.
viewer_scopemarketmask_pii
partner_acmeWesttrue
finance_allWestfalse
finance_allEastfalse
finance_allCentralfalse
ops_westWestfalse
  • La vue sécurisée (secured view) joint la table de base aux autorisations, de sorte qu'un utilisateur ne voit que les régions auxquelles il a droit, avec les e-mails masqués lorsque l'indicateur (flag) est activé.

Dans la plupart des déploiements, un système d'autorisation en amont ou un mappage groupe-région propre à l'application alimente cette table ; elle n'est pas modifiée manuellement pour chaque utilisateur.

Cela évite d'avoir des tableaux de bord par client et des filtres répétés dans les requêtes. Les règles résident dans une table qui peut être interrogée, auditée et modifiée sans changer le tableau de bord.

Appliquée par utilisateur, la vue sécurisée ne renvoie que ce à quoi cet utilisateur a droit :

Acme (external_value = partner_acme) : West uniquement, e-mail de contact masqué.

task_idmarketoperating_partneramount_opencontact_email
T-1001WestAcme Ops$12,400****@acme.com

Finance (external_value = finance_all) : les trois régions au complet, e-mail de contact visible en entier.

task_idmarketoperating_partneramount_opencontact_email
T-1001WestAcme Ops$12,400jane@acme.com
T-1002EastBolt Partners$8,900raj@bolt.com
T-1003CentralCore Logistics$15,200mia@core.com

D'où vient __aibi_external_value

Le backend définit cette valeur lorsqu'il génère le jeton d'intégration (embed token). Il s'authentifie en tant que principal de service (service principal) et demande un jeton limité à Databricks avec deux valeurs : external_viewer_id, qui identifie l'utilisateur à des fins d'audit, et external_value, qui représente la portée (scope) de l'utilisateur. Databricks signe le jeton, et l'utilisateur ne peut pas modifier la valeur intégrée, qui est exposée au SQL du tableau de bord sous la forme __aibi_external_value. Comme il détient les identifiants du principal de service, ce backend est un composant côté serveur de confiance, jamais le navigateur, et ces identifiants sont conservés dans un gestionnaire de secrets plutôt que dans le contrôle de code source.

Le détail clé est de savoir sous quelle identité la requête est exécutée. Les requêtes intégrées s'exécutent sous l'identité de publication configurée, et non sous l'identité Databricks de l'utilisateur.

Pour l'intégration externe, Databricks recommande d'utiliser des autorisations de données individuelles et d'accorder au principal de service son propre accès aux données, afin que les requêtes s'exécutent sous l'identité du principal de service. (À l'inverse, la publication avec des autorisations de données partagées exécute les requêtes avec les identifiants de l'éditeur.) La vue restreint ensuite cet accès pour chaque utilisateur via __aibi_external_value. Comme la requête s'exécute en tant que principal de service, is_account_group_member() ne peut pas identifier la personne réelle qui consulte le tableau de bord sur le chemin d'intégration.

Le détail important est que external_value ne se limite pas à un identifiant de partenaire. Il peut s'agir de n'importe quelle portée (scope) que le backend inscrit dans le jeton, par exemple partner_acme pour un partenaire externe ou finance_all pour un groupe interne (le nom de leur groupe).

Comme la table des autorisations contient les identifiants des partenaires et les noms des groupes dans la même colonne, un seul tableau de bord, une seule vue et un seul filtre couvrent les deux cas.

Comme l'utilisateur ne voit ni ne définit jamais la valeur signée, il ne peut pas la modifier. Une portée inconnue ne correspond à aucune ligne, ce qui offre un comportement de refus par défaut (default-deny).

La même vue et le même filtre (WHERE viewer_scope = __aibi_external_value) s'adressent aux deux publics. Seule la source de cette portée diffère :

AttributPartenaire externeÉquipe interne
Connexion DatabricksNon ; accès via le portailOui ; connexion via l'IdP
Ce qui définit la portéeIdentifiant de partenaire fixeGroupe IdP autorisé
Valeur signée en tant que __aibi_external_valuepartner_acmefinance_all
Autorisation correspondanteUne régionUne ou plusieurs régions autorisées

Accorder l'accès à des groupes, pas à des personnes

Les organisations gèrent généralement l'accès via des groupes synchronisés à partir d'un fournisseur d'identité. Lorsque quelqu'un rejoint le groupe Finance dans Okta, son accès est automatiquement associé à finance_all, sans qu'aucune table de données ne soit modifiée. Dans cet exemple, finance_all est associé à toutes les régions et ops_west est associé à la région West.

Comment l'application sait-elle à quels groupes appartient le lecteur ? Elle ne peut pas s'appuyer sur SQL lors de l'intégration, car la requête s'exécute en tant que principal de service et is_account_group_member() vérifierait la mauvaise identité. L'application doit résoudre les groupes du lecteur dans le backend, qui peut voir le lecteur, avant de générer le jeton.

Une option consiste à exécuter l'application sur Databricks Apps avec l'autorisation de l'utilisateur activée.

Pour un utilisateur interne connecté, la plateforme transmet le contexte d'identité de confiance au backend, y compris l'e-mail du lecteur et un jeton on-behalf-of (OBO). Le backend utilise ce jeton pour appeler SCIM /Me en tant que lecteur et lire ses groupes.

Cette approche ne nécessite pas de droits d'administrateur sur le principal de service, car l'utilisateur lit son propre enregistrement. Elle nécessite des étendues d'autorisation d'utilisateur, et Apps OBO est encore en cours de développement, il convient donc de la valider par rapport au déploiement cible avant de s'y fier.

Si aucun groupe autorisé n'est trouvé, appliquez un verrouillage de sécurité (fail closed) et refusez de générer un jeton plutôt que de vous rabattre sur une identité plus large comme l'e-mail brut.

Si un lecteur appartient à plusieurs groupes autorisés, résolvez le résultat de manière déterministe. Définissez un ordre de priorité, ou associez plusieurs groupes à une seule étendue (scope) canonique avant de générer le jeton, afin que le même lecteur reçoive toujours un accès cohérent.

Les partenaires externes sont plus simples. Sans identité Databricks, leur étendue est un identifiant de partenaire fixe attribué lors de la connexion. Même jeton, même filtre, pas de recherche.

Une limitation à prendre en compte lors de la conception : un jeton signé ne contient qu'une seule valeur external_value. Si un lecteur appartient à plusieurs groupes avec des autorisations différentes, un jeton ne peut toujours représenter qu'une seule étendue. Pour le cas courant d'un rôle unique par personne, cela convient parfaitement.

Pour une véritable union de plusieurs groupes, utilisez un groupe d'accès total ou le chemin SQL direct ci-dessous, où un filtre de ligne peut appliquer un opérateur OR sur chaque groupe. Une étendue composite (comme JSON) peut être intégrée dans external_value, mais l'analyse et la correspondance se déplacent alors dans le SQL du jeu de données et restent limitées par la taille maximale de charge utile de 1 KB.

Renforcer les garanties

Le filtrage de lignes fournit le comportement de base : chaque lecteur ne voit que ses propres lignes. Trois couches supplémentaires renforcent les contrôles, et toutes trois lisent à partir de la même table d'autorisations.

Masquer les colonnes sensibles par lecteur

La sécurité au niveau des lignes détermine les lignes auxquelles un lecteur peut accéder. Le masquage détermine les colonnes qu'ils peuvent voir, car les partenaires externes n'ont généralement pas besoin du même niveau de détail que les équipes internes.

L'indicateur mask_pii, défini sur true pour les partenaires et false pour les groupes internes, pilote la logique de masquage dans la vue sécurisée (l'expression CASE dans le SQL ci-dessus). Le même tableau de bord peut afficher l'e-mail complet pour un lecteur interne tout en affichant une valeur masquée telle que ****@example.com pour un partenaire. Lorsque les règles de masquage s'étendent sur de nombreuses tables et deviennent complexes, les masques de colonne de Unity Catalog et le contrôle d'accès basé sur les attributs (ABAC) constituent la meilleure solution à long terme ; ici, la vue permet de garder l'exemple autonome.

Refus par défaut, et preuve à l'appui

Une étendue inconnue ne devrait renvoyer aucune ligne de tableau de bord et ne devrait rien révéler de la structure des données sous-jacentes : pas d'erreur suggérant la structure, pas de données partielles, juste un résultat vide. S'arrêter plus tôt est encore plus propre : avant que le backend ne génère un jeton, il vérifie la table des autorisations et refuse toute étendue ayant droit à zéro ligne. Tout aussi important, l'étendue est dérivée du lecteur authentifié, jamais d'un paramètre fourni par le client, de sorte qu'un lecteur ne peut pas demander l'étendue d'un autre locataire.

Le contrôle lors de l'émission du jeton empêche un lecteur refusé de recevoir un jeton, ce qui est préférable à une simple dépendance au filtre SQL comme unique protection. L'enregistrement de l'émission réussie des jetons et des demandes refusées rend les décisions d'autorisation auditables : le lecteur refusé n'apparaît tout simplement jamais, et même si une demande passait à travers, une étendue inconnue ne renverrait toujours aucune ligne de tableau de bord. L'enregistrement de l'external_viewer_id et de son étendue dans ces journaux permet de retracer chaque décision d'autorisation jusqu'à un client ou un utilisateur réel lors d'un audit.

Protéger le chemin SQL direct

Les contrôles d'intégration protègent le chemin de l'application. Un utilisateur Databricks qui interroge directement la table de base représente une menace distincte. Ajoutez un filtre de ligne Unity Catalog à la table de base, lié à l'identité et aux groupes de l'utilisateur qui effectue la requête. Dans ce chemin de requête directe, is_account_group_member() évalue l'utilisateur réel et peut combiner tous ses groupes autorisés. L'exception de l'éditeur dans la fonction ci-dessous (current_user() égal à l'identité de publication) est une autorisation d'urgence délibérée (break-glass) pour l'identité qui publie ou actualise le tableau de bord, et non un contournement général pour l'opérateur. Elle est facultative et présente un risque élevé, ne l'incluez donc que si elle est justifiée et approuvez-la par déploiement.

Un masque de colonne pour les champs sensibles fonctionne de la même manière. Ces contrôles Unity Catalog sont distincts du chemin d'intégration : les lecteurs intégrés sont limités via __aibi_external_value et la vue des autorisations, tandis que les requêtes directes de l'espace de travail sont protégées par le filtre de ligne Unity Catalog, qui évalue l'identité et les groupes de l'appelant. Les deux points d'application utilisent la même table d'autorisations.

Avant de commencer la construction

À propos du "multi-tenancy". Il s'agit d'un multi-tenancy logique et mutualisé : les partenaires externes sont isolés les uns des autres, tandis que les employés internes bénéficient d'un accès basé sur les groupes (basé sur les rôles) au sein du propre locataire de l'entreprise. Toutes les données restent dans des tables partagées ; le jeton, le filtre de vue et la table des autorisations imposent la séparation. Le filtre de ligne SQL direct étend la même garantie en dehors de l'application.

Quand utiliser ce modèle. Utilisez ce modèle lorsque des partenaires externes sans compte Databricks et des employés internes doivent partager un même tableau de bord. Si chaque lecteur est un utilisateur Databricks interne, une intégration de base avec la sécurité des lignes et des colonnes de Unity Catalog peut suffire.

Contraintes pratiques et pièges à éviter. Vérifiez les limites de produit et les détails opérationnels suivants par rapport à la documentation actuelle avant la publication :

  • Les jetons ont une courte durée de vie (1 heure), l'application doit donc les renouveler, en particulier pour un onglet laissé ouvert. Le SDK client facilite cette tâche : un rappel getNewToken effectue une nouvelle récupération à partir du point de terminaison /api/token à l'approche de l'expiration du jeton.
  • Conservez la combinaison de `external_viewer_id` + `external_value` sous 1 KB : utilisez des identifiants compacts, pas des blobs JSON ou de longs e-mails.
  • Limite de débit de 20 chargements de tableau de bord par seconde par espace de travail pour l'intégration externe. Utile à savoir pour un grand portail B2B.
  • Utilisez un `external_viewer_id` sans PII. Il se retrouve dans les journaux d'audit, donc un identifiant client ou utilisateur stable est préférable à un nom complet ou à un e-mail brut.
  • Les téléchargements sont activés par défaut. Les lecteurs intégrés peuvent exporter des fichiers CSV, TSV, Excel et PNG, à moins qu'un administrateur d'espace de travail ne désactive les téléchargements. Vérifiez que les résultats exportés correspondent aux données restreintes attendues pour le lecteur.
  • Configurez des groupes au niveau du compte pour le chemin SQL direct. Les filtres de ligne et les masques UC évaluent is_account_group_member() par rapport aux groupes au niveau du compte, et non aux groupes locaux de l'espace de travail. Le chemin d'intégration n'appelle jamais cette fonction.
  • Surveillez les grandes tables d'autorisations. Pour des milliers de portées ou des hiérarchies profondes, gardez les expressions de jointure et de masque simples et appuyez-vous sur l'ABAC lorsque les règles deviennent complexes.

Points clés à retenir

  • Conservez les règles d'accès dans une seule table d'autorisations et utilisez-la à la fois pour les tableaux de bord intégrés et la protection SQL direct.
  • Signez une portée de lecteur ou de groupe dans __aibi_external_value ; ne vous fiez pas aux filtres modifiables par l'utilisateur.
  • Utilisez le refus par défaut, le masquage et les filtres de ligne Unity Catalog comme contrôles superposés.
  • L'intégration n'est que la première étape ; la conception et la validation des contrôles d'accès constituent le travail crucial.

Passez à l'action

Commencez par le guide fondamental, Comment intégrer les tableaux de bord Databricks AI/BI dans des applications destinées aux clients, puis appliquez les modèles d'autorisation, de masquage et de refus par défaut décrits dans cet article. Pour les contrôles de gouvernance sous-jacents, consultez la documentation sur l'intégration d'AI/BI ainsi que les filtres de ligne et les masques de colonne Unity Catalog et les conseils sur l'ABAC.

(Cet article de blog a été traduit à l'aide d'outils basés sur l'intelligence artificielle) Article original

Recevez les derniers articles dans votre boîte mail

Abonnez-vous à notre blog et recevez les derniers articles directement dans votre boîte mail.