There are a number of options available to implement row level security in Tableau.
Row level security is to restrict access to only certain records within a Tableau data source.
For example, perhaps a data source contains global data. Only salespeople covering Europe can see European sales numbers, salespeople covering APAC can only see APAC, etc.
Row level security makes this possible; one data source would work for all users and each user would only see their records.
Implementing Row Level security in Tableau
Often the clearest way to show how to use row level security is to include some examples. Assuming we have the following data:
There are 3 main ways to implement row level security in Tableau.
1. Connect Live
Join to the permissions data at the database layer when connecting live. This only returns those rows that are relevant to the user.
- Is the most flexible and easiest to implement
- The underlying data set needs to be highly optimised to allow connecting live
- Performance may be an issue
2. Create a string of usernames for each row of data and use the CONTAINS function to look up
- Works well within a Tableau data extract (tde / hyper file)
- Can combine with Tableau groups to also allow role based permissions
- The string has to be built and alongside each record of the underlying data
- Instinctively feels non-performant, string comparisons generally aren’t fast (although in reality the performance isn’t as bad as it feels it should be)
- There’s a limit to the size of the string. For example a SQL stored procedure has a limit of approximately 3000. The dependency is on and field size limits in the underlying data
- Building the data extract file is slower due to pulling in a large additional string field in the data set
3. Data blend against a permissions file
- Means pulling in a simple flat permissions file into the Tableau workbook, which is simple and not a large impact on the size of the file
- The data blending join field needs to be included in every worksheet on the dashboard. This can make the worksheets significantly more complex. For example if building a summary sheet, such as a Total sheet, will be far more complex. Each value will have to be a table calcuation to ignore the join field within the worksheet.
Which option you choose to take depends on your individual circumstances. I’ve used 1 and 2 in real world situations. 3 has always been too complex as the join field needs to appear in all views.
To use option 2 you need a formula in the filters to do the actual lookup, something like: CONTAINS([YourString],USERNAME()), setting the filter value to TRUE.