diff --git a/server/lib/billing/tierMatrix.ts b/server/lib/billing/tierMatrix.ts index b87b247ad..d4aeb67f2 100644 --- a/server/lib/billing/tierMatrix.ts +++ b/server/lib/billing/tierMatrix.ts @@ -72,5 +72,5 @@ export const tierMatrix: Record = { [TierFeature.NewtAutoUpdate]: ["tier1", "tier2", "tier3", "enterprise"], [TierFeature.ResourcePolicies]: ["tier3", "enterprise"], [TierFeature.RoleBasedSSHControls]: ["tier3", "enterprise"], - [TierFeature.Mtls]: ["tier1", "tier2", "tier3", "enterprise"] + [TierFeature.Mtls]: ["tier3", "enterprise"] }; diff --git a/server/private/routers/external.ts b/server/private/routers/external.ts index 6f2176317..720183a36 100644 --- a/server/private/routers/external.ts +++ b/server/private/routers/external.ts @@ -35,6 +35,7 @@ import * as eventStreamingDestination from "#private/routers/eventStreamingDesti import * as alertRule from "#private/routers/alertRule"; import * as healthChecks from "#private/routers/healthChecks"; import * as policy from "#private/routers/policy"; +import * as resource from "#private/routers/resource"; import { verifyOrgAccess, @@ -49,7 +50,8 @@ import { verifySiteProvisioningKeyAccess, verifyIsLoggedInUser, verifyAdmin, - verifyResourcePolicyAccess + verifyResourcePolicyAccess, + verifyResourceAccess } from "@server/middlewares"; import { ActionsEnum } from "@server/auth/actions"; import { @@ -893,3 +895,25 @@ authenticated.post( verifyOrgAccess, logs.logAccessAuditAttempt ); + +authenticated.put( + "/resource/:resourceId/mtls/certificate", + verifyResourceAccess, + verifyLimits, + verifyUserHasAction(ActionsEnum.createResourceMtlsCertificate), + logActionAudit(ActionsEnum.createResourceMtlsCertificate), + resource.createResourceMtlsCertificate +); +authenticated.get( + "/resource/:resourceId/mtls/certificates", + verifyResourceAccess, + verifyUserHasAction(ActionsEnum.listResourceMtlsCertificates), + resource.listResourceMtlsCertificates +); +authenticated.delete( + "/resource/:resourceId/mtls/certificate/:mtlsCertificateId", + verifyResourceAccess, + verifyUserHasAction(ActionsEnum.deleteResourceMtlsCertificate), + logActionAudit(ActionsEnum.deleteResourceMtlsCertificate), + resource.deleteResourceMtlsCertificate +); diff --git a/server/private/routers/integration.ts b/server/private/routers/integration.ts index 221d16c31..c4bbe4160 100644 --- a/server/private/routers/integration.ts +++ b/server/private/routers/integration.ts @@ -21,7 +21,7 @@ import * as policy from "#private/routers/policy"; import * as eventStreamingDestination from "#private/routers/eventStreamingDestination"; import * as alertRule from "#private/routers/alertRule"; import * as healthChecks from "#private/routers/healthChecks"; - +import * as resource from "#private/routers/resource"; import { verifyApiKeyHasAction, verifyApiKeyIsRoot, @@ -32,7 +32,8 @@ import { verifyApiKeyClientAccess, verifyApiKeySiteProvisioningKeyAccess, verifyApiKeyResourcePolicyAccess, - verifyLimits + verifyLimits, + verifyApiKeyResourceAccess } from "@server/middlewares"; import * as user from "#private/routers/user"; import * as client from "#private/routers/client"; @@ -466,3 +467,36 @@ authenticated.get( verifyApiKeyHasAction(ActionsEnum.getTarget), healthChecks.getHealthCheckStatusHistory ); + +authenticated.put( + [ + "/resource/:resourceId/mtls/certificate", + "/public-resource/:resourceId/mtls/certificate" + ], + verifyApiKeyResourceAccess, + verifyLimits, + verifyApiKeyHasAction(ActionsEnum.createResourceMtlsCertificate), + logActionAudit(ActionsEnum.createResourceMtlsCertificate), + resource.createResourceMtlsCertificate +); + +authenticated.get( + [ + "/resource/:resourceId/mtls/certificates", + "/public-resource/:resourceId/mtls/certificates" + ], + verifyApiKeyResourceAccess, + verifyApiKeyHasAction(ActionsEnum.listResourceMtlsCertificates), + resource.listResourceMtlsCertificates +); + +authenticated.delete( + [ + "/resource/:resourceId/mtls/certificate/:mtlsCertificateId", + "/public-resource/:resourceId/mtls/certificate/:mtlsCertificateId" + ], + verifyApiKeyResourceAccess, + verifyApiKeyHasAction(ActionsEnum.deleteResourceMtlsCertificate), + logActionAudit(ActionsEnum.deleteResourceMtlsCertificate), + resource.deleteResourceMtlsCertificate +); diff --git a/server/routers/resource/createResourceMtlsCertificate.ts b/server/private/routers/resource/createResourceMtlsCertificate.ts similarity index 93% rename from server/routers/resource/createResourceMtlsCertificate.ts rename to server/private/routers/resource/createResourceMtlsCertificate.ts index e701dd6a3..c9c6fec7c 100644 --- a/server/routers/resource/createResourceMtlsCertificate.ts +++ b/server/private/routers/resource/createResourceMtlsCertificate.ts @@ -14,6 +14,7 @@ import { MAX_MTLS_CERTIFICATES_PER_RESOURCE, parseCaCertificates } from "@server/lib/mtlsCertificate"; +import { CreateResourceMtlsCertificateResponse } from "@server/routers/resource/types"; const createResourceMtlsCertificateParamsSchema = z.strictObject({ resourceId: z.coerce.number().int().positive() @@ -24,21 +25,6 @@ const createResourceMtlsCertificateBodySchema = z.strictObject({ certificate: z.string().min(1).max(100_000) }); -export type CreateResourceMtlsCertificateResponse = { - certificates: { - mtlsCertificateId: number; - resourceId: number; - name: string | null; - subject: string | null; - issuer: string | null; - serialNumber: string | null; - fingerprint: string | null; - notBefore: number | null; - notAfter: number | null; - createdAt: number; - }[]; -}; - const description = "Add a trusted CA certificate that clients must present a certificate signed by when mTLS is enabled on a resource. Accepts a PEM bundle; each certificate in the bundle is stored separately."; @@ -164,7 +150,9 @@ export async function createResourceMtlsCertificate( .from(resourceMtlsCertificates) .where(eq(resourceMtlsCertificates.resourceId, resourceId)); - const existingFingerprints = new Set(existing.map((c) => c.fingerprint)); + const existingFingerprints = new Set( + existing.map((c) => c.fingerprint) + ); const newCerts = parsedCerts.filter( (c) => !existingFingerprints.has(c.fingerprint) ); diff --git a/server/routers/resource/deleteResourceMtlsCertificate.ts b/server/private/routers/resource/deleteResourceMtlsCertificate.ts similarity index 96% rename from server/routers/resource/deleteResourceMtlsCertificate.ts rename to server/private/routers/resource/deleteResourceMtlsCertificate.ts index c4b5ed178..f9ee3c2f6 100644 --- a/server/routers/resource/deleteResourceMtlsCertificate.ts +++ b/server/private/routers/resource/deleteResourceMtlsCertificate.ts @@ -8,6 +8,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; +import { DeleteResourceMtlsCertificateResponse } from "@server/routers/resource/types"; const deleteResourceMtlsCertificateParamsSchema = z.strictObject({ resourceId: z.coerce.number().int().positive(), @@ -39,11 +40,6 @@ registry.registerPath({ responses: {} }); -export type DeleteResourceMtlsCertificateResponse = { - // true when removing the last CA certificate forced mTLS off - mtlsDisabled: boolean; -}; - export async function deleteResourceMtlsCertificate( req: Request, res: Response, diff --git a/server/private/routers/resource/index.ts b/server/private/routers/resource/index.ts index ea9b87d65..6cf21f900 100644 --- a/server/private/routers/resource/index.ts +++ b/server/private/routers/resource/index.ts @@ -12,3 +12,6 @@ */ export * from "./getMaintenanceInfo"; +export * from "./deleteResourceMtlsCertificate"; +export * from "./createResourceMtlsCertificate"; +export * from "./listResourceMtlsCertificates"; diff --git a/server/routers/resource/listResourceMtlsCertificates.ts b/server/private/routers/resource/listResourceMtlsCertificates.ts similarity index 93% rename from server/routers/resource/listResourceMtlsCertificates.ts rename to server/private/routers/resource/listResourceMtlsCertificates.ts index 41ca46430..142d225b7 100644 --- a/server/routers/resource/listResourceMtlsCertificates.ts +++ b/server/private/routers/resource/listResourceMtlsCertificates.ts @@ -8,6 +8,7 @@ import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { OpenAPITags, registry } from "@server/openApi"; +import { ListResourceMtlsCertificatesResponse } from "@server/routers/resource/types"; const listResourceMtlsCertificatesParamsSchema = z.strictObject({ resourceId: z.coerce.number().int().positive() @@ -32,10 +33,6 @@ function queryResourceMtlsCertificates(resourceId: number) { .orderBy(asc(resourceMtlsCertificates.mtlsCertificateId)); } -export type ListResourceMtlsCertificatesResponse = { - certificates: Awaited>; -}; - const description = "List the trusted CA certificates for a resource's mTLS."; registry.registerPath({ @@ -66,8 +63,9 @@ export async function listResourceMtlsCertificates( next: NextFunction ): Promise { try { - const parsedParams = - listResourceMtlsCertificatesParamsSchema.safeParse(req.params); + const parsedParams = listResourceMtlsCertificatesParamsSchema.safeParse( + req.params + ); if (!parsedParams.success) { return next( createHttpError( diff --git a/server/routers/external.ts b/server/routers/external.ts index 866b110f2..d5faba7cd 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -849,28 +849,6 @@ authenticated.delete( resource.deleteResourceRule ); -authenticated.put( - "/resource/:resourceId/mtls/certificate", - verifyResourceAccess, - verifyLimits, - verifyUserHasAction(ActionsEnum.createResourceMtlsCertificate), - logActionAudit(ActionsEnum.createResourceMtlsCertificate), - resource.createResourceMtlsCertificate -); -authenticated.get( - "/resource/:resourceId/mtls/certificates", - verifyResourceAccess, - verifyUserHasAction(ActionsEnum.listResourceMtlsCertificates), - resource.listResourceMtlsCertificates -); -authenticated.delete( - "/resource/:resourceId/mtls/certificate/:mtlsCertificateId", - verifyResourceAccess, - verifyUserHasAction(ActionsEnum.deleteResourceMtlsCertificate), - logActionAudit(ActionsEnum.deleteResourceMtlsCertificate), - resource.deleteResourceMtlsCertificate -); - authenticated.get( "/target/:targetId", verifyTargetAccess, diff --git a/server/routers/integration.ts b/server/routers/integration.ts index b8f15cf23..20b6d9c78 100644 --- a/server/routers/integration.ts +++ b/server/routers/integration.ts @@ -740,39 +740,6 @@ authenticated.delete( resource.deleteResourceRule ); -authenticated.put( - [ - "/resource/:resourceId/mtls/certificate", - "/public-resource/:resourceId/mtls/certificate" - ], - verifyApiKeyResourceAccess, - verifyLimits, - verifyApiKeyHasAction(ActionsEnum.createResourceMtlsCertificate), - logActionAudit(ActionsEnum.createResourceMtlsCertificate), - resource.createResourceMtlsCertificate -); - -authenticated.get( - [ - "/resource/:resourceId/mtls/certificates", - "/public-resource/:resourceId/mtls/certificates" - ], - verifyApiKeyResourceAccess, - verifyApiKeyHasAction(ActionsEnum.listResourceMtlsCertificates), - resource.listResourceMtlsCertificates -); - -authenticated.delete( - [ - "/resource/:resourceId/mtls/certificate/:mtlsCertificateId", - "/public-resource/:resourceId/mtls/certificate/:mtlsCertificateId" - ], - verifyApiKeyResourceAccess, - verifyApiKeyHasAction(ActionsEnum.deleteResourceMtlsCertificate), - logActionAudit(ActionsEnum.deleteResourceMtlsCertificate), - resource.deleteResourceMtlsCertificate -); - authenticated.get( "/target/:targetId", verifyApiKeyTargetAccess, diff --git a/server/routers/resource/index.ts b/server/routers/resource/index.ts index e29d941b6..7c888004c 100644 --- a/server/routers/resource/index.ts +++ b/server/routers/resource/index.ts @@ -21,9 +21,6 @@ export * from "./createResourceRule"; export * from "./deleteResourceRule"; export * from "./listResourceRules"; export * from "./updateResourceRule"; -export * from "./createResourceMtlsCertificate"; -export * from "./deleteResourceMtlsCertificate"; -export * from "./listResourceMtlsCertificates"; export * from "./getUserResources"; export * from "./listUserResourceAliases"; export * from "./setResourceHeaderAuth"; @@ -47,3 +44,4 @@ export * from "./setResourceAiProviders"; export * from "./addAiProviderToResource"; export * from "./removeAiProviderFromResource"; export * from "./exportResourceBlueprint"; +export * from "./types"; diff --git a/server/routers/resource/types.ts b/server/routers/resource/types.ts index edfe49b00..fff2dfac6 100644 --- a/server/routers/resource/types.ts +++ b/server/routers/resource/types.ts @@ -27,3 +27,38 @@ export type ResourcePolicyWithResources = Pick< export type ListResourcePoliciesResponse = PaginatedResponse<{ policies: Array; }>; + +export type DeleteResourceMtlsCertificateResponse = { + // true when removing the last CA certificate forced mTLS off + mtlsDisabled: boolean; +}; + +export type CreateResourceMtlsCertificateResponse = { + certificates: { + mtlsCertificateId: number; + resourceId: number; + name: string | null; + subject: string | null; + issuer: string | null; + serialNumber: string | null; + fingerprint: string | null; + notBefore: number | null; + notAfter: number | null; + createdAt: number; + }[]; +}; + +export type ListResourceMtlsCertificatesResponse = { + certificates: { + mtlsCertificateId: number; + resourceId: number; + name: string | null; + subject: string | null; + issuer: string | null; + serialNumber: string | null; + fingerprint: string | null; + notBefore: number | null; + notAfter: number | null; + createdAt: number; + }[]; +};