diff --git a/server/dev-server.mjs b/server/dev-server.mjs index 9b1b6c1..9253718 100644 --- a/server/dev-server.mjs +++ b/server/dev-server.mjs @@ -406,6 +406,7 @@ app.post("/api/internal/access/check", (req, res) => { const snapshot = controlPlaneStore.getSnapshot({ name: "NODE.DC internal access check" }); const user = findInternalAccessUser(snapshot.data, req.body); const serviceSlug = sanitizeServiceSlug(req.body?.serviceSlug); + const workspaceSlug = normalizeOptionalText(req.body?.workspaceSlug ?? req.body?.workspace?.slug); if (!user) { res.json({ @@ -423,10 +424,10 @@ app.post("/api/internal/access/check", (req, res) => { const groups = resolveRequiredGroups(snapshot.data, user); const app = getAppsForUser(groups).find((candidate) => candidate.slug === serviceSlug); const allowed = Boolean(app?.hasAccess); - const serviceModules = resolveUserServiceModules(snapshot.data, user, serviceSlug); + const serviceModules = resolveUserServiceModules(snapshot.data, user, serviceSlug, workspaceSlug); const workspacePolicy = serviceSlug === "task-manager" - ? resolveTaskManagerWorkspacePolicy(snapshot.data, groups, allowed, user, null, serviceModules) + ? resolveTaskManagerWorkspacePolicy(snapshot.data, groups, allowed, user, workspaceSlug, serviceModules) : null; res.json({ @@ -2508,20 +2509,47 @@ function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess, u }; } -function resolveUserServiceModules(data, user, serviceSlug) { +function resolveUserServiceModules(data, user, serviceSlug, workspaceSlug = null) { if (!user?.id) return {}; const service = data.services.find( (candidate) => candidate.slug === serviceSlug || candidate.authentikApplicationSlug === serviceSlug ); if (!service?.id) return {}; + const workspaceClientIds = resolveTaskManagerWorkspaceClientIds(data, user, workspaceSlug); return Object.fromEntries( (data.serviceModuleEntitlements ?? []) - .filter((entitlement) => entitlement.userId === user.id && entitlement.serviceId === service.id && entitlement.enabled) + .filter( + (entitlement) => + entitlement.userId === user.id && + entitlement.serviceId === service.id && + entitlement.enabled && + (!workspaceClientIds || workspaceClientIds.has(entitlement.clientId)) + ) .map((entitlement) => [entitlement.moduleId, true]) ); } +function resolveTaskManagerWorkspaceClientIds(data, user, workspaceSlug) { + const normalizedWorkspaceSlug = normalizeOptionalText(workspaceSlug); + if (!normalizedWorkspaceSlug || !user?.id) return null; + + const clientIds = new Set(); + for (const membership of data.taskManagerMemberships ?? []) { + if (membership.userId === user.id && normalizeOptionalText(membership.workspaceSlug) === normalizedWorkspaceSlug) { + clientIds.add(membership.clientId); + } + } + + for (const client of data.clients ?? []) { + if (resolveTaskManagerWorkspaceBinding(client, normalizedWorkspaceSlug)) { + clientIds.add(client.id); + } + } + + return clientIds; +} + function getFrontchannelLogoutUrls() { const urls = [config.taskLogoutUrl]; const launcherData = readLauncherData();