If you are here for the first time (or come back frequently), there’s a new Row Level Security page (up in the top toolbar) which gives an overview of your options in Tableau and links to the individual blog posts that dive into detail. Recommended reading for everyone, and it will be kept up to date over time with any changes or additions to functionality.
Even though Tableau Server stores the “full names” of published Workbooks, Datasources and Worksheets in Unicode exactly as published, the algorithm used to create the “contentUrl” used in the URL to reference the published content is based only on the ASCII English characters. The algorithm completely drops any non-ASCII character (and a few other non-URL safe characters). If any other existing contentUrl is found, an underscore and numeral is appended at the end.
In European languages with an expanded character set beyond the ASCII characters, the algorithm results in reduced but still distinctive contentUrls. For example (in Spanish): “Años” -> “Aos”. The risk here, depending on language, is that there may be distinct content names that are reduced to the same contentUrl, resulting in the appending of the numeral to distinguish them like: “Aos” vs. “Aos_1”.
In Chinese, Korean and Japanese (and any other fully non-Latin character sets), the names are reduced to nothing, and then a random number is assigned: “销售运营分析仪表板”-> “_0” or “_1”.
- Always publish all Published Data Sources first to a Tableau Server, before publishing Workbooks
- Give unique name to all Content you publish, especially Data Sources. Tableau Server allows you to use the same name if Content is in different Projects on the same Site, but this will result in the contentUrls being given “_1” or “_923304292” type endings, with no way to control or change them other than through the names you choose at publish time
- If your content is named using non-Latin characters, add additional ASCII text to your names to ensure distinctive and controllable content URLs. This is especially important for Chinese, Japanese and Korean (CJK) text or other fully non-Latin scripts, but also might be necessary in Latin based scripts that frequently use diacritic marks on characters to distinguish (Vietnamese comes to mind)
- One solution for CJK languages: Add a transliteration (Pinyin, Hepburn, etc.) in parentheses after the name in the actual script. Alternatively add a unique numeric identifier after a Workbook or Data Source Name, and number your published Worksheets within a workbook.
Editor’s Note: Huge thanks to Robin Cottiss who helped me navigate the world of authentication in Tableau as he has for so many years to be able to write this. He doesn’t have the same online presence as a lot of other Tableau wizards but I can’t say how much of my and others Tableau knowledge that is out there has either been guided by or just a straight repackaging of the hard-gained work Robin has put in.
When embedding Tableau vizes for external use, the three most common SSO mechanisms are Trusted Authentication, SAML, and OpenID Connect (OIDC).
Trusted Authentication or Trusted Tickets has two modes, which are set as server-level flags:
- Restricted (default): A user can only see Vizes, and cannot see the full Tableau Server UI.
- Unrestricted: Redeeming the ticket creates a full session to Tableau Server, and the user can see the full Tableau Server UI
SAML and OpenID Connect always create a full session, so they work like Unrestricted Tickets.
Additionally, Restricted Trusted Tickets (the default Trusted Authentication mode) also disables:
- Custom Views
- Other interactions with Web Edit, Ask Data, etc.
Many customers would prefer to have a session started with any of the three SSO mechanisms, that allows all the Viz features, but doesn’t let the user ever get to the full Tableau Server UI. Is it possible?
There’s a great Whitepaper which really goes in depth on how to configure Tableau for Row Level Security, but sometimes it’s nice to have a quick overview that cuts right to the chase. Over a lot of years of explaining how it works, I hope this will outline the standard way of implementing RLS in Tableau in a way that helps you get results as quickly as possible.
Row Level Security is the process of securely filtering data down so a user only see what they should once they are looking at a data source. You could call it “User Data Filtering” just as easily. In essence, you are limiting the returned rows – hence “row level security”.
One of the great advantages of tsm is that it is a fully functioning shell / command-line system that works the same on Linux or Windows. Finding the commands to do what you want to do in Tableau – that’s the easy part these days. But both Windows and Linux have their own quirks, especially when you want to fully automate a process. This post discusses how to get things working on Linux – if you need to automate on Windows, take a look here.
Shell scripting on Linux, particularly when you want to schedule using cron, have a lot more quirks than you’d think at first glance. It’s very common for the same set of commands you just typed in to not work at all when you put them in a shell script. And even then it may still fall over when run via cron. To run tsm commands from a script, we have to make sure our environment is always specified correctly.
This question was brought by my fantastisch colleague Timo Tautenhahn, who runs tableaufans.com. He was seeing some performance issues with using the Refresh commands in the JS and Extensions API, but not encountering the issues when changing a filter, which also was causing a new query to the database
In essence, whatever happens when the Refresh command fires off involves more steps than just re-querying. So the question is: what can force a re-query without the Refresh commands?
Solution: Add an Integer Tableau Parameter to a Custom SQL query that doesn’t actually affect the logic of the Query
You can have your own fun coming up with logical statements that are always true, but you don’t need anything more complex than:
SELECT * FROM table t WHERE [Tableau Parameter] = [Tableau Parameter]
As long as you change the value of the parameter to something you haven’t used yet within the session, each change should force a query refresh without using the Refresh action. Simply incrementing up from zero is enough to make it work.
Given all the updates to Tableau Online over the last few years, now seems like as good of time as any to review over when Tableau Online works for embedding Tableau.
What do I mean by embedding in this case? Specifically, to display Tableau visualizations in another web application, without the customer being aware of Tableau Online as a separate entity from the rest of the web application.
Tableau has the most flexibility for doing Row Level Security filtering using what is termed the “Standard Method”: a JOIN between a Data Table and an Entitlements table. The basic assumption, as seen in the RLS Whitepaper, is that this Entitlements table exists in the same database at the Data Table. This is a very narrow assumption, that is only true for a small subset of real deployments.
In many environments and applications, there is no “Entitlements” table within the same logical database as the Data table. In fact, there may be no “Entitlements table” that can be reached at all — entitlements can be retrieved somehow, but they may be in any number of forms.
The most common request I see is to pass a set of Attributes (lists or arrays) of values directly into a Tableau report at run-time. The rest of the post will describe a flexible and generic method for making this possible in a secure way. It builds upon the baseline of the PostgreSQL/Python environment in the using a Web Service as a Data Source post, but is even simpler.
For a variety of reasons,Trusted Authentication / Trusted Tickets is often the best choice for SSO into a Tableau Server. Trusted Authentication actually establishes a Tableau Session in the browser, so you can use it once to establish SSO for a whole browser session.
You can find the auth_helpers.js file on GitHub with all the code necessary for this process (except for the back-end Trusted Tickets request, which you need to build in your own web application environment).
The Tableau REST API has very convenient endpoints for retrieving a Workbook or View Preview Image. Unfortunately, there are many conditions where Tableau Server itself doesn’t generate anything other than the gray “User Filtered View” thumbnail. This post describes a process for using the full Image (not preview) REST API endpoints to generate attractive thumbnails images.
One of the conditions that results in a gray generic thumbnails is any workbook that is connected to a Published Data Source that has Row Level Security set up through Data Source Filters.
The basic workflow is:
- Request all of the Views from a Workbook (or the Default View if you only need thumbnails at the Workbook level) for one particular user, who you know has enough data access to result in a good looking thumbnail
- Call the actual Image endpoints (instead of the Thumbnail / Preview endpoints) for every View you need
- Reduce the images to thumbnail size
- Cache the thumbnails for efficient retrieval by your embedded web application
You’re probably aware that Tableau Parameters are not an inherently secure way to filter down data. A lot of people are using JWTs to pass around tokens with security entitlements, so if you are good with that, did you know you can pass one right into a Tableau Parameter?
I’ve built out an example where the JWT comes through then a function fires off a web service request, as part of the work for using a Web Service / REST API as a live data source, but there’s no reason you couldn’t build a smaller function for processing the JWT and then use just that in Custom SQL in Tableau when building your data source.
Although the following example uses PostgreSQL (and PL/Python), you could theoretically implement this in any language with access to a language that can process JWT. In MS SQL Server, a CLR Stored Procedure can access C# functionality, and the Systems.IdentityModel.Tokens.Jwt namespace appears to have all the necessary functionality to implement a very similar workflow.
Below I’ll work through a workflow using PL/Python on PostgreSQL — again, the same concept could be implemented on any RDBMS with functions.