Forum Discussion
Row Level Security in Power BI Report Server
davidm5 Can you explain step by step process. How can we use SYSTEM_USER in SQL Server?
All right, here's my best attempt to walk through it. For this example I’ll be using the AdventureWorksDW2012 database.
For my hypothetical case, I’ve got regional sales directors that should only be able to see information for employees that are in their assigned sales territories. I’m going to implement that by putting a security policy on the DimEmployee table. In summary the steps are:
- Create and populate a new table that defines which sales territories a particular user (as defined by their Windows login ID) is allowed to access.
- Create a new function in SQL Server that checks the relationship between user and allowed sales territories.
- Create a new security policy on the DimEmployee table using the function created in step 2.
Once those steps are done I can create a report in Power BI that accesses the DimEmployee table and take advantage of the implemented RLS.
In more detail:
- Create a new table that defines which sales territories a user may access. I called my table SalesTerritoryRLS. It’s a simple table with two columns that looks like this:
NetID | SalesTerritoryKey |
Davidm5 | 1 |
Davidm5 | 4 |
SomeOtherUser | 2 |
The NetID column is simply the Windows user ID of the user, and SalesTerritoryKey correlates to the column SalesTerritoryKey in the DimEmployee table. So as currently populated this table says that I (davidm5) am allowed to see data for sales territories 1 and 4; the user ‘SomeOtherUser’ is allowed to see data for sales territory 2.
- Create a new function in SQL Server. The SQL to create the function looks like this:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[fn_SalesTerritory_SecurityPredicate] (@salesTerritory AS int)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
SELECT 1 AS fn_SecurityPredicateResult
FROM dbo.SalesTerritoryRLS rls
WHERE rls.SalesTerritoryKey = @salesTerritory
AND rls.NetID = SYSTEM_USER
GO
We’ll apply this function as a filter in the next step. Basically this function checks to see if the sales territory on a row it’s evaluating is one of the sales territories the currently logged in user is allowed to access. That’s where SYSTEM_USER comes in – see the final line of the WHERE clause. SYSTEM_USER is the ID of the currently logged in user.
- Create a security policy using the function. The SQL for this is:
CREATE SECURITY POLICY [dbo].[dimEmployeeFilter]
ADD FILTER PREDICATE [dbo].[fn_SalesTerritory_SecurityPredicate]([SalesTerritoryKey]) ON [dbo].[DimEmployee]
WITH (STATE = ON, SCHEMABINDING = ON)
GO
This ties the function created above to the DimEmployee table. When you do any SQL operation on the table, it passes in the SalesTerritoryKey for each row and the function is used to determine if it’s a row the user is allowed to access, based on the values in the RLS table created in step 1.
Now you’re free to access DimEmployee in your Power BI report and RLS will be enforced. Note that for this to work you MUST use ‘Windows Authentication’/’As the user viewing the report’ on the Data Source Settings for the report.
Notice that creating the function and applying it as a security policy are two different steps. That means you could apply that same function to multiple tables to implement our RLS on sales territory across multiple tables if you’d like.
Also be aware that this means RLS is applied at the data layer; for any tool the user might use to access the DimEmployee table, RLS will be applied. Even if I log into SSMS directly to query the DimEmployee table, I’ll only be able to see rows where the sales territory is 1 or 4. And the way I wrote this particular function means that by default users get NO access to DimEmployee data; until a row is entered for them in the SalesTerritoryRLS table, they won’t be able to see any DimEmployee data.
- Anonymous7 years agoNot applicable
davidm5, this is the method we've got running on our database layer, RLS is enabled for the currently connected user. The only issue we're having is passing the logged in user's credentials to the database via the report in the Report Server (on-prem). Do you have a similar set up or any insight this double-hop authentication?
- davidm57 years agoHelper I
I'm about out of my depth here, but I'll try to help.
When you set up the report on Report Server, when you go to Manage > Data Sources and look at the Credentials section, you MUST use 'Windows Authentication' as the Authentication Type, and you MUST use the 'As the user viewing the report' option.
Then in SQL Server, for whatever database you're using that same user ID must have permissions on the database. I think just db_datareader is all you need but I'm not certain about that.
The only trick we do is to use an active directory group. This is where I'm sketchy, but if I understand it correctly we actually give the AD group the SQL permissions, then just add the Windows user ID to that AD group. It isn't necessary to do it this way, it just makes our user management a little easier. We already have mechanisms in place to handle adding and removing users from the AD group as employment and roles change - by using the AD group we don't have to seperately manage SQL permissions per user ID.
I hope that helps. If not, if you can provide details I can try and provide more info.
- Anonymous7 years agoNot applicable
Hmm. Thanks Davidm5, but we've already got that setup. We have a group saved into the DB Server and have datareader permissions set for the required DB.
The following link in a previous thread describes our setup: DirectQuery-Login-failed-for-user-NT-AUTHORITY-ANONYMOUS-LOGON.
I'll clarify the problem by saying that the DirectQuery and RLS stuff works - but only when viewing the report via Chrome. Using IE throws either a Kerberos constrained delegation error, or 'connection string not properly formed' error. So even though we're looking at the same report, different browsers behave differently.
Internet options have been set on IE and the site has been added as 'Intranet' level security.
:smileyfrustrated: