diff --git a/backend/src/ee/routes/v1/secret-sync-routers/oci-vault-sync-router.ts b/backend/src/ee/routes/v1/secret-sync-routers/oci-vault-sync-router.ts index 4d316369c..2efe3e3f5 100644 --- a/backend/src/ee/routes/v1/secret-sync-routers/oci-vault-sync-router.ts +++ b/backend/src/ee/routes/v1/secret-sync-routers/oci-vault-sync-router.ts @@ -3,10 +3,9 @@ import { OCIVaultSyncSchema, UpdateOCIVaultSyncSchema } from "@app/ee/services/secret-sync/oci-vault"; +import { registerSyncSecretsEndpoints } from "@app/server/routes/v1/secret-sync-routers/secret-sync-endpoints"; import { SecretSync } from "@app/services/secret-sync/secret-sync-enums"; -import { registerSyncSecretsEndpoints } from "../../../../server/routes/v1/secret-sync-routers/secret-sync-endpoints"; - export const registerOCIVaultSyncRouter = async (server: FastifyZodProvider) => registerSyncSecretsEndpoints({ destination: SecretSync.OCIVault, diff --git a/backend/src/server/routes/v1/app-connection-routers/index.ts b/backend/src/server/routes/v1/app-connection-routers/index.ts index 357c617e1..04c6a802f 100644 --- a/backend/src/server/routes/v1/app-connection-routers/index.ts +++ b/backend/src/server/routes/v1/app-connection-routers/index.ts @@ -1,6 +1,6 @@ +import { registerOCIConnectionRouter } from "@app/ee/routes/v1/app-connection-routers/oci-connection-router"; import { AppConnection } from "@app/services/app-connection/app-connection-enums"; -import { registerOCIConnectionRouter } from "../../../../ee/routes/v1/app-connection-routers/oci-connection-router"; import { registerAuth0ConnectionRouter } from "./auth0-connection-router"; import { registerAwsConnectionRouter } from "./aws-connection-router"; import { registerAzureAppConfigurationConnectionRouter } from "./azure-app-configuration-connection-router"; diff --git a/backend/src/services/app-connection/app-connection-fns.ts b/backend/src/services/app-connection/app-connection-fns.ts index e6d1b2c55..12b58ee36 100644 --- a/backend/src/services/app-connection/app-connection-fns.ts +++ b/backend/src/services/app-connection/app-connection-fns.ts @@ -1,7 +1,8 @@ import { TAppConnections } from "@app/db/schemas/app-connections"; +import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { generateHash } from "@app/lib/crypto/encryption"; import { BadRequestError } from "@app/lib/errors"; -import { APP_CONNECTION_NAME_MAP } from "@app/services/app-connection/app-connection-maps"; +import { APP_CONNECTION_NAME_MAP, APP_CONNECTION_PLAN_MAP } from "@app/services/app-connection/app-connection-maps"; import { transferSqlConnectionCredentialsToPlatform, validateSqlConnectionCredentials @@ -13,7 +14,7 @@ import { OCIConnectionMethod, validateOCIConnectionCredentials } from "../../ee/services/app-connections/oci"; -import { AppConnection } from "./app-connection-enums"; +import { AppConnection, AppConnectionPlanType } from "./app-connection-enums"; import { TAppConnectionServiceFactoryDep } from "./app-connection-service"; import { TAppConnection, @@ -261,3 +262,18 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record< [AppConnection.TeamCity]: platformManagedCredentialsNotSupported, [AppConnection.OCI]: platformManagedCredentialsNotSupported }; + +export const enterpriseAppCheck = async ( + licenseService: Pick, + appConnection: AppConnection, + orgId: string, + errorMessage: string +) => { + if (APP_CONNECTION_PLAN_MAP[appConnection] === AppConnectionPlanType.Enterprise) { + const plan = await licenseService.getPlan(orgId); + if (!plan.enterpriseAppConnections) + throw new BadRequestError({ + message: errorMessage + }); + } +}; diff --git a/backend/src/services/app-connection/app-connection-service.ts b/backend/src/services/app-connection/app-connection-service.ts index 662edab07..6637fd93d 100644 --- a/backend/src/services/app-connection/app-connection-service.ts +++ b/backend/src/services/app-connection/app-connection-service.ts @@ -10,6 +10,7 @@ import { DiscriminativePick, OrgServiceActor } from "@app/lib/types"; import { decryptAppConnection, encryptAppConnectionCredentials, + enterpriseAppCheck, getAppConnectionMethodName, listAppConnectionOptions, TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM, @@ -21,8 +22,8 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { ValidateOCIConnectionCredentialsSchema } from "../../ee/services/app-connections/oci"; import { ociConnectionService } from "../../ee/services/app-connections/oci/oci-connection-service"; import { TAppConnectionDALFactory } from "./app-connection-dal"; -import { AppConnection, AppConnectionPlanType } from "./app-connection-enums"; -import { APP_CONNECTION_NAME_MAP, APP_CONNECTION_PLAN_MAP } from "./app-connection-maps"; +import { AppConnection } from "./app-connection-enums"; +import { APP_CONNECTION_NAME_MAP } from "./app-connection-maps"; import { TAppConnection, TAppConnectionConfig, @@ -194,15 +195,12 @@ export const appConnectionServiceFactory = ({ OrgPermissionSubjects.AppConnections ); - // Enterprise check - if (APP_CONNECTION_PLAN_MAP[app] === AppConnectionPlanType.Enterprise) { - const plan = await licenseService.getPlan(actor.orgId); - if (!plan.enterpriseAppConnections) - throw new BadRequestError({ - message: - "Failed to create app connection due to plan restriction. Upgrade plan to access enterprise app connections." - }); - } + await enterpriseAppCheck( + licenseService, + app, + actor.orgId, + "Failed to create app connection due to plan restriction. Upgrade plan to access enterprise app connections." + ); const validatedCredentials = await validateAppConnectionCredentials({ app, @@ -266,15 +264,12 @@ export const appConnectionServiceFactory = ({ if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` }); - // Enterprise check - if (APP_CONNECTION_PLAN_MAP[appConnection.app as AppConnection] === AppConnectionPlanType.Enterprise) { - const plan = await licenseService.getPlan(actor.orgId); - if (!plan.enterpriseAppConnections) - throw new BadRequestError({ - message: - "Failed to update app connection due to plan restriction. Upgrade plan to access enterprise app connections." - }); - } + await enterpriseAppCheck( + licenseService, + appConnection.app as AppConnection, + actor.orgId, + "Failed to update app connection due to plan restriction. Upgrade plan to access enterprise app connections." + ); const { permission } = await permissionService.getOrgPermission( actor.type, @@ -422,15 +417,12 @@ export const appConnectionServiceFactory = ({ if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` }); - // Enterprise check - if (APP_CONNECTION_PLAN_MAP[app] === AppConnectionPlanType.Enterprise) { - const plan = await licenseService.getPlan(actor.orgId); - if (!plan.enterpriseAppConnections) - throw new BadRequestError({ - message: - "Failed to connect app connection due to plan restriction. Upgrade plan to access enterprise app connections." - }); - } + await enterpriseAppCheck( + licenseService, + app, + actor.orgId, + "Failed to connect app due to plan restriction. Upgrade plan to access enterprise app connections." + ); const { permission: orgPermission } = await permissionService.getOrgPermission( actor.type, diff --git a/backend/src/services/secret-sync/secret-sync-fns.ts b/backend/src/services/secret-sync/secret-sync-fns.ts index bf00e5d31..8f58b6fcc 100644 --- a/backend/src/services/secret-sync/secret-sync-fns.ts +++ b/backend/src/services/secret-sync/secret-sync-fns.ts @@ -1,7 +1,9 @@ import { AxiosError } from "axios"; import RE2 from "re2"; +import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { OCI_VAULT_SYNC_LIST_OPTION, OCIVaultSyncFns } from "@app/ee/services/secret-sync/oci-vault"; +import { BadRequestError } from "@app/lib/errors"; import { AWS_PARAMETER_STORE_SYNC_LIST_OPTION, AwsParameterStoreSyncFns @@ -12,7 +14,7 @@ import { } from "@app/services/secret-sync/aws-secrets-manager"; import { DATABRICKS_SYNC_LIST_OPTION, databricksSyncFactory } from "@app/services/secret-sync/databricks"; import { GITHUB_SYNC_LIST_OPTION, GithubSyncFns } from "@app/services/secret-sync/github"; -import { SecretSync } from "@app/services/secret-sync/secret-sync-enums"; +import { SecretSync, SecretSyncPlanType } from "@app/services/secret-sync/secret-sync-enums"; import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors"; import { TSecretMap, @@ -30,6 +32,7 @@ import { GcpSyncFns } from "./gcp/gcp-sync-fns"; import { HC_VAULT_SYNC_LIST_OPTION, HCVaultSyncFns } from "./hc-vault"; import { HUMANITEC_SYNC_LIST_OPTION } from "./humanitec"; import { HumanitecSyncFns } from "./humanitec/humanitec-sync-fns"; +import { SECRET_SYNC_PLAN_MAP } from "./secret-sync-maps"; import { TEAMCITY_SYNC_LIST_OPTION, TeamCitySyncFns } from "./teamcity"; import { TERRAFORM_CLOUD_SYNC_LIST_OPTION, TerraformCloudSyncFns } from "./terraform-cloud"; import { VERCEL_SYNC_LIST_OPTION, VercelSyncFns } from "./vercel"; @@ -327,3 +330,18 @@ export const parseSyncErrorMessage = (err: unknown): string => { ? errorMessage : `${errorMessage.substring(0, MAX_MESSAGE_LENGTH - 3)}...`; }; + +export const enterpriseSyncCheck = async ( + licenseService: Pick, + secretSync: SecretSync, + orgId: string, + errorMessage: string +) => { + if (SECRET_SYNC_PLAN_MAP[secretSync] === SecretSyncPlanType.Enterprise) { + const plan = await licenseService.getPlan(orgId); + if (!plan.enterpriseSecretSyncs) + throw new BadRequestError({ + message: errorMessage + }); + } +}; diff --git a/backend/src/services/secret-sync/secret-sync-queue.ts b/backend/src/services/secret-sync/secret-sync-queue.ts index e8b4d569e..6f627c24e 100644 --- a/backend/src/services/secret-sync/secret-sync-queue.ts +++ b/backend/src/services/secret-sync/secret-sync-queue.ts @@ -8,7 +8,6 @@ import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore"; import { getConfig } from "@app/lib/config/env"; -import { BadRequestError } from "@app/lib/errors"; import { logger } from "@app/lib/logger"; import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue"; import { decryptAppConnectionCredentials } from "@app/services/app-connection/app-connection-fns"; @@ -31,12 +30,11 @@ import { TSecretSyncDALFactory } from "@app/services/secret-sync/secret-sync-dal import { SecretSync, SecretSyncImportBehavior, - SecretSyncInitialSyncBehavior, - SecretSyncPlanType + SecretSyncInitialSyncBehavior } from "@app/services/secret-sync/secret-sync-enums"; import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors"; -import { parseSyncErrorMessage, SecretSyncFns } from "@app/services/secret-sync/secret-sync-fns"; -import { SECRET_SYNC_NAME_MAP, SECRET_SYNC_PLAN_MAP } from "@app/services/secret-sync/secret-sync-maps"; +import { enterpriseSyncCheck, parseSyncErrorMessage, SecretSyncFns } from "@app/services/secret-sync/secret-sync-fns"; +import { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps"; import { SecretSyncAction, SecretSyncStatus, @@ -336,14 +334,12 @@ export const secretSyncQueueFactory = ({ connection: { orgId } } = secretSync; - // Enterprise Check - if (SECRET_SYNC_PLAN_MAP[destination] === SecretSyncPlanType.Enterprise) { - const plan = await licenseService.getPlan(orgId); - if (!plan.enterpriseSecretSyncs) - throw new BadRequestError({ - message: "Failed to import secrets due to plan restriction. Upgrade plan to access enterprise secret syncs." - }); - } + await enterpriseSyncCheck( + licenseService, + destination, + orgId, + "Failed to import secrets due to plan restriction. Upgrade plan to access enterprise secret syncs." + ); if (!environment || !folder) throw new Error( @@ -420,14 +416,12 @@ export const secretSyncQueueFactory = ({ if (!secretSync) throw new Error(`Cannot find secret sync with ID ${syncId}`); - // Enterprise Check - if (SECRET_SYNC_PLAN_MAP[secretSync.destination as SecretSync] === SecretSyncPlanType.Enterprise) { - const plan = await licenseService.getPlan(secretSync.connection.orgId); - if (!plan.enterpriseSecretSyncs) - throw new BadRequestError({ - message: "Failed to sync secrets due to plan restriction. Upgrade plan to access enterprise secret syncs." - }); - } + await enterpriseSyncCheck( + licenseService, + secretSync.destination as SecretSync, + secretSync.connection.orgId, + "Failed to sync secrets due to plan restriction. Upgrade plan to access enterprise secret syncs." + ); await secretSyncDAL.updateById(syncId, { syncStatus: SecretSyncStatus.Running @@ -688,15 +682,12 @@ export const secretSyncQueueFactory = ({ if (!secretSync) throw new Error(`Cannot find secret sync with ID ${syncId}`); - // Enterprise Check - if (SECRET_SYNC_PLAN_MAP[secretSync.destination as SecretSync] === SecretSyncPlanType.Enterprise) { - const plan = await licenseService.getPlan(secretSync.connection.orgId); - if (!plan.enterpriseSecretSyncs) - throw new BadRequestError({ - message: - "Failed to access secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs." - }); - } + await enterpriseSyncCheck( + licenseService, + secretSync.destination as SecretSync, + secretSync.connection.orgId, + "Failed to remove secrets due to plan restriction. Upgrade plan to access enterprise secret syncs." + ); await secretSyncDAL.updateById(syncId, { removeStatus: SecretSyncStatus.Running diff --git a/backend/src/services/secret-sync/secret-sync-service.ts b/backend/src/services/secret-sync/secret-sync-service.ts index 2fc1f103d..e7751d3f9 100644 --- a/backend/src/services/secret-sync/secret-sync-service.ts +++ b/backend/src/services/secret-sync/secret-sync-service.ts @@ -16,8 +16,8 @@ import { OrgServiceActor } from "@app/lib/types"; import { TAppConnectionServiceFactory } from "@app/services/app-connection/app-connection-service"; import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service"; import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal"; -import { SecretSync, SecretSyncPlanType } from "@app/services/secret-sync/secret-sync-enums"; -import { listSecretSyncOptions } from "@app/services/secret-sync/secret-sync-fns"; +import { SecretSync } from "@app/services/secret-sync/secret-sync-enums"; +import { enterpriseSyncCheck, listSecretSyncOptions } from "@app/services/secret-sync/secret-sync-fns"; import { SecretSyncStatus, TCreateSecretSyncDTO, @@ -35,7 +35,7 @@ import { import { TSecretImportDALFactory } from "../secret-import/secret-import-dal"; import { TSecretSyncDALFactory } from "./secret-sync-dal"; -import { SECRET_SYNC_CONNECTION_MAP, SECRET_SYNC_NAME_MAP, SECRET_SYNC_PLAN_MAP } from "./secret-sync-maps"; +import { SECRET_SYNC_CONNECTION_MAP, SECRET_SYNC_NAME_MAP } from "./secret-sync-maps"; import { TSecretSyncQueueFactory } from "./secret-sync-queue"; type TSecretSyncServiceFactoryDep = { @@ -194,15 +194,12 @@ export const secretSyncServiceFactory = ({ { projectId, secretPath, environment, ...params }: TCreateSecretSyncDTO, actor: OrgServiceActor ) => { - // Enterprise check - if (SECRET_SYNC_PLAN_MAP[params.destination] === SecretSyncPlanType.Enterprise) { - const plan = await licenseService.getPlan(actor.orgId); - if (!plan.enterpriseSecretSyncs) - throw new BadRequestError({ - message: - "Failed to create secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs." - }); - } + await enterpriseSyncCheck( + licenseService, + params.destination, + actor.orgId, + "Failed to create secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs." + ); const { permission: projectPermission } = await permissionService.getProjectPermission({ actor: actor.type, @@ -273,15 +270,12 @@ export const secretSyncServiceFactory = ({ message: `Could not find ${SECRET_SYNC_NAME_MAP[destination]} Sync with ID ${syncId}` }); - // Enterprise check - if (SECRET_SYNC_PLAN_MAP[secretSync.destination as SecretSync] === SecretSyncPlanType.Enterprise) { - const plan = await licenseService.getPlan(actor.orgId); - if (!plan.enterpriseSecretSyncs) - throw new BadRequestError({ - message: - "Failed to update secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs." - }); - } + await enterpriseSyncCheck( + licenseService, + secretSync.destination as SecretSync, + actor.orgId, + "Failed to update secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs." + ); const { permission } = await permissionService.getProjectPermission({ actor: actor.type, @@ -431,15 +425,12 @@ export const secretSyncServiceFactory = ({ message: `Could not find ${SECRET_SYNC_NAME_MAP[destination]} Sync with ID "${syncId}"` }); - // Enterprise check - if (SECRET_SYNC_PLAN_MAP[secretSync.destination as SecretSync] === SecretSyncPlanType.Enterprise) { - const plan = await licenseService.getPlan(actor.orgId); - if (!plan.enterpriseSecretSyncs) - throw new BadRequestError({ - message: - "Failed to trigger secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs." - }); - } + await enterpriseSyncCheck( + licenseService, + secretSync.destination as SecretSync, + actor.orgId, + "Failed to trigger secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs." + ); const { permission } = await permissionService.getProjectPermission({ actor: actor.type, @@ -496,15 +487,12 @@ export const secretSyncServiceFactory = ({ message: `Could not find ${SECRET_SYNC_NAME_MAP[destination]} Sync with ID "${syncId}"` }); - // Enterprise check - if (SECRET_SYNC_PLAN_MAP[secretSync.destination as SecretSync] === SecretSyncPlanType.Enterprise) { - const plan = await licenseService.getPlan(actor.orgId); - if (!plan.enterpriseSecretSyncs) - throw new BadRequestError({ - message: - "Failed to trigger secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs." - }); - } + await enterpriseSyncCheck( + licenseService, + secretSync.destination as SecretSync, + actor.orgId, + "Failed to trigger secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs." + ); const { permission } = await permissionService.getProjectPermission({ actor: actor.type, @@ -555,15 +543,12 @@ export const secretSyncServiceFactory = ({ message: `Could not find ${SECRET_SYNC_NAME_MAP[destination]} Sync with ID "${syncId}"` }); - // Enterprise check - if (SECRET_SYNC_PLAN_MAP[secretSync.destination as SecretSync] === SecretSyncPlanType.Enterprise) { - const plan = await licenseService.getPlan(actor.orgId); - if (!plan.enterpriseSecretSyncs) - throw new BadRequestError({ - message: - "Failed to trigger secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs." - }); - } + await enterpriseSyncCheck( + licenseService, + secretSync.destination as SecretSync, + actor.orgId, + "Failed to trigger secret sync due to plan restriction. Upgrade plan to access enterprise secret syncs." + ); const { permission } = await permissionService.getProjectPermission({ actor: actor.type, diff --git a/frontend/src/components/secret-syncs/SecretSyncSelect.tsx b/frontend/src/components/secret-syncs/SecretSyncSelect.tsx index 1b2fe9c5b..693c17888 100644 --- a/frontend/src/components/secret-syncs/SecretSyncSelect.tsx +++ b/frontend/src/components/secret-syncs/SecretSyncSelect.tsx @@ -35,13 +35,13 @@ export const SecretSyncSelect = ({ onSelect }: Props) => { const { image, name } = SECRET_SYNC_MAP[destination]; return (