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