From 68f0c4df3af3d84cedb29087f2c8eb539a704517 Mon Sep 17 00:00:00 2001 From: Owen Date: Fri, 24 Oct 2025 10:11:28 -0700 Subject: [PATCH] Working on licencing --- messages/en-US.json | 6 ++- server/auth/actions.ts | 4 +- server/private/middlewares/index.ts | 3 +- .../private/middlewares/verifySubscription.ts | 50 +++++++++++++++++++ server/private/routers/external.ts | 21 +++++++- .../routers/auditLogs/exportRequstAuditLog.ts | 5 -- server/routers/external.ts | 28 +++++++---- src/app/[orgId]/settings/logs/layout.tsx | 33 +----------- .../[orgId]/settings/logs/request/page.tsx | 6 +++ src/app/navigation.tsx | 29 ++++++++--- src/components/HorizontalTabs.tsx | 3 +- 11 files changed, 129 insertions(+), 59 deletions(-) create mode 100644 server/private/middlewares/verifySubscription.ts diff --git a/messages/en-US.json b/messages/en-US.json index 9157e84c..0a62ad7e 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1921,5 +1921,9 @@ "requestLogs": "Request Logs", "host": "Host", "location": "Location", - "actionLogs": "Action Logs" + "actionLogs": "Action Logs", + "sidebarLogsRequest": "Request Logs", + "sidebarLogsAccess": "Access Logs", + "sidebarLogsAction": "Action Logs", + "requestLogsDescription": "View detailed pre-request logs for resources in this organization" } diff --git a/server/auth/actions.ts b/server/auth/actions.ts index e48bc502..1d22cff1 100644 --- a/server/auth/actions.ts +++ b/server/auth/actions.ts @@ -116,7 +116,9 @@ export enum ActionsEnum { updateLoginPage = "updateLoginPage", getLoginPage = "getLoginPage", deleteLoginPage = "deleteLoginPage", - applyBlueprint = "applyBlueprint" + applyBlueprint = "applyBlueprint", + viewLogs = "viewLogs", + exportLogs = "exportLogs" } export async function checkUserActionPermission( diff --git a/server/private/middlewares/index.ts b/server/private/middlewares/index.ts index 32aa1a1f..bb4d9c05 100644 --- a/server/private/middlewares/index.ts +++ b/server/private/middlewares/index.ts @@ -15,4 +15,5 @@ export * from "./verifyCertificateAccess"; export * from "./verifyRemoteExitNodeAccess"; export * from "./verifyIdpAccess"; export * from "./verifyLoginPageAccess"; -export * from "./logActionAudit"; \ No newline at end of file +export * from "./logActionAudit"; +export * from "./verifySubscription"; \ No newline at end of file diff --git a/server/private/middlewares/verifySubscription.ts b/server/private/middlewares/verifySubscription.ts new file mode 100644 index 00000000..5249c026 --- /dev/null +++ b/server/private/middlewares/verifySubscription.ts @@ -0,0 +1,50 @@ +/* + * 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 { Request, Response, NextFunction } from "express"; +import createHttpError from "http-errors"; +import HttpCode from "@server/types/HttpCode"; +import { build } from "@server/build"; +import { getOrgTierData } from "#private/lib/billing"; + +export async function verifyValidSubscription( + req: Request, + res: Response, + next: NextFunction +) { + try { + if (build != "saas") { + return next(); + } + + const tier = await getOrgTierData(req.params.orgId); + + if (!tier.active) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Organization does not have an active subscription" + ) + ); + } + + return next(); + } catch (e) { + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Error verifying subscription" + ) + ); + } +} diff --git a/server/private/routers/external.ts b/server/private/routers/external.ts index 445af82f..ddfabb30 100644 --- a/server/private/routers/external.ts +++ b/server/private/routers/external.ts @@ -34,7 +34,8 @@ import { verifyCertificateAccess, verifyIdpAccess, verifyLoginPageAccess, - verifyRemoteExitNodeAccess + verifyRemoteExitNodeAccess, + verifyValidSubscription } from "#private/middlewares"; import rateLimit, { ipKeyGenerator } from "express-rate-limit"; import createHttpError from "http-errors"; @@ -348,20 +349,38 @@ authenticated.post( authenticated.get( "/org/:orgId/logs/action", + verifyValidLicense, + verifyValidSubscription, + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.exportLogs), logs.queryActionAuditLogs ) authenticated.get( "/org/:orgId/logs/action/export", + verifyValidLicense, + verifyValidSubscription, + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.exportLogs), + logActionAudit(ActionsEnum.exportLogs), logs.exportActionAuditLogs ) authenticated.get( "/org/:orgId/logs/access", + verifyValidLicense, + verifyValidSubscription, + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.exportLogs), logs.queryAccessAuditLogs ) authenticated.get( "/org/:orgId/logs/access/export", + verifyValidLicense, + verifyValidSubscription, + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.exportLogs), + logActionAudit(ActionsEnum.exportLogs), logs.exportAccessAuditLogs ) \ No newline at end of file diff --git a/server/routers/auditLogs/exportRequstAuditLog.ts b/server/routers/auditLogs/exportRequstAuditLog.ts index c1fb2872..89df2d3f 100644 --- a/server/routers/auditLogs/exportRequstAuditLog.ts +++ b/server/routers/auditLogs/exportRequstAuditLog.ts @@ -1,15 +1,10 @@ -import { db, requestAuditLog } from "@server/db"; import { registry } from "@server/openApi"; import { NextFunction } from "express"; import { Request, Response } from "express"; -import { eq, gt, lt, and, count } from "drizzle-orm"; import { OpenAPITags } from "@server/openApi"; -import { z } from "zod"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import { fromError } from "zod-validation-error"; -import { QueryRequestAuditLogResponse } from "@server/routers/auditLogs/types"; -import response from "@server/lib/response"; import logger from "@server/logger"; import { queryAccessAuditLogsQuery, queryRequestAuditLogsParams, queryRequest } from "./queryRequstAuditLog"; import { generateCSV } from "./generateCSV"; diff --git a/server/routers/external.ts b/server/routers/external.ts index 2b8c270f..335bc252 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -46,6 +46,7 @@ import createHttpError from "http-errors"; import { build } from "@server/build"; import { createStore } from "#dynamic/lib/rateLimitStore"; import { logActionAudit } from "#dynamic/middlewares"; +import { log } from "console"; // Root routes export const unauthenticated = Router(); @@ -860,6 +861,21 @@ authenticated.delete( domain.deleteAccountDomain, ); +authenticated.get( + "/org/:orgId/logs/request", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.viewLogs), + logs.queryRequestAuditLogs +) + +authenticated.get( + "/org/:orgId/logs/request/export", + verifyOrgAccess, + verifyUserHasAction(ActionsEnum.exportLogs), + logActionAudit(ActionsEnum.exportLogs), + logs.exportRequestAuditLogs +) + // Auth routes export const authRouter = Router(); unauthenticated.use("/auth", authRouter); @@ -1180,14 +1196,4 @@ authRouter.delete( store: createStore() }), auth.deleteSecurityKey -); - -authenticated.get( - "/org/:orgId/logs/request", - logs.queryRequestAuditLogs -) - -authenticated.get( - "/org/:orgId/logs/request/export", - logs.exportRequestAuditLogs -) \ No newline at end of file +); \ No newline at end of file diff --git a/src/app/[orgId]/settings/logs/layout.tsx b/src/app/[orgId]/settings/logs/layout.tsx index fb1d2071..96958403 100644 --- a/src/app/[orgId]/settings/logs/layout.tsx +++ b/src/app/[orgId]/settings/logs/layout.tsx @@ -1,9 +1,6 @@ -import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; -import { HorizontalTabs } from "@app/components/HorizontalTabs"; import { verifySession } from "@app/lib/auth/verifySession"; import { redirect } from "next/navigation"; import { cache } from "react"; -import { getTranslations } from "next-intl/server"; type GeneralSettingsProps = { children: React.ReactNode; @@ -14,8 +11,6 @@ export default async function GeneralSettingsPage({ children, params }: GeneralSettingsProps) { - const { orgId } = await params; - const getUser = cache(verifySession); const user = await getUser(); @@ -23,31 +18,5 @@ export default async function GeneralSettingsPage({ redirect(`/`); } - const t = await getTranslations(); - - const navItems = [ - { - title: t("request"), - href: `/{orgId}/settings/logs/request` - }, - { - title: t("access"), - href: `/{orgId}/settings/logs/access` - }, - { - title: t("action"), - href: `/{orgId}/settings/logs/action` - } - ]; - - return ( - <> - - - {children} - - ); + return children; } diff --git a/src/app/[orgId]/settings/logs/request/page.tsx b/src/app/[orgId]/settings/logs/request/page.tsx index dcca97d2..6a1a1659 100644 --- a/src/app/[orgId]/settings/logs/request/page.tsx +++ b/src/app/[orgId]/settings/logs/request/page.tsx @@ -12,6 +12,7 @@ import { DateTimeValue } from "@app/components/DateTimePicker"; import { Key, RouteOff, User, Lock, Unlock, ArrowUpRight } from "lucide-react"; import Link from "next/link"; import { ColumnFilter } from "@app/components/ColumnFilter"; +import SettingsSectionTitle from "@app/components/SettingsSectionTitle"; export default function GeneralPage() { const router = useRouter(); @@ -755,6 +756,11 @@ export default function GeneralPage() { return ( <> + + + }, + { + title: "sidebarLogsAccess", + href: "/{orgId}/settings/logs/access", + icon: + }, + { + title: "sidebarLogsAction", + href: "/{orgId}/settings/logs/action", + icon: + }, + ] + }, { heading: "Organization", items: [ @@ -139,11 +161,6 @@ export const orgNavSections = ( } ] : []), - { - title: "sidebarLogs", - href: "/{orgId}/settings/logs/request", - icon: - }, { title: "sidebarSettings", href: "/{orgId}/settings/general", diff --git a/src/components/HorizontalTabs.tsx b/src/components/HorizontalTabs.tsx index b529dbba..5b0fdbab 100644 --- a/src/components/HorizontalTabs.tsx +++ b/src/components/HorizontalTabs.tsx @@ -4,10 +4,10 @@ import React from "react"; import Link from "next/link"; import { useParams, usePathname } from "next/navigation"; import { cn } from "@app/lib/cn"; -import { buttonVariants } from "@/components/ui/button"; import { Badge } from "@app/components/ui/badge"; import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext"; import { useTranslations } from "next-intl"; +import { useSubscriptionStatusContext } from "@app/hooks/useSubscriptionStatusContext"; export type HorizontalTabs = Array<{ title: string; @@ -30,6 +30,7 @@ export function HorizontalTabs({ const pathname = usePathname(); const params = useParams(); const { licenseStatus, isUnlocked } = useLicenseStatusContext(); + const subscription = useSubscriptionStatusContext(); const t = useTranslations(); function hydrateHref(href: string) {