diff --git a/backend/src/ee/services/access-approval-request/access-approval-request-service.ts b/backend/src/ee/services/access-approval-request/access-approval-request-service.ts index 5e8b8edcc..d0236b05e 100644 --- a/backend/src/ee/services/access-approval-request/access-approval-request-service.ts +++ b/backend/src/ee/services/access-approval-request/access-approval-request-service.ts @@ -216,7 +216,7 @@ export const accessApprovalRequestServiceFactory = ({ ); const requesterFullName = `${requestedByUser.firstName} ${requestedByUser.lastName}`; - const approvalUrl = `${cfg.SITE_URL}/secret-manager/${project.id}/approval`; + const approvalUrl = `${cfg.SITE_URL}/projects/${project.id}/secret-manager/approval`; await triggerWorkflowIntegrationNotification({ input: { @@ -545,7 +545,7 @@ export const accessApprovalRequestServiceFactory = ({ bypassReason: bypassReason || "No reason provided", secretPath: policy.secretPath || "/", environment, - approvalUrl: `${cfg.SITE_URL}/secret-manager/${project.id}/approval`, + approvalUrl: `${cfg.SITE_URL}/projects/${project.id}/secret-manager/approval`, requestType: "access" }, template: SmtpTemplates.AccessSecretRequestBypassed diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-fns.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-fns.ts index 58a39dfa7..5e4e0e3d6 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-fns.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-fns.ts @@ -36,7 +36,7 @@ export const sendApprovalEmailsFn = async ({ firstName: reviewerUser.firstName, projectName: project.name, organizationName: project.organization.name, - approvalUrl: `${cfg.SITE_URL}/secret-manager/${project.id}/approval?requestId=${secretApprovalRequest.id}` + approvalUrl: `${cfg.SITE_URL}/projects/${project.id}/secret-manager/approval?requestId=${secretApprovalRequest.id}` }, template: SmtpTemplates.SecretApprovalRequestNeedsReview }); diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts index ab0314473..6fa0248d0 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts @@ -944,7 +944,7 @@ export const secretApprovalRequestServiceFactory = ({ bypassReason, secretPath: policy.secretPath, environment: env.name, - approvalUrl: `${cfg.SITE_URL}/secret-manager/${project.id}/approval` + approvalUrl: `${cfg.SITE_URL}/projects/${project.id}/secret-manager/approval` }, template: SmtpTemplates.AccessSecretRequestBypassed }); diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-queue.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-queue.ts index f15cc4974..38ac137dc 100644 --- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-queue.ts +++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-queue.ts @@ -166,7 +166,9 @@ export const secretRotationV2QueueServiceFactory = async ({ secretPath: folder.path, environment: environment.name, projectName: project.name, - rotationUrl: encodeURI(`${appCfg.SITE_URL}/secret-manager/${projectId}/secrets/${environment.slug}`) + rotationUrl: encodeURI( + `${appCfg.SITE_URL}/projects/${projectId}/secret-manager/secrets/${environment.slug}` + ) } }); } catch (error) { diff --git a/backend/src/ee/services/secret-scanning-v2/secret-scanning-v2-queue.ts b/backend/src/ee/services/secret-scanning-v2/secret-scanning-v2-queue.ts index aa8519027..938d50a77 100644 --- a/backend/src/ee/services/secret-scanning-v2/secret-scanning-v2-queue.ts +++ b/backend/src/ee/services/secret-scanning-v2/secret-scanning-v2-queue.ts @@ -588,7 +588,7 @@ export const secretScanningV2QueueServiceFactory = async ({ numberOfSecrets: payload.numberOfSecrets, isDiffScan: payload.isDiffScan, url: encodeURI( - `${appCfg.SITE_URL}/secret-scanning/${projectId}/findings?search=scanId:${payload.scanId}` + `${appCfg.SITE_URL}/projects/${projectId}/secret-scanning/findings?search=scanId:${payload.scanId}` ), timestamp } @@ -599,7 +599,7 @@ export const secretScanningV2QueueServiceFactory = async ({ timestamp, errorMessage: payload.errorMessage, url: encodeURI( - `${appCfg.SITE_URL}/secret-scanning/${projectId}/data-sources/${dataSource.type}/${dataSource.id}` + `${appCfg.SITE_URL}/projects/${projectId}/secret-scanning/data-sources/${dataSource.type}/${dataSource.id}` ) } }); diff --git a/backend/src/ee/services/ssh-host/ssh-host-service.ts b/backend/src/ee/services/ssh-host/ssh-host-service.ts index e4136431a..87fcdb250 100644 --- a/backend/src/ee/services/ssh-host/ssh-host-service.ts +++ b/backend/src/ee/services/ssh-host/ssh-host-service.ts @@ -12,11 +12,13 @@ import { SshCertKeyAlgorithm } from "@app/ee/services/ssh-certificate/ssh-certif import { TSshHostDALFactory } from "@app/ee/services/ssh-host/ssh-host-dal"; import { TSshHostLoginUserMappingDALFactory } from "@app/ee/services/ssh-host/ssh-host-login-user-mapping-dal"; import { TSshHostLoginUserDALFactory } from "@app/ee/services/ssh-host/ssh-login-user-dal"; +import { PgSqlLock } from "@app/keystore/keystore"; import { BadRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors"; import { ActorType } from "@app/services/auth/auth-type"; import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { TProjectDALFactory } from "@app/services/project/project-dal"; +import { bootstrapSshProject } from "@app/services/project/project-fns"; import { TProjectSshConfigDALFactory } from "@app/services/project/project-ssh-config-dal"; import { TUserDALFactory } from "@app/services/user/user-dal"; @@ -43,9 +45,9 @@ type TSshHostServiceFactoryDep = { userDAL: Pick; groupDAL: Pick; projectDAL: Pick; - projectSshConfigDAL: Pick; - sshCertificateAuthorityDAL: Pick; - sshCertificateAuthoritySecretDAL: Pick; + projectSshConfigDAL: Pick; + sshCertificateAuthorityDAL: Pick; + sshCertificateAuthoritySecretDAL: Pick; sshCertificateDAL: Pick; sshCertificateBodyDAL: Pick; userGroupMembershipDAL: Pick; @@ -182,7 +184,22 @@ export const sshHostServiceFactory = ({ return ca.id; }; - const projectSshConfig = await projectSshConfigDAL.findOne({ projectId }); + const projectSshConfig = await projectSshConfigDAL.transaction(async (tx) => { + await tx.raw("SELECT pg_advisory_xact_lock(?)", [PgSqlLock.SshInit(projectId)]); + + let sshConfig = await projectSshConfigDAL.findOne({ projectId }); + if (sshConfig) return sshConfig; + + sshConfig = await bootstrapSshProject({ + projectId, + sshCertificateAuthorityDAL, + sshCertificateAuthoritySecretDAL, + kmsService, + projectSshConfigDAL, + tx + }); + return sshConfig; + }); const userSshCaId = await resolveSshCaId({ requestedId: requestedUserSshCaId, diff --git a/backend/src/keystore/keystore.ts b/backend/src/keystore/keystore.ts index 0aa35b73d..79679d256 100644 --- a/backend/src/keystore/keystore.ts +++ b/backend/src/keystore/keystore.ts @@ -12,7 +12,8 @@ export const PgSqlLock = { OrgGatewayCertExchange: (orgId: string) => pgAdvisoryLockHashText(`org-gateway-cert-exchange:${orgId}`), SecretRotationV2Creation: (folderId: string) => pgAdvisoryLockHashText(`secret-rotation-v2-creation:${folderId}`), CreateProject: (orgId: string) => pgAdvisoryLockHashText(`create-project:${orgId}`), - CreateFolder: (envId: string, projectId: string) => pgAdvisoryLockHashText(`create-folder:${envId}-${projectId}`) + CreateFolder: (envId: string, projectId: string) => pgAdvisoryLockHashText(`create-folder:${envId}-${projectId}`), + SshInit: (projectId: string) => pgAdvisoryLockHashText(`ssh-bootstrap:${projectId}`) } as const; // all the key prefixes used must be set here to avoid conflict diff --git a/backend/src/services/microsoft-teams/microsoft-teams-fns.ts b/backend/src/services/microsoft-teams/microsoft-teams-fns.ts index 4111115bf..d477143a9 100644 --- a/backend/src/services/microsoft-teams/microsoft-teams-fns.ts +++ b/backend/src/services/microsoft-teams/microsoft-teams-fns.ts @@ -400,7 +400,7 @@ export const buildTeamsPayload = (notification: TNotification) => { { type: "Action.OpenUrl", title: "View request in Infisical", - url: `${appCfg.SITE_URL}/secret-manager/${payload.projectId}/approval?requestId=${payload.requestId}` + url: `${appCfg.SITE_URL}/projects/${payload.projectId}/secret-manager/approval?requestId=${payload.requestId}` } ] }; diff --git a/backend/src/services/project/project-fns.ts b/backend/src/services/project/project-fns.ts index 08652e348..4500d4b66 100644 --- a/backend/src/services/project/project-fns.ts +++ b/backend/src/services/project/project-fns.ts @@ -141,7 +141,7 @@ export const bootstrapSshProject = async ({ tx }); - await projectSshConfigDAL.create( + const sshConfig = await projectSshConfigDAL.create( { projectId, defaultHostSshCaId: hostSshCa.id, @@ -149,4 +149,5 @@ export const bootstrapSshProject = async ({ }, tx ); + return sshConfig; }; diff --git a/backend/src/services/secret-sync/secret-sync-queue.ts b/backend/src/services/secret-sync/secret-sync-queue.ts index a8ff94e82..2acba91e5 100644 --- a/backend/src/services/secret-sync/secret-sync-queue.ts +++ b/backend/src/services/secret-sync/secret-sync-queue.ts @@ -868,7 +868,7 @@ export const secretSyncQueueFactory = ({ secretPath: folder?.path, environment: environment?.name, projectName: project.name, - syncUrl: `${appCfg.SITE_URL}/secret-manager/${projectId}/integrations/secret-syncs/${destination}/${secretSync.id}` + syncUrl: `${appCfg.SITE_URL}/projects/${projectId}/secret-manager/integrations/secret-syncs/${destination}/${secretSync.id}` } }); }; diff --git a/backend/src/services/secret/secret-queue.ts b/backend/src/services/secret/secret-queue.ts index 25b6e6572..d00ffa2f3 100644 --- a/backend/src/services/secret/secret-queue.ts +++ b/backend/src/services/secret/secret-queue.ts @@ -744,7 +744,7 @@ export const secretQueueFactory = ({ environment: jobPayload.environmentName, count: jobPayload.count, projectName: project.name, - integrationUrl: `${appCfg.SITE_URL}/secret-manager/${project.id}/integrations?selectedTab=native-integrations` + integrationUrl: `${appCfg.SITE_URL}/projects/${project.id}/secret-manager/integrations?selectedTab=native-integrations` } }); } diff --git a/backend/src/services/service-token/service-token-service.ts b/backend/src/services/service-token/service-token-service.ts index 4c3cc67c0..b2a27341d 100644 --- a/backend/src/services/service-token/service-token-service.ts +++ b/backend/src/services/service-token/service-token-service.ts @@ -212,7 +212,7 @@ export const serviceTokenServiceFactory = ({ substitutions: { tokenName: token.name, projectName: token.projectName, - url: `${appCfg.SITE_URL}/secret-manager/${token.projectId}/access-management?selectedTab=service-tokens` + url: `${appCfg.SITE_URL}/projects/${token.projectId}/secret-manager/access-management?selectedTab=service-tokens` } }); await serviceTokenDAL.update({ id: token.id }, { expiryNotificationSent: true }); diff --git a/backend/src/services/slack/slack-fns.ts b/backend/src/services/slack/slack-fns.ts index d4f5e29cc..bee414179 100644 --- a/backend/src/services/slack/slack-fns.ts +++ b/backend/src/services/slack/slack-fns.ts @@ -50,7 +50,7 @@ const buildSlackPayload = (notification: TNotification) => { *Secret path*: ${payload.secretPath || "/"} *Secret Key${payload.secretKeys.length > 1 ? "s" : ""}*: ${payload.secretKeys.join(", ")} -View the complete details <${appCfg.SITE_URL}/secret-manager/${payload.projectId}/approval?requestId=${ +View the complete details <${appCfg.SITE_URL}/projects/${payload.projectId}/secret-manager/approval?requestId=${ payload.requestId }|here>.`;