Restrict an embedding link
The embedding feature in Imply Polaris provides non-Polaris users with a way to view visualizations outside the Polaris UI.
You can create public links to visualizations that anyone can access and view. See Embed visualizations for information on creating public links.
Restricted links are a more secure alternative to public links. Restricted links permit access only when the URL includes a parameter containing a signature. You create signatures programmatically using the secret key and additional parameters.
Generate restricted links
To generate restricted links you must perform the following tasks. The parameter names relate to the example scripts below.
- Create a restricted link and secret key.
url
andSECRET_KEY
parameters
- Define the link creation time.
created
parameter
- Optional: Apply a data cube access filter.
cubeAccessFilter
parameter
- Optional: Define an access filter for the link.
linkAccessFilter
parameter
- Generate a restricted link.
You must use the exact parameter names created
, cubeAccessFilter
, and linkAccessFilter
for Polaris to recognize them. See the example scripts for implementation examples.
Create a restricted link and secret key
See Embed visualizations for details on how to create a restricted link and secret key in the Polaris UI. You can also use the Embedding API to create a private key for an embedding link.
Define the link creation time
Set the created
parameter to a string value representing the time the link was created, in milliseconds. See the example scripts for examples.
When a user accesses a restricted link, Polaris stores a cookie containing the authentication details. Links must be used with in an hour of signature creation and can only create one cookie.
A signature for a restricted embedding link is designed to be used to initialize the embedded application once and only once. The link’s TTL determines how long the signature is valid for after it’s generated—it controls the maximum length of a session—but after the signature is first exchanged for a cookie the signature becomes unusable for 10 hours.
Optional: Apply a data cube access filter
If you're creating a restricted link for data cube that has an access filter applied, you can apply that access filter to your link.
For example, you might set up a Koalas to the Max data cube with an access filter that allows analysts in France to see only the data relevant to their home country and the United States. See the access filter example for a full example.
To find the access filter ID to plug into your script:
- In the Polaris UI, click Data cubes in the left pane.
- Click the name of the data cube that underlies the link view.
- Click the edit icon in the top right to edit the data cube.
- Click the Access filters tab and click the edit icon in the top right corner of the page.
- Click the view details icon to the right of the filter name.
- Copy the Group ID that appears.
Set the cubeAccessFilter
parameter to the ID.
Optional: Define a link access filter
In addition to, or instead of, applying a data cube access filter, you can define an access filter for your specific link.
For example, if you apply the data cube filter for analysts in France outlined in the access filter example, you might want to apply an extra filter to your link for managers in Paris to view. This filter would further restrict the data in the embedded visualization to match the city name Paris
:
t."cityname" = 'Paris'
Define the access filter in the linkAccessFilter
parameter.
Generate a restricted link
Next, programmatically generate a signature for your link using the restricted link as follows:
- Import your secret key.
The key is in Privacy Enhanced Mail (PEM) format. Import it using the Elliptic Curve Digital Signature Algorithm (ECDSA) with a P-256 curve and ensure that it has signing permissions. - Create a payload of parameters with the following names, in the following order:
{cubeAccessFilter, linkAccessFilter, created}
.
You can include unused parameters asundefined
if you're using JavaScript or TypeScript. Otherwise, omit unused parameters. - Sign the payload using the ECDSA algorithm SHA-256 and ensure it's in IEEE P1363 format.
Access a restricted link
To access the restricted link, include the signature and signed parameters as query parameters.
The URL format is as follows:
https://ORGANIZATION_NAME.REGION.CLOUD_PROVIDER.api.imply.io/e/REGION:CLOUD+PROJECT_ID+EMBED_LINK_ID/UNIQUE_ID?signature=SIGNATURE&cubeAccessFilter=CUBE_FILTER&linkAccessFilter=LINK_FILTER
Replace UNIQUE_ID
with an alphanumeric string—this is required if you want to embed the same visualization multiple times on the same page.
Including the CLOUD
property is optional. If omitted, Polaris infers the cloud service provider from the REGION
property.
For example:
https://ORGANIZATION_NAME.REGION.CLOUD_PROVIDER.api.imply.io/e/us-east-1:aws+adc305e1-2e77-40df-805f-ef0f6cd4f164+34392300657dc3c5d0/1?signature=W4HDEO4-FOX54V-cskOX&cubeAccessFilter=0e760716-b0b4-4b60-bd68&linkAccessFilter=t."cityname"='Paris'&created=1679003827755
You can add extra query parameters to the link, to vary the data in the visualization while applying the same restrictions through the signature.
Implementing restricted links
The way in which you implement the link generation depends on your specific use case. As an example, you could create a session token when a user logs into your application. You could set the session token expiry to the same period as your restricted link's time to live.
Using this configuration, you would only need to create the restricted link once per user session.
Example scripts
See the following scripts for detailed Python and JavaScript examples of programmatically generating links:
- Python
- JavaScript
import base64
import json
import time
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives import hashes
from URLSearchParams import URLSearchParams
# Secret key from the Polaris UI
secretKeyPEM = """-----BEGIN PRIVATE KEY-----
{priv_key}
-----END PRIVATE KEY-----""".format(priv_key=priv_key).strip().encode('utf-8');
# Link URL from the Polaris UI
url = ```https://example.app.imply.io/e/us-east-1:aws+adc305e1-2e77-40df-805f-ef0f6cd4f164+34392300657dc3c5d0/1?signature=W4HDEO4-FOX54V-cskOX&cubeAccessFilter=0e760716-b0b4-4b60-bd68&linkAccessFilter=t."cityname"='Paris'&created=1679003827755```
# ID of the data cube access filter
cubeAccessFilter = "676fd330-aabc-4d76-b1a3-e9f1eff4b3a2";
# SQL access filter to apply to the link
linkAccessFilter = """t."cityname"='Paris'""";
# String value of the time the link was created, in milliseconds
created = str(int(time.time() * 1000));
# Payload containing the required parameters
# Sign the payload parameters in the following order: {cubeAccessFilter, linkAccessFilter, created}
payload = {"created": created}
# Separators to prevent adding whitespace around symbols
payload = json.dumps(payload, separators=(',', ':')).encode('utf-8')
# Load the secret key
secretKey = load_pem_private_key(secretKeyPEM, password=None, backend=default_backend())
# Generate a signature format using ECDSA P-256
signature = secretKey.sign(
payload,
ec.ECDSA(hashes.SHA256())
)
# Convert signature format to IEEE P1363
(r, s) = decode_dss_signature(signature)
signatureP1363 = r.to_bytes(32, byteorder='big') + s.to_bytes(32, byteorder='big')
# Output signature as a URL-safe base64 string
print('payload signed: ', payload)
print('signature created: ', base64.urlsafe_b64encode(signatureP1363).decode('utf-8'))
# Build the restricted link from the signed parameters
src = URLSearchParams(url).append(
{"created": created, "signature": base64.urlsafe_b64encode(signatureP1363).decode('utf-8')})
print('You can now access your restricted link from: ', src)
const crypto = require('crypto');
function importSecretKeyFromPEM(pem) {
// Import secret key using ECDSA and P-256
return crypto.webcrypto.subtle.importKey(
'pkcs8',
Buffer.from(pem, 'base64url'),
{
name: 'ECDSA',
namedCurve: 'P-256',
},
false,
['sign']
);
}
async function signPayload(secretKeyPem, data) {
// Import secret key from PEM format
const secretKey = await importSecretKeyFromPEM(secretKeyPem);
// Sign payload with secret key using SHA-256
const signature = await crypto.webcrypto.subtle.sign(
{
name: 'ECDSA',
hash: { name: 'SHA-256' },
...secretKey.algorithm,
},
secretKey,
Buffer.from(JSON.stringify(data))
);
return signature;
}
const secretKeyPEM = 'PRIV_KEY';
const url = `https://example.app.imply.io/e/us-east-1:aws+adc305e1-2e77-40df-805f-ef0f6cd4f164+34392300657dc3c5d0/1?signature=W4HDEO4-FOX54V-cskOX&cubeAccessFilter=0e760716-b0b4-4b60-bd68&linkAccessFilter=t."cityname"='Paris'&created=1679003827755`;
// Data cube access filter ID
const cubeAccessFilter = '676fd330-aabc-4d76-b1a3-e9f1eff4b3a2';
// SQL access filter to apply to the link
const linkAccessFilter = `t."cityname"='Paris'`;
// String value of the time the link was created, in milliseconds
const created = Date.now().toString();
// Payload containing the required parameters
// Sign the payload parameters in the following order
const payload = { cubeAccessFilter: cubeAccessFilter, linkAccessFilter: linkAccessFilter, created: created };
signPayload(secretKeyPEM, payload)
.then((signature) => {
const signatureString = Buffer.from(signature).toString('base64url');
// Build the restricted link from the signed parameters
const urlObject = new URL(url)
const searchParams = new URLSearchParams(urlObject.searchParams);
if (signatureString) {
searchParams.append('signature', signatureString);
}
if (payload.cubeAccessFilter) {
searchParams.append('cubeAccessFilter', payload.cubeAccessFilter);
}
if (payload.linkAccessFilter) {
searchParams.append('linkAccessFilter', payload.linkAccessFilter);
}
searchParams.append('created', payload.created);
// Print the signed URL
console.log('payload signed: ' + JSON.stringify(payload));
console.log('signature created: ' + signatureString);
console.error('You can now access your restricted link from:');
console.log(urlObject.origin + urlObject.pathname + '?' + searchParams.toString());
})
.catch((err) => {
});