La sicurezza dei dati in Microsoft Fabric non si esprime solo con i permessi a livello di workspace o di elemento: quando devi controllare quali righe e quali colonne un utente può leggere all’interno di una tabella, entrano in gioco la Row-Level Security (RLS) e la Column-Level Security (CLS) implementate a livello di motore SQL. Sono meccanismi granulari, valutati a ogni query, che si applicano al Warehouse e al SQL analytics endpoint del Lakehouse.
Row-Level Security con SECURITY POLICY
La RLS filtra le righe restituite in base all’identità di chi esegue la query, senza che l’applicazione debba aggiungere clausole WHERE. Si costruisce in due passi:
- una predicate function (una funzione inline con valori di tabella,
SCHEMABINDING) che restituisce una riga quando l’accesso è consentito; - una SECURITY POLICY che aggancia quella funzione a una o più tabelle come
FILTER PREDICATE.
Il cuore è il predicato, tipicamente basato su USER_NAME() (che in Fabric restituisce l’UPN dell’utente Entra ID) o su una tabella di mappatura utente→area:
CREATE FUNCTION Security.fn_regione(@regione AS sysname)
RETURNS TABLE WITH SCHEMABINDING
AS RETURN
SELECT 1 AS ok
WHERE @regione = USER_NAME()
OR IS_ROLEMEMBER('DataAdmin') = 1;
CREATE SECURITY POLICY Security.RlsVendite
ADD FILTER PREDICATE Security.fn_regione(Regione) ON dbo.Vendite
WITH (STATE = ON);
Nota il fallback su IS_ROLEMEMBER: senza una condizione di bypass, anche un amministratore vedrebbe zero righe. Il predicato può appoggiarsi anche al contesto di sessione (SESSION_CONTEXT()) quando un middleware imposta l’identità applicativa, ma in Fabric lo scenario tipico d’esame è l’identità Entra ID passthroughi via USER_NAME().
Column-Level Security con GRANT/DENY
La CLS non usa policy: si gestisce con i permessi standard sulle singole colonne. Per nascondere una colonna sensibile (es. Stipendio, CodiceFiscale) si nega esplicitamente il SELECT su quella colonna:
DENY SELECT ON dbo.Dipendenti(Stipendio) TO Analisti;
Punti architetturali da ricordare:
DENYprevale sempre suGRANT: se concediSELECTsull’intera tabella ma neghi una colonna, l’utente vede tutto tranne quella.- Una
SELECT *fallisce se include una colonna negata; l’utente deve enumerare le colonne consentite. È il comportamento voluto, non un bug. - La CLS agisce prima della RLS non è vero il contrario: concettualmente CLS decide quali colonne, RLS decide quali righe; si combinano.
Warehouse vs SQL analytics endpoint del Lakehouse
Entrambi condividono lo stesso motore SQL, quindi la sintassi di RLS e CLS è identica. La differenza è la scrivibilità: sul Warehouse hai pieno controllo T-SQL (DDL, CREATE SECURITY POLICY, GRANT/DENY). Sul SQL analytics endpoint del Lakehouse i dati Delta sono di sola lettura, ma puoi comunque definire policy di sicurezza e permessi sull’endpoint SQL, perché la sicurezza vive nel layer SQL, non nei file Delta sottostanti. In entrambi i casi la sicurezza segue l’oggetto solo dentro l’esperienza SQL.
La distinzione chiave: RLS SQL vs RLS del semantic model
Qui si concentra la trappola. La RLS che definisci con SECURITY POLICY sul Warehouse/endpoint è un meccanismo del motore SQL. La RLS del semantic model di Power BI è tutt’altro: si definisce con ruoli e regole DAX nel modello, e agisce quando si interroga il modello (report, Q&A). Sono layer diversi:
- se un report usa Import mode, i dati sono copiati nel modello e la RLS SQL non ha più effetto: conta solo la RLS del semantic model;
- in DirectQuery con single sign-on, le query arrivano all’endpoint SQL con l’identità dell’utente, quindi la RLS SQL viene applicata a valle;
- non sono cumulabili “gratis”: vanno progettate consapevolmente per evitare doppie regole contraddittorie o falle di sicurezza.
Trappole tipiche d’esame
- Scenario: un utente in DirectQuery vede tutte le righe malgrado la RLS definita sul Warehouse → verifica che il predicato non abbia un bypass
IS_ROLEMEMBERtroppo largo e che l’endpoint riceva l’identità reale (SSO), non un account di servizio condiviso. - Scenario: devi nascondere una singola colonna sensibile mantenendo l’accesso al resto della tabella → CLS con
DENY SELECTsulla colonna, non RLS. - Scenario: la sicurezza a righe deve valere sia nei report Import sia nelle query SQL dirette → non basta la RLS SQL: in Import serve anche la RLS del semantic model (ruoli + DAX), perché sono layer distinti.
- Scenario: un amministratore riceve zero righe dopo aver attivato la policy → manca la condizione di bypass nel predicate function; il filtro si applica a tutti finché non lo escludi esplicitamente.
- Scenario:
SELECT *va in errore per un gruppo → una colonna è coperta da CLS; l’applicazione deve elencare solo le colonne consentite.