Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Importante
Questa funzionalità è in Anteprima Pubblica.
Questa pagina descrive il funzionamento dell'incorporamento per gli utenti esterni, come configurare l'area di lavoro di Azure Databricks per la condivisione sicura dei dashboard incorporati e come usare applicazioni di esempio per iniziare. L'incorporamento per gli utenti esterni usa un principal di servizio e token di accesso con ambito specifico per autenticare e autorizzare l'accesso al dashboard incorporato. Questo approccio consente di condividere dashboard con visualizzatori esterni all'organizzazione, ad esempio partner e clienti, senza effettuare il provisioning degli account Azure Databricks per tali utenti.
Per informazioni su altre opzioni di incorporamento, inclusi i dashboard di incorporamento per gli utenti all'interno dell'organizzazione, vedere Incorporare un dashboard.
Funzionamento dell'embedding per utenti esterni
Il diagramma e i passaggi numerati che seguono illustrano come gli utenti vengono autenticati e i dashboard vengono popolati con risultati con ambito utente quando si incorpora un dashboard per gli utenti esterni.
- Autenticazione utente e richiesta: L'utente accede all'applicazione. Il front-end dell'applicazione invia una richiesta autenticata al server per un token di accesso al dashboard.
-
Autenticazione dell'entità servizio: Il server usa il segreto dell'entità servizio per richiedere e ricevere un token OAuth dal server Databricks. Si tratta di un token con ambito generale che può chiamare tutte le API del dashboard a cui Azure Databricks può accedere per conto dell'entità servizio. Il server chiama l'endpoint
/tokeninfousando questo token, passando informazioni utente di base, ad esempioexternal_viewer_ideexternal_value. Vedere Presentare in modo sicuro i dashboard ai singoli utenti. -
Generazione di token con ambito utente: Utilizzando la risposta dall'endpoint
/tokeninfoe dall'endpoint OIDC (Databricks OpenID Connect), il tuo server genera un nuovo token con un ambito ristretto che codifica le informazioni utente che hai passato. - Rendering del dashboard e filtro dei dati: La pagina dell'applicazione istanzia
DatabricksDashboardda@databricks/aibi-cliente passa il token con ambito utente durante l'instanziazione. Il dashboard viene visualizzato in base al contesto dell'utente. Questo token autorizza l'accesso, supporta il controllo conexternal_viewer_ide esegue ilexternal_valuefiltro dei dati. Le query nei set di dati del dashboard possono richiamare__aibi_external_valueper applicare filtri per utente, garantendo che ogni utente veda solo i dati che è autorizzato a visualizzare.
Ask Genie non è disponibile nell'incorporamento esterno
Il pulsante Ask Genie non è supportato nell'incorporamento per gli utenti esterni. Se si vogliono fornire funzionalità di query sui dati in linguaggio naturale agli utenti esterni, usare invece l'API Genie Conversation . L'API Conversation consente di integrare la funzionalità Genie nell'applicazione a livello di codice, indipendentemente dall'incorporamento del dashboard.
Per i dashboard integrati con integrazione di base, Ask Genie è disponibile. Vedere Ask Genie nei dashboard incorporati.
Presentare in modo sicuro i dashboard ai singoli utenti
Configurare il server applicazioni per generare un token univoco con ambito utente per ogni utente in base al relativo external_viewer_id. In questo modo è possibile tenere traccia delle visualizzazioni del dashboard e dell'utilizzo tramite i log di controllo. Il external_viewer_id è associato a un external_valueoggetto, che funge da variabile globale che può essere inserita nelle query SQL usate nei set di dati del dashboard. In questo modo è possibile filtrare i dati visualizzati nel dashboard per ogni utente.
external_viewer_id viene passato ai log di controllo del dashboard e non deve includere informazioni personali. Questo valore deve anche essere univoco per utente.
external_value viene usato nell'elaborazione delle query e può includere informazioni personali.
L'esempio seguente illustra come usare il valore esterno come filtro nelle query del set di dati:
SELECT *
FROM sales
WHERE region = __aibi_external_value
Panoramica dell'installazione
Questa sezione include una panoramica concettuale generale dei passaggi da eseguire per configurare per l'incorporamento di un dashboard in una posizione esterna.
Per incorporare un dashboard in un'applicazione esterna, creare prima un'entità servizio in Azure Databricks e generare un segreto. All'entità del servizio deve essere concesso l'accesso in lettura al dashboard e ai dati sottostanti ad esso. Il server utilizza il segreto del principale del servizio per recuperare un token che può accedere alle API del dashboard per conto del principale del servizio. Con questo token, il server chiama l'endpoint /tokeninfo API, un endpoint OpenID Connect (OIDC) che restituisce informazioni di base sul profilo utente, inclusi i external_value valori e external_viewer_id . Questi valori consentono di associare le richieste ai singoli utenti.
Usando il token ottenuto dall'entità servizio, il server genera un nuovo token limitato all'utente specifico che accede al dashboard. Questo token con ambito utente viene passato alla pagina dell'applicazione, in cui l'applicazione crea un'istanza dell'oggetto DatabricksDashboard dalla @databricks/aibi-client libreria. Il token contiene informazioni specifiche dell'utente che supportano il controllo e applicano il filtro in modo che ogni utente veda solo i dati a cui è autorizzato ad accedere. Dal punto di vista dell'utente, l'accesso all'applicazione fornisce automaticamente l'accesso al dashboard incorporato con la visibilità corretta dei dati.
Considerazioni sulle prestazioni e i limiti di frequenza
L'incorporamento esterno prevede un limite di frequenza di 20 caricamenti del dashboard al secondo. È possibile aprire più di 20 dashboard contemporaneamente, ma non più di 20 possono iniziare il caricamento contemporaneamente.
Prerequisiti
Per implementare l'incorporamento esterno, assicurarsi di soddisfare i prerequisiti seguenti:
- È necessario disporre almeno delle autorizzazioni CAN MANAGE per un dashboard pubblicato. Consultare Tutorial: Utilizzare dashboard di esempio per creare e pubblicare un dashboard campione rapidamente, se necessario.
- È necessario che sia installata l'interfaccia della riga di comando di Databricks versione 0.205 o successiva. Per istruzioni, vedere Installare o aggiornare l'interfaccia della riga di comando di Databricks . Per configurare e usare l'autenticazione OAuth, vedere Autenticazione da utente a computer (U2M) OAuth.
- Un amministratore dell'area di lavoro deve definire un elenco di domini approvati che possono ospitare il dashboard incorporato. Per istruzioni, vedere Gestire l'incorporamento del dashboard .
- Un'applicazione esterna per ospitare il dashboard incorporato. È possibile usare la propria applicazione o usare le applicazioni di esempio fornite.
Passaggio 1: Creare un'entità servizio
Creare un "service principal" per fungere da identità per l'applicazione esterna in Azure Databricks. Questa service principal autentica le richieste per conto della tua applicazione.
Per creare un'entità servizio:
- In quanto amministratore dell'area di lavoro, accedere all'area di lavoro di Azure Databricks.
- Fare clic sul nome utente nella barra superiore dell'area di lavoro di Azure Databricks e selezionare Impostazioni.
- Fare clic su Identità e accesso nel riquadro sinistro.
- Accanto a Oggetti principali del servizio, cliccare su Gestisci.
- Fare clic su Aggiungi entità servizio.
- Fare clic su Aggiungi nuovo.
- Immettere un nome descrittivo per il principale del servizio.
- Fare clic su Aggiungi.
- Apri il principale del servizio appena creato dalla pagina di elenco Principali del Servizio. Usare il campo di testo Filtro per cercare un elemento in base al nome, se necessario.
- Nella pagina Dettagli entità servizio registrare l'ID applicazione. Verificare che le caselle di controllo Accesso a Databricks SQL e Accesso all'area di lavoro siano selezionate.
Passaggio 2: Creare un segreto OAuth
Generare un segreto per l'entità servizio e raccogliere i valori di configurazione seguenti, necessari per l'applicazione esterna:
- ID entità servizio (cliente)
- Segreto del cliente
L'entità del servizio usa un segreto OAuth per verificare la sua identità quando richiede un token di accesso dall'applicazione esterna.
Per generare un segreto:
- Fare clic su Segreti nella pagina dettagli del principale del servizio.
- Fare clic su Genera segreto.
- Immettere un valore di durata per il nuovo segreto in giorni ,ad esempio tra 1 e 730 giorni.
- Copiare immediatamente il segreto. Non è possibile visualizzare di nuovo il segreto dopo aver lasciato questa schermata.
Passaggio 3: Assegnare le autorizzazioni al principale del servizio
L'entità servizio creata funge da identità che fornisce l'accesso al dashboard tramite l'applicazione. Le autorizzazioni si applicano solo se il dashboard non viene pubblicato con autorizzazioni per i dati condivisi. Se vengono usate autorizzazioni per i dati condivisi, le credenziali dell'editore accedono ai dati. Per altri dettagli e consigli, vedere Incorporamento degli approcci di autenticazione.
- Fare clic su Dashboard nella barra laterale dell'area di lavoro per aprire la pagina di presentazione del dashboard.
- Fare clic sul nome del dashboard da incorporare. Si apre il dashboard pubblicato.
- Fare clic su Condividi.
- Usare il campo di inserimento del testo nella finestra di dialogo Condivisione per trovare il principale del servizio e farci clic. Impostare il livello di autorizzazione su CAN RUN. Fare quindi clic su Aggiungi.
- Registrare l'ID della dashboard. È possibile trovare l'ID del dashboard nell'URL del dashboard , ad esempio
https://<your-workspace-url>/dashboards/<dashboard-id>. Vedere Dettagli dell'area di lavoro di Databricks.
Annotazioni
Se si pubblica un dashboard con autorizzazioni di dati individuali, è necessario concedere all'entità servizio l'accesso ai dati usati nel dashboard. L'accesso di calcolo usa sempre le credenziali dell'editore, pertanto non è necessario concedere autorizzazioni di calcolo all'entità servizio.
Per leggere e visualizzare i dati, il principale del servizio deve disporre di almeno SELECT privilegi per le tabelle e le viste a cui si fa riferimento nella dashboard. Vedere Chi può gestire i privilegi?
Passaggio 4: Usare l'app di esempio per autenticare e generare token
Utilizza un'applicazione di esempio per esercitarti nell'integrazione esterna del cruscotto. Le applicazioni includono istruzioni e codice che avvia lo scambio di token necessario per generare token con ambito. I blocchi di codice seguenti non hanno dipendenze. Copiare e salvare una delle applicazioni seguenti.
Pitone
Copiare e salvare il file in un file denominato example.py.
#!/usr/bin/env python3
import os
import sys
import json
import base64
import urllib.request
import urllib.parse
from http.server import HTTPServer, BaseHTTPRequestHandler
# -----------------------------------------------------------------------------
# Config
# -----------------------------------------------------------------------------
CONFIG = {
"instance_url": os.environ.get("INSTANCE_URL"),
"dashboard_id": os.environ.get("DASHBOARD_ID"),
"service_principal_id": os.environ.get("SERVICE_PRINCIPAL_ID"),
"service_principal_secret": os.environ.get("SERVICE_PRINCIPAL_SECRET"),
"external_viewer_id": os.environ.get("EXTERNAL_VIEWER_ID"),
"external_value": os.environ.get("EXTERNAL_VALUE"),
"workspace_id": os.environ.get("WORKSPACE_ID"),
"port": int(os.environ.get("PORT", 3000)),
}
basic_auth = base64.b64encode(
f"{CONFIG['service_principal_id']}:{CONFIG['service_principal_secret']}".encode()
).decode()
# -----------------------------------------------------------------------------
# HTTP Request Helper
# -----------------------------------------------------------------------------
def http_request(url, method="GET", headers=None, body=None):
headers = headers or {}
if body is not None and not isinstance(body, (bytes, str)):
raise ValueError("Body must be bytes or str")
req = urllib.request.Request(url, method=method, headers=headers)
if body is not None:
if isinstance(body, str):
body = body.encode()
req.data = body
try:
with urllib.request.urlopen(req) as resp:
data = resp.read().decode()
try:
return {"data": json.loads(data)}
except json.JSONDecodeError:
return {"data": data}
except urllib.error.HTTPError as e:
raise RuntimeError(f"HTTP {e.code}: {e.read().decode()}") from None
# -----------------------------------------------------------------------------
# Token logic
# -----------------------------------------------------------------------------
def get_scoped_token():
# 1. Get all-api token
oidc_res = http_request(
f"{CONFIG['instance_url']}/oidc/v1/token",
method="POST",
headers={
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": f"Basic {basic_auth}",
},
body=urllib.parse.urlencode({
"grant_type": "client_credentials",
"scope": "all-apis"
})
)
oidc_token = oidc_res["data"]["access_token"]
# 2. Get token info
token_info_url = (
f"{CONFIG['instance_url']}/api/2.0/lakeview/dashboards/"
f"{CONFIG['dashboard_id']}/published/tokeninfo"
f"?external_viewer_id={urllib.parse.quote(CONFIG['external_viewer_id'])}"
f"&external_value={urllib.parse.quote(CONFIG['external_value'])}"
)
token_info = http_request(
token_info_url,
headers={"Authorization": f"Bearer {oidc_token}"}
)["data"]
# 3. Generate scoped token
params = token_info.copy()
authorization_details = params.pop("authorization_details", None)
params.update({
"grant_type": "client_credentials",
"authorization_details": json.dumps(authorization_details)
})
scoped_res = http_request(
f"{CONFIG['instance_url']}/oidc/v1/token",
method="POST",
headers={
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": f"Basic {basic_auth}",
},
body=urllib.parse.urlencode(params)
)
return scoped_res["data"]["access_token"]
# -----------------------------------------------------------------------------
# HTML generator
# -----------------------------------------------------------------------------
def generate_html(token):
return f"""<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Demo</title>
<style>
body {{ font-family: system-ui; margin: 0; padding: 20px; background: #f5f5f5; }}
.container {{ max-width: 1200px; margin: 0 auto; height:calc(100vh - 40px) }}
</style>
</head>
<body>
<div id="dashboard-content" class="container"></div>
<script type="module">
import {{ DatabricksDashboard }} from "https://cdn.jsdelivr.net/npm/@databricks/aibi-client@0.0.0-alpha.7/+esm";
const dashboard = new DatabricksDashboard({{
instanceUrl: "{CONFIG['instance_url']}",
workspaceId: "{CONFIG['workspace_id']}",
dashboardId: "{CONFIG['dashboard_id']}",
token: "{token}",
container: document.getElementById("dashboard-content")
}});
dashboard.initialize();
</script>
</body>
</html>"""
# -----------------------------------------------------------------------------
# HTTP server
# -----------------------------------------------------------------------------
class RequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path != "/":
self.send_response(404)
self.send_header("Content-Type", "text/plain")
self.end_headers()
self.wfile.write(b"Not Found")
return
try:
token = get_scoped_token()
html = generate_html(token)
status = 200
except Exception as e:
html = f"<h1>Error</h1><p>{e}</p>"
status = 500
self.send_response(status)
self.send_header("Content-Type", "text/html")
self.end_headers()
self.wfile.write(html.encode())
def start_server():
missing = [k for k, v in CONFIG.items() if not v]
if missing:
print(f"Missing: {', '.join(missing)}", file=sys.stderr)
sys.exit(1)
server = HTTPServer(("localhost", CONFIG["port"]), RequestHandler)
print(f":rocket: Server running on http://localhost:{CONFIG['port']}")
try:
server.serve_forever()
except KeyboardInterrupt:
sys.exit(0)
if __name__ == "__main__":
start_server()
JavaScript
Copiare e salvare il file in un file denominato example.js.
#!/usr/bin/env node
const http = require('http');
const https = require('https');
const { URL, URLSearchParams } = require('url');
// This constant is just a mapping of environment variables to their respective
// values.
const CONFIG = {
instanceUrl: process.env.INSTANCE_URL,
dashboardId: process.env.DASHBOARD_ID,
servicePrincipalId: process.env.SERVICE_PRINCIPAL_ID,
servicePrincipalSecret: process.env.SERVICE_PRINCIPAL_SECRET,
externalViewerId: process.env.EXTERNAL_VIEWER_ID,
externalValue: process.env.EXTERNAL_VALUE,
workspaceId: process.env.WORKSPACE_ID,
port: process.env.PORT || 3000,
};
const basicAuth = Buffer.from(`${CONFIG.servicePrincipalId}:${CONFIG.servicePrincipalSecret}`).toString('base64');
// ------------------------------------------------------------------------------------------------
// Main
// ------------------------------------------------------------------------------------------------
function startServer() {
const missing = Object.keys(CONFIG).filter((key) => !CONFIG[key]);
if (missing.length > 0) throw new Error(`Missing: ${missing.join(', ')}`);
const server = http.createServer(async (req, res) => {
// This is a demo server, we only support GET requests to the root URL.
if (req.method !== 'GET' || req.url !== '/') {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
return;
}
let html = '';
let status = 200;
try {
const token = await getScopedToken();
html = generateHTML(token);
} catch (error) {
html = `<h1>Error</h1><p>${error.message}</p>`;
status = 500;
} finally {
res.writeHead(status, { 'Content-Type': 'text/html' });
res.end(html);
}
});
server.listen(CONFIG.port, () => {
console.log(`🚀 Server running on http://localhost:${CONFIG.port}`);
});
process.on('SIGINT', () => process.exit(0));
process.on('SIGTERM', () => process.exit(0));
}
async function getScopedToken() {
// 1. Get all-api token. This will allow you to access the /tokeninfo
// endpoint, which contains the information required to generate a scoped token
const {
data: { access_token: oidcToken },
} = await httpRequest(`${CONFIG.instanceUrl}/oidc/v1/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${basicAuth}`,
},
body: new URLSearchParams({
grant_type: 'client_credentials',
scope: 'all-apis',
}),
});
// 2. Get token info. This information is **required** for generating a token that is correctly downscoped.
// A correctly downscoped token will only have access to a handful of APIs, and within those APIs, only
// a the specific resources required to render the dashboard.
//
// This is essential to prevent leaking a privileged token.
//
// At the time of writing, OAuth tokens in Databricks are valid for 1 hour.
const tokenInfoUrl = new URL(
`${CONFIG.instanceUrl}/api/2.0/lakeview/dashboards/${CONFIG.dashboardId}/published/tokeninfo`,
);
tokenInfoUrl.searchParams.set('external_viewer_id', CONFIG.externalViewerId);
tokenInfoUrl.searchParams.set('external_value', CONFIG.externalValue);
const { data: tokenInfo } = await httpRequest(tokenInfoUrl.toString(), {
headers: { Authorization: `Bearer ${oidcToken}` },
});
// 3. Generate scoped token. This call is very similar to what was issued before, but now we are providing the scoping to make the generated token
// safe to pass to a browser.
const { authorization_details, ...params } = tokenInfo;
const {
data: { access_token },
} = await httpRequest(`${CONFIG.instanceUrl}/oidc/v1/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${basicAuth}`,
},
body: new URLSearchParams({
grant_type: 'client_credentials',
...params,
authorization_details: JSON.stringify(authorization_details),
}),
});
return access_token;
}
startServer();
// ------------------------------------------------------------------------------------------------
// Helper functions
// ------------------------------------------------------------------------------------------------
/**
* Helper function to create HTTP requests.
* @param {string} url - The URL to make the request to.
* @param {Object} options - The options for the request.
* @param {string} options.method - The HTTP method to use.
* @param {Object} options.headers - The headers to include in the request.
* @param {Object} options.body - The body to include in the request.
* @returns {Promise<Object>} A promise that resolves to the response data.
*/
function httpRequest(url, { method = 'GET', headers = {}, body } = {}) {
return new Promise((resolve, reject) => {
const isHttps = url.startsWith('https://');
const lib = isHttps ? https : http;
const options = new URL(url);
options.method = method;
options.headers = headers;
const req = lib.request(options, (res) => {
let data = '';
res.on('data', (chunk) => (data += chunk));
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
try {
resolve({ data: JSON.parse(data) });
} catch {
resolve({ data });
}
} else {
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
}
});
});
req.on('error', reject);
if (body) {
if (typeof body === 'string' || Buffer.isBuffer(body)) {
req.write(body);
} else if (body instanceof URLSearchParams) {
req.write(body.toString());
} else {
req.write(JSON.stringify(body));
}
}
req.end();
});
}
function generateHTML(token) {
return `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Demo</title>
<style>
body { font-family: system-ui; margin: 0; padding: 20px; background: #f5f5f5; }
.container { max-width: 1200px; margin: 0 auto; height:calc(100vh - 40px) }
</style>
</head>
<body>
<div id="dashboard-content" class="container"></div>
<script type="module">
/**
* We recommend bundling the dependency instead of using a CDN. However, for demonstration purposes,
* we are just using a CDN.
*
* We do not recommend one CDN over another and encourage decoupling the dependency from third-party code.
*/
import { DatabricksDashboard } from "https://cdn.jsdelivr.net/npm/@databricks/aibi-client@0.0.0-alpha.7/+esm";
const dashboard = new DatabricksDashboard({
instanceUrl: "${CONFIG.instanceUrl}",
workspaceId: "${CONFIG.workspaceId}",
dashboardId: "${CONFIG.dashboardId}",
token: "${token}",
container: document.getElementById("dashboard-content")
});
dashboard.initialize();
</script>
</body>
</html>`;
}
Passaggio 5: Eseguire l'applicazione di esempio
Sostituire i valori seguenti e quindi eseguire il blocco di codice dal terminale. I valori non devono essere racchiusi tra trattini angolari (< >):
- Usare l'URL dell'area di lavoro per trovare e sostituire i valori seguenti:
<your-instance><workspace_id><dashboard_id>
- Sostituire i valori seguenti con i valori creati durante la creazione dell'entità servizio (passaggio 2):
<service_principal_id>-
<service_principal_secret>(segreto del cliente)
- Sostituire i valori seguenti con gli identificatori associati agli utenti dell'applicazione esterna:
<some-external-viewer><some-external-value>
- Sostituire
</path/to/example>con il percorso del.pyfile o.jscreato nel passaggio precedente. Includere l'estensione del file.
Annotazioni
Non includere informazioni personali identificabili (PII) nel valore EXTERNAL_VIEWER_ID.
INSTANCE_URL='https://<your-instance>.databricks.com' \
WORKSPACE_ID='<workspace_id>' \
DASHBOARD_ID='<dashboard_id>' \
SERVICE_PRINCIPAL_ID='<service-principal-id>' \
SERVICE_PRINCIPAL_SECRET='<service-principal_secret>' \
EXTERNAL_VIEWER_ID='<some-external-viewer>' \
EXTERNAL_VALUE='<some-external-value>' \
~</path/to/example>
# Terminal will output: :rocket: Server running on http://localhost:3000