mirror of
https://github.com/fosrl/pangolin.git
synced 2026-01-28 13:50:43 +00:00
clean up paid features check
This commit is contained in:
committed by
Owen Schwartz
parent
9a5bcb9099
commit
92331d7a33
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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<boolean> {
|
||||
if (build === "enterprise") {
|
||||
return await license.isUnlocked();
|
||||
}
|
||||
|
||||
if (build === "saas") {
|
||||
const { tier } = await getOrgTierData(orgId);
|
||||
return tier === TierId.STANDARD;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
30
server/private/lib/isLicencedOrSubscribed.ts
Normal file
30
server/private/lib/isLicencedOrSubscribed.ts
Normal file
@@ -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<boolean> {
|
||||
if (build === "enterprise") {
|
||||
return await license.isUnlocked();
|
||||
}
|
||||
|
||||
if (build === "saas") {
|
||||
const { tier } = await getOrgTierData(orgId);
|
||||
return tier === TierId.STANDARD;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user