apiVersion: v1 kind: ConfigMap metadata: name: {{ taskserv.name }}-cube-config namespace: {{ taskserv.namespace }} labels: app.kubernetes.io/name: {{ taskserv.name }}-cube app.kubernetes.io/managed-by: provisioning data: cube.js: | /* eslint-env es2022 */ const TENANT_MEMBERS = ["FeedbackRecords.tenantId"]; const REQUIRED_SCOPE = "xm:cube:query"; function assertRequiredEnvironmentVariable(name) { const value = process.env[name]; if (typeof value !== "string" || value.trim().length === 0) { throw new Error(`${name} is required to run Cube`); } } assertRequiredEnvironmentVariable("CUBEJS_API_SECRET"); function getStringClaim(securityContext, claim) { const value = securityContext?.[claim]; if (typeof value !== "string") return null; const trimmedValue = value.trim(); return trimmedValue.length > 0 ? trimmedValue : null; } function getRequiredStringClaim(securityContext, claim) { const value = getStringClaim(securityContext, claim); if (!value) throw new Error(`Cube query rejected: missing ${claim} security context`); return value; } function collectFilterMembers(filters) { if (!Array.isArray(filters)) return []; return filters.flatMap((filter) => [ ...(typeof filter?.member === "string" ? [filter.member] : []), ...(typeof filter?.dimension === "string" ? [filter.dimension] : []), ...collectFilterMembers(filter?.and), ...collectFilterMembers(filter?.or), ]); } function collectOrderMembers(order) { if (!order) return []; if (Array.isArray(order)) { return order.map((e) => (Array.isArray(e) ? e[0] : null)).filter((m) => typeof m === "string"); } if (typeof order === "object") return Object.keys(order); return []; } function collectTimeDimensionMembers(timeDimensions) { if (!Array.isArray(timeDimensions)) return []; return timeDimensions.map((td) => td?.dimension).filter((d) => typeof d === "string"); } function collectQueryMembers(query) { const q = query ?? {}; const members = [ ...(Array.isArray(q.measures) ? q.measures : []), ...(Array.isArray(q.dimensions) ? q.dimensions : []), ...(Array.isArray(q.segments) ? q.segments : []), ...collectTimeDimensionMembers(q.timeDimensions), ...collectFilterMembers(q.filters), ...collectOrderMembers(q.order), ].filter((m) => typeof m === "string"); return Array.from(new Set(members)).sort((a, b) => a.localeCompare(b)); } function assertValidSecurityContext(securityContext) { const tenantId = getRequiredStringClaim(securityContext, "tenantId"); const feedbackDirectoryId = getRequiredStringClaim(securityContext, "feedbackDirectoryId"); const workspaceId = getRequiredStringClaim(securityContext, "workspaceId"); const scope = getRequiredStringClaim(securityContext, "scope"); if (scope !== REQUIRED_SCOPE) throw new Error("Cube query rejected: invalid Cube query scope"); if (tenantId !== feedbackDirectoryId) throw new Error("Cube query rejected: tenantId/feedbackDirectoryId mismatch"); return { tenantId, feedbackDirectoryId, workspaceId, organizationId: getRequiredStringClaim(securityContext, "organizationId"), userId: getRequiredStringClaim(securityContext, "userId"), requestId: getRequiredStringClaim(securityContext, "jti"), source: getRequiredStringClaim(securityContext, "source"), }; } function assertNoCallerTenantMember(query) { for (const member of collectQueryMembers(query)) { if (TENANT_MEMBERS.includes(member)) throw new Error("Cube query rejected: tenant filters are enforced by Cube"); } } function logCubeQueryAuditEvent(context, query, { error, status = "success" } = {}) { const errorName = error instanceof Error ? error.name : undefined; const errorMessage = error instanceof Error ? error.message : error ? String(error) : undefined; console.log(JSON.stringify({ type: "audit", event: "cube.query", status, timestamp: new Date().toISOString(), tenantId: context.tenantId, feedbackDirectoryId: context.feedbackDirectoryId, workspaceId: context.workspaceId, organizationId: context.organizationId, userId: context.userId, requestId: context.requestId, source: context.source, members: collectQueryMembers(query), ...(errorName ? { errorName } : {}), ...(errorMessage ? { errorMessage } : {}), })); } function logCubeQuerySecurityContextFailure(query, error) { console.log(JSON.stringify({ type: "audit", event: "cube.query", status: "failure", timestamp: new Date().toISOString(), members: collectQueryMembers(query), errorName: error instanceof Error ? error.name : undefined, errorMessage: error instanceof Error ? error.message : String(error), })); } function queryRewrite(query, rewriteContext) { const cubeQuery = query ?? {}; let context; try { context = assertValidSecurityContext(rewriteContext?.securityContext); } catch (error) { logCubeQuerySecurityContextFailure(cubeQuery, error); throw error; } try { assertNoCallerTenantMember(cubeQuery); } catch (error) { logCubeQueryAuditEvent(context, cubeQuery, { error, status: "failure" }); throw error; } const queriedCubePrefixes = new Set(collectQueryMembers(cubeQuery).map((m) => m.split(".")[0])); const rewrittenQuery = { ...cubeQuery, filters: [ ...(Array.isArray(cubeQuery.filters) ? cubeQuery.filters : []), ...TENANT_MEMBERS.filter((m) => queriedCubePrefixes.has(m.split(".")[0])).map((member) => ({ member, operator: "equals", values: [context.tenantId], })), ], }; logCubeQueryAuditEvent(context, rewrittenQuery); return rewrittenQuery; } module.exports = { queryRewrite };