diff --git a/.vscode/settings.json b/.vscode/settings.json index 77440d96..767e57b5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,13 +4,13 @@ }, "editor.defaultFormatter": "esbenp.prettier-vscode", "[jsonc]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "vscode.json-language-features" }, "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "vscode.typescript-language-features" }, "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" @@ -19,4 +19,4 @@ "editor.defaultFormatter": "esbenp.prettier-vscode" }, "editor.formatOnSave": true -} +} \ No newline at end of file diff --git a/server/lib/blueprints/proxyResources.ts b/server/lib/blueprints/proxyResources.ts index 0ae4c529..c0faad63 100644 --- a/server/lib/blueprints/proxyResources.ts +++ b/server/lib/blueprints/proxyResources.ts @@ -31,7 +31,7 @@ import { pickPort } from "@server/routers/target/helpers"; import { resourcePassword } from "@server/db"; import { hashPassword } from "@server/auth/password"; import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators"; -import { isLicensedOrSubscribed } from "../isLicencedOrSubscribed"; +import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; import { build } from "@server/build"; export type ProxyResourcesResults = { @@ -213,11 +213,7 @@ export async function updateProxyResources( // Update existing resource const isLicensed = await isLicensedOrSubscribed(orgId); - if (build == "enterprise" && !isLicensed) { - logger.warn( - "Server is not licensed! Clearing set maintenance screen values" - ); - // null the maintenance mode fields if not licensed + if (!isLicensed) { resourceData.maintenance = undefined; } @@ -594,7 +590,7 @@ export async function updateProxyResources( existingRule.action !== getRuleAction(rule.action) || existingRule.match !== rule.match.toUpperCase() || existingRule.value !== - getRuleValue(rule.match.toUpperCase(), rule.value) || + getRuleValue(rule.match.toUpperCase(), rule.value) || existingRule.priority !== intendedPriority ) { validateRule(rule); @@ -653,11 +649,7 @@ export async function updateProxyResources( } const isLicensed = await isLicensedOrSubscribed(orgId); - if (build == "enterprise" && !isLicensed) { - logger.warn( - "Server is not licensed! Clearing set maintenance screen values" - ); - // null the maintenance mode fields if not licensed + if (!isLicensed) { resourceData.maintenance = undefined; } diff --git a/server/lib/calculateUserClientsForOrgs.ts b/server/lib/calculateUserClientsForOrgs.ts index b2ea08a3..15837890 100644 --- a/server/lib/calculateUserClientsForOrgs.ts +++ b/server/lib/calculateUserClientsForOrgs.ts @@ -14,7 +14,7 @@ import { } from "@server/db"; import { getUniqueClientName } from "@server/db/names"; import { getNextAvailableClientSubnet } from "@server/lib/ip"; -import { isLicensedOrSubscribed } from "@server/lib/isLicencedOrSubscribed"; +import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; import logger from "@server/logger"; import { sendTerminateClient } from "@server/routers/client/terminate"; import { and, eq, notInArray, type InferInsertModel } from "drizzle-orm"; diff --git a/server/lib/isLicencedOrSubscribed.ts b/server/lib/isLicencedOrSubscribed.ts index 3de3a915..748bb1b1 100644 --- a/server/lib/isLicencedOrSubscribed.ts +++ b/server/lib/isLicencedOrSubscribed.ts @@ -1,17 +1,3 @@ -import { build } from "@server/build"; -import license from "#dynamic/license/license"; -import { getOrgTierData } from "#dynamic/lib/billing"; -import { TierId } from "@server/lib/billing/tiers"; - export async function isLicensedOrSubscribed(orgId: string): Promise { - if (build === "enterprise") { - return await license.isUnlocked(); - } - - if (build === "saas") { - const { tier } = await getOrgTierData(orgId); - return tier === TierId.STANDARD; - } - - return true; -} + return false; +} \ No newline at end of file diff --git a/server/private/lib/isLicencedOrSubscribed.ts b/server/private/lib/isLicencedOrSubscribed.ts new file mode 100644 index 00000000..494deb7a --- /dev/null +++ b/server/private/lib/isLicencedOrSubscribed.ts @@ -0,0 +1,30 @@ +/* + * This file is part of a proprietary work. + * + * Copyright (c) 2025 Fossorial, Inc. + * All rights reserved. + * + * This file is licensed under the Fossorial Commercial License. + * You may not use this file except in compliance with the License. + * Unauthorized use, copying, modification, or distribution is strictly prohibited. + * + * This file is not licensed under the AGPLv3. + */ + +import { build } from "@server/build"; +import license from "#private/license/license"; +import { getOrgTierData } from "#private/lib/billing"; +import { TierId } from "@server/lib/billing/tiers"; + +export async function isLicensedOrSubscribed(orgId: string): Promise { + if (build === "enterprise") { + return await license.isUnlocked(); + } + + if (build === "saas") { + const { tier } = await getOrgTierData(orgId); + return tier === TierId.STANDARD; + } + + return false; +} \ No newline at end of file diff --git a/server/routers/client/getClient.ts b/server/routers/client/getClient.ts index 1171430f..138a286c 100644 --- a/server/routers/client/getClient.ts +++ b/server/routers/client/getClient.ts @@ -12,7 +12,7 @@ import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; import { getUserDeviceName } from "@server/db/names"; import { build } from "@server/build"; -import { isLicensedOrSubscribed } from "@server/lib/isLicencedOrSubscribed"; +import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; const getClientSchema = z.strictObject({ clientId: z @@ -78,58 +78,108 @@ function getPlatformPostureData( // Windows: Hard drive encryption, Firewall, Auto updates, TPM availability, Windows Antivirus status if (normalizedPlatform === "windows") { - if (fingerprint.diskEncrypted !== null && fingerprint.diskEncrypted !== undefined) { + if ( + fingerprint.diskEncrypted !== null && + fingerprint.diskEncrypted !== undefined + ) { posture.diskEncrypted = fingerprint.diskEncrypted; } - if (fingerprint.firewallEnabled !== null && fingerprint.firewallEnabled !== undefined) { + if ( + fingerprint.firewallEnabled !== null && + fingerprint.firewallEnabled !== undefined + ) { posture.firewallEnabled = fingerprint.firewallEnabled; } - if (fingerprint.tpmAvailable !== null && fingerprint.tpmAvailable !== undefined) { + if ( + fingerprint.tpmAvailable !== null && + fingerprint.tpmAvailable !== undefined + ) { posture.tpmAvailable = fingerprint.tpmAvailable; } - if (fingerprint.windowsAntivirusEnabled !== null && fingerprint.windowsAntivirusEnabled !== undefined) { - posture.windowsAntivirusEnabled = fingerprint.windowsAntivirusEnabled; + if ( + fingerprint.windowsAntivirusEnabled !== null && + fingerprint.windowsAntivirusEnabled !== undefined + ) { + posture.windowsAntivirusEnabled = + fingerprint.windowsAntivirusEnabled; } } // macOS: Hard drive encryption, Biometric configuration, Firewall, System Integrity Protection (SIP), Gatekeeper, Firewall stealth mode else if (normalizedPlatform === "macos") { - if (fingerprint.diskEncrypted !== null && fingerprint.diskEncrypted !== undefined) { + if ( + fingerprint.diskEncrypted !== null && + fingerprint.diskEncrypted !== undefined + ) { posture.diskEncrypted = fingerprint.diskEncrypted; } - if (fingerprint.biometricsEnabled !== null && fingerprint.biometricsEnabled !== undefined) { + if ( + fingerprint.biometricsEnabled !== null && + fingerprint.biometricsEnabled !== undefined + ) { posture.biometricsEnabled = fingerprint.biometricsEnabled; } - if (fingerprint.firewallEnabled !== null && fingerprint.firewallEnabled !== undefined) { + if ( + fingerprint.firewallEnabled !== null && + fingerprint.firewallEnabled !== undefined + ) { posture.firewallEnabled = fingerprint.firewallEnabled; } - if (fingerprint.macosSipEnabled !== null && fingerprint.macosSipEnabled !== undefined) { + if ( + fingerprint.macosSipEnabled !== null && + fingerprint.macosSipEnabled !== undefined + ) { posture.macosSipEnabled = fingerprint.macosSipEnabled; } - if (fingerprint.macosGatekeeperEnabled !== null && fingerprint.macosGatekeeperEnabled !== undefined) { + if ( + fingerprint.macosGatekeeperEnabled !== null && + fingerprint.macosGatekeeperEnabled !== undefined + ) { posture.macosGatekeeperEnabled = fingerprint.macosGatekeeperEnabled; } - if (fingerprint.macosFirewallStealthMode !== null && fingerprint.macosFirewallStealthMode !== undefined) { - posture.macosFirewallStealthMode = fingerprint.macosFirewallStealthMode; + if ( + fingerprint.macosFirewallStealthMode !== null && + fingerprint.macosFirewallStealthMode !== undefined + ) { + posture.macosFirewallStealthMode = + fingerprint.macosFirewallStealthMode; } - if (fingerprint.autoUpdatesEnabled !== null && fingerprint.autoUpdatesEnabled !== undefined) { + if ( + fingerprint.autoUpdatesEnabled !== null && + fingerprint.autoUpdatesEnabled !== undefined + ) { posture.autoUpdatesEnabled = fingerprint.autoUpdatesEnabled; } } // Linux: Hard drive encryption, Firewall, AppArmor, SELinux, TPM availability else if (normalizedPlatform === "linux") { - if (fingerprint.diskEncrypted !== null && fingerprint.diskEncrypted !== undefined) { + if ( + fingerprint.diskEncrypted !== null && + fingerprint.diskEncrypted !== undefined + ) { posture.diskEncrypted = fingerprint.diskEncrypted; } - if (fingerprint.firewallEnabled !== null && fingerprint.firewallEnabled !== undefined) { + if ( + fingerprint.firewallEnabled !== null && + fingerprint.firewallEnabled !== undefined + ) { posture.firewallEnabled = fingerprint.firewallEnabled; } - if (fingerprint.linuxAppArmorEnabled !== null && fingerprint.linuxAppArmorEnabled !== undefined) { + if ( + fingerprint.linuxAppArmorEnabled !== null && + fingerprint.linuxAppArmorEnabled !== undefined + ) { posture.linuxAppArmorEnabled = fingerprint.linuxAppArmorEnabled; } - if (fingerprint.linuxSELinuxEnabled !== null && fingerprint.linuxSELinuxEnabled !== undefined) { + if ( + fingerprint.linuxSELinuxEnabled !== null && + fingerprint.linuxSELinuxEnabled !== undefined + ) { posture.linuxSELinuxEnabled = fingerprint.linuxSELinuxEnabled; } - if (fingerprint.tpmAvailable !== null && fingerprint.tpmAvailable !== undefined) { + if ( + fingerprint.tpmAvailable !== null && + fingerprint.tpmAvailable !== undefined + ) { posture.tpmAvailable = fingerprint.tpmAvailable; } } @@ -139,7 +189,10 @@ function getPlatformPostureData( } // Android: Screen lock, Biometric configuration, Hard drive encryption else if (normalizedPlatform === "android") { - if (fingerprint.diskEncrypted !== null && fingerprint.diskEncrypted !== undefined) { + if ( + fingerprint.diskEncrypted !== null && + fingerprint.diskEncrypted !== undefined + ) { posture.diskEncrypted = fingerprint.diskEncrypted; } } @@ -236,33 +289,31 @@ export async function getClient( // Build fingerprint data if available const fingerprintData = client.currentFingerprint ? { - username: client.currentFingerprint.username || null, - hostname: client.currentFingerprint.hostname || null, - platform: client.currentFingerprint.platform || null, - osVersion: client.currentFingerprint.osVersion || null, - kernelVersion: - client.currentFingerprint.kernelVersion || null, - arch: client.currentFingerprint.arch || null, - deviceModel: client.currentFingerprint.deviceModel || null, - serialNumber: client.currentFingerprint.serialNumber || null, - firstSeen: client.currentFingerprint.firstSeen || null, - lastSeen: client.currentFingerprint.lastSeen || null - } + username: client.currentFingerprint.username || null, + hostname: client.currentFingerprint.hostname || null, + platform: client.currentFingerprint.platform || null, + osVersion: client.currentFingerprint.osVersion || null, + kernelVersion: + client.currentFingerprint.kernelVersion || null, + arch: client.currentFingerprint.arch || null, + deviceModel: client.currentFingerprint.deviceModel || null, + serialNumber: client.currentFingerprint.serialNumber || null, + firstSeen: client.currentFingerprint.firstSeen || null, + lastSeen: client.currentFingerprint.lastSeen || null + } : null; // Build posture data if available (platform-specific) // Only return posture data if org is licensed/subscribed let postureData: PostureData | null = null; - if (build !== "oss") { - const isOrgLicensed = await isLicensedOrSubscribed( - client.clients.orgId + const isOrgLicensed = await isLicensedOrSubscribed( + client.clients.orgId + ); + if (isOrgLicensed) { + postureData = getPlatformPostureData( + client.currentFingerprint?.platform || null, + client.currentFingerprint ); - if (isOrgLicensed) { - postureData = getPlatformPostureData( - client.currentFingerprint?.platform || null, - client.currentFingerprint - ); - } } const data: GetClientResponse = { diff --git a/server/routers/org/updateOrg.ts b/server/routers/org/updateOrg.ts index 844ca200..44ff9190 100644 --- a/server/routers/org/updateOrg.ts +++ b/server/routers/org/updateOrg.ts @@ -13,7 +13,7 @@ import { build } from "@server/build"; import { getOrgTierData } from "#dynamic/lib/billing"; import { TierId } from "@server/lib/billing/tiers"; import { cache } from "@server/lib/cache"; -import { isLicensedOrSubscribed } from "@server/lib/isLicencedOrSubscribed"; +import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; const updateOrgParamsSchema = z.strictObject({ orgId: z.string() @@ -89,7 +89,7 @@ export async function updateOrg( const { orgId } = parsedParams.data; const isLicensed = await isLicensedOrSubscribed(orgId); - if (build == "enterprise" && !isLicensed) { + if (!isLicensed) { parsedBody.data.requireTwoFactor = undefined; parsedBody.data.maxSessionLengthHours = undefined; parsedBody.data.passwordExpiryDays = undefined; diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index 80b7a00a..62a466d7 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -23,7 +23,7 @@ import { OpenAPITags } from "@server/openApi"; import { createCertificate } from "#dynamic/routers/certificates/createCertificate"; import { validateAndConstructDomain } from "@server/lib/domainUtils"; import { build } from "@server/build"; -import { isLicensedOrSubscribed } from "@server/lib/isLicencedOrSubscribed"; +import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; const updateResourceParamsSchema = z.strictObject({ resourceId: z.string().transform(Number).pipe(z.int().positive()) @@ -342,11 +342,7 @@ async function updateHttpResource( } const isLicensed = await isLicensedOrSubscribed(resource.orgId); - if (build == "enterprise" && !isLicensed) { - logger.warn( - "Server is not licensed! Clearing set maintenance screen values" - ); - // null the maintenance mode fields if not licensed + if (!isLicensed) { updateData.maintenanceModeEnabled = undefined; updateData.maintenanceModeType = undefined; updateData.maintenanceTitle = undefined; diff --git a/server/routers/role/createRole.ts b/server/routers/role/createRole.ts index a1e21d7a..666eb756 100644 --- a/server/routers/role/createRole.ts +++ b/server/routers/role/createRole.ts @@ -11,7 +11,7 @@ import { ActionsEnum } from "@server/auth/actions"; import { eq, and } from "drizzle-orm"; import { OpenAPITags, registry } from "@server/openApi"; import { build } from "@server/build"; -import { isLicensedOrSubscribed } from "@server/lib/isLicencedOrSubscribed"; +import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; const createRoleParamsSchema = z.strictObject({ orgId: z.string() @@ -101,7 +101,7 @@ export async function createRole( } const isLicensed = await isLicensedOrSubscribed(orgId); - if (build === "oss" || !isLicensed) { + if (!isLicensed) { roleData.requireDeviceApproval = undefined; } diff --git a/server/routers/role/updateRole.ts b/server/routers/role/updateRole.ts index 03034ea1..6724d622 100644 --- a/server/routers/role/updateRole.ts +++ b/server/routers/role/updateRole.ts @@ -8,8 +8,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; -import { build } from "@server/build"; -import { isLicensedOrSubscribed } from "@server/lib/isLicencedOrSubscribed"; +import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; import { OpenAPITags, registry } from "@server/openApi"; const updateRoleParamsSchema = z.strictObject({ @@ -112,7 +111,7 @@ export async function updateRole( } const isLicensed = await isLicensedOrSubscribed(orgId); - if (build === "oss" || !isLicensed) { + if (!isLicensed) { updateData.requireDeviceApproval = undefined; }