- 28 Aug 2022
- 7 Minutes to read
- Print
- DarkLight
Step 2: Deployment
- Updated on 28 Aug 2022
- 7 Minutes to read
- Print
- DarkLight
Overview
Now that you have all the required information from Step 1: Preparation, it's time to dive into the deployment process.
We have organized the dashboard embedding process into a series of four steps:
- Create guest tokens
- Supply guest tokens to front-end
- Install the Superset embedded SDK
- Embed the dashboard using the SDK
Before starting, we highly recommend you initially review the Preparing your System for Embedding section below.
Have fun!
Preparing your System for Embedding
A strong system will have safeguards built in at multiple levels. With that in mind, it is important to take whatever protective steps you can when using embedded dashboards to expose data outside your organization.
The following precautionary measures are advised:
- Ensure that the database connections Preset uses to display your dashboards have the minimal necessary access. This could mean using a separate physical database, or a limited role, schema, or some other construction, depending on your data architecture.
- Use a separate Preset Workspace for embedded analytics than the ones you use for internal business analytics or other purposes.
1: Create Guest Tokens (Back-end)
A guest token authenticates an end user to access an embedded dashboard. Guest tokens are created by calling the Manager endpoint POST https://manage.app.preset.io/api/v1/teams/<TEAM NAME>/workspaces/<WORKSPACE NAME>/guest-token/
from your back-end service.
To make this request, you will first need to authenticate with the Preset API and get an access token. Access tokens have a short lifetime of a few hours, so in a long-running service, they’ll need to be refreshed periodically using your API Key. Token creation must only be done in a trusted back-end execution context. Never expose your Preset API Key or your Preset Access Token!Please keep in mind...
An example of how to create a guest token using Python:
payload = {
"user": {
"username": "example_username",
"first_name": "First",
"last_name": "Last"
},
"resources": [{
"type": "dashboard",
"id": EMBEDDED_DASHBOARD_ID
}],
"rls": []
}
response = requests.post(
"https://manage.app.preset.io/api/v1/teams/<TEAM NAME>/workspaces" +
"/<WORKSPACE NAME>/guest-token/",
data=payload,
headers={ "Authorization": my_preset_access_token }
).json()
guest_token = response["data"]["payload"]["token"]
Explanation of the fields used in the /guest-token
endpoint:
user
(Required): Profile info about the user viewing the dashboard. Theusername
field should be unique for each user.resources
(Required): A list of the dashboards the user will be allowed to see (if you have multiple embedded dashboards, you can specify multiple here).rls
(Required): Specify a list of row-level security rules that will apply to the bearer of this token. Row Level Security applies additionalWHERE
clauses when querying a dataset for this user (such as when a chart is viewed). Some things to know:- A rule can have an optional
dataset: id
parameter if you only want it applied to a certain dataset. - Example clauses:
"publisher = 'Nintendo'"
,"location_id = {warehouse.id}"
,"NOT events.is_secret"
, andteam_id = {team_id}
. - If you list multiple rules, they are joined in SQL using
AND
. - If you want to use
OR
or similar clause, it should be enclosed within the quotations - Jinja templating
can be used in the RLS clause for further customization.
- A rule can have an optional
Warnings
- Without a dataset parameter, a rule will be applied to all datasets. This is pretty convenient, but beware of introducing errors by referencing a column that does not exist in one of the datasets backing your dashboards.
- Do not insert untrusted input into RLS clauses (e.g., such as information submitted to your app from a form, or from an external service). This would open up your analytics database to SQL injection attacks.
For context, here is an example payload which authenticates a user to access two dashboards.
The user has three RLS rules applied to them:
{
"user": {
"username": "grace_hopper",
"first_name": "Grace",
"last_name": "Hopper"
},
"resources": [
{ "type": "dashboard", "id": "7dc26b42-fd38-4965-8f60-e156ae233f6d" },
{ "type": "dashboard", "id": "d12678ae-b001-4f97-9822-a6bdf893e97c" }
],
"rls": [
{ "clause": "username = 'grace_hopper'" }, // this rule applies to all datasets in the workspace
{ "dataset": 16, "clause": "environment = 'production'" },
{ "dataset": 42, "clause": "state = 'published'" }
]
}
2: Supply Guest Tokens to Front-end
The token created by the back-end will need to be passed to the front-end before your end user can view a dashboard.
You can perform this transfer in whatever way is appropriate for your application. The typical way is to expose an internal HTTP endpoint that authenticates your user, and creates and returns the guest token.
Here is an example Python / Flask endpoint building on the example from before:
@app.route("/guest-token", methods=["GET"])
@login_required
def get_guest_token():
payload = {
"user": {
"username": g.user.username,
"first_name": g.user.first_name,
"last_name": g.user.last_name
},
"resources": [{
"type": "dashboard",
"id": EMBEDDED_DASHBOARD_ID
}],
"rls": [
{ "clause": f"user_id = '{g.user.id}'" }
]
}
token_response = requests.post(
"https://manage.app.preset.io/api/v1/teams/<TEAM NAME>/workspaces" +
"/<WORKSPACE NAME>/guest-token/",
data=payload,
headers={ "Authorization": preset_access_token }
).json()
return token_response["payload"]["token"]
Adding access control
Your code can decide which users should be granted guest tokens. You can also define row-level security rules in the rls
parameter to dynamically determine the data that will be displayed to each user.
You will need to determine what the rules should be for your unique application, dashboard, and dataset, so that only the appropriate data will be displayed to your users.
The RLS rule defined above shows how you could filter dashboard data per-user based on their id. For another example, if your users belong to an organization and should only see their organization’s data, you might write an RLS rule like:
{ "clause": "organization = {user.organization}" }
3: Install the Superset Embedded SDK (Front-end)
From NPM:
npm install --save @preset-sdk/embedded
From a CDN:
You can load the SDK as a script in your HTML, if preferred. The SDK will be available as presetSdk
globally:
<script src="https://unpkg.com/@preset-sdk/embedded"></script>
4: Embed the Dashboard Using the SDK
When using ES6 imports or a build tool:
import { embedDashboard } from "@preset-sdk/embedded";
embedDashboard({
id: "6f03195d-0bf6-47a7-b44e-baa7636ac128", // given by the Superset embedding UI
supersetDomain: "https://{WORKSPACE NAME}.{WORKSPACE REGION}.app.preset.io",
mountPoint: document.getElementById("my-superset-container"), // any html element that can contain an iframe
fetchGuestToken: () => fetchGuestTokenFromBackend(),
dashboardUiConfig: { hideTitle: true }, // dashboard UI config: hideTitle, hideTab, hideChartControls (optional)
});
Using a script tag with CDN:
<script>
presetSdk.embedDashboard({
id: "abc123", // given by the Superset embedding UI
supersetDomain: "https://{WORKSPACE NAME}.{WORKSPACE REGION}.app.preset.io",
mountPoint: document.getElementById("my-superset-container"), // any html element that can contain an iframe
fetchGuestToken: () => fetchGuestTokenFromBackend(),
dashboardUiConfig: { hideTitle: true }, // dashboard UI config: hideTitle, hideTab, hideChartControls (optional)
});
</script>
You may pass the following options to embedDashboard
:
id
(Required): An identifier of the embedded dashboard, per Step 1: Preparation:
supersetDomain
(Required): The domain where your Preset Workspace is located. This is found in the beginning of the Preset URL from the homepage:
mountPoint
(Required): Any HTML element that can contain an iframe.fetchGuestToken
(Required): An async function that fetches a fresh guest token from the endpoint created in 2: Supply Guest Tokens above. This should return a new guest token each time it is called.dashboardUiConfig
(Optional): An object containing options to alter the dashboard UI. Possible options include:
{
hideTitle: boolean
hideTab: boolean
hideChartControls: boolean
}
embeddedDashboard()
asynchronously returns an Embedded Dashboard object. You can callunmount()
on this object to clean up an embedded dashboard (and its timers and other effects) from the page.
About iframe sizing...
You can get your embedded dashboards looking a bit more seamless by resizing the iframe so that the browser sizes the content window appropriately. The getScrollSize()
method asynchronously returns the full width and height of the embedded dashboard page.
Example making the iframe poll the embedded page to fit its content:
const myDashboard = await embedDashboard();
setInterval(async () => {
const { width, height } = myDashboard.getScrollSize();
// imaginary setCSS function. Replace with whatever method of styling is standard in your application.
setCss(`
.embedded-superset iframe {
width: ${width};
height: ${height};
}
`);
}, 1000);
About Embedded Authentication
A Preset guest token is what enables your users to access embedded Preset resources, such as dashboards, without having to log in to Preset normally. The guest token is used for all Workspace API calls made while viewing an embedded dashboard. It contains user profile data and all the permissions of the user.
Guest tokens have a short lifetime of 5 minutes, and need to be refreshed regularly. To accomplish this, the frontend SDK will call fetchGuestToken
to get a new token.
Guest tokens have no ties to the Preset user who set up your embedded dashboard. The permissions your application assigns a guest token at creation are the full set of permissions that the end user will have.