From 37a80fa1acfdd55d0e028b1fc08e1b937a3b982e Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Sep 2025 23:35:12 +0530 Subject: [PATCH] feat: resolved backend ts issues --- backend/src/lib/api-docs/constants.ts | 4 +- .../src/server/routes/v1/dashboard-router.ts | 4 +- .../routes/v1/depreciated-project-router.ts | 14 +- .../src/server/routes/v1/project-router.ts | 631 ++++++++++++------ .../routes/v2/depreciated-project-router.ts | 6 +- ...router.ts => depreciated-secret-router.ts} | 4 +- backend/src/server/routes/v3/index.ts | 4 +- backend/src/server/routes/v4/secret-router.ts | 18 +- .../external-migration-fns/import.ts | 2 +- .../src/services/project/project-service.ts | 17 +- backend/src/services/project/project-types.ts | 8 +- backend/src/services/secret/secret-service.ts | 19 +- frontend/src/hooks/api/workspace/queries.tsx | 28 +- .../src/hooks/api/workspace/query-keys.tsx | 18 +- frontend/src/hooks/api/workspace/types.ts | 2 + .../components/CertificatesTable.tsx | 2 +- .../components/AddPkiCollectionItemModal.tsx | 2 +- 17 files changed, 512 insertions(+), 271 deletions(-) rename backend/src/server/routes/v3/{secret-router.ts => depreciated-secret-router.ts} (99%) diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index ff56e5e01..bef0b78be 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -964,7 +964,7 @@ export const RAW_SECRETS = { expand: "Whether or not to expand secret references.", recursive: "Whether or not to fetch all secrets from the specified base path, and all of its subdirectories. Note, the max depth is 20 deep.", - workspaceId: "The ID of the project to list secrets from.", + projectId: "The ID of the project to list secrets from.", workspaceSlug: "The slug of the project to list secrets from. This parameter is only applicable by machine identities.", environment: "The slug of the environment to list secrets from.", @@ -1029,7 +1029,7 @@ export const RAW_SECRETS = { }, GET_REFERENCE_TREE: { secretName: "The name of the secret to get the reference tree for.", - workspaceId: "The ID of the project where the secret is located.", + projectId: "The ID of the project where the secret is located.", environment: "The slug of the environment where the the secret is located.", secretPath: "The folder path where the secret is located." }, diff --git a/backend/src/server/routes/v1/dashboard-router.ts b/backend/src/server/routes/v1/dashboard-router.ts index a8132a3fa..255106c61 100644 --- a/backend/src/server/routes/v1/dashboard-router.ts +++ b/backend/src/server/routes/v1/dashboard-router.ts @@ -207,7 +207,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { const environments = req.query.environments.split(","); if (!projectId || environments.length === 0) - throw new BadRequestError({ message: "Missing workspace id or environment(s)" }); + throw new BadRequestError({ message: "Missing project id or environment(s)" }); const { shouldUseSecretV2Bridge } = await server.services.projectBot.getBotKey(projectId); @@ -696,7 +696,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => { includeSecretRotations } = req.query; - if (!projectId || !environment) throw new BadRequestError({ message: "Missing workspace id or environment" }); + if (!projectId || !environment) throw new BadRequestError({ message: "Missing project id or environment" }); const { shouldUseSecretV2Bridge } = await server.services.projectBot.getBotKey(projectId); diff --git a/backend/src/server/routes/v1/depreciated-project-router.ts b/backend/src/server/routes/v1/depreciated-project-router.ts index 89fb93662..7ee336507 100644 --- a/backend/src/server/routes/v1/depreciated-project-router.ts +++ b/backend/src/server/routes/v1/depreciated-project-router.ts @@ -85,7 +85,7 @@ export const registerDepreciatedProjectRouter = async (server: FastifyZodProvide } ], params: z.object({ - workspaceId: z.string().trim().describe(PROJECTS.GET.workspaceId) + workspaceId: z.string().trim().describe(PROJECTS.GET.projectId) }), response: { 200: z.object({ @@ -176,7 +176,7 @@ export const registerDepreciatedProjectRouter = async (server: FastifyZodProvide } ], params: z.object({ - workspaceId: z.string().trim().describe(PROJECTS.UPDATE.workspaceId) + workspaceId: z.string().trim().describe(PROJECTS.UPDATE.projectId) }), body: z.object({ name: z @@ -284,7 +284,11 @@ export const registerDepreciatedProjectRouter = async (server: FastifyZodProvide actor: req.permission.type, actorAuthMethod: req.permission.authMethod, actorOrgId: req.permission.orgId, - workspaceSlug: req.params.workspaceSlug, + filter: { + type: ProjectFilterType.SLUG, + slug: req.params.workspaceSlug, + orgId: req.permission.orgId + }, auditLogsRetentionDays: req.body.auditLogsRetentionDays }); @@ -321,7 +325,7 @@ export const registerDepreciatedProjectRouter = async (server: FastifyZodProvide } ], params: z.object({ - workspaceId: z.string().trim().describe(PROJECTS.LIST_INTEGRATION.workspaceId) + workspaceId: z.string().trim().describe(PROJECTS.LIST_INTEGRATION.projectId) }), response: { 200: z.object({ @@ -366,7 +370,7 @@ export const registerDepreciatedProjectRouter = async (server: FastifyZodProvide } ], params: z.object({ - workspaceId: z.string().trim().describe(PROJECTS.LIST_INTEGRATION_AUTHORIZATION.workspaceId) + workspaceId: z.string().trim().describe(PROJECTS.LIST_INTEGRATION_AUTHORIZATION.projectId) }), response: { 200: z.object({ diff --git a/backend/src/server/routes/v1/project-router.ts b/backend/src/server/routes/v1/project-router.ts index 13295974a..1b86bb241 100644 --- a/backend/src/server/routes/v1/project-router.ts +++ b/backend/src/server/routes/v1/project-router.ts @@ -2,7 +2,10 @@ import slugify from "@sindresorhus/slugify"; import { z } from "zod"; import { + CertificatesSchema, IntegrationsSchema, + PkiAlertsSchema, + PkiCollectionsSchema, ProjectEnvironmentsSchema, ProjectMembershipsSchema, ProjectRolesSchema, @@ -27,9 +30,25 @@ import { ProjectFilterType, SearchProjectSortBy } from "@app/services/project/pr import { validateSlackChannelsField } from "@app/services/slack/slack-auth-validators"; import { WorkflowIntegration } from "@app/services/workflow-integration/workflow-integration-types"; -import { integrationAuthPubSchema, SanitizedProjectSchema } from "../sanitizedSchemas"; +import { + integrationAuthPubSchema, + InternalCertificateAuthorityResponseSchema, + SanitizedProjectSchema +} from "../sanitizedSchemas"; import { sanitizedServiceTokenSchema } from "../v2/service-token-router"; import { slugSchema } from "@app/server/lib/schemas"; +import { CaStatus } from "@app/services/certificate-authority/certificate-authority-enums"; +import { sanitizedCertificateTemplate } from "@app/services/certificate-template/certificate-template-schema"; +import { sanitizedSshCertificate } from "@app/ee/services/ssh-certificate/ssh-certificate-schema"; +import { sanitizedSshCertificateTemplate } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-schema"; +import { sanitizedSshCa } from "@app/ee/services/ssh/ssh-certificate-authority-schema"; +import { loginMappingSchema, sanitizedSshHost } from "@app/ee/services/ssh-host/ssh-host-schema"; +import { LoginMappingSource } from "@app/ee/services/ssh-host/ssh-host-types"; +import { sanitizedSshHostGroup } from "@app/ee/services/ssh-host-group/ssh-host-group-schema"; +import { InfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-types"; +import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; +import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; +import { sanitizedPkiSubscriber } from "@app/services/pki-subscriber/pki-subscriber-schema"; const projectWithEnv = SanitizedProjectSchema.merge( z.object({ @@ -158,8 +177,8 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { actor: req.permission.type, actorOrgId: req.permission.orgId, actorAuthMethod: req.permission.authMethod, - workspaceName: req.body.projectName, - workspaceDescription: req.body.projectDescription, + projectName: req.body.projectName, + projectDescription: req.body.projectDescription, slug: req.body.slug, kmsKeyId: req.body.kmsKeyId, template: req.body.template, @@ -365,58 +384,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { } }); - /* Delete a project by slug */ - server.route({ - method: "DELETE", - url: "/slug/:slug", - config: { - rateLimit: writeLimit - }, - schema: { - hide: false, - tags: [ApiDocsTags.Projects], - description: "Delete project", - security: [ - { - bearerAuth: [] - } - ], - params: z.object({ - slug: slugSchema({ min: 5, max: 36 }).describe("The slug of the project to delete.") - }), - response: { - 200: SanitizedProjectSchema - } - }, - onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), - - handler: async (req) => { - const project = await server.services.project.deleteProject({ - filter: { - type: ProjectFilterType.SLUG, - slug: req.params.slug, - orgId: req.permission.orgId - }, - actorId: req.permission.id, - actorAuthMethod: req.permission.authMethod, - actorOrgId: req.permission.orgId, - actor: req.permission.type - }); - - await server.services.auditLog.createAuditLog({ - ...req.auditLogInfo, - orgId: req.permission.orgId, - projectId: project.id, - event: { - type: EventType.DELETE_PROJECT, - metadata: project - } - }); - - return project; - } - }); - + // TODO(depri): replcae frontned auto cap and delete protection with this patch server.route({ method: "PATCH", url: "/:projectId", @@ -462,11 +430,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { .describe(PROJECTS.UPDATE.slug), secretSharing: z.boolean().optional().describe(PROJECTS.UPDATE.secretSharing), showSnapshotsLegacy: z.boolean().optional().describe(PROJECTS.UPDATE.showSnapshotsLegacy), - defaultProduct: z.nativeEnum(ProjectType).optional().describe(PROJECTS.UPDATE.defaultProduct), secretDetectionIgnoreValues: z .array(z.string()) .optional() - .describe(PROJECTS.UPDATE.secretDetectionIgnoreValues) + .describe(PROJECTS.UPDATE.secretDetectionIgnoreValues), + pitVersionLimit: z.number().min(1).max(100).optional() }), response: { 200: z.object({ @@ -485,12 +453,12 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { name: req.body.name, description: req.body.description, autoCapitalization: req.body.autoCapitalization, - defaultProduct: req.body.defaultProduct, hasDeleteProtection: req.body.hasDeleteProtection, slug: req.body.slug, secretSharing: req.body.secretSharing, showSnapshotsLegacy: req.body.showSnapshotsLegacy, - secretDetectionIgnoreValues: req.body.secretDetectionIgnoreValues + secretDetectionIgnoreValues: req.body.secretDetectionIgnoreValues, + pitVersionLimit: req.body.pitVersionLimit }, actorAuthMethod: req.permission.authMethod, actorId: req.permission.id, @@ -515,8 +483,8 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { }); server.route({ - method: "POST", - url: "/:projectId/auto-capitalization", + method: "PUT", + url: "/:projectId/audit-logs-retention", config: { rateLimit: writeLimit }, @@ -524,150 +492,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { params: z.object({ projectId: z.string().trim() }), - body: z.object({ - autoCapitalization: z.boolean() - }), - response: { - 200: z.object({ - message: z.string(), - project: SanitizedProjectSchema - }) - } - }, - onRequest: verifyAuth([AuthMode.JWT]), - handler: async (req) => { - const project = await server.services.project.toggleAutoCapitalization({ - actorId: req.permission.id, - actor: req.permission.type, - actorAuthMethod: req.permission.authMethod, - actorOrgId: req.permission.orgId, - projectId: req.params.projectId, - autoCapitalization: req.body.autoCapitalization - }); - - await server.services.auditLog.createAuditLog({ - ...req.auditLogInfo, - orgId: req.permission.orgId, - projectId: req.params.projectId, - event: { - type: EventType.UPDATE_PROJECT, - metadata: req.body - } - }); - - return { - message: "Successfully changed project settings", - project - }; - } - }); - - server.route({ - method: "POST", - url: "/:projectId/delete-protection", - config: { - rateLimit: writeLimit - }, - schema: { - params: z.object({ - projectId: z.string().trim() - }), - body: z.object({ - hasDeleteProtection: z.boolean() - }), - response: { - 200: z.object({ - message: z.string(), - project: SanitizedProjectSchema - }) - } - }, - onRequest: verifyAuth([AuthMode.JWT]), - handler: async (req) => { - const project = await server.services.project.toggleDeleteProtection({ - actorId: req.permission.id, - actor: req.permission.type, - actorAuthMethod: req.permission.authMethod, - actorOrgId: req.permission.orgId, - projectId: req.params.projectId, - hasDeleteProtection: req.body.hasDeleteProtection - }); - - await server.services.auditLog.createAuditLog({ - ...req.auditLogInfo, - orgId: req.permission.orgId, - projectId: req.params.projectId, - event: { - type: EventType.UPDATE_PROJECT, - metadata: req.body - } - }); - - return { - message: "Successfully changed project settings", - project - }; - } - }); - - server.route({ - method: "PUT", - url: "/:workspaceSlug/version-limit", - config: { - rateLimit: writeLimit - }, - schema: { - params: z.object({ - workspaceSlug: z.string().trim() - }), - body: z.object({ - pitVersionLimit: z.number().min(1).max(100) - }), - response: { - 200: z.object({ - message: z.string(), - project: SanitizedProjectSchema - }) - } - }, - onRequest: verifyAuth([AuthMode.JWT]), - handler: async (req) => { - const project = await server.services.project.updateVersionLimit({ - actorId: req.permission.id, - actor: req.permission.type, - actorAuthMethod: req.permission.authMethod, - actorOrgId: req.permission.orgId, - pitVersionLimit: req.body.pitVersionLimit, - workspaceSlug: req.params.workspaceSlug - }); - - await server.services.auditLog.createAuditLog({ - ...req.auditLogInfo, - orgId: req.permission.orgId, - projectId: project.id, - event: { - type: EventType.UPDATE_PROJECT, - metadata: req.body - } - }); - - return { - message: "Successfully changed project version limit", - project - }; - } - }); - - server.route({ - method: "PUT", - url: "/:workspaceSlug/audit-logs-retention", - config: { - rateLimit: writeLimit - }, - schema: { - params: z.object({ - workspaceSlug: z.string().trim() - }), body: z.object({ auditLogsRetentionDays: z.number().min(0) }), @@ -685,8 +509,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { actor: req.permission.type, actorAuthMethod: req.permission.authMethod, actorOrgId: req.permission.orgId, - workspaceSlug: req.params.workspaceSlug, - auditLogsRetentionDays: req.body.auditLogsRetentionDays + auditLogsRetentionDays: req.body.auditLogsRetentionDays, + filter: { + projectId: req.params.projectId, + type: ProjectFilterType.ID + } }); await server.services.auditLog.createAuditLog({ @@ -1303,4 +1130,394 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { return { status }; } }); + + server.route({ + method: "GET", + url: "/:projectId/cas", + config: { + rateLimit: readLimit + }, + schema: { + hide: false, + tags: [ApiDocsTags.PkiCertificateAuthorities], + params: z.object({ + projectId: z.string().trim() + }), + querystring: z.object({ + status: z.enum([CaStatus.ACTIVE, CaStatus.PENDING_CERTIFICATE]).optional().describe(PROJECTS.LIST_CAS.status), + friendlyName: z.string().optional().describe(PROJECTS.LIST_CAS.friendlyName), + commonName: z.string().optional().describe(PROJECTS.LIST_CAS.commonName), + offset: z.coerce.number().min(0).max(100).default(0).describe(PROJECTS.LIST_CAS.offset), + limit: z.coerce.number().min(1).max(100).default(25).describe(PROJECTS.LIST_CAS.limit) + }), + response: { + 200: z.object({ + cas: z.array(InternalCertificateAuthorityResponseSchema) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const cas = await server.services.project.listProjectCas({ + filter: { + projectId: req.params.projectId, + type: ProjectFilterType.ID + }, + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, + actor: req.permission.type, + ...req.query + }); + return { cas }; + } + }); + + server.route({ + method: "GET", + url: "/:projectId/certificates", + config: { + rateLimit: readLimit + }, + schema: { + hide: false, + tags: [ApiDocsTags.PkiCertificates], + params: z.object({ + projectId: z.string().trim() + }), + querystring: z.object({ + friendlyName: z.string().optional().describe(PROJECTS.LIST_CERTIFICATES.friendlyName), + commonName: z.string().optional().describe(PROJECTS.LIST_CERTIFICATES.commonName), + offset: z.coerce.number().min(0).max(100).default(0).describe(PROJECTS.LIST_CERTIFICATES.offset), + limit: z.coerce.number().min(1).max(100).default(25).describe(PROJECTS.LIST_CERTIFICATES.limit) + }), + response: { + 200: z.object({ + certificates: z.array(CertificatesSchema), + totalCount: z.number() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const { certificates, totalCount } = await server.services.project.listProjectCertificates({ + filter: { + projectId: req.params.projectId, + type: ProjectFilterType.ID + }, + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, + actor: req.permission.type, + ...req.query + }); + return { certificates, totalCount }; + } + }); + + server.route({ + method: "GET", + url: "/:projectId/pki-alerts", + config: { + rateLimit: readLimit + }, + schema: { + hide: false, + tags: [ApiDocsTags.PkiAlerting], + params: z.object({ + projectId: z.string().trim() + }), + response: { + 200: z.object({ + alerts: z.array(PkiAlertsSchema) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const { alerts } = await server.services.project.listProjectAlerts({ + projectId: req.params.projectId, + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, + actor: req.permission.type + }); + + return { alerts }; + } + }); + + server.route({ + method: "GET", + url: "/:projectId/pki-collections", + config: { + rateLimit: readLimit + }, + schema: { + hide: false, + tags: [ApiDocsTags.PkiCertificateCollections], + params: z.object({ + projectId: z.string().trim() + }), + response: { + 200: z.object({ + collections: z.array(PkiCollectionsSchema) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const { pkiCollections } = await server.services.project.listProjectPkiCollections({ + projectId: req.params.projectId, + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, + actor: req.permission.type + }); + + return { collections: pkiCollections }; + } + }); + + server.route({ + method: "GET", + url: "/:projectId/pki-subscribers", + config: { + rateLimit: readLimit + }, + schema: { + hide: false, + tags: [ApiDocsTags.PkiSubscribers], + params: z.object({ + projectId: z.string().trim().describe(PROJECTS.LIST_PKI_SUBSCRIBERS.projectId) + }), + response: { + 200: z.object({ + subscribers: z.array(sanitizedPkiSubscriber) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const subscribers = await server.services.project.listProjectPkiSubscribers({ + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, + actor: req.permission.type, + projectId: req.params.projectId + }); + + return { subscribers }; + } + }); + + server.route({ + method: "GET", + url: "/:projectId/certificate-templates", + config: { + rateLimit: readLimit + }, + schema: { + hide: false, + tags: [ApiDocsTags.PkiCertificateTemplates], + params: z.object({ + projectId: z.string().trim() + }), + response: { + 200: z.object({ + certificateTemplates: sanitizedCertificateTemplate.array() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const { certificateTemplates } = await server.services.project.listProjectCertificateTemplates({ + projectId: req.params.projectId, + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, + actor: req.permission.type + }); + + return { certificateTemplates }; + } + }); + + server.route({ + method: "GET", + url: "/:projectId/ssh-certificates", + config: { + rateLimit: readLimit + }, + schema: { + params: z.object({ + projectId: z.string().trim().describe(PROJECTS.LIST_SSH_CAS.projectId) + }), + querystring: z.object({ + offset: z.coerce.number().default(0).describe(PROJECTS.LIST_SSH_CERTIFICATES.offset), + limit: z.coerce.number().default(25).describe(PROJECTS.LIST_SSH_CERTIFICATES.limit) + }), + response: { + 200: z.object({ + certificates: z.array(sanitizedSshCertificate), + totalCount: z.number() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const { certificates, totalCount } = await server.services.project.listProjectSshCertificates({ + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, + actor: req.permission.type, + projectId: req.params.projectId, + offset: req.query.offset, + limit: req.query.limit + }); + + return { certificates, totalCount }; + } + }); + + server.route({ + method: "GET", + url: "/:projectId/ssh-certificate-templates", + config: { + rateLimit: readLimit + }, + schema: { + hide: false, + tags: [ApiDocsTags.SshCertificateTemplates], + params: z.object({ + projectId: z.string().trim().describe(PROJECTS.LIST_SSH_CERTIFICATE_TEMPLATES.projectId) + }), + response: { + 200: z.object({ + certificateTemplates: z.array(sanitizedSshCertificateTemplate) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const { certificateTemplates } = await server.services.project.listProjectSshCertificateTemplates({ + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, + actor: req.permission.type, + projectId: req.params.projectId + }); + + return { certificateTemplates }; + } + }); + + server.route({ + method: "GET", + url: "/:projectId/ssh-cas", + config: { + rateLimit: readLimit + }, + schema: { + hide: false, + tags: [ApiDocsTags.SshCertificateAuthorities], + params: z.object({ + projectId: z.string().trim().describe(PROJECTS.LIST_SSH_CAS.projectId) + }), + response: { + 200: z.object({ + cas: z.array(sanitizedSshCa) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const cas = await server.services.project.listProjectSshCas({ + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, + actor: req.permission.type, + projectId: req.params.projectId + }); + + return { cas }; + } + }); + + server.route({ + method: "GET", + url: "/:projectId/ssh-hosts", + config: { + rateLimit: readLimit + }, + schema: { + hide: false, + tags: [ApiDocsTags.SshHosts], + params: z.object({ + projectId: z.string().trim().describe(PROJECTS.LIST_SSH_HOSTS.projectId) + }), + response: { + 200: z.object({ + hosts: z.array( + sanitizedSshHost.extend({ + loginMappings: loginMappingSchema + .extend({ + source: z.nativeEnum(LoginMappingSource) + }) + .array() + }) + ) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const hosts = await server.services.project.listProjectSshHosts({ + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, + actor: req.permission.type, + projectId: req.params.projectId + }); + + return { hosts }; + } + }); + + server.route({ + method: "GET", + url: "/:projectId/ssh-host-groups", + config: { + rateLimit: readLimit + }, + schema: { + hide: false, + tags: [ApiDocsTags.SshHostGroups], + params: z.object({ + projectId: z.string().trim().describe(PROJECTS.LIST_SSH_HOST_GROUPS.projectId) + }), + response: { + 200: z.object({ + groups: z.array( + sanitizedSshHostGroup.extend({ + loginMappings: loginMappingSchema.array(), + hostCount: z.number() + }) + ) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const groups = await server.services.project.listProjectSshHostGroups({ + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, + actor: req.permission.type, + projectId: req.params.projectId + }); + + return { groups }; + } + }); }; diff --git a/backend/src/server/routes/v2/depreciated-project-router.ts b/backend/src/server/routes/v2/depreciated-project-router.ts index a8abab54c..076f1b832 100644 --- a/backend/src/server/routes/v2/depreciated-project-router.ts +++ b/backend/src/server/routes/v2/depreciated-project-router.ts @@ -47,7 +47,7 @@ export const registerDepreciatedProjectRouter = async (server: FastifyZodProvide schema: { description: "Return encrypted project key", params: z.object({ - workspaceId: z.string().trim().describe(PROJECTS.GET_KEY.workspaceId) + workspaceId: z.string().trim().describe(PROJECTS.GET_KEY.projectId) }), response: { 200: ProjectKeysSchema.merge( @@ -125,8 +125,8 @@ export const registerDepreciatedProjectRouter = async (server: FastifyZodProvide actor: req.permission.type, actorOrgId: req.permission.orgId, actorAuthMethod: req.permission.authMethod, - workspaceName: req.body.projectName, - workspaceDescription: req.body.projectDescription, + projectName: req.body.projectName, + projectDescription: req.body.projectDescription, slug: req.body.slug, kmsKeyId: req.body.kmsKeyId, template: req.body.template, diff --git a/backend/src/server/routes/v3/secret-router.ts b/backend/src/server/routes/v3/depreciated-secret-router.ts similarity index 99% rename from backend/src/server/routes/v3/secret-router.ts rename to backend/src/server/routes/v3/depreciated-secret-router.ts index eaeeb8bb8..14eb9f0cd 100644 --- a/backend/src/server/routes/v3/secret-router.ts +++ b/backend/src/server/routes/v3/depreciated-secret-router.ts @@ -39,7 +39,7 @@ const SecretReferenceNodeTree: z.ZodType = SecretReference children: z.lazy(() => SecretReferenceNodeTree.array()) }); -export const registerSecretRouter = async (server: FastifyZodProvider) => { +export const registerDepreciatedSecretRouter = async (server: FastifyZodProvider) => { server.route({ method: "POST", url: "/tags/:secretName", @@ -230,7 +230,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { } }) .describe(RAW_SECRETS.LIST.metadataFilter), - workspaceId: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceId), + workspaceId: z.string().trim().optional().describe(RAW_SECRETS.LIST.projectId), workspaceSlug: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceSlug), environment: z.string().trim().optional().describe(RAW_SECRETS.LIST.environment), secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.LIST.secretPath), diff --git a/backend/src/server/routes/v3/index.ts b/backend/src/server/routes/v3/index.ts index 16880e840..6b1744d10 100644 --- a/backend/src/server/routes/v3/index.ts +++ b/backend/src/server/routes/v3/index.ts @@ -1,6 +1,6 @@ import { registerExternalMigrationRouter } from "./external-migration-router"; import { registerLoginRouter } from "./login-router"; -import { registerSecretRouter } from "./secret-router"; +import { registerDepreciatedSecretRouter } from "./depreciated-secret-router"; import { registerSignupRouter } from "./signup-router"; import { registerUserRouter } from "./user-router"; @@ -8,6 +8,6 @@ export const registerV3Routes = async (server: FastifyZodProvider) => { await server.register(registerSignupRouter, { prefix: "/signup" }); await server.register(registerLoginRouter, { prefix: "/auth" }); await server.register(registerUserRouter, { prefix: "/users" }); - await server.register(registerSecretRouter, { prefix: "/secrets" }); + await server.register(registerDepreciatedSecretRouter, { prefix: "/secrets" }); await server.register(registerExternalMigrationRouter, { prefix: "/external-migration" }); }; diff --git a/backend/src/server/routes/v4/secret-router.ts b/backend/src/server/routes/v4/secret-router.ts index 8776beb8d..f18c87497 100644 --- a/backend/src/server/routes/v4/secret-router.ts +++ b/backend/src/server/routes/v4/secret-router.ts @@ -117,7 +117,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { } }) .describe(RAW_SECRETS.LIST.metadataFilter), - projectId: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceId), + projectId: z.string().trim().optional().describe(RAW_SECRETS.LIST.projectId), environment: z.string().trim().optional().describe(RAW_SECRETS.LIST.environment), secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.LIST.secretPath), viewSecretValue: convertStringBoolean(true).describe(RAW_SECRETS.LIST.viewSecretValue), @@ -901,7 +901,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { ); await server.services.auditLog.createAuditLog({ - projectId: secrets[0].workspace, + projectId: req.body.projectId, ...req.auditLogInfo, event: { type: EventType.CREATE_SECRETS, @@ -924,7 +924,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { organizationId: req.permission.orgId, properties: { numberOfSecrets: secrets.length, - projectId: secrets[0].workspace, + projectId: req.body.projectId, environment: req.body.environment, secretPath: req.body.secretPath, channel: getUserAgentType(req.headers["user-agent"]), @@ -1050,7 +1050,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { ); await server.services.auditLog.createAuditLog({ - projectId: secrets[0].workspace, + projectId: req.body.projectId, ...req.auditLogInfo, event: { type: EventType.UPDATE_SECRETS, @@ -1072,7 +1072,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { const createdSecrets = secrets.filter((el) => el.version === 1); if (createdSecrets.length) { await server.services.auditLog.createAuditLog({ - projectId: secrets[0].workspace, + projectId: req.body.projectId, ...req.auditLogInfo, event: { type: EventType.CREATE_SECRETS, @@ -1097,7 +1097,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { organizationId: req.permission.orgId, properties: { numberOfSecrets: secrets.length, - projectId: secrets[0].workspace, + projectId: req.body.projectId, environment: req.body.environment, secretPath: req.body.secretPath, channel: getUserAgentType(req.headers["user-agent"]), @@ -1243,7 +1243,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { secretName: z.string().trim().describe(RAW_SECRETS.GET_REFERENCE_TREE.secretName) }), querystring: z.object({ - projectId: z.string().trim().describe(RAW_SECRETS.GET_REFERENCE_TREE.workspaceId), + projectId: z.string().trim().describe(RAW_SECRETS.GET_REFERENCE_TREE.projectId), environment: z.string().trim().describe(RAW_SECRETS.GET_REFERENCE_TREE.environment), secretPath: z .string() @@ -1262,13 +1262,13 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { onRequest: verifyAuth([AuthMode.JWT]), handler: async (req) => { const { secretName } = req.params; - const { secretPath, environment, projectId: workspaceId } = req.query; + const { secretPath, environment, projectId } = req.query; const { tree, value } = await server.services.secret.getSecretReferenceTree({ actorId: req.permission.id, actor: req.permission.type, actorAuthMethod: req.permission.authMethod, actorOrgId: req.permission.orgId, - projectId: workspaceId, + projectId, secretName, secretPath, environment diff --git a/backend/src/services/external-migration/external-migration-fns/import.ts b/backend/src/services/external-migration/external-migration-fns/import.ts index 5728bf1c0..62888c5bf 100644 --- a/backend/src/services/external-migration/external-migration-fns/import.ts +++ b/backend/src/services/external-migration/external-migration-fns/import.ts @@ -55,7 +55,7 @@ export const importDataIntoInfisicalFn = async ({ actorId, actorOrgId, actorAuthMethod, - workspaceName: project.name, + projectName: project.name, createDefaultEnvs: false, tx }) diff --git a/backend/src/services/project/project-service.ts b/backend/src/services/project/project-service.ts index d59f20bc6..b10432e0a 100644 --- a/backend/src/services/project/project-service.ts +++ b/backend/src/services/project/project-service.ts @@ -237,8 +237,8 @@ export const projectServiceFactory = ({ actorId, actorOrgId, actorAuthMethod, - workspaceName, - workspaceDescription, + projectName: workspaceName, + projectDescription: workspaceDescription, slug: projectSlug, kmsKeyId, tx: trx, @@ -591,7 +591,8 @@ export const projectServiceFactory = ({ secretSharing: update.secretSharing, defaultProduct: update.defaultProduct, showSnapshotsLegacy: update.showSnapshotsLegacy, - secretDetectionIgnoreValues: update.secretDetectionIgnoreValues + secretDetectionIgnoreValues: update.secretDetectionIgnoreValues, + pitVersionLimit: update.pitVersionLimit }); return updatedProject; @@ -684,19 +685,21 @@ export const projectServiceFactory = ({ actorOrgId, actorAuthMethod, auditLogsRetentionDays, - workspaceSlug + filter }: TUpdateAuditLogsRetentionDTO) => { - const project = await projectDAL.findProjectBySlug(workspaceSlug, actorOrgId); + const project = await projectDAL.findProjectByFilter(filter); + const projectId = project.id; + if (!project) { throw new NotFoundError({ - message: `Project with slug '${workspaceSlug}' not found` + message: `Project not found` }); } const { hasRole } = await permissionService.getProjectPermission({ actor, actorId, - projectId: project.id, + projectId, actorAuthMethod, actorOrgId, actionProjectType: ActionProjectType.Any diff --git a/backend/src/services/project/project-types.ts b/backend/src/services/project/project-types.ts index ceef78f6a..ebe30b266 100644 --- a/backend/src/services/project/project-types.ts +++ b/backend/src/services/project/project-types.ts @@ -42,12 +42,13 @@ export type TCreateProjectDTO = { actorAuthMethod: ActorAuthMethod; actorId: string; actorOrgId?: string; - workspaceName: string; - workspaceDescription?: string; + projectName: string; + projectDescription?: string; slug?: string; kmsKeyId?: string; createDefaultEnvs?: boolean; template?: string; + pitVersionLimit?: number; tx?: Knex; type?: ProjectType; }; @@ -78,7 +79,7 @@ export type TUpdateProjectVersionLimitDTO = { export type TUpdateAuditLogsRetentionDTO = { auditLogsRetentionDays: number; - workspaceSlug: string; + filter: Filter; } & Omit; export type TUpdateProjectNameDTO = { @@ -90,6 +91,7 @@ export type TUpdateProjectDTO = { update: { name?: string; description?: string; + pitVersionLimit?: number; autoCapitalization?: boolean; hasDeleteProtection?: boolean; defaultProduct?: ProjectType; diff --git a/backend/src/services/secret/secret-service.ts b/backend/src/services/secret/secret-service.ts index 6bafa3ba6..8758a992e 100644 --- a/backend/src/services/secret/secret-service.ts +++ b/backend/src/services/secret/secret-service.ts @@ -2970,14 +2970,23 @@ export const secretServiceFactory = ({ actor, actorId, actorAuthMethod, - actorOrgId + actorOrgId, + projectId: inputProjectId }: TMoveSecretsDTO) => { - const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); + let project; + if (projectSlug) { + project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); + } else if (inputProjectId) { + project = await projectDAL.findById(inputProjectId); + } + if (!project) { throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` }); } + + const projectId = project.id; if (project.version === ProjectVersion.V3) { return secretV2BridgeService.moveSecrets({ sourceEnvironment, @@ -3170,7 +3179,7 @@ export const secretServiceFactory = ({ }); } const destinationFolderPolicy = await secretApprovalPolicyService.getSecretApprovalPolicy( - project.id, + projectId, destinationFolder.environment.slug, destinationFolder.path ); @@ -3257,7 +3266,7 @@ export const secretServiceFactory = ({ } if (locallyUpdatedSecrets.length) { await fnSecretBulkUpdate({ - projectId: project.id, + projectId, folderId: destinationFolder.id, secretVersionDAL, secretDAL, @@ -3300,7 +3309,7 @@ export const secretServiceFactory = ({ const locallyDeletedSecrets = decryptedSourceSecrets.map((el) => ({ ...el, operation: SecretOperations.Delete })); const sourceFolderPolicy = await secretApprovalPolicyService.getSecretApprovalPolicy( - project.id, + projectId, sourceFolder.environment.slug, sourceFolder.path ); diff --git a/frontend/src/hooks/api/workspace/queries.tsx b/frontend/src/hooks/api/workspace/queries.tsx index 0e312967f..4f21ebb5e 100644 --- a/frontend/src/hooks/api/workspace/queries.tsx +++ b/frontend/src/hooks/api/workspace/queries.tsx @@ -245,7 +245,9 @@ export const useUpdateProject = () => { newSlug, secretSharing, showSnapshotsLegacy, - secretDetectionIgnoreValues + secretDetectionIgnoreValues, + autoCapitalization, + pitVersionLimit }) => { const { data } = await apiRequest.patch<{ project: Project }>( `/api/v1/projects/${projectID}`, @@ -255,7 +257,9 @@ export const useUpdateProject = () => { slug: newSlug, secretSharing, showSnapshotsLegacy, - secretDetectionIgnoreValues + secretDetectionIgnoreValues, + autoCapitalization, + pitVersionLimit } ); return data.project; @@ -682,15 +686,15 @@ export const useListWorkspaceGroups = (projectId: string) => { }; export const useListWorkspaceCas = ({ - projectSlug, + projectId, status }: { - projectSlug: string; + projectId: string; status?: CaStatus; }) => { return useQuery({ queryKey: projectKeys.specificWorkspaceCas({ - projectSlug, + projectId, status }), queryFn: async () => { @@ -701,29 +705,29 @@ export const useListWorkspaceCas = ({ const { data: { cas } } = await apiRequest.get<{ cas: TCertificateAuthority[] }>( - `/api/v1/projects/${projectSlug}/cas`, + `/api/v1/projects/${projectId}/cas`, { params } ); return cas; }, - enabled: Boolean(projectSlug) + enabled: Boolean(projectId) }); }; export const useListWorkspaceCertificates = ({ - projectSlug, + projectId, offset, limit }: { - projectSlug: string; + projectId: string; offset: number; limit: number; }) => { return useQuery({ queryKey: projectKeys.specificWorkspaceCertificates({ - slug: projectSlug, + projectId, offset, limit }), @@ -736,7 +740,7 @@ export const useListWorkspaceCertificates = ({ const { data: { certificates, totalCount } } = await apiRequest.get<{ certificates: TCertificate[]; totalCount: number }>( - `/api/v1/projects/${projectSlug}/certificates`, + `/api/v1/projects/${projectId}/certificates`, { params } @@ -744,7 +748,7 @@ export const useListWorkspaceCertificates = ({ return { certificates, totalCount }; }, - enabled: Boolean(projectSlug) + enabled: Boolean(projectId) }); }; diff --git a/frontend/src/hooks/api/workspace/query-keys.tsx b/frontend/src/hooks/api/workspace/query-keys.tsx index 2da17b3aa..fe17dc68d 100644 --- a/frontend/src/hooks/api/workspace/query-keys.tsx +++ b/frontend/src/hooks/api/workspace/query-keys.tsx @@ -34,22 +34,22 @@ export const projectKeys = { getWorkspaceGroupMemberships: (projectId: string) => [{ projectId }, "project-groups"] as const, getWorkspaceGroupMembershipDetails: (projectId: string, groupId: string) => [{ projectId, groupId }, "project-group-membership-details"] as const, - getWorkspaceCas: ({ projectSlug }: { projectSlug: string }) => - [{ projectSlug }, "project-cas"] as const, - specificWorkspaceCas: ({ projectSlug, status }: { projectSlug: string; status?: CaStatus }) => - [...projectKeys.getWorkspaceCas({ projectSlug }), { status }] as const, + getWorkspaceCas: ({ projectId }: { projectId: string }) => + [{ projectId }, "project-cas"] as const, + specificWorkspaceCas: ({ projectId, status }: { projectId: string; status?: CaStatus }) => + [...projectKeys.getWorkspaceCas({ projectId }), { status }] as const, allWorkspaceCertificates: () => ["project-certificates"] as const, - forWorkspaceCertificates: (slug: string) => - [...projectKeys.allWorkspaceCertificates(), slug] as const, + forWorkspaceCertificates: (projectId: string) => + [...projectKeys.allWorkspaceCertificates(), projectId] as const, specificWorkspaceCertificates: ({ - slug, + projectId, offset, limit }: { - slug: string; + projectId: string; offset: number; limit: number; - }) => [...projectKeys.forWorkspaceCertificates(slug), { offset, limit }] as const, + }) => [...projectKeys.forWorkspaceCertificates(projectId), { offset, limit }] as const, getWorkspacePkiAlerts: (projectId: string) => [{ projectId }, "project-pki-alerts"] as const, getWorkspacePkiSubscribers: (projectId: string) => [{ projectId }, "project-pki-subscribers"] as const, diff --git a/frontend/src/hooks/api/workspace/types.ts b/frontend/src/hooks/api/workspace/types.ts index 50dff8337..7c8da4d4b 100644 --- a/frontend/src/hooks/api/workspace/types.ts +++ b/frontend/src/hooks/api/workspace/types.ts @@ -83,6 +83,8 @@ export type UpdateProjectDTO = { secretSharing?: boolean; showSnapshotsLegacy?: boolean; secretDetectionIgnoreValues?: string[]; + pitVersionLimit?: number; + autoCapitalization?: boolean; }; export type UpdatePitVersionLimitDTO = { projectSlug: string; pitVersionLimit: number }; diff --git a/frontend/src/pages/cert-manager/CertificatesPage/components/CertificatesTable.tsx b/frontend/src/pages/cert-manager/CertificatesPage/components/CertificatesTable.tsx index 98f83bc02..9a204afac 100644 --- a/frontend/src/pages/cert-manager/CertificatesPage/components/CertificatesTable.tsx +++ b/frontend/src/pages/cert-manager/CertificatesPage/components/CertificatesTable.tsx @@ -64,7 +64,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => { const { currentWorkspace } = useWorkspace(); const { data, isPending } = useListWorkspaceCertificates({ - projectSlug: currentWorkspace?.slug ?? "", + projectId: currentWorkspace?.slug ?? "", offset: (page - 1) * perPage, limit: perPage }); diff --git a/frontend/src/pages/cert-manager/PkiCollectionDetailsByIDPage/components/AddPkiCollectionItemModal.tsx b/frontend/src/pages/cert-manager/PkiCollectionDetailsByIDPage/components/AddPkiCollectionItemModal.tsx index f356f2190..e1b6ebe17 100644 --- a/frontend/src/pages/cert-manager/PkiCollectionDetailsByIDPage/components/AddPkiCollectionItemModal.tsx +++ b/frontend/src/pages/cert-manager/PkiCollectionDetailsByIDPage/components/AddPkiCollectionItemModal.tsx @@ -49,7 +49,7 @@ export const AddPkiCollectionItemModal = ({ }); const { data } = useListWorkspaceCertificates({ - projectSlug: currentWorkspace?.slug || "", + projectId: currentWorkspace?.slug || "", offset: 0, limit: 25 });