From 0782e13c776dc2c4641195c171dba767d3b9b0a1 Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Thu, 14 May 2026 20:40:40 +0300 Subject: [PATCH] =?UTF-8?q?FEAT=20-=20LAUNCHER:=20=D0=B2=D1=8B=D0=B4=D0=B0?= =?UTF-8?q?=D1=87=D0=B0=20service=20module=20entitlement=20=D0=B2=20access?= =?UTF-8?q?=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/dev-server.mjs | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/server/dev-server.mjs b/server/dev-server.mjs index 6e9b173..9b1b6c1 100644 --- a/server/dev-server.mjs +++ b/server/dev-server.mjs @@ -423,8 +423,11 @@ 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 workspacePolicy = - serviceSlug === "task-manager" ? resolveTaskManagerWorkspacePolicy(snapshot.data, groups, allowed, user) : null; + serviceSlug === "task-manager" + ? resolveTaskManagerWorkspacePolicy(snapshot.data, groups, allowed, user, null, serviceModules) + : null; res.json({ ok: true, @@ -433,6 +436,7 @@ app.post("/api/internal/access/check", (req, res) => { serviceSlug, groups, matchedGroups: app?.matchedGroups ?? [], + serviceModules, workspacePolicy, user: { id: user.id, @@ -2390,7 +2394,7 @@ function pruneExpiredServiceHandoffs() { } } -function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess, user, workspaceSlug = null) { +function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess, user, workspaceSlug = null, serviceModules = {}) { const mode = data.settings?.taskManager?.workspaceCreationPolicy ?? "any_authorized_user"; const groupSet = new Set(groups); const isSuperAdmin = groupSet.has("nodedc:superadmin"); @@ -2415,6 +2419,7 @@ function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess, u inviteApproval: "disabled", defaultInviteApproval, workspaces, + serviceModules, canCreateWorkspace: false, reason: "Нет доступа к Operational Core.", }; @@ -2428,6 +2433,7 @@ function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess, u inviteApproval: "disabled", defaultInviteApproval, workspaces, + serviceModules, canCreateWorkspace: false, reason: "Создание рабочих пространств отключено на уровне платформы.", }; @@ -2442,6 +2448,7 @@ function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess, u inviteApproval: "launcher", defaultInviteApproval: "launcher", workspaces, + serviceModules, canCreateWorkspace: false, reason: "Рабочие пространства этого пользователя управляются через Launcher.", }; @@ -2455,6 +2462,7 @@ function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess, u inviteApproval: defaultInviteApproval, defaultInviteApproval, workspaces, + serviceModules, canCreateWorkspace: false, reason: "Self-service workspace работает через Tasker, approve инвайтов выполняет Launcher.", }; @@ -2467,6 +2475,7 @@ function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess, u inviteApproval: "launcher", defaultInviteApproval: "launcher", workspaces, + serviceModules, canCreateWorkspace: false, reason: "Рабочие пространства этого пользователя управляются через Launcher.", }; @@ -2480,6 +2489,7 @@ function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess, u inviteApproval: defaultInviteApproval, defaultInviteApproval, workspaces, + serviceModules, canCreateWorkspace: false, reason: "Создание рабочих пространств доступно только администраторам Operational Core.", }; @@ -2492,11 +2502,26 @@ function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess, u inviteApproval: defaultInviteApproval, defaultInviteApproval, workspaces, + serviceModules, canCreateWorkspace: true, reason: "Создание рабочих пространств разрешено платформенной policy.", }; } +function resolveUserServiceModules(data, user, serviceSlug) { + if (!user?.id) return {}; + const service = data.services.find( + (candidate) => candidate.slug === serviceSlug || candidate.authentikApplicationSlug === serviceSlug + ); + if (!service?.id) return {}; + + return Object.fromEntries( + (data.serviceModuleEntitlements ?? []) + .filter((entitlement) => entitlement.userId === user.id && entitlement.serviceId === service.id && entitlement.enabled) + .map((entitlement) => [entitlement.moduleId, true]) + ); +} + function getFrontchannelLogoutUrls() { const urls = [config.taskLogoutUrl]; const launcherData = readLauncherData();