From a81bbb919283a0c274e2e8b2caa2b544f740ef6e Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Fri, 9 Jan 2026 01:18:15 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20create=20approval=20request=20and?= =?UTF-8?q?=20mark=20client=20approval=20as=20pending=20if=20the=20user's?= =?UTF-8?q?=20role=20requires=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/lib/calculateUserClientsForOrgs.ts | 70 ++++++++++++++++------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/server/lib/calculateUserClientsForOrgs.ts b/server/lib/calculateUserClientsForOrgs.ts index ac3d719f..d7c1692b 100644 --- a/server/lib/calculateUserClientsForOrgs.ts +++ b/server/lib/calculateUserClientsForOrgs.ts @@ -1,21 +1,24 @@ +import { listExitNodes } from "#dynamic/lib/exitNodes"; +import { build } from "@server/build"; import { + approvals, clients, db, olms, orgs, roleClients, roles, + Transaction, userClients, - userOrgs, - Transaction + userOrgs } from "@server/db"; -import { eq, and, notInArray } from "drizzle-orm"; -import { listExitNodes } from "#dynamic/lib/exitNodes"; -import { getNextAvailableClientSubnet } from "@server/lib/ip"; -import logger from "@server/logger"; -import { rebuildClientAssociationsFromClient } from "./rebuildClientAssociations"; -import { sendTerminateClient } from "@server/routers/client/terminate"; import { getUniqueClientName } from "@server/db/names"; +import { getNextAvailableClientSubnet } from "@server/lib/ip"; +import { isLicensedOrSubscribed } from "@server/lib/isLicencedOrSubscribed"; +import logger from "@server/logger"; +import { sendTerminateClient } from "@server/routers/client/terminate"; +import { and, eq, notInArray, type InferInsertModel } from "drizzle-orm"; +import { rebuildClientAssociationsFromClient } from "./rebuildClientAssociations"; export async function calculateUserClientsForOrgs( userId: string, @@ -38,13 +41,15 @@ export async function calculateUserClientsForOrgs( const allUserOrgs = await transaction .select() .from(userOrgs) + .innerJoin(roles, eq(roles.roleId, userOrgs.roleId)) .where(eq(userOrgs.userId, userId)); - const userOrgIds = allUserOrgs.map((uo) => uo.orgId); + const userOrgIds = allUserOrgs.map(({ userOrgs: uo }) => uo.orgId); // For each OLM, ensure there's a client in each org the user is in for (const olm of userOlms) { - for (const userOrg of allUserOrgs) { + for (const userRoleOrg of allUserOrgs) { + const { userOrgs: userOrg, roles: role } = userRoleOrg; const orgId = userOrg.orgId; const [org] = await transaction @@ -182,21 +187,46 @@ export async function calculateUserClientsForOrgs( const niceId = await getUniqueClientName(orgId); + const isOrgLicensed = await isLicensedOrSubscribed( + userOrg.orgId + ); + const requireApproval = + build !== "oss" && + isOrgLicensed && + role.requireDeviceApproval; + + const newClientData: InferInsertModel = { + userId, + orgId: userOrg.orgId, + exitNodeId: randomExitNode.exitNodeId, + name: olm.name || "User Client", + subnet: updatedSubnet, + olmId: olm.olmId, + type: "olm", + niceId, + approvalState: requireApproval ? "pending" : "approved" + }; + // Create the client const [newClient] = await transaction .insert(clients) - .values({ - userId, - orgId: userOrg.orgId, - exitNodeId: randomExitNode.exitNodeId, - name: olm.name || "User Client", - subnet: updatedSubnet, - olmId: olm.olmId, - type: "olm", - niceId - }) + .values(newClientData) .returning(); + // create approval request + if (requireApproval) { + await transaction + .insert(approvals) + .values({ + timestamp: new Date().getTime() / 1000, + orgId: userOrg.orgId, + clientId: newClient.clientId, + userId, + type: "user_device" + }) + .returning(); + } + await rebuildClientAssociationsFromClient( newClient, transaction