From 6e47e12f2d350dca77053eea6980df2bc2fc7308 Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Tue, 12 May 2026 19:35:44 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=A3=D0=9D=D0=9A=D0=A6=D0=98=D0=98=20-?= =?UTF-8?q?=20=D0=9C=D0=95=D0=96=D0=9F=D0=A0=D0=9E=D0=95=D0=9A=D0=A2=D0=9D?= =?UTF-8?q?=D0=90=D0=AF=20=D0=9A=D0=9E=D0=9C=D0=9C=D0=A3=D0=9D=D0=98=D0=9A?= =?UTF-8?q?=D0=90=D0=A6=D0=98=D0=AF:=20guest-=D0=B4=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D1=83=D0=BF=20Operational=20Core?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/dev-server.mjs | 28 ++++++++++++++++++++++ src/widgets/admin-overlay/AdminOverlay.tsx | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/server/dev-server.mjs b/server/dev-server.mjs index 936742f..dd60111 100644 --- a/server/dev-server.mjs +++ b/server/dev-server.mjs @@ -1449,6 +1449,16 @@ app.post("/api/admin/access/user-service", requireLauncherAdmin, asyncRoute(asyn return; } + const snapshot = controlPlaneStore.getSnapshot(req.nodedcSession.user); + if (isPublicTaskManagerGuestServiceAssignment(snapshot.data, req.body)) { + res.status(400).json({ + ok: false, + error: "task_manager_public_guest_not_assignable", + message: "Workspace Guest выдаётся только через настройки Operational Core.", + }); + return; + } + const result = await controlPlaneStore.setUserServiceAccess(req.body, req.nodedcSession.user); const syncResult = await syncUsersToAuthentik(result.data, [req.body?.userId], req.nodedcSession.user); publishControlPlaneEvent("admin.access.user-service.updated", syncResult.userIds); @@ -2181,6 +2191,24 @@ function normalizeTaskManagerRole(value) { return value === "guest" || value === "admin" || value === "member" ? value : null; } +function isPublicTaskManagerGuestServiceAssignment(data, payload) { + if (payload?.value !== "viewer") return false; + + const service = data.services.find((candidate) => candidate.id === payload?.serviceId); + if (!service || !isTaskManagerService(service)) return false; + + return data.memberships.some( + (membership) => + membership.userId === payload?.userId && + membership.clientId === publicPoolClientId && + membership.status === "active" + ); +} + +function isTaskManagerService(service) { + return service?.slug === "task-manager" || service?.authentikApplicationSlug === "task-manager"; +} + function resolveTaskManagerRoleForMembership(role) { return role === "client_owner" || role === "client_admin" ? "admin" : "member"; } diff --git a/src/widgets/admin-overlay/AdminOverlay.tsx b/src/widgets/admin-overlay/AdminOverlay.tsx index a89e118..437f46b 100644 --- a/src/widgets/admin-overlay/AdminOverlay.tsx +++ b/src/widgets/admin-overlay/AdminOverlay.tsx @@ -1347,7 +1347,7 @@ const accessAssignmentOptions: Array> const publicOperationalCoreAccessOptions: Array> = [ { value: "unset", label: "—", description: "Не назначен", hidden: true }, - { value: "viewer", label: "Workspace Guest", description: "Доступ к приглашённому workspace", tone: "green" }, + { value: "viewer", label: "Workspace Guest", description: "Выдаётся только через Tasker", tone: "green", hidden: true }, { value: "member", label: "Workspace Member", description: "Доступ к приглашённому workspace", tone: "green" }, { value: "admin", label: "Service Admin", description: "Self-service", tone: "green" }, { value: "deny", label: "Заблокирован", description: "Запрет доступа", tone: "red" },