From 39843b7737694687799c398ae74fdd91ff5ca627 Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Fri, 15 May 2026 00:47:59 +0300 Subject: [PATCH] SEC - LAUNCHER: bind agent modules to Tasker workspace --- server/dev-server.mjs | 65 ++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/server/dev-server.mjs b/server/dev-server.mjs index 9253718..65a654a 100644 --- a/server/dev-server.mjs +++ b/server/dev-server.mjs @@ -424,7 +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, workspaceSlug); + const serviceModules = + serviceSlug === "task-manager" + ? resolveTaskManagerWorkspaceServiceModules(snapshot.data, user, serviceSlug, workspaceSlug) + : resolveUserServiceModules(snapshot.data, user, serviceSlug, null); const workspacePolicy = serviceSlug === "task-manager" ? resolveTaskManagerWorkspacePolicy(snapshot.data, groups, allowed, user, workspaceSlug, serviceModules) @@ -2509,47 +2512,59 @@ function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess, u }; } -function resolveUserServiceModules(data, user, serviceSlug, workspaceSlug = null) { +function resolveTaskManagerWorkspaceServiceModules(data, user, serviceSlug, workspaceSlug) { + const normalizedWorkspaceSlug = normalizeOptionalText(workspaceSlug); + + if (!normalizedWorkspaceSlug) { + return {}; + } + + const workspaces = resolveTaskManagerWorkspaceAssignments(data, user); + const workspaceAssignment = workspaces.find((workspace) => workspace.slug === normalizedWorkspaceSlug); + const boundClientId = resolveTaskManagerWorkspaceClientId(data, normalizedWorkspaceSlug); + const clientId = + workspaceAssignment?.clientId ?? + (boundClientId ? null : isPublicPoolUser(data, user) ? publicPoolClientId : null); + + return resolveUserServiceModules(data, user, serviceSlug, clientId); +} + +function resolveTaskManagerWorkspaceClientId(data, workspaceSlug) { + const normalizedWorkspaceSlug = normalizeOptionalText(workspaceSlug); + if (!normalizedWorkspaceSlug) return null; + + const client = data.clients.find((candidate) => resolveTaskManagerWorkspaceBinding(candidate, normalizedWorkspaceSlug)); + return client?.id ?? null; +} + +function isPublicPoolUser(data, user) { + return data.memberships.some( + (membership) => membership.userId === user?.id && membership.clientId === publicPoolClientId && membership.status === "active" + ); +} + +function resolveUserServiceModules(data, user, serviceSlug, clientId) { if (!user?.id) return {}; + const normalizedClientId = normalizeOptionalText(clientId); + if (!normalizedClientId) 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.clientId === normalizedClientId && entitlement.userId === user.id && entitlement.serviceId === service.id && - entitlement.enabled && - (!workspaceClientIds || workspaceClientIds.has(entitlement.clientId)) + entitlement.enabled ) .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();