From 0ba6dc7115bfb8bc7fe039c1a6ffee6618a15a3c Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Tue, 12 May 2026 22:26:48 +0300 Subject: [PATCH] =?UTF-8?q?SECURITY=20-=20=D0=9C=D0=95=D0=96=D0=9F=D0=A0?= =?UTF-8?q?=D0=9E=D0=95=D0=9A=D0=A2=D0=9D=D0=90=D0=AF=20=D0=9A=D0=9E=D0=9C?= =?UTF-8?q?=D0=9C=D0=A3=D0=9D=D0=98=D0=9A=D0=90=D0=A6=D0=98=D0=AF:=20?= =?UTF-8?q?=D0=BE=D1=87=D0=B8=D1=81=D1=82=D0=BA=D0=B0=20=D0=B4=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D1=83=D0=BF=D0=B0=20Tasker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/dev-server.mjs | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/server/dev-server.mjs b/server/dev-server.mjs index dd60111..daea2ce 100644 --- a/server/dev-server.mjs +++ b/server/dev-server.mjs @@ -1108,12 +1108,22 @@ app.patch("/api/admin/users/:userId/profile", requireLauncherAdmin, asyncRoute(a return; } + const beforeSnapshot = controlPlaneStore.getSnapshot(req.nodedcSession.user); + const beforeUser = beforeSnapshot.data.users.find((candidate) => candidate.id === req.params.userId) ?? null; const result = await controlPlaneStore.updateUserProfile(req.params.userId, req.body, req.nodedcSession.user); const syncResult = await syncUsersToAuthentik(result.data, [req.params.userId], req.nodedcSession.user); const updatedUser = syncResult.data.users.find((candidate) => candidate.id === req.params.userId); const taskManagerProfile = await syncTaskManagerUserProfile(updatedUser); + const taskManagerCleanup = + beforeUser?.globalStatus === "active" && updatedUser?.globalStatus === "blocked" + ? await cleanupTaskManagerUserAccess(updatedUser, { + source: "launcher-user-blocked", + revokeIdentityLinks: false, + revokeTaskerAccess: true, + }) + : null; publishControlPlaneEvent("admin.user.updated", syncResult.userIds); - res.json({ ...scopeAdminMutationResult(req, { ...result, data: syncResult.data }), taskManagerProfile }); + res.json({ ...scopeAdminMutationResult(req, { ...result, data: syncResult.data }), taskManagerProfile, taskManagerCleanup }); })); app.delete("/api/admin/users/:userId", requireLauncherAdmin, requireRootLauncherAdmin, asyncRoute(async (req, res) => { @@ -1131,7 +1141,11 @@ app.delete("/api/admin/users/:userId", requireLauncherAdmin, requireRootLauncher authentik = await authentikSyncClient.deleteUser({ data: snapshot.data, userId: req.params.userId }); } - const taskManagerCleanup = await cleanupTaskManagerUserAccess(user); + const taskManagerCleanup = await cleanupTaskManagerUserAccess(user, { + source: "launcher-user-hard-delete", + revokeIdentityLinks: true, + revokeTaskerAccess: true, + }); const result = await controlPlaneStore.deleteUser(req.params.userId, req.nodedcSession.user); publishControlPlaneEvent("admin.user.deleted", [req.params.userId]); res.json({ ...scopeAdminMutationResult(req, result), authentik, taskManagerCleanup }); @@ -2152,7 +2166,7 @@ async function syncTaskManagerUserProfile(user) { } } -async function cleanupTaskManagerUserAccess(user) { +async function cleanupTaskManagerUserAccess(user, options = {}) { if (!user?.email || !config.internalAccessToken) { return null; } @@ -2161,11 +2175,11 @@ async function cleanupTaskManagerUserAccess(user) { return await requestTaskManagerInternalJson("/api/internal/nodedc/logout/", { method: "POST", body: { - source: "launcher-user-hard-delete", + source: normalizeOptionalText(options.source) ?? "launcher-user-access-revoked", subject: user.authentikUserId ?? undefined, email: user.email, - revokeIdentityLinks: true, - revokeTaskerAccess: true, + revokeIdentityLinks: options.revokeIdentityLinks === true, + revokeTaskerAccess: options.revokeTaskerAccess !== false, }, }); } catch (error) { @@ -2369,7 +2383,7 @@ function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess, u }; } - if (hasLauncherManagedWorkspace && !isSuperAdmin) { + if (hasLauncherManagedWorkspace && !isSuperAdmin && !isTaskManagerAdmin) { if (workspaceAssignment?.managedBy === "launcher") { return { mode,