diff --git a/.env.example b/.env.example index 70d2c439..7759231f 100644 --- a/.env.example +++ b/.env.example @@ -10,7 +10,7 @@ REDIS_ADDR=redis://redis:6379 # --- cubejs --- CUBESQL_DEBUG_QTRACE=true -CUBESTORE_VERSION=v1.3.23-arm64v8 +CUBESTORE_VERSION=v1.6.37 CUBEJS_URL=http://cubejs:4000 CUBEJS_SECRET=cubejsKey diff --git a/services/cubejs/package.json b/services/cubejs/package.json index 8df4b689..ee7cc1dd 100644 --- a/services/cubejs/package.json +++ b/services/cubejs/package.json @@ -7,36 +7,36 @@ "jsdoc": "jsdoc index.js -r src -d docs" }, "dependencies": { - "@cubejs-backend/api-gateway": "^1.6.19", - "@cubejs-backend/athena-driver": "^1.6.19", - "@cubejs-backend/bigquery-driver": "^1.6.19", - "@cubejs-backend/clickhouse-driver": "^1.6.19", - "@cubejs-backend/crate-driver": "^1.6.19", - "@cubejs-backend/cubestore-driver": "^1.6.19", - "@cubejs-backend/databricks-jdbc-driver": "^1.6.19", - "@cubejs-backend/dremio-driver": "^1.6.19", - "@cubejs-backend/druid-driver": "^1.6.19", - "@cubejs-backend/duckdb-driver": "^1.6.19", - "@cubejs-backend/elasticsearch-driver": "^1.6.19", - "@cubejs-backend/firebolt-driver": "^1.6.19", - "@cubejs-backend/hive-driver": "^1.6.19", - "@cubejs-backend/jdbc-driver": "^1.6.19", - "@cubejs-backend/ksql-driver": "^1.6.19", - "@cubejs-backend/materialize-driver": "^1.6.19", - "@cubejs-backend/mongobi-driver": "^1.6.19", - "@cubejs-backend/mssql-driver": "^1.6.19", - "@cubejs-backend/mysql-driver": "^1.6.19", - "@cubejs-backend/oracle-driver": "^1.6.19", - "@cubejs-backend/pinot-driver": "^1.6.19", - "@cubejs-backend/postgres-driver": "^1.6.19", - "@cubejs-backend/prestodb-driver": "^1.6.19", - "@cubejs-backend/query-orchestrator": "^1.6.19", - "@cubejs-backend/questdb-driver": "^1.6.19", - "@cubejs-backend/redshift-driver": "^1.6.19", - "@cubejs-backend/server-core": "^1.6.19", - "@cubejs-backend/snowflake-driver": "^1.6.19", - "@cubejs-backend/sqlite-driver": "^1.6.19", - "@cubejs-backend/trino-driver": "^1.6.19", + "@cubejs-backend/api-gateway": "^1.6.37", + "@cubejs-backend/athena-driver": "^1.6.37", + "@cubejs-backend/bigquery-driver": "^1.6.37", + "@cubejs-backend/clickhouse-driver": "^1.6.37", + "@cubejs-backend/crate-driver": "^1.6.37", + "@cubejs-backend/cubestore-driver": "^1.6.37", + "@cubejs-backend/databricks-jdbc-driver": "^1.6.37", + "@cubejs-backend/dremio-driver": "^1.6.37", + "@cubejs-backend/druid-driver": "^1.6.37", + "@cubejs-backend/duckdb-driver": "^1.6.37", + "@cubejs-backend/elasticsearch-driver": "^1.6.37", + "@cubejs-backend/firebolt-driver": "^1.6.37", + "@cubejs-backend/hive-driver": "^1.6.37", + "@cubejs-backend/jdbc-driver": "^1.6.37", + "@cubejs-backend/ksql-driver": "^1.6.37", + "@cubejs-backend/materialize-driver": "^1.6.37", + "@cubejs-backend/mongobi-driver": "^1.6.37", + "@cubejs-backend/mssql-driver": "^1.6.37", + "@cubejs-backend/mysql-driver": "^1.6.37", + "@cubejs-backend/oracle-driver": "^1.6.37", + "@cubejs-backend/pinot-driver": "^1.6.37", + "@cubejs-backend/postgres-driver": "^1.6.37", + "@cubejs-backend/prestodb-driver": "^1.6.37", + "@cubejs-backend/query-orchestrator": "^1.6.37", + "@cubejs-backend/questdb-driver": "^1.6.37", + "@cubejs-backend/redshift-driver": "^1.6.37", + "@cubejs-backend/server-core": "^1.6.37", + "@cubejs-backend/snowflake-driver": "^1.6.37", + "@cubejs-backend/sqlite-driver": "^1.6.37", + "@cubejs-backend/trino-driver": "^1.6.37", "@cubejs-backend/vertica-driver": "npm:@knowitall/vertica-driver@^0.32.3", "apache-arrow": "^21.1.0", "body-parser": "^1.19.0", diff --git a/services/cubejs/src/utils/checkSqlAuth.js b/services/cubejs/src/utils/checkSqlAuth.js index 31a2a973..43c3cab8 100644 --- a/services/cubejs/src/utils/checkSqlAuth.js +++ b/services/cubejs/src/utils/checkSqlAuth.js @@ -26,29 +26,60 @@ const buildSqlSecurityContext = (sqlCredentials) => { teamId ); - const dataSourceContext = buildSecurityContext(sqlCredentials?.datasource); + // Resolve team settings + per-member properties so queryRewrite rules can + // evaluate `property_source: team` / `property_source: member` lookups for + // SQL API logins. Without these, every rule blocks and queries get rewritten + // to the `__blocked_by_access_control__` sentinel. + const teamMember = Array.isArray(allMembers) + ? allMembers.find((m) => m.team_id === teamId) + : null; + const teamSettings = teamMember?.team?.settings || {}; + const memberProperties = teamMember?.properties || {}; + + const dataSourceContext = buildSecurityContext( + sqlCredentials?.datasource, + undefined, + undefined, + teamSettings + ); return { dataSource: dataSourceContext, ...dataSourceAccessList, + teamProperties: teamSettings, + memberProperties, }; }; /** * Check SQL authentication for a user. + * + * Cube.js v1.6 invokes this callback as `checkSqlAuth(request, user, password)` + * (see @cubejs-backend/api-gateway/dist/src/sql-server.js — the callback is + * wrapped with three positional args). Earlier builds passed a single object + * `{ username, password }`; the defensive branches below keep backward- + * compatibility with that older shape. + * * Supports two authentication methods: - * 1. WorkOS JWT as password (new): password is a JWT, username is datasource ID - * 2. Legacy sql_credentials lookup (existing): username/password from sql_credentials table + * 1. WorkOS / FraiOS JWT as password: password is a JWT, username is the datasource ID + * 2. Legacy sql_credentials lookup: username/password from the sql_credentials table * - * @param {null} _ - Unused parameter. - * @param {Object} user - The user object with username and password. - * @returns {Promise} - Resolves to { password, securityContext } + * @param {Object} request - Cube.js SQL request metadata (protocol, method, apiType) + * @param {string|Object} userArg - Username string (v1.6+) or legacy { username, password } object + * @param {string} [passwordArg] - Password string (v1.6+); absent in legacy object-shape calls + * @returns {Promise<{ password: string, securityContext: Object }>} */ -const checkSqlAuth = async (_, user) => { - const password = typeof user === "string" ? user : user?.password; - const username = typeof user === "string" ? _ : user?.username; - - // Detect if password looks like a JWT (WorkOS RS256) +const checkSqlAuth = async (request, userArg, passwordArg) => { + // Resolve the two shapes Cube has used for this callback: + // new: (request, username: string, password: string) + // legacy: (_req, { username, password }) + const username = + typeof userArg === "string" ? userArg : userArg?.username; + const password = + passwordArg ?? + (typeof userArg === "string" ? undefined : userArg?.password); + + // Detect if password looks like a JWT (WorkOS RS256 / FraiOS HS256) if (password && password.includes(".") && password.split(".").length === 3) { const tokenType = detectTokenType(password); @@ -134,8 +165,12 @@ const checkSqlAuth = async (_, user) => { } } - // Legacy sql_credentials path (unchanged) - const sqlCredentials = await findSqlCredentials(username || user); + // Legacy sql_credentials path — lookup by the plaintext username. + // Cube.js compares the supplied password against `password` in the return value. + if (!username || typeof username !== "string") { + throw new Error("Incorrect user name or password"); + } + const sqlCredentials = await findSqlCredentials(username); return { password: sqlCredentials?.password, diff --git a/services/cubejs/src/utils/dataSourceHelpers.js b/services/cubejs/src/utils/dataSourceHelpers.js index 079c00fb..ac75ef86 100644 --- a/services/cubejs/src/utils/dataSourceHelpers.js +++ b/services/cubejs/src/utils/dataSourceHelpers.js @@ -92,6 +92,11 @@ const membersFragment = ` members { id team_id + properties + team { + id + settings + } member_roles { id team_role