Sviluppare e testare le regole RLS in Power BI

Develop and test RLS rules in Power BI.

Molto spesso, non tutti gli utenti dovrebbero avere il permesso di accedere a tutti i dati in un report. Qui spiegherò come sviluppare regole di RLS in Power BI per configurare l’accesso e come testarle.

Foto di FLY:D su Unsplash

Introduzione

Molti dei miei clienti vogliono limitare l’accesso ai dati nei loro report in base a specifiche regole.

L’accesso ai dati è chiamato Row Level Security (abbreviato in RLS).

Puoi trovare molti articoli su RLS in Power BI su Nisoo.

Ho aggiunto due di essi nella sezione Riferimenti qui sotto.

Anche se tutti gli articoli fanno un buon lavoro nel spiegare le basi, mi manca sempre una spiegazione su come sviluppare regole più complesse e come testarle facilmente.

In questo articolo, spiegherò le basi di RLS e aggiungerò complessità passo dopo passo.

Inoltre, ti mostrerò come utilizzare DAX Studio per costruire query per testare le regole RLS prima di aggiungerle al modello dati.

Quindi, eccoci qui.

Scenario

Utilizzo lo scenario in cui gli utenti ottengono l’accesso ai dati delle vendite al dettaglio in base ai negozi o alle posizioni geografiche dei negozi all’interno dell’azienda, compresa una combinazione di entrambi.

Nel modello dati Contoso, utilizzo le seguenti tabelle:

Figura 1 - Tabelle coinvolte nel mio scenario (Figura dell'autore)

Creo il seguente report per testare i miei risultati:

Figura 2 - Report di partenza (Figura dell'autore)

Crea una regola semplice

Per creare una regola di RLS, è necessario aprire l’editor del ruolo di sicurezza:

Figura 3 - Apri l'editor del ruolo di sicurezza (Figura dell'autore)

Successivamente, è possibile creare un nuovo ruolo e impostare il nome per questo ruolo:

Figura 4 - Crea un ruolo e rinominalo (Figura dell'autore)

Nel mio caso, ho messo il nome “StorePermissions”.

Ora posso iniziare ad aggiungere un’espressione per controllare l’accesso alla tabella Store:

Figura 5 - Aggiungi un'espressione DAX al nuovo ruolo (Figura dell'autore)

Abbiamo un nuovo editor più semplice per le regole RLS da alcuni mesi.

Nel mio caso, voglio aggiungere un’espressione DAX. Quindi, faccio clic sul pulsante “Passa all’editor DAX”.

All’inizio, aggiungo l’espressione più semplice possibile: TRUE ()

Figura 6 - Regola RLS più semplice possibile (Figura dell'Autore)

Per capire le regole RLS, devi sapere che l’accesso è controllato dall’output dell’espressione nell’editor della regola RLS.

L’utente otterrà l’accesso se l’output dell’espressione non è vuoto o FALSE().

In principio, qualsiasi espressione nell’editor della regola RLS viene aggiunta come filtro a qualsiasi query. Vediamo l’effetto di questa prima espressione prima di spiegare questo in modo più dettagliato.

Per testare la regola, salvo l’espressione e chiudo l’editor.

Ora posso visualizzare il report con la nuova regola:

Figura 7 - Test della regola RLS (Figura dell'Autore)

In cima alla pagina del report, vedrai un banner giallo che indica che stai guardando il report utilizzando la regola StorePermission.

Dal momento che la regola StorePermission non limita l’accesso, non vedrai alcuna differenza.

Proviamo qualcosa di diverso.

Ora cambio l’espressione nella regola RLS in FALSE().

Quando testo la regola, non vedrò alcun dato:

Figura 8 - Test della regola con FALSE() (Figura dell'Autore)

Ciò dimostra che i dati sono accessibili se l’espressione non restituisce FALSE().

Query di test

Per comprendere questo effetto in dettaglio, lascia che mostri una query DAX per ottenere il risultato senza alcuna restrizione:

EVALUATE  SUMMARIZECOLUMNS(          Store[Store]          ,"Retail_Sales", 'All Measures'[Retail Sales]          )ORDER BY Store[Store]

Quando aggiungo una regola RLS con TRUE(), come mostrato sopra, la query cambia in una query simile a questa:

EVALUATE  FILTER(      SUMMARIZECOLUMNS(            Store[Store]            ,"Retail_Sales", 'All Measures'[Retail Sales]            )      ,TRUE()      )ORDER BY Store[Store]

Ho racchiuso la query all’interno di una funzione FILTER() e ho aggiunto TRUE() come espressione di filtro.

Negli esempi seguenti, utilizzerò CALCULATETABLE(), poiché la scrittura del codice è più efficiente e flessibile.

Ne parlerò un po’ più avanti.

Rendiamolo più complesso

Successivamente, voglio limitare l’accesso a tutti i negozi contenenti la stringa “Contoso T”.

Per questo, cambio l’espressione nell’editor della regola in quanto segue:

CONTAINSSTRING('Store'[Store], "Contoso T")

Quando testo la regola, ottengo il seguente risultato:

Testare la regola con una query DAX

Sarebbe bello testare l’esito di tale regola con una query DAX.

In questo caso, utilizzo la seguente query in DAX Studio per verificare il risultato:

EVALUATE  CALCULATETABLE(    SUMMARIZECOLUMNS(          Store[Store]          ,"Retail_Sales", 'All Measures'[Retail Sales]          )    CONTAINSSTRING('Store'[Store], "Contoso T") = TRUE()    )ORDER BY Store[Store]

La parte interna, con SUMMARIZECOLUMNS(), genera la tabella di output.

In questo caso, sono interessato solo alla lista dei negozi.

Quindi, racchiudo la chiamata SUMMARIZECOLUMNS() con CALCULATETABLE() per aggiungere un filtro alla query.

In questo caso, aggiungo l’espressione dalla regola RLS, includendo una verifica “= TRUE()”.

Il risultato è il seguente:

Figura 10 - Risultato della query di controllo (Figura dell'autore)

Ma cosa succede sotto il cofano?

Guardiamo la query del motore di archiviazione:

Figura 11 - Risultato della query di controllo (Figura dell'autore)

E cosa succede quando applico la regola RLS a questa query?

Posso applicare una regola RLS da DAX Studio con pochi clic:

Figura 12 - Attiva una regola RLS (Figura dell'autore)

La query del motore di archiviazione è la seguente:

Figura 13 - Analisi della query con la regola RLS

La prima query (Linea 2) recupera l’elenco di tutti i negozi.

La seconda query include la regola RLS nella clausola WHERE.

Invece di avere l’elenco dei negozi corrispondenti (in base al filtro), vediamo una riga criptica, che include la regola RLS.

Puoi vedere che il risultato della query del motore di archiviazione (SE) contiene ancora 309 righe, come sopra, che è il numero di tutti i negozi + 3 righe. Un suggerimento del motivo per cui abbiamo la discrepanza di 3 righe si trova nel testo sotto la query SE: Dimensioni stimata: righe = 309

Il numero effettivo di righe restituite potrebbe essere effettivamente 306.

Ma questa analisi mostra che le regole RLS sono applicate dopo il motore di archiviazione, poiché il risultato della query contiene solo 21 righe: tutti i negozi che iniziano con “Contoso T”.

Questo è importante, poiché il motore di formule (FE), che calcolerà il risultato finale dopo il motore di archiviazione, è single-threaded e può utilizzare solo un core della CPU.

Mentre il SE è multi-threaded e può utilizzare più core della CPU.

Di conseguenza, dobbiamo astenerci dal scrivere codice inefficiente nella regola RLS.

Regole combinate

Successivamente, voglio combinare due espressioni:

  1. Solo negozi che iniziano con “Contoso T”
  2. Solo negozi in Europa

Per ottenere questo, aggiungo una seconda espressione alla tabella Geografia usando l’editor semplice:

Figura 14 - Aggiungi espressione alla tabella Geografia (Figura dell'autore)

Quando passo all’editor DAX, ottengo la seguente espressione:

Figura 15 - Espressione DAX dall'editor semplice (Figura dell'autore)

Si noti l’uso dell’operatore di uguaglianza stretto.

Cambiare l’operatore di uguaglianza semplice per la tua espressione può essere necessario.

Questo è il risultato durante il test della regola:

Figura 16 - Risultato della regola combinata (Figura dell'Autore)

La query DAX per questa regola sarà così:

Figura 17 - Traduzione in una query DAX e risultati (Figura dell'Autore)

Ora, aggiungiamo un altro livello di complessità alla regola RLS:

Voglio limitare l’accesso ai negozi che:

  • Il nome dei negozi inizia con “Contoso T” e si trovano in Europa o
  • Il nome dei negozi inizia con “Contoso S” e si trovano in Nord America

Questa volta, inizio con la query DAX. Questo è il modo più semplice per sviluppare e testare l’espressione.

Prendo la prima query e la racchiudo con l’espressione di filtro.

Poiché devo filtrare due tabelle (Store e Geography), devo usare FILTER() e RELATED() :

EVALUATE  CALCULATETABLE(    ADDCOLUMNS(      SUMMARIZECOLUMNS(Store[Store], 'Geography'[Continent])            ,"Retail_Sales", 'All Measures'[Retail Sales]            )    ,FILTER(Store        ,OR(CONTAINSSTRING('Store'[Store], "Contoso T") && RELATED(Geography[Continent]) = "Europe"          ,CONTAINSSTRING('Store'[Store], "Contoso S") && RELATED(Geography[Continent]) = "North America")        )    )ORDER BY [Retail Sales] DESC, 'Geography'[Continent], Store[Store]

Ho bisogno della funzione RELATED() poiché uso FILTER() per iterare attraverso la tabella Store e ho bisogno della colonna Continent dalla tabella Geography.

Poiché la tabella Geography è dalla parte uno della relazione, posso usare RELATED() per ottenere la colonna Continent.

Questo è il risultato:

Figura 18 - Query per la regola combinata (Figura dell'Autore)

Successivamente, dobbiamo tradurre questo filtro in una regola RLS.

Per la regola RLS, possiamo rimuovere la funzione FILTER(), poiché la regola RLS funziona intrinsecamente come un filtro.

Figura 19 - Traduzione in una sola regola RLS (Figura dell'Autore)

Si noti che ho rimosso l’espressione dalla tabella “Geography”.

Quando testo questa regola in Power BI, ottengo il seguente risultato, che corrisponde al risultato della query DAX:

Figura 20 - Test della regola RLS combinata (Figura dell'Autore)

Per testare la regola RLS, ad esempio, quando si desidera ottenere solo l’elenco di negozi filtrati, è possibile scrivere una semplice query con solo la funzione FILTER():

Figura 21 - Esecuzione solo della funzione FILTER() (Figura dell'Autore)

Configurare l’accesso in base al login dell’utente

Fino ad ora, abbiamo esaminato le regole RLS statiche.

Ma la maggior parte delle volte abbiamo bisogno di regole basate sul login dell’utente.

Per ottenere questo, abbiamo bisogno di una tabella che mappi l’utente alle righe a cui l’utente ha accesso.

Ad esempio, una tabella come questa:

Figura 22 - Elenco utenti con geografie assegnate (Figura dell'autore)

Dopo aver aggiunto la tabella al modello dati, dobbiamo aggiungere una relazione tra la nuova tabella e la tabella “Geografia”:

Figura 23 - Modello dati espanso (Figura dell'autore)

La relazione tra la nuova tabella “Accesso alla geografia” e la tabella “Geografia” deve essere configurata correttamente.

Dopo aver aggiunto la relazione, Power BI la configura come una relazione 1:n, con la tabella “Geografia” sul lato uno e il filtro che fluisce dalla tabella “Geografia” a “Accesso alla geografia”.

Ma vogliamo filtrare la tabella “Geografia” in base a una regola RLS (un filtro) su “Accesso alla geografia”. Per questo motivo, dobbiamo cambiare la direzione di cross-filter in entrambi:

Figura 24 - Impostazioni della relazione (Figura dell'autore)

Inoltre, dobbiamo impostare il flag su “Applica filtro di sicurezza in entrambe le direzioni”, poiché Power BI ignora l’impostazione di direzione di cross-filter quando si applicano le regole RLS.

Ora possiamo aggiungere la regola RLS:

Figura 25 - Configurare la regola RLS (Figura dell'autore)

Ricorda di rimuovere qualsiasi espressione di filtro sulla tabella Store prima di aggiungere questa regola.

Quando testo la regola RLS, ottengo questo:

Figura 26 - Risultato vuoto (Figura dell'autore)

Per scoprire cosa succede, torniamo all’editor della regola RLS e cambiamo la vista per la regola in DAX:

Figura 27 - Regola RLS sbagliata (Figura dell'autore)

L’editor semplice della regola RLS non riconosce le funzioni DAX e le aggiunge come testo per il filtro.

Dobbiamo cambiare l’espressione in questo modo:

Figura 28 - Regola DAX corretta (Figura dell'autore)

Ora il risultato è come previsto:

Figura 29 - Test della regola RLS con il mio utente e l'espressione RLS corretta (Figura dell'autore)

La Card nell’angolo in alto a sinistra della pagina del report contiene una Misura con la funzione USERPRINCIPALNAME() per garantire che l’utente corretto sia attivo durante il test.

Posso persino testare una regola RLS utilizzando un altro utente:

Figura 30 — Test della regola RLS con un altro utente (Figura dell'autore)

È divertente che questo utente non debba esistere. Deve solo essere contenuto nella lista “Accesso geografico”.

Ecco il risultato del test:

Figura 31 — Risultato del test con l'utente di test (Figura dell'autore)

Nella riga gialla in alto, è possibile vedere l’utente attivo durante il test.

Conclusione

Ho mostrato come creare regole RLS elementari e come testarle.

Poi ho aggiunto maggiore complessità e ho analizzato gli effetti delle regole RLS sul motore di archiviazione sottostante.

Abbiamo visto che il motore di formule elabora parte della regola RLS. Pertanto, dobbiamo scrivere codice efficiente nelle regole RLS.

Sapere come testare le regole RLS prima di implementarle nel modello dei dati è molto importante.

È molto più facile capire i risultati errati capendo come la regola viene applicata al modello dati.

Infine, ho aggiunto regole RLS dinamiche basate sull’utente al modello.

Queste regole sono più difficili da testare in una query DAX, poiché è necessario conoscere i dati a cui ogni utente può accedere per scrivere la query di test corretta per convalidare il risultato.

Spero di averti dato alcuni suggerimenti per semplificarti la vita con la funzione RLS in Power BI.

Foto di Andrew George su Unsplash

Riferimenti

Puoi trovare un elenco delle funzioni di sicurezza in Power BI in questo articolo:

4 + 2 funzioni di sicurezza in Power BI

Un anno dopo il mio primo articolo su questo argomento, ecco un aggiornamento sulle nuove funzioni di sicurezza in Power BI

towardsdatascience.com

Puoi trovare una spiegazione semplice su Row Level Security in Power BI sulla pagina della Community di Power BI (ora Fabric): Row-level security (RLS) with Power BI — Power BI | Microsoft Learn.

Posso consigliare questo articolo di Nikola Ilic, dove puoi ottenere un punto di partenza su RLS:

La guida definitiva alla sicurezza a livello di riga e oggetto in Power BI

“Chi vede cosa nel rapporto?” è una delle domande chiave sulla sicurezza in Power BI. Impara due possibili modi per implementare…

towardsdatascience.com

Un altro buon articolo introduttivo su Row-Level-Security in Power BI di Elias Nordlinder:

Come implementare Row Level Security in Power BI (Parte I)

La Row-Level Security è un modo per filtrare i dati in modo diverso a seconda dei diversi ruoli. Questo potrebbe essere fatto staticamente…

elias-nordlinder.medium.com

Visita la mia lista di storie per maggiori informazioni sulla funzione FILTER() e su come analizzare la query DAX con DAX Studio.

Utilizzo il set di dati di esempio Contoso, come nei miei articoli precedenti. Puoi scaricare gratuitamente il dataset ContosoRetailDW da Microsoft qui.

I dati di Contoso possono essere utilizzati liberamente sotto la licenza MIT, come descritto qui.

Come membro di Nisoo, una parte della tua quota di iscrizione va agli scrittori che leggi e ottieni l’accesso completo a ogni storia…

Nisoo.com