Skip to content

Commit 8537cc8

Browse files
acmeguyclaude
andcommitted
010: Filter discover endpoint by JWT partition claim, remap dev ports
- Discover endpoint now only returns datasources from the team matching the WorkOS JWT partition claim instead of all teams the user belongs to - Remap Actions host port 3000→3001 and Redis 6379→6381 in dev compose to avoid conflicts with other local services Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c4a484a commit 8537cc8

3 files changed

Lines changed: 103 additions & 6 deletions

File tree

docker-compose.dev.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ services:
2323
image: redis:7.0.0
2424
restart: always
2525
ports:
26-
- 6379:6379
26+
- 6381:6379
2727
networks:
2828
- synmetrix_default
2929

@@ -36,7 +36,7 @@ services:
3636
- ./services/actions/src:/app/src
3737
- ./services/actions/index.js:/app/index.js
3838
ports:
39-
- 3000:3000
39+
- 3001:3000
4040
env_file:
4141
- .env
4242
- .dev.env

services/cubejs/src/routes/__tests__/discover.test.js

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { describe, it } from "node:test";
22
import assert from "node:assert/strict";
33

4-
import { extractCubes, buildDiscoverResponse } from "../discover.js";
4+
import {
5+
extractCubes,
6+
buildDiscoverResponse,
7+
resolvePartitionTeamIds,
8+
} from "../discover.js";
59

610
// --- Helpers ---
711

@@ -236,4 +240,71 @@ describe("buildDiscoverResponse", () => {
236240
assert.equal(result[0].version_id, null);
237241
assert.deepEqual(result[0].cubes, []);
238242
});
243+
244+
it("filters datasources by partition team IDs", () => {
245+
const dataSources = [
246+
makeDatasource("ds-a", "match", [yamlSchema("a.yml", [{ name: "A" }])], {
247+
team_id: "team-1",
248+
}),
249+
makeDatasource("ds-b", "other", [yamlSchema("b.yml", [{ name: "B" }])], {
250+
team_id: "team-2",
251+
}),
252+
];
253+
const partitionTeamIds = new Set(["team-1"]);
254+
const result = buildDiscoverResponse(dataSources, partitionTeamIds);
255+
assert.equal(result.length, 1);
256+
assert.equal(result[0].id, "ds-a");
257+
});
258+
259+
it("returns all datasources when no partitionTeamIds provided", () => {
260+
const dataSources = [
261+
makeDatasource("ds-a", "a", [], { team_id: "team-1" }),
262+
makeDatasource("ds-b", "b", [], { team_id: "team-2" }),
263+
];
264+
const result = buildDiscoverResponse(dataSources, null);
265+
assert.equal(result.length, 2);
266+
});
267+
});
268+
269+
// --- resolvePartitionTeamIds ---
270+
271+
describe("resolvePartitionTeamIds", () => {
272+
it("returns team IDs matching the partition", () => {
273+
const members = [
274+
{ team_id: "t1", team: { settings: { partition: "blue.is" } } },
275+
{ team_id: "t2", team: { settings: { partition: "other.co" } } },
276+
{ team_id: "t3", team: { settings: { partition: "blue.is" } } },
277+
];
278+
const ids = resolvePartitionTeamIds(members, "blue.is");
279+
assert.equal(ids.size, 2);
280+
assert.ok(ids.has("t1"));
281+
assert.ok(ids.has("t3"));
282+
assert.ok(!ids.has("t2"));
283+
});
284+
285+
it("returns null when no partition in token", () => {
286+
const members = [
287+
{ team_id: "t1", team: { settings: { partition: "blue.is" } } },
288+
];
289+
assert.equal(resolvePartitionTeamIds(members, undefined), null);
290+
assert.equal(resolvePartitionTeamIds(members, null), null);
291+
});
292+
293+
it("returns empty set when no teams match", () => {
294+
const members = [
295+
{ team_id: "t1", team: { settings: { partition: "other.co" } } },
296+
];
297+
const ids = resolvePartitionTeamIds(members, "blue.is");
298+
assert.equal(ids.size, 0);
299+
});
300+
301+
it("handles teams with no settings", () => {
302+
const members = [
303+
{ team_id: "t1", team: { settings: null } },
304+
{ team_id: "t2", team: {} },
305+
{ team_id: "t3" },
306+
];
307+
const ids = resolvePartitionTeamIds(members, "blue.is");
308+
assert.equal(ids.size, 0);
309+
});
239310
});

services/cubejs/src/routes/discover.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,29 @@ export function extractCubes(schema) {
3636
}));
3737
}
3838

39+
/**
40+
* Build a set of team IDs whose partition matches the JWT partition claim.
41+
*/
42+
export function resolvePartitionTeamIds(members, partition) {
43+
if (!partition) return null; // no filtering if no partition in token
44+
const ids = new Set();
45+
for (const member of members) {
46+
if (member.team?.settings?.partition === partition) {
47+
ids.add(member.team_id);
48+
}
49+
}
50+
return ids;
51+
}
52+
3953
/**
4054
* Build the datasources response from findUser result.
55+
* When partitionTeamIds is provided, only include datasources from those teams.
4156
*/
42-
export function buildDiscoverResponse(dataSources) {
43-
return dataSources.map((ds) => {
57+
export function buildDiscoverResponse(dataSources, partitionTeamIds) {
58+
const filtered = partitionTeamIds
59+
? dataSources.filter((ds) => partitionTeamIds.has(ds.team_id))
60+
: dataSources;
61+
return filtered.map((ds) => {
4462
const activeBranch =
4563
ds.branches?.find((b) => b.status === "active") || ds.branches?.[0];
4664
const latestVersion = activeBranch?.versions?.[0] || null;
@@ -103,7 +121,15 @@ export default async function discover(req, res) {
103121
return res.json({ datasources: [] });
104122
}
105123

106-
res.json({ datasources: buildDiscoverResponse(user.dataSources) });
124+
// Only return datasources from the team matching the JWT partition claim
125+
const partitionTeamIds = resolvePartitionTeamIds(
126+
user.members,
127+
payload.partition
128+
);
129+
130+
res.json({
131+
datasources: buildDiscoverResponse(user.dataSources, partitionTeamIds),
132+
});
107133
} catch (err) {
108134
const status = err.status || 500;
109135
if (status >= 500) {

0 commit comments

Comments
 (0)