Skip to main content

Semantic Layer Authentication & Authorization: Ontology, Cube, Trino, and Ranger

This page covers how a user's identity flows from the BFF API through the Ontology backend and Cube semantic layer to Trino query execution, and where Ranger enforces access policies.

For a higher-level overview of the token exchange model, see Token Exchange and Data Access.


Routes Covered

All routes are defined in src/routes/ontologyRoute.js and protected by validateJWT + exchangeTokenForBackendMiddleware.

MethodRoutePurpose
GET/ontology/v1/objectsList ontology objects
GET/ontology/v1/objects/catalogList catalog-level objects
POST/ontology/v1/objectsCreate ontology object
GET/ontology/v1/objects/by-name/:nameGet object by name
GET/ontology/v1/objects/:object_idGet object by ID
DELETE/ontology/v1/objects/:object_idDelete object
GET/ontology/v1/cube/repositoryGet Cube data model repository
GET/ontology/v1/cube/schema-versionGet Cube schema version
POST/ontology/v1/cube/loadExecute a Cube query (analytical load)
GET/ontology/v1/cube/loadExecute a Cube query via query params
POST/ontology/v1/cube/sqlExecute Cube SQL endpoint
POST/ontology/v1/cube/dry-runDry-run a Cube query
GET/ontology/v1/cube/dry-runDry-run via query params
GET/ontology/v1/cube/metaGet Cube metadata (measures, dimensions)
GET/POST/PUT/DELETE/ontology/v1/workbooks/*Workbook CRUD

Cube routes (/ontology/v1/cube/*) are the ones that result in SQL execution against Trino.


End-to-End Flow


Step 1: JWT Validation — BFF

The validateJWT middleware validates the user's Keycloak JWT. The exchange to the Ontology backend audience happens next via exchangeTokenForBackendMiddleware.


Step 2: Token Exchange — BFF to Ontology Backend

The BFF exchanges the user JWT for a token scoped to the Ontology backend before forwarding:

  • Audience: ${extWorkspaceId}-${config.ontology.audience} (default: {workspaceId}-ontology-backend)
  • The exchanged token is stored on req.exchangedToken
  • The user's original session token is never forwarded to the Ontology backend
  • Exchanged tokens are cached in Valkey (default TTL: 180s)

src/services/ontologyService.js (genericOntologyRequest) forwards all proxied requests with:

Authorization: Bearer ${req.exchangedToken}
SECURITY GAP

ONTOLOGY_AUTH_DISABLED=true bypasses token exchange entirely and forwards the raw user session token to the Ontology backend. There is no guard preventing this from being set in production. See Security Gaps.


Step 3: Ontology Backend to Cube

The Ontology backend receives the BFF request carrying the user's ontology-scoped JWT, extracts the username from its claims, and creates a Cube-specific HS256 JWT signed with CUBE_API_SECRET. This JWT contains a trino_user claim set to the authenticated user's identity.

This is the handoff point between the Keycloak-based auth chain and Cube's own auth scheme.

TODO: Confirm how the Ontology backend derives the value it sets in trino_user — whether it uses preferred_username, sub, or another claim from the incoming ontology-scoped JWT. This determines which identity Ranger sees.

SECURITY GAP

The source claim used for trino_user has not been confirmed. If it does not match the username format Ranger expects, all semantic layer queries will be denied. See Security Gaps.


Step 4: Cube Auth — Validating the Incoming JWT

Cube validates every incoming request against a symmetric HS256 secret (CUBE_API_SECRET). The token must contain a trino_user claim identifying the originating user. Requests without a valid trino_user are rejected.

Health probe requests (/livez, /readyz) bypass this check and run under the Trino service account identity.

info

Cube uses a symmetric HS256 secret rather than Keycloak RS256 JWTs. This is a separate trust boundary from the rest of the platform's token exchange chain — the Ontology backend is the issuer.


Step 5: driverFactory — Per-User Trino Identity

driverFactory is Cube's equivalent of Superset's DATABASE_CONNECTION_MUTATOR. For each query it builds a Trino connection using the trino_user from the validated JWT as the query identity, while using a shared service account (TRINO_SERVICE_USER) for the underlying BasicAuth connection credentials.

The split mirrors the Superset pattern:

ValuePurpose
Query usertrino_user from JWT claimIdentity Ranger evaluates policies against
Connection credentialsTRINO_SERVICE_USER / TRINO_SERVICE_PASSWORDCredentials Trino uses to accept the connection

Each unique trino_user also gets its own Cube query orchestrator, ensuring query isolation between users.


Step 6: Ranger Policy Enforcement — Trino

Because driverFactory sets user to the real user's identity (not the service account), Ranger evaluates per-user policies on every semantic layer query:

What Ranger checksHow it's resolved
User identitytrinoUser from the Cube JWT claim (via user field → X-Trino-User)
ResourceCatalog → Schema → Table → Column
Access typeselect, row filters, column masking
Policy matchFirst matching policy wins

Row filters and column masks configured in Ranger apply transparently — the user never sees the restricted data and Cube receives only the filtered result set.

info

The TRINO_SERVICE_USER credentials handle connection authentication. Ranger policy enforcement uses the user field, not the BasicAuth user. The service account must have impersonation rights in Trino to submit queries on behalf of other users.

TODO: Confirm that TRINO_SERVICE_USER is granted the impersonate privilege in the Ranger trinouser seed policy. If it does not have this privilege, Trino may reject queries where the BasicAuth user and the query user differ.

SECURITY GAP

It has not been confirmed that the Cube service account holds the impersonate privilege in Ranger. Without it, all non-probe Cube queries to Trino will be rejected. See Security Gaps.


Cube Model Loading

Cube loads its semantic data model dynamically from the Ontology backend. For this internal call, Cube first uses an ontology_access_token embedded in the request's security context (if provided by the Ontology backend). If none is present, it falls back to a Keycloak client credentials token issued to the Cube service account.

Schema versioning follows the same token resolution path and is used to invalidate the Cube model cache when the data model changes.


Auth Bypass (BFF Layer)

Setting ONTOLOGY_AUTH_DISABLED=true causes the BFF to skip token exchange and forward the user's raw JWT to the Ontology backend. This is a development convenience and must not be enabled in production.

See Security Gaps for the risk this poses if misconfigured.


Configuration

Key configuration areas:

  • BFF: Ontology backend URL and Keycloak audience for the token exchange, plus the dev auth bypass flag
  • Cube: CUBEJS_API_SECRET (HS256 signing secret), Trino connection details, TRINO_SERVICE_USER/PASSWORD for the connection credentials, and Keycloak client credentials for model loading fallback

Go Deeper