diff --git a/backend/src/server/routes/v1/app-connection-routers/oci-connection-router.ts b/backend/src/ee/routes/v1/app-connection-routers/oci-connection-router.ts similarity index 94% rename from backend/src/server/routes/v1/app-connection-routers/oci-connection-router.ts rename to backend/src/ee/routes/v1/app-connection-routers/oci-connection-router.ts index d78eee3d9..e87e5b69e 100644 --- a/backend/src/server/routes/v1/app-connection-routers/oci-connection-router.ts +++ b/backend/src/ee/routes/v1/app-connection-routers/oci-connection-router.ts @@ -1,16 +1,16 @@ import z from "zod"; -import { readLimit } from "@app/server/config/rateLimiter"; -import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; -import { AppConnection } from "@app/services/app-connection/app-connection-enums"; import { CreateOCIConnectionSchema, SanitizedOCIConnectionSchema, UpdateOCIConnectionSchema -} from "@app/services/app-connection/oci"; +} from "@app/ee/services/app-connections/oci"; +import { readLimit } from "@app/server/config/rateLimiter"; +import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; +import { AppConnection } from "@app/services/app-connection/app-connection-enums"; import { AuthMode } from "@app/services/auth/auth-type"; -import { registerAppConnectionEndpoints } from "./app-connection-endpoints"; +import { registerAppConnectionEndpoints } from "../../../../server/routes/v1/app-connection-routers/app-connection-endpoints"; export const registerOCIConnectionRouter = async (server: FastifyZodProvider) => { registerAppConnectionEndpoints({ diff --git a/backend/src/server/routes/v1/secret-sync-routers/oci-vault-sync-router.ts b/backend/src/ee/routes/v1/secret-sync-routers/oci-vault-sync-router.ts similarity index 73% rename from backend/src/server/routes/v1/secret-sync-routers/oci-vault-sync-router.ts rename to backend/src/ee/routes/v1/secret-sync-routers/oci-vault-sync-router.ts index b46f27a50..2efe3e3f5 100644 --- a/backend/src/server/routes/v1/secret-sync-routers/oci-vault-sync-router.ts +++ b/backend/src/ee/routes/v1/secret-sync-routers/oci-vault-sync-router.ts @@ -2,11 +2,10 @@ import { CreateOCIVaultSyncSchema, OCIVaultSyncSchema, UpdateOCIVaultSyncSchema -} from "@app/services/secret-sync/oci-vault"; +} 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 "./secret-sync-endpoints"; - export const registerOCIVaultSyncRouter = async (server: FastifyZodProvider) => registerSyncSecretsEndpoints({ destination: SecretSync.OCIVault, diff --git a/backend/src/services/app-connection/oci/index.ts b/backend/src/ee/services/app-connections/oci/index.ts similarity index 100% rename from backend/src/services/app-connection/oci/index.ts rename to backend/src/ee/services/app-connections/oci/index.ts diff --git a/backend/src/services/app-connection/oci/oci-connection-enums.ts b/backend/src/ee/services/app-connections/oci/oci-connection-enums.ts similarity index 100% rename from backend/src/services/app-connection/oci/oci-connection-enums.ts rename to backend/src/ee/services/app-connections/oci/oci-connection-enums.ts diff --git a/backend/src/services/app-connection/oci/oci-connection-fns.ts b/backend/src/ee/services/app-connections/oci/oci-connection-fns.ts similarity index 100% rename from backend/src/services/app-connection/oci/oci-connection-fns.ts rename to backend/src/ee/services/app-connections/oci/oci-connection-fns.ts diff --git a/backend/src/services/app-connection/oci/oci-connection-schemas.ts b/backend/src/ee/services/app-connections/oci/oci-connection-schemas.ts similarity index 100% rename from backend/src/services/app-connection/oci/oci-connection-schemas.ts rename to backend/src/ee/services/app-connections/oci/oci-connection-schemas.ts diff --git a/backend/src/services/app-connection/oci/oci-connection-service.ts b/backend/src/ee/services/app-connections/oci/oci-connection-service.ts similarity index 68% rename from backend/src/services/app-connection/oci/oci-connection-service.ts rename to backend/src/ee/services/app-connections/oci/oci-connection-service.ts index 2d72135e5..c2e60399c 100644 --- a/backend/src/services/app-connection/oci/oci-connection-service.ts +++ b/backend/src/ee/services/app-connections/oci/oci-connection-service.ts @@ -1,7 +1,9 @@ +import { BadRequestError } from "@app/lib/errors"; import { logger } from "@app/lib/logger"; import { OrgServiceActor } from "@app/lib/types"; -import { AppConnection } from "../app-connection-enums"; +import { AppConnection } from "../../../../services/app-connection/app-connection-enums"; +import { TLicenseServiceFactory } from "../../license/license-service"; import { listOCICompartments, listOCIVaultKeys, listOCIVaults } from "./oci-connection-fns"; import { TOCIConnection } from "./oci-connection-types"; @@ -22,8 +24,23 @@ type TListOCIVaultKeysDTO = { vaultOcid: string; }; -export const ociConnectionService = (getAppConnection: TGetAppConnectionFunc) => { +// Enterprise check +export const checkPlan = async (licenseService: Pick, orgId: string) => { + const plan = await licenseService.getPlan(orgId); + if (!plan.enterpriseAppConnections) + throw new BadRequestError({ + message: + "Failed to use app connection due to plan restriction. Upgrade plan to access enterprise app connections." + }); +}; + +export const ociConnectionService = ( + getAppConnection: TGetAppConnectionFunc, + licenseService: Pick +) => { const listCompartments = async (connectionId: string, actor: OrgServiceActor) => { + await checkPlan(licenseService, actor.orgId); + const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor); try { @@ -36,6 +53,8 @@ export const ociConnectionService = (getAppConnection: TGetAppConnectionFunc) => }; const listVaults = async ({ connectionId, compartmentOcid }: TListOCIVaultsDTO, actor: OrgServiceActor) => { + await checkPlan(licenseService, actor.orgId); + const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor); try { @@ -51,6 +70,8 @@ export const ociConnectionService = (getAppConnection: TGetAppConnectionFunc) => { connectionId, compartmentOcid, vaultOcid }: TListOCIVaultKeysDTO, actor: OrgServiceActor ) => { + await checkPlan(licenseService, actor.orgId); + const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor); try { diff --git a/backend/src/services/app-connection/oci/oci-connection-types.ts b/backend/src/ee/services/app-connections/oci/oci-connection-types.ts similarity index 87% rename from backend/src/services/app-connection/oci/oci-connection-types.ts rename to backend/src/ee/services/app-connections/oci/oci-connection-types.ts index 74ddfe0c8..e07554f29 100644 --- a/backend/src/services/app-connection/oci/oci-connection-types.ts +++ b/backend/src/ee/services/app-connections/oci/oci-connection-types.ts @@ -2,7 +2,7 @@ import z from "zod"; import { DiscriminativePick } from "@app/lib/types"; -import { AppConnection } from "../app-connection-enums"; +import { AppConnection } from "../../../../services/app-connection/app-connection-enums"; import { CreateOCIConnectionSchema, OCIConnectionSchema, diff --git a/backend/src/ee/services/license/__mocks__/license-fns.ts b/backend/src/ee/services/license/__mocks__/license-fns.ts index 6a8f807ad..5259d4616 100644 --- a/backend/src/ee/services/license/__mocks__/license-fns.ts +++ b/backend/src/ee/services/license/__mocks__/license-fns.ts @@ -29,7 +29,9 @@ export const getDefaultOnPremFeatures = () => { secretApproval: true, secretRotation: true, caCrl: false, - sshHostGroups: false + sshHostGroups: false, + enterpriseSecretSyncs: false, + enterpriseAppConnections: false }; }; diff --git a/backend/src/ee/services/license/license-fns.ts b/backend/src/ee/services/license/license-fns.ts index 8ef91c6f8..d8ca362bd 100644 --- a/backend/src/ee/services/license/license-fns.ts +++ b/backend/src/ee/services/license/license-fns.ts @@ -55,7 +55,9 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ projectTemplates: false, kmip: false, gateway: false, - sshHostGroups: false + sshHostGroups: false, + enterpriseSecretSyncs: false, + enterpriseAppConnections: false }); export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => { diff --git a/backend/src/ee/services/license/license-types.ts b/backend/src/ee/services/license/license-types.ts index 358849fb2..9511771ff 100644 --- a/backend/src/ee/services/license/license-types.ts +++ b/backend/src/ee/services/license/license-types.ts @@ -72,6 +72,8 @@ export type TFeatureSet = { kmip: false; gateway: false; sshHostGroups: false; + enterpriseSecretSyncs: false; + enterpriseAppConnections: false; }; export type TOrgPlansTableDTO = { diff --git a/backend/src/services/secret-sync/oci-vault/index.ts b/backend/src/ee/services/secret-sync/oci-vault/index.ts similarity index 100% rename from backend/src/services/secret-sync/oci-vault/index.ts rename to backend/src/ee/services/secret-sync/oci-vault/index.ts diff --git a/backend/src/services/secret-sync/oci-vault/oci-vault-sync-constants.ts b/backend/src/ee/services/secret-sync/oci-vault/oci-vault-sync-constants.ts similarity index 89% rename from backend/src/services/secret-sync/oci-vault/oci-vault-sync-constants.ts rename to backend/src/ee/services/secret-sync/oci-vault/oci-vault-sync-constants.ts index 9e2aad056..b864e354b 100644 --- a/backend/src/services/secret-sync/oci-vault/oci-vault-sync-constants.ts +++ b/backend/src/ee/services/secret-sync/oci-vault/oci-vault-sync-constants.ts @@ -6,5 +6,6 @@ export const OCI_VAULT_SYNC_LIST_OPTION: TSecretSyncListItem = { name: "OCI Vault", destination: SecretSync.OCIVault, connection: AppConnection.OCI, - canImportSecrets: true + canImportSecrets: true, + enterprise: true }; diff --git a/backend/src/services/secret-sync/oci-vault/oci-vault-sync-fns.ts b/backend/src/ee/services/secret-sync/oci-vault/oci-vault-sync-fns.ts similarity index 98% rename from backend/src/services/secret-sync/oci-vault/oci-vault-sync-fns.ts rename to backend/src/ee/services/secret-sync/oci-vault/oci-vault-sync-fns.ts index e270f2e02..5b05b2301 100644 --- a/backend/src/services/secret-sync/oci-vault/oci-vault-sync-fns.ts +++ b/backend/src/ee/services/secret-sync/oci-vault/oci-vault-sync-fns.ts @@ -1,7 +1,6 @@ import { secrets, vault } from "oci-sdk"; -import { delay } from "@app/lib/delay"; -import { getOCIProvider } from "@app/services/app-connection/oci"; +import { getOCIProvider } from "@app/ee/services/app-connections/oci"; import { TCreateOCIVaultVariable, TDeleteOCIVaultVariable, @@ -9,7 +8,8 @@ import { TOCIVaultSyncWithCredentials, TUnmarkOCIVaultVariableFromDeletion, TUpdateOCIVaultVariable -} from "@app/services/secret-sync/oci-vault/oci-vault-sync-types"; +} from "@app/ee/services/secret-sync/oci-vault/oci-vault-sync-types"; +import { delay } from "@app/lib/delay"; import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors"; import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns"; import { TSecretMap } from "@app/services/secret-sync/secret-sync-types"; diff --git a/backend/src/services/secret-sync/oci-vault/oci-vault-sync-schemas.ts b/backend/src/ee/services/secret-sync/oci-vault/oci-vault-sync-schemas.ts similarity index 97% rename from backend/src/services/secret-sync/oci-vault/oci-vault-sync-schemas.ts rename to backend/src/ee/services/secret-sync/oci-vault/oci-vault-sync-schemas.ts index 84a58bc8a..a0bd29382 100644 --- a/backend/src/services/secret-sync/oci-vault/oci-vault-sync-schemas.ts +++ b/backend/src/ee/services/secret-sync/oci-vault/oci-vault-sync-schemas.ts @@ -66,5 +66,6 @@ export const OCIVaultSyncListItemSchema = z.object({ name: z.literal("OCI Vault"), connection: z.literal(AppConnection.OCI), destination: z.literal(SecretSync.OCIVault), - canImportSecrets: z.literal(true) + canImportSecrets: z.literal(true), + enterprise: z.boolean() }); diff --git a/backend/src/services/secret-sync/oci-vault/oci-vault-sync-types.ts b/backend/src/ee/services/secret-sync/oci-vault/oci-vault-sync-types.ts similarity index 94% rename from backend/src/services/secret-sync/oci-vault/oci-vault-sync-types.ts rename to backend/src/ee/services/secret-sync/oci-vault/oci-vault-sync-types.ts index c040cd0c0..8804b1322 100644 --- a/backend/src/services/secret-sync/oci-vault/oci-vault-sync-types.ts +++ b/backend/src/ee/services/secret-sync/oci-vault/oci-vault-sync-types.ts @@ -1,7 +1,7 @@ import { SimpleAuthenticationDetailsProvider } from "oci-sdk"; import { z } from "zod"; -import { TOCIConnection } from "@app/services/app-connection/oci"; +import { TOCIConnection } from "@app/ee/services/app-connections/oci"; import { CreateOCIVaultSyncSchema, OCIVaultSyncListItemSchema, OCIVaultSyncSchema } from "./oci-vault-sync-schemas"; diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index cb22c8c3c..5e9890ff2 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -1014,7 +1014,8 @@ export const registerRoutes = async ( secretVersionV2BridgeDAL, secretVersionTagV2BridgeDAL, resourceMetadataDAL, - appConnectionDAL + appConnectionDAL, + licenseService }); const secretQueueService = secretQueueFactory({ @@ -1631,7 +1632,8 @@ export const registerRoutes = async ( const appConnectionService = appConnectionServiceFactory({ appConnectionDAL, permissionService, - kmsService + kmsService, + licenseService }); const secretSyncService = secretSyncServiceFactory({ @@ -1642,7 +1644,8 @@ export const registerRoutes = async ( folderDAL, secretSyncQueue, projectBotService, - keyStore + keyStore, + licenseService }); const kmipService = kmipServiceFactory({ diff --git a/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts b/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts index b9ce3deb8..a05a2f263 100644 --- a/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts +++ b/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts @@ -1,5 +1,6 @@ import { z } from "zod"; +import { OCIConnectionListItemSchema, SanitizedOCIConnectionSchema } from "@app/ee/services/app-connections/oci"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { ApiDocsTags } from "@app/lib/api-docs"; import { readLimit } from "@app/server/config/rateLimiter"; @@ -38,7 +39,6 @@ import { } from "@app/services/app-connection/humanitec"; import { LdapConnectionListItemSchema, SanitizedLdapConnectionSchema } from "@app/services/app-connection/ldap"; import { MsSqlConnectionListItemSchema, SanitizedMsSqlConnectionSchema } from "@app/services/app-connection/mssql"; -import { OCIConnectionListItemSchema, SanitizedOCIConnectionSchema } from "@app/services/app-connection/oci"; import { PostgresConnectionListItemSchema, SanitizedPostgresConnectionSchema 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 6f6fa1991..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,3 +1,4 @@ +import { registerOCIConnectionRouter } from "@app/ee/routes/v1/app-connection-routers/oci-connection-router"; import { AppConnection } from "@app/services/app-connection/app-connection-enums"; import { registerAuth0ConnectionRouter } from "./auth0-connection-router"; @@ -13,7 +14,6 @@ import { registerHCVaultConnectionRouter } from "./hc-vault-connection-router"; import { registerHumanitecConnectionRouter } from "./humanitec-connection-router"; import { registerLdapConnectionRouter } from "./ldap-connection-router"; import { registerMsSqlConnectionRouter } from "./mssql-connection-router"; -import { registerOCIConnectionRouter } from "./oci-connection-router"; import { registerPostgresConnectionRouter } from "./postgres-connection-router"; import { registerTeamCityConnectionRouter } from "./teamcity-connection-router"; import { registerTerraformCloudConnectionRouter } from "./terraform-cloud-router"; diff --git a/backend/src/server/routes/v1/secret-sync-routers/index.ts b/backend/src/server/routes/v1/secret-sync-routers/index.ts index b5bd62ad6..c22a40432 100644 --- a/backend/src/server/routes/v1/secret-sync-routers/index.ts +++ b/backend/src/server/routes/v1/secret-sync-routers/index.ts @@ -1,5 +1,6 @@ import { SecretSync } from "@app/services/secret-sync/secret-sync-enums"; +import { registerOCIVaultSyncRouter } from "../../../../ee/routes/v1/secret-sync-routers/oci-vault-sync-router"; import { registerAwsParameterStoreSyncRouter } from "./aws-parameter-store-sync-router"; import { registerAwsSecretsManagerSyncRouter } from "./aws-secrets-manager-sync-router"; import { registerAzureAppConfigurationSyncRouter } from "./azure-app-configuration-sync-router"; @@ -10,7 +11,6 @@ import { registerGcpSyncRouter } from "./gcp-sync-router"; import { registerGitHubSyncRouter } from "./github-sync-router"; import { registerHCVaultSyncRouter } from "./hc-vault-sync-router"; import { registerHumanitecSyncRouter } from "./humanitec-sync-router"; -import { registerOCIVaultSyncRouter } from "./oci-vault-sync-router"; import { registerTeamCitySyncRouter } from "./teamcity-sync-router"; import { registerTerraformCloudSyncRouter } from "./terraform-cloud-sync-router"; import { registerVercelSyncRouter } from "./vercel-sync-router"; diff --git a/backend/src/server/routes/v1/secret-sync-routers/secret-sync-router.ts b/backend/src/server/routes/v1/secret-sync-routers/secret-sync-router.ts index a7a561738..9fb1c825a 100644 --- a/backend/src/server/routes/v1/secret-sync-routers/secret-sync-router.ts +++ b/backend/src/server/routes/v1/secret-sync-routers/secret-sync-router.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; +import { OCIVaultSyncListItemSchema, OCIVaultSyncSchema } from "@app/ee/services/secret-sync/oci-vault"; import { ApiDocsTags, SecretSyncs } from "@app/lib/api-docs"; import { readLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; @@ -24,7 +25,6 @@ import { GcpSyncListItemSchema, GcpSyncSchema } from "@app/services/secret-sync/ import { GitHubSyncListItemSchema, GitHubSyncSchema } from "@app/services/secret-sync/github"; import { HCVaultSyncListItemSchema, HCVaultSyncSchema } from "@app/services/secret-sync/hc-vault"; import { HumanitecSyncListItemSchema, HumanitecSyncSchema } from "@app/services/secret-sync/humanitec"; -import { OCIVaultSyncListItemSchema, OCIVaultSyncSchema } from "@app/services/secret-sync/oci-vault"; import { TeamCitySyncListItemSchema, TeamCitySyncSchema } from "@app/services/secret-sync/teamcity"; import { TerraformCloudSyncListItemSchema, TerraformCloudSyncSchema } from "@app/services/secret-sync/terraform-cloud"; import { VercelSyncListItemSchema, VercelSyncSchema } from "@app/services/secret-sync/vercel"; diff --git a/backend/src/services/app-connection/app-connection-enums.ts b/backend/src/services/app-connection/app-connection-enums.ts index 6e09f1293..afae27039 100644 --- a/backend/src/services/app-connection/app-connection-enums.ts +++ b/backend/src/services/app-connection/app-connection-enums.ts @@ -66,3 +66,8 @@ export enum AWSRegion { // South America SA_EAST_1 = "sa-east-1" // Sao Paulo } + +export enum AppConnectionPlanType { + Enterprise = "enterprise", + Regular = "regular" +} diff --git a/backend/src/services/app-connection/app-connection-fns.ts b/backend/src/services/app-connection/app-connection-fns.ts index f6fd894a6..12b58ee36 100644 --- a/backend/src/services/app-connection/app-connection-fns.ts +++ b/backend/src/services/app-connection/app-connection-fns.ts @@ -1,14 +1,20 @@ 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 } from "@app/services/app-connection/shared/sql"; import { KmsDataKey } from "@app/services/kms/kms-types"; -import { AppConnection } from "./app-connection-enums"; +import { + getOCIConnectionListItem, + OCIConnectionMethod, + validateOCIConnectionCredentials +} from "../../ee/services/app-connections/oci"; +import { AppConnection, AppConnectionPlanType } from "./app-connection-enums"; import { TAppConnectionServiceFactoryDep } from "./app-connection-service"; import { TAppConnection, @@ -53,7 +59,6 @@ import { } from "./humanitec"; import { getLdapConnectionListItem, LdapConnectionMethod, validateLdapConnectionCredentials } from "./ldap"; import { getMsSqlConnectionListItem, MsSqlConnectionMethod } from "./mssql"; -import { getOCIConnectionListItem, OCIConnectionMethod, validateOCIConnectionCredentials } from "./oci"; import { getPostgresConnectionListItem, PostgresConnectionMethod } from "./postgres"; import { getTeamCityConnectionListItem, @@ -257,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-maps.ts b/backend/src/services/app-connection/app-connection-maps.ts index c32336453..b4b968031 100644 --- a/backend/src/services/app-connection/app-connection-maps.ts +++ b/backend/src/services/app-connection/app-connection-maps.ts @@ -1,4 +1,4 @@ -import { AppConnection } from "./app-connection-enums"; +import { AppConnection, AppConnectionPlanType } from "./app-connection-enums"; export const APP_CONNECTION_NAME_MAP: Record = { [AppConnection.AWS]: "AWS", @@ -21,3 +21,25 @@ export const APP_CONNECTION_NAME_MAP: Record = { [AppConnection.TeamCity]: "TeamCity", [AppConnection.OCI]: "OCI" }; + +export const APP_CONNECTION_PLAN_MAP: Record = { + [AppConnection.AWS]: AppConnectionPlanType.Regular, + [AppConnection.GitHub]: AppConnectionPlanType.Regular, + [AppConnection.GCP]: AppConnectionPlanType.Regular, + [AppConnection.AzureKeyVault]: AppConnectionPlanType.Regular, + [AppConnection.AzureAppConfiguration]: AppConnectionPlanType.Regular, + [AppConnection.AzureClientSecrets]: AppConnectionPlanType.Regular, + [AppConnection.Databricks]: AppConnectionPlanType.Regular, + [AppConnection.Humanitec]: AppConnectionPlanType.Regular, + [AppConnection.TerraformCloud]: AppConnectionPlanType.Regular, + [AppConnection.Vercel]: AppConnectionPlanType.Regular, + [AppConnection.Postgres]: AppConnectionPlanType.Regular, + [AppConnection.MsSql]: AppConnectionPlanType.Regular, + [AppConnection.Camunda]: AppConnectionPlanType.Regular, + [AppConnection.Windmill]: AppConnectionPlanType.Regular, + [AppConnection.Auth0]: AppConnectionPlanType.Regular, + [AppConnection.HCVault]: AppConnectionPlanType.Regular, + [AppConnection.LDAP]: AppConnectionPlanType.Regular, + [AppConnection.TeamCity]: AppConnectionPlanType.Regular, + [AppConnection.OCI]: AppConnectionPlanType.Enterprise +}; diff --git a/backend/src/services/app-connection/app-connection-service.ts b/backend/src/services/app-connection/app-connection-service.ts index 85b63138a..6637fd93d 100644 --- a/backend/src/services/app-connection/app-connection-service.ts +++ b/backend/src/services/app-connection/app-connection-service.ts @@ -1,5 +1,6 @@ import { ForbiddenError, subject } from "@casl/ability"; +import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { OrgPermissionAppConnectionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { generateHash } from "@app/lib/crypto/encryption"; @@ -9,6 +10,7 @@ import { DiscriminativePick, OrgServiceActor } from "@app/lib/types"; import { decryptAppConnection, encryptAppConnectionCredentials, + enterpriseAppCheck, getAppConnectionMethodName, listAppConnectionOptions, TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM, @@ -17,6 +19,8 @@ import { import { auth0ConnectionService } from "@app/services/app-connection/auth0/auth0-connection-service"; 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 } from "./app-connection-enums"; import { APP_CONNECTION_NAME_MAP } from "./app-connection-maps"; @@ -49,8 +53,6 @@ import { ValidateHumanitecConnectionCredentialsSchema } from "./humanitec"; import { humanitecConnectionService } from "./humanitec/humanitec-connection-service"; import { ValidateLdapConnectionCredentialsSchema } from "./ldap"; import { ValidateMsSqlConnectionCredentialsSchema } from "./mssql"; -import { ValidateOCIConnectionCredentialsSchema } from "./oci"; -import { ociConnectionService } from "./oci/oci-connection-service"; import { ValidatePostgresConnectionCredentialsSchema } from "./postgres"; import { ValidateTeamCityConnectionCredentialsSchema } from "./teamcity"; import { teamcityConnectionService } from "./teamcity/teamcity-connection-service"; @@ -65,6 +67,7 @@ export type TAppConnectionServiceFactoryDep = { appConnectionDAL: TAppConnectionDALFactory; permissionService: Pick; kmsService: Pick; + licenseService: Pick; }; export type TAppConnectionServiceFactory = ReturnType; @@ -94,7 +97,8 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record { const listAppConnectionsByOrg = async (actor: OrgServiceActor, app?: AppConnection) => { const { permission } = await permissionService.getOrgPermission( @@ -191,6 +195,13 @@ export const appConnectionServiceFactory = ({ OrgPermissionSubjects.AppConnections ); + 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, credentials, @@ -253,6 +264,13 @@ export const appConnectionServiceFactory = ({ if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` }); + 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, actor.id, @@ -399,6 +417,13 @@ export const appConnectionServiceFactory = ({ if (!appConnection) throw new NotFoundError({ message: `Could not find App Connection with ID ${connectionId}` }); + 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, actor.id, @@ -468,6 +493,6 @@ export const appConnectionServiceFactory = ({ hcvault: hcVaultConnectionService(connectAppConnectionById), windmill: windmillConnectionService(connectAppConnectionById), teamcity: teamcityConnectionService(connectAppConnectionById), - oci: ociConnectionService(connectAppConnectionById) + oci: ociConnectionService(connectAppConnectionById, licenseService) }; }; diff --git a/backend/src/services/app-connection/app-connection-types.ts b/backend/src/services/app-connection/app-connection-types.ts index 42ccfc84e..496f5032e 100644 --- a/backend/src/services/app-connection/app-connection-types.ts +++ b/backend/src/services/app-connection/app-connection-types.ts @@ -2,6 +2,12 @@ import { TAppConnectionDALFactory } from "@app/services/app-connection/app-conne import { TSqlConnectionConfig } from "@app/services/app-connection/shared/sql/sql-connection-types"; import { SecretSync } from "@app/services/secret-sync/secret-sync-enums"; +import { + TOCIConnection, + TOCIConnectionConfig, + TOCIConnectionInput, + TValidateOCIConnectionCredentialsSchema +} from "../../ee/services/app-connections/oci"; import { AWSRegion } from "./app-connection-enums"; import { TAuth0Connection, @@ -76,12 +82,6 @@ import { TValidateLdapConnectionCredentialsSchema } from "./ldap"; import { TMsSqlConnection, TMsSqlConnectionInput, TValidateMsSqlConnectionCredentialsSchema } from "./mssql"; -import { - TOCIConnection, - TOCIConnectionConfig, - TOCIConnectionInput, - TValidateOCIConnectionCredentialsSchema -} from "./oci"; import { TPostgresConnection, TPostgresConnectionInput, diff --git a/backend/src/services/secret-sync/secret-sync-enums.ts b/backend/src/services/secret-sync/secret-sync-enums.ts index a0982c5b6..e829e2131 100644 --- a/backend/src/services/secret-sync/secret-sync-enums.ts +++ b/backend/src/services/secret-sync/secret-sync-enums.ts @@ -26,3 +26,8 @@ export enum SecretSyncImportBehavior { PrioritizeSource = "prioritize-source", PrioritizeDestination = "prioritize-destination" } + +export enum SecretSyncPlanType { + Enterprise = "enterprise", + Regular = "regular" +} diff --git a/backend/src/services/secret-sync/secret-sync-fns.ts b/backend/src/services/secret-sync/secret-sync-fns.ts index 1bb4da9db..8f58b6fcc 100644 --- a/backend/src/services/secret-sync/secret-sync-fns.ts +++ b/backend/src/services/secret-sync/secret-sync-fns.ts @@ -1,6 +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 @@ -11,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, @@ -29,7 +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 { OCI_VAULT_SYNC_LIST_OPTION, OCIVaultSyncFns } from "./oci-vault"; +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-maps.ts b/backend/src/services/secret-sync/secret-sync-maps.ts index 21cb912b4..5b2906f1c 100644 --- a/backend/src/services/secret-sync/secret-sync-maps.ts +++ b/backend/src/services/secret-sync/secret-sync-maps.ts @@ -1,5 +1,5 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums"; -import { SecretSync } from "@app/services/secret-sync/secret-sync-enums"; +import { SecretSync, SecretSyncPlanType } from "@app/services/secret-sync/secret-sync-enums"; export const SECRET_SYNC_NAME_MAP: Record = { [SecretSync.AWSParameterStore]: "AWS Parameter Store", @@ -36,3 +36,21 @@ export const SECRET_SYNC_CONNECTION_MAP: Record = { [SecretSync.TeamCity]: AppConnection.TeamCity, [SecretSync.OCIVault]: AppConnection.OCI }; + +export const SECRET_SYNC_PLAN_MAP: Record = { + [SecretSync.AWSParameterStore]: SecretSyncPlanType.Regular, + [SecretSync.AWSSecretsManager]: SecretSyncPlanType.Regular, + [SecretSync.GitHub]: SecretSyncPlanType.Regular, + [SecretSync.GCPSecretManager]: SecretSyncPlanType.Regular, + [SecretSync.AzureKeyVault]: SecretSyncPlanType.Regular, + [SecretSync.AzureAppConfiguration]: SecretSyncPlanType.Regular, + [SecretSync.Databricks]: SecretSyncPlanType.Regular, + [SecretSync.Humanitec]: SecretSyncPlanType.Regular, + [SecretSync.TerraformCloud]: SecretSyncPlanType.Regular, + [SecretSync.Camunda]: SecretSyncPlanType.Regular, + [SecretSync.Vercel]: SecretSyncPlanType.Regular, + [SecretSync.Windmill]: SecretSyncPlanType.Regular, + [SecretSync.HCVault]: SecretSyncPlanType.Regular, + [SecretSync.TeamCity]: SecretSyncPlanType.Regular, + [SecretSync.OCIVault]: SecretSyncPlanType.Enterprise +}; diff --git a/backend/src/services/secret-sync/secret-sync-queue.ts b/backend/src/services/secret-sync/secret-sync-queue.ts index 62b4ba3cc..6f627c24e 100644 --- a/backend/src/services/secret-sync/secret-sync-queue.ts +++ b/backend/src/services/secret-sync/secret-sync-queue.ts @@ -5,6 +5,7 @@ import { Job } from "bullmq"; import { ProjectMembershipRole, SecretType } from "@app/db/schemas"; import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service"; 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 { logger } from "@app/lib/logger"; @@ -32,7 +33,7 @@ import { 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 { 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, @@ -93,6 +94,7 @@ type TSecretSyncQueueFactoryDep = { secretVersionV2BridgeDAL: Pick; secretVersionTagV2BridgeDAL: Pick; resourceMetadataDAL: Pick; + licenseService: Pick; }; type SecretSyncActionJob = Job< @@ -133,7 +135,8 @@ export const secretSyncQueueFactory = ({ secretVersionTagDAL, secretVersionV2BridgeDAL, secretVersionTagV2BridgeDAL, - resourceMetadataDAL + resourceMetadataDAL, + licenseService }: TSecretSyncQueueFactoryDep) => { const appCfg = getConfig(); @@ -323,7 +326,20 @@ export const secretSyncQueueFactory = ({ secretSync: TSecretSyncWithCredentials, importBehavior: SecretSyncImportBehavior ): Promise => { - const { projectId, environment, folder } = secretSync; + const { + projectId, + environment, + folder, + destination, + connection: { orgId } + } = secretSync; + + 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( @@ -400,6 +416,13 @@ export const secretSyncQueueFactory = ({ if (!secretSync) throw new Error(`Cannot find secret sync with ID ${syncId}`); + 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 }); @@ -659,6 +682,13 @@ export const secretSyncQueueFactory = ({ if (!secretSync) throw new Error(`Cannot find secret sync with ID ${syncId}`); + 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 db350f785..e7751d3f9 100644 --- a/backend/src/services/secret-sync/secret-sync-service.ts +++ b/backend/src/services/secret-sync/secret-sync-service.ts @@ -1,6 +1,7 @@ import { ForbiddenError } from "@casl/ability"; import { ActionProjectType } from "@app/db/schemas"; +import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { throwIfMissingSecretReadValueOrDescribePermission } from "@app/ee/services/permission/permission-fns"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { @@ -16,7 +17,7 @@ import { TAppConnectionServiceFactory } from "@app/services/app-connection/app-c import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service"; import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal"; import { SecretSync } from "@app/services/secret-sync/secret-sync-enums"; -import { listSecretSyncOptions } from "@app/services/secret-sync/secret-sync-fns"; +import { enterpriseSyncCheck, listSecretSyncOptions } from "@app/services/secret-sync/secret-sync-fns"; import { SecretSyncStatus, TCreateSecretSyncDTO, @@ -49,6 +50,7 @@ type TSecretSyncServiceFactoryDep = { TSecretSyncQueueFactory, "queueSecretSyncSyncSecretsById" | "queueSecretSyncImportSecretsById" | "queueSecretSyncRemoveSecretsById" >; + licenseService: Pick; }; export type TSecretSyncServiceFactory = ReturnType; @@ -61,7 +63,8 @@ export const secretSyncServiceFactory = ({ appConnectionService, projectBotService, secretSyncQueue, - keyStore + keyStore, + licenseService }: TSecretSyncServiceFactoryDep) => { const listSecretSyncsByProjectId = async ( { projectId, destination }: TListSecretSyncsByProjectId, @@ -191,6 +194,13 @@ export const secretSyncServiceFactory = ({ { projectId, secretPath, environment, ...params }: TCreateSecretSyncDTO, actor: OrgServiceActor ) => { + 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, actorId: actor.id, @@ -260,6 +270,13 @@ export const secretSyncServiceFactory = ({ message: `Could not find ${SECRET_SYNC_NAME_MAP[destination]} Sync with ID ${syncId}` }); + 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, actorId: actor.id, @@ -408,6 +425,13 @@ export const secretSyncServiceFactory = ({ message: `Could not find ${SECRET_SYNC_NAME_MAP[destination]} Sync with ID "${syncId}"` }); + 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, actorId: actor.id, @@ -463,6 +487,13 @@ export const secretSyncServiceFactory = ({ message: `Could not find ${SECRET_SYNC_NAME_MAP[destination]} Sync with ID "${syncId}"` }); + 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, actorId: actor.id, @@ -512,6 +543,13 @@ export const secretSyncServiceFactory = ({ message: `Could not find ${SECRET_SYNC_NAME_MAP[destination]} Sync with ID "${syncId}"` }); + 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, actorId: actor.id, diff --git a/backend/src/services/secret-sync/secret-sync-types.ts b/backend/src/services/secret-sync/secret-sync-types.ts index 64d027e18..adc4888ab 100644 --- a/backend/src/services/secret-sync/secret-sync-types.ts +++ b/backend/src/services/secret-sync/secret-sync-types.ts @@ -1,6 +1,12 @@ import { Job } from "bullmq"; import { AuditLogInfo } from "@app/ee/services/audit-log/audit-log-types"; +import { + TOCIVaultSync, + TOCIVaultSyncInput, + TOCIVaultSyncListItem, + TOCIVaultSyncWithCredentials +} from "@app/ee/services/secret-sync/oci-vault"; import { QueueJobs } from "@app/queue"; import { ResourceMetadataDTO } from "@app/services/resource-metadata/resource-metadata-schema"; import { @@ -67,7 +73,6 @@ import { THumanitecSyncListItem, THumanitecSyncWithCredentials } from "./humanitec"; -import { TOCIVaultSync, TOCIVaultSyncInput, TOCIVaultSyncListItem, TOCIVaultSyncWithCredentials } from "./oci-vault"; import { TTeamCitySync, TTeamCitySyncInput, diff --git a/docs/integrations/app-connections/oci.mdx b/docs/integrations/app-connections/oci.mdx index ff51ce1d9..58fb3c1d3 100644 --- a/docs/integrations/app-connections/oci.mdx +++ b/docs/integrations/app-connections/oci.mdx @@ -3,6 +3,13 @@ title: "OCI Connection" description: "Learn how to configure an Oracle Cloud Infrastructure Connection for Infisical." --- + + OCI App Connection is a paid feature. + + If you're using Infisical Cloud, then it is available under the **Enterprise Tier**. If you're self-hosting Infisical, + then you should contact team@infisical.com to purchase an enterprise license to use it. + + Infisical supports the use of [API Signing Key Authentication](https://docs.oracle.com/en-us/iaas/Content/API/Concepts/apisigningkey.htm) to connect with OCI. ## Create OCI User diff --git a/docs/integrations/secret-syncs/oci-vault.mdx b/docs/integrations/secret-syncs/oci-vault.mdx index 67a3426aa..00b7120e7 100644 --- a/docs/integrations/secret-syncs/oci-vault.mdx +++ b/docs/integrations/secret-syncs/oci-vault.mdx @@ -3,6 +3,13 @@ title: "OCI Vault Sync" description: "Learn how to configure an Oracle Cloud Infrastructure Vault Sync for Infisical." --- + + OCI Vault Sync is a paid feature. + + If you're using Infisical Cloud, then it is available under the **Enterprise Tier**. If you're self-hosting Infisical, + then you should contact team@infisical.com to purchase an enterprise license to use it. + + **Prerequisites:** - Create an [OCI Connection](/integrations/app-connections/oci) with the required **Secret Sync** permissions - [Create](https://docs.oracle.com/en-us/iaas/Content/Identity/compartments/To_create_a_compartment.htm) or use an existing OCI Compartment (which the OCI Connection is authorized to access) diff --git a/frontend/src/components/secret-syncs/SecretSyncSelect.tsx b/frontend/src/components/secret-syncs/SecretSyncSelect.tsx index cbcba4513..62d99544f 100644 --- a/frontend/src/components/secret-syncs/SecretSyncSelect.tsx +++ b/frontend/src/components/secret-syncs/SecretSyncSelect.tsx @@ -2,16 +2,23 @@ import { faWrench } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Spinner, Tooltip } from "@app/components/v2"; +import { useSubscription } from "@app/context"; import { SECRET_SYNC_MAP } from "@app/helpers/secretSyncs"; +import { usePopUp } from "@app/hooks"; import { SecretSync, useSecretSyncOptions } from "@app/hooks/api/secretSyncs"; +import { UpgradePlanModal } from "../license/UpgradePlanModal"; + type Props = { onSelect: (destination: SecretSync) => void; }; export const SecretSyncSelect = ({ onSelect }: Props) => { + const { subscription } = useSubscription(); const { isPending, data: secretSyncOptions } = useSecretSyncOptions(); + const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const); + if (isPending) { return (
@@ -23,14 +30,17 @@ export const SecretSyncSelect = ({ onSelect }: Props) => { return (
- {secretSyncOptions?.map(({ destination }) => { + {secretSyncOptions?.map(({ destination, enterprise }) => { const { image, name } = SECRET_SYNC_MAP[destination]; return ( ); })} + handlePopUpToggle("upgradePlan", isOpen)} + text="You can use every Secret Sync if you switch to Infisical's Enterprise plan." + /> = { [AppConnection.AWS]: { name: "AWS", image: "Amazon Web Services.png" }, [AppConnection.GitHub]: { name: "GitHub", image: "GitHub.png" }, @@ -63,7 +63,7 @@ export const APP_CONNECTION_MAP: Record< [AppConnection.HCVault]: { name: "Hashicorp Vault", image: "Vault.png", size: 65 }, [AppConnection.LDAP]: { name: "LDAP", image: "LDAP.png", size: 65 }, [AppConnection.TeamCity]: { name: "TeamCity", image: "TeamCity.png" }, - [AppConnection.OCI]: { name: "OCI", image: "Oracle.png" } + [AppConnection.OCI]: { name: "OCI", image: "Oracle.png", enterprise: true } }; export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => { diff --git a/frontend/src/hooks/api/secretSyncs/types/index.ts b/frontend/src/hooks/api/secretSyncs/types/index.ts index e3de6029a..85daa07fd 100644 --- a/frontend/src/hooks/api/secretSyncs/types/index.ts +++ b/frontend/src/hooks/api/secretSyncs/types/index.ts @@ -21,6 +21,7 @@ export type TSecretSyncOption = { name: string; destination: SecretSync; canImportSecrets: boolean; + enterprise?: boolean; }; export type TSecretSync = diff --git a/frontend/src/hooks/api/subscriptions/types.ts b/frontend/src/hooks/api/subscriptions/types.ts index ec7b6a2dd..a861c215a 100644 --- a/frontend/src/hooks/api/subscriptions/types.ts +++ b/frontend/src/hooks/api/subscriptions/types.ts @@ -50,4 +50,6 @@ export type SubscriptionPlan = { enforceMfa: boolean; projectTemplates: boolean; kmip: boolean; + enterpriseSecretSyncs: boolean; + enterpriseAppConnections: boolean; }; diff --git a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionList.tsx b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionList.tsx index 5e9f69335..ab6a44f43 100644 --- a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionList.tsx +++ b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionList.tsx @@ -1,8 +1,11 @@ import { faWrench } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal"; import { Spinner, Tooltip } from "@app/components/v2"; +import { useSubscription } from "@app/context"; import { APP_CONNECTION_MAP } from "@app/helpers/appConnections"; +import { usePopUp } from "@app/hooks"; import { useAppConnectionOptions } from "@app/hooks/api/appConnections"; import { AppConnection } from "@app/hooks/api/appConnections/enums"; @@ -11,8 +14,11 @@ type Props = { }; export const AppConnectionsSelect = ({ onSelect }: Props) => { + const { subscription } = useSubscription(); const { isPending, data: appConnectionOptions } = useAppConnectionOptions(); + const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const); + if (isPending) { return (
@@ -25,13 +31,16 @@ export const AppConnectionsSelect = ({ onSelect }: Props) => { return (
{appConnectionOptions?.map((option) => { - const { image, name, size = 50 } = APP_CONNECTION_MAP[option.app]; + const { image, name, size = 50, enterprise = false } = APP_CONNECTION_MAP[option.app]; return ( ); })} + handlePopUpToggle("upgradePlan", isOpen)} + text="You can use every App Connection if you switch to Infisical's Enterprise plan." + />