RLS avec table de correspondance (plusieurs-à-plusieurs)
La page RLS (Row Level Security) associe chaque Channel à un seul utilisateur. En entreprise, c'est rarement le cas :
- un utilisateur doit voir plusieurs canaux, régions ou agences ;
- un même canal est visible par plusieurs utilisateurs ;
- les droits changent souvent et doivent être gérés par les métiers, sans modifier le modèle.
On stocke alors les droits dans une table de correspondance (ou table de droits), avec une ligne par couple utilisateur / périmètre autorisé. Entre les utilisateurs et les canaux, la relation devient plusieurs-à-plusieurs : il faut l'utiliser de façon contrôlée, c'est-à-dire limiter le filtrage bidirectionnel à la seule table de droits.
0- L'exemple
On reprend le modèle de la page RLS (FactSales, DimChannel) et on ajoute une table Droits :
| Login | ChannelKey |
| grady@contoso.com | Online |
| grady@contoso.com | Store |
| alice@contoso.com | Store |
| alice@contoso.com | Reseller |
- Grady doit voir les ventes Online et Store, Alice les ventes Store et Reseller.
- La table peut venir d'un fichier Excel ou d'une liste SharePoint tenus par les métiers, d'une table SQL, ou d'un Dataflow synchronisé avec Microsoft Entra ID.
- La colonne Login doit contenir exactement la valeur renvoyée par
USERPRINCIPALNAME()(l'UPN), qui n'est pas toujours l'adresse e-mail. - Source
1- Modéliser la relation (table pont)
Schéma cible : Droits (Login, ChannelKey) → DimChannel (ChannelKey unique) → FactSales
- Importer la table Droits.
- Relier Droits[ChannelKey] à DimChannel[ChannelKey] : cardinalité Plusieurs-à-un (*:1), Droits côté « plusieurs ».
- Conserver la relation DimChannel → FactSales en un-à-plusieurs, direction unique.
- Double-cliquer sur la relation Droits / DimChannel et régler Direction du filtre croisé sur Les deux.
- Cocher Appliquer le filtre de sécurité dans les deux directions sur cette même relation.
- Masquer la table Droits en vue rapport.
Pourquoi l'étape 5 est indispensable :
- par défaut, les filtres de sécurité RLS ne se propagent que dans un seul sens, même si la relation est bidirectionnelle ;
- sans cette case, le filtre posé sur Droits ne remonte pas jusqu'à DimChannel, et donc pas jusqu'à FactSales ;
- cette option ne peut être cochée que sur une seule relation bidirectionnelle par table ;
- Source
Pourquoi « contrôlée » :
- le double sens est limité à la relation de la table de droits, petite et masquée ;
- toutes les autres relations du modèle restent en un-à-plusieurs, direction unique ;
- c'est la démarche recommandée par Microsoft : une table pont, des relations un-à-plusieurs, et une seule relation bidirectionnelle pour laisser passer le filtre ;
- Source
2- Gérer les rôles
- Modélisation > Gérer les rôles > Nouveau, nommer le rôle (par exemple Canal filtré).
- Sélectionner la table Droits et saisir le filtre DAX :
[Login] = USERPRINCIPALNAME()Enfin, enregistrer, puis publier et affecter les utilisateurs au rôle dans le service, comme dans la page RLS.
Le filtre conserve les lignes de l'utilisateur connecté dans Droits, se propage à DimChannel grâce au filtre de sécurité bidirectionnel, puis à FactSales.
3- Variante sans relation bidirectionnelle (filtre DAX)
On peut aussi laisser la table Droits sans relation et écrire le filtre directement sur DimChannel :
DimChannel[ChannelKey]
IN CALCULATETABLE (
VALUES ( Droits[ChannelKey] ),
Droits[Login] = USERPRINCIPALNAME ()
)- Avantage : aucun filtrage bidirectionnel dans le modèle, comportement plus lisible.
- Inconvénient : la règle est évaluée pour chaque ligne de DimChannel ; à réserver aux dimensions de taille raisonnable.
4- Variante « accès total » (direction, administrateurs)
Pour éviter de saisir tous les canaux pour un directeur :
- Solution simple : créer un second rôle sans règle, qui donne accès à toutes les lignes, et y affecter le groupe de sécurité de la direction.
- Solution pilotée par les données : ajouter une table Utilisateurs (une ligne par Login) avec une colonne AccesTotal (VRAI / FAUX), et écrire sur DimChannel :
VAR _Upn = USERPRINCIPALNAME ()
VAR _AccesTotal =
LOOKUPVALUE ( Utilisateurs[AccesTotal], Utilisateurs[Login], _Upn )
RETURN
_AccesTotal = TRUE ()
|| DimChannel[ChannelKey]
IN CALCULATETABLE ( VALUES ( Droits[ChannelKey] ), Droits[Login] = _Upn )- Mapper les rôles sur des groupes de sécurité plutôt que sur des personnes : moins d'affectations, gestion par le propriétaire du groupe.
- Source
5- Tester
- Dans Power BI Desktop : Modélisation > Afficher en tant que, cocher le rôle et Autre utilisateur, puis saisir grady@contoso.com : seules les ventes Online et Store doivent apparaître.
- Ajouter temporairement une carte avec la mesure
Utilisateur = USERPRINCIPALNAME()pour vérifier la valeur réellement renvoyée. - Dans le service, Tester en tant que rôle utilise votre propre identité pour
USERPRINCIPALNAME(): pour valider un invité externe, il faut se connecter avec son compte. - Source
6- Points de vigilance
- Performances : le filtre de sécurité bidirectionnel peut ralentir les requêtes sur les gros modèles ; garder la table de droits compacte et mesurer avec l'Analyseur de performances.
- Utilisateur absent de la table Droits : il ne voit aucune donnée. C'est le comportement voulu, mais il faut le prévoir dans la procédure d'arrivée des nouveaux utilisateurs.
- Totaux non additifs : dans un visuel par utilisateur, les ventes d'un canal partagé comptent pour chacun ; le total général n'est donc pas la somme des lignes. C'est normal en plusieurs-à-plusieurs.
- Rôles dans l'espace de travail : le RLS ne s'applique qu'aux Lecteurs (Visionneuse) ; Administrateurs, Membres et Contributeurs voient toutes les données.
- Source
Pour aller plus loin :