Exposing Limited Amounts of Tableau REST API Functionality to Users via tableau_tools

As of 2019.2, the Tableau Server REST API only allows logging in for a REST API session using a combination of username and password. This means there is no effective way to directly start a REST API session using a SSO mechanism (SAML, JWT, etc.)  Even if you were able to, you might still want to restrict the user to only do certain actions (for example, enabling Querying methods but not Updates or Deletes).

The best practice for working around this is to wrap the Tableau REST API in another REST API service of your own design. Then within that wrapper, use a Server or Site Administrator level account to log in to the Tableau Server REST API. In this article, we’ll discuss how to achieve this using tableau_tools, with both a simple and a more complex but efficient design pattern.

The Sign-In Method of the REST API allows a Server or Site Administrator to sign-in AS any other user (similar to SQL Server User Impersonation). This creates an actual separate REST API session with its own distinct Token, as if the user had logged in themselves with their own username and password.

Using tableau_tools, you can easily implement this paradigm. At the most simple level, you could simply spawn off a new TableauRestApiConnectionNN() object for each user that comes in, using the same administrator. If you don’t have multiple sites, this can be pretty easily achieved:


user_rest_connections = {}
master_username = 'site_admin'
master_password = 'hackm3'
site_content_url = "mysite"
m = TableauRestApiConnection32(server=server, username=master_username, password=master_password,
site_content_url=site_content_url)
m.signin()

user_to_impersonate_1 = 'user_a'
user_luid = m.query_user_luid(user_to_impersonate_1)
user_rest_connections[user_to_impersonate_1] = TableauRestApiConnection32(server=server, username=master_username, password=master_password,
site_content_url=site_content_url)

user_rest_connections[user_to_impersonate_1].signin(user_luid)

user_to_impersonate_2 = 'user_b'
user_luid = m.query_user_luid(user_to_impersonate_2)
user_rest_connections[user_to_impersonate_2] = TableauRestApiConnection32(server=server, username=master_username,
password=master_password,
site_content_url=site_content_url)
user_rest_connections[user_to_impersonate_2].signin(user_luid)

RestConnectionsManager Class

However, if you have multiple sites, you have a whole other level of complexity, because you need to an Admin login for each site, as well as individual tokens for each user/site combination. For this reason, there is an example script called “use_admin_account_with_impersonation.py” in the tableau_tools examples folder of a class called RestConnectionsManager.

RestConnectionsManager is designed to handle all of the necessary connections on any number of sites. Rather than create a new TableauRestApiConnectionNN object for each connection, it simply swaps the login tokens in and out of a single connection. The example file shows how to use it, but here the code without the class definition:


# Connect to the default site to bootstrap the process

server = ""
master_default_username = ""
master_default_password = ""
default_site_content_url = ""
d = TableauRestApiConnection32(server=server, username=master_default_username, password=master_default_password,
site_content_url=default_site_content_url)

# This manages all the connections here on out
connections = RestConnectionsManager(rest_connection_object=d)
connections.sign_in_connection_object()

# Examples of using the connection manager
connections.switch_user_and_site("some_user", "site_a")
my_projects = connections.rest_connection.query_projects()
my_projects_dict = connections.rest_connection.convert_xml_list_to_name_id_dict(my_projects)
print(my_projects_dict)

# Now switching to a different user
connections.switch_user_and_site("some_other_user", "site_b")
my_projects = connections.rest_connection.query_projects()
my_projects_dict = connections.rest_connection.convert_xml_list_to_name_id_dict(my_projects)
print(my_projects_dict)

You can see that the switch_user_and_site() method of RestConnectionsManger is called and then you can use the rest_connection property to do any TableauRestApiConnectionNN methods you want. It’s an efficient way to reuse a single connection to handle all of the various requests that come in.

Wiring up the Full Wrapper

I don’t have a full example at this time of a full wrapper of REST API commands, but here is a method from a Django project that utilizes the RestConnectionsManager and then returns a JSON response as part of a REST API. This secondary REST API application, wrapping the Tableau REST API connections, can implement its own authentication system, whether JWT or SAML or anything else, so that you can use your organization’s SSO logins and have it pass through and drive the Tableau Server REST API.


def projects(request, site):
# Check for connection, attempt to reestablish if possible
if connections.connection_signed_in is False:
if connections.sign_in_connection_object() is False:
return HttpResponseServerError()

username = check_user_session(request)
if username is None:
return HttpResponseForbidden()
connections.switch_user_and_site_content_url(username=username, site_content_url=site)
p_sort = Sort('name', 'asc')
p = connections.rest_connection.query_projects_json(sorts=[p_sort, ])
return JsonResponse(p)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s