0

We've been getting started with CubeJS. We are using BiqQuery, with the following heirarchy:

  • Project (All client)
    • Dataset (Corresponding to a single client)
      • Tables (Different data-types for a single client)

We'd like to use COMPILE_CONTEXT to allow different clients to access different Datasets based on the JWT that we issue them after authentication. The JWT includes the user info that'd cause our schema to select a different dataset:

const {
    securityContext: { dataset_id },
} = COMPILE_CONTEXT;


cube(`Sessions`, {
    sql: `SELECT * FROM ${ dataset_id }.sessions_export`,

    measures: {
        // Count of all session objects
        count: {
            sql: `Status`,
            type: `count`,
        },

In testing, we've found that the COMPILE_CONTEXT global variable is set when the server is launched, meaning that even if a different client submits a request to Cube with a different dataset_id, the old one is used by the server, sending info from the old dataset. The Cube docs on Multi-tenancy state that COMPILE_CONTEXT should be used in our scenario (at least, this is my understanding):

Multitenant COMPILE_CONTEXT should be used when users in fact access different databases. For example, if you provide SaaS ecommerce hosting and each of your customers have a separate database, then each ecommerce store should be modelled as a separate tenant.

SECURITY_CONTEXT, on the other hand, is set at Query time, so we tried to also access the appropriate data from SECURITY_CONTEXT like so:

cube(`Sessions`, {
    sql: `SELECT * FROM ${SECURITY_CONTEXT.dataset_id}.sessions_export`,

But the query being sent to the database (found in the error log in the Cube dev server) is SELECT * FROM [object Object].sessions_export) AS sessions.

I'd love to inspect the SECURITY_CONTEXT variable but I'm having trouble finding how to do this, as it's only accessible within our cube Sql to my knowledge.

Any help would be appreciated! We are open to other routes besides those described above. In a nutshell, how can we deliver a specific dataset to a client using a unique JWT?

1 Answers1

1

Given that all your datasets are in the same BigQuery database, I think your use-case reflects the Multiple DB Instances with Same Schema part of the documentation (that title could definitely be improved):

// cube.js
const PostgresDriver = require('@cubejs-backend/postgres-driver');

module.exports = {
  contextToAppId: ({ securityContext }) =>
    `CUBEJS_APP_${securityContext.dataset_id}`,
  driverFactory: ({ securityContext }) =>
    new PostgresDriver({
      database: `${securityContext.dataset_id}`,
    }),
};

// schema/Sessions.js
cube(`Sessions`, {
    sql: `SELECT * FROM sessions_export`,
}
Hassan Khan
  • 766
  • 3
  • 9
  • 21
  • 1
    Thanks! Is driverFactory necessary? I've gotten it working by just using contextToAppId. By the way, from my semi-novice perspective, the key insight / confusion was that cube is caching different compiled schemas behind the scenes depending on the result of this function. Perhaps a note on this in SECURITY_CONTEXT vs COMPILE_CONTEXT would have helped, or elsewhere. – Ben Holmquist May 27 '21 at 01:30