From 316259f218d4e6917f4ac34607827e77f159edb1 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 29 May 2024 21:03:52 +0530 Subject: [PATCH] feat: added subscription plan to secret replication --- .../src/ee/services/license/licence-fns.ts | 1 + .../src/ee/services/license/license-types.ts | 1 + backend/src/server/routes/index.ts | 1 + .../secret-import/secret-import-service.ts | 20 ++++++++++++++++++- frontend/src/hooks/api/subscriptions/types.ts | 1 + .../components/ActionBar/ActionBar.tsx | 1 + .../ActionBar/CreateSecretImportForm.tsx | 12 +++++++++-- 7 files changed, 34 insertions(+), 3 deletions(-) diff --git a/backend/src/ee/services/license/licence-fns.ts b/backend/src/ee/services/license/licence-fns.ts index 189a3c4e0..411882b25 100644 --- a/backend/src/ee/services/license/licence-fns.ts +++ b/backend/src/ee/services/license/licence-fns.ts @@ -16,6 +16,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ environmentLimit: null, environmentsUsed: 0, dynamicSecret: false, + secretReplication: false, secretVersioning: true, pitRecovery: false, ipAllowlisting: false, diff --git a/backend/src/ee/services/license/license-types.ts b/backend/src/ee/services/license/license-types.ts index 0c8fdc197..f1ee6ab13 100644 --- a/backend/src/ee/services/license/license-types.ts +++ b/backend/src/ee/services/license/license-types.ts @@ -52,6 +52,7 @@ export type TFeatureSet = { has_used_trial: true; secretApproval: false; secretRotation: true; + secretReplication: false; }; export type TOrgPlansTableDTO = { diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 0d7a7e907..9bbb49d64 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -590,6 +590,7 @@ export const registerRoutes = async ( secretVersionTagDAL }); const secretImportService = secretImportServiceFactory({ + licenseService, projectEnvDAL, folderDAL, permissionService, diff --git a/backend/src/services/secret-import/secret-import-service.ts b/backend/src/services/secret-import/secret-import-service.ts index 2e9c682a2..0d351d3e9 100644 --- a/backend/src/services/secret-import/secret-import-service.ts +++ b/backend/src/services/secret-import/secret-import-service.ts @@ -1,6 +1,7 @@ import { ForbiddenError, subject } from "@casl/ability"; import { TableName } from "@app/db/schemas"; +import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; @@ -30,6 +31,7 @@ type TSecretImportServiceFactoryDep = { projectEnvDAL: TProjectEnvDALFactory; permissionService: Pick; secretQueueService: Pick; + licenseService: Pick; }; const ERR_SEC_IMP_NOT_FOUND = new BadRequestError({ message: "Secret import not found" }); @@ -43,7 +45,8 @@ export const secretImportServiceFactory = ({ folderDAL, projectDAL, secretDAL, - secretQueueService + secretQueueService, + licenseService }: TSecretImportServiceFactoryDep) => { const createImport = async ({ environment, @@ -78,6 +81,14 @@ export const secretImportServiceFactory = ({ secretPath: data.path }) ); + if (isReplication) { + const plan = await licenseService.getPlan(actorOrgId); + if (!plan.secretReplication) { + throw new BadRequestError({ + message: "Failed to create secret replication due to plan restriction. Upgrade plan to create replication." + }); + } + } await projectDAL.checkProjectUpgradeStatus(projectId); @@ -273,6 +284,13 @@ export const secretImportServiceFactory = ({ subject(ProjectPermissionSub.Secrets, { environment, secretPath: path }) ); + const plan = await licenseService.getPlan(actorOrgId); + if (!plan.secretReplication) { + throw new BadRequestError({ + message: "Failed to create secret replication due to plan restriction. Upgrade plan to create replication." + }); + } + const folder = await folderDAL.findBySecretPath(projectId, environment, path); if (!folder) throw new BadRequestError({ message: "Folder not found", name: "Update import" }); diff --git a/frontend/src/hooks/api/subscriptions/types.ts b/frontend/src/hooks/api/subscriptions/types.ts index 45414292d..4eb54cc11 100644 --- a/frontend/src/hooks/api/subscriptions/types.ts +++ b/frontend/src/hooks/api/subscriptions/types.ts @@ -24,6 +24,7 @@ export type SubscriptionPlan = { scim: boolean; ldap: boolean; groups: boolean; + secretReplication: boolean; status: | "incomplete" | "incomplete_expired" diff --git a/frontend/src/views/SecretMainPage/components/ActionBar/ActionBar.tsx b/frontend/src/views/SecretMainPage/components/ActionBar/ActionBar.tsx index 9b235867b..bed5d6a84 100644 --- a/frontend/src/views/SecretMainPage/components/ActionBar/ActionBar.tsx +++ b/frontend/src/views/SecretMainPage/components/ActionBar/ActionBar.tsx @@ -482,6 +482,7 @@ export const ActionBar = ({ environment={environment} workspaceId={workspaceId} secretPath={secretPath} + onUpgradePlan={() => handlePopUpOpen("upgradePlan")} isOpen={popUp.addSecretImport.isOpen} onClose={() => handlePopUpClose("addSecretImport")} onTogglePopUp={(isOpen) => handlePopUpToggle("addSecretImport", isOpen)} diff --git a/frontend/src/views/SecretMainPage/components/ActionBar/CreateSecretImportForm.tsx b/frontend/src/views/SecretMainPage/components/ActionBar/CreateSecretImportForm.tsx index bbc5cc978..b9ddb7bfe 100644 --- a/frontend/src/views/SecretMainPage/components/ActionBar/CreateSecretImportForm.tsx +++ b/frontend/src/views/SecretMainPage/components/ActionBar/CreateSecretImportForm.tsx @@ -14,7 +14,7 @@ import { SelectItem } from "@app/components/v2"; import { SecretPathInput } from "@app/components/v2/SecretPathInput"; -import { useWorkspace } from "@app/context"; +import { useSubscription, useWorkspace } from "@app/context"; import { useCreateSecretImport } from "@app/hooks/api"; const typeSchema = z.object({ @@ -38,6 +38,7 @@ type Props = { isOpen?: boolean; onClose: () => void; onTogglePopUp: (isOpen: boolean) => void; + onUpgradePlan: () => void; }; export const CreateSecretImportForm = ({ @@ -46,7 +47,8 @@ export const CreateSecretImportForm = ({ secretPath = "/", isOpen, onClose, - onTogglePopUp + onTogglePopUp, + onUpgradePlan }: Props) => { const { handleSubmit, @@ -58,6 +60,7 @@ export const CreateSecretImportForm = ({ const { currentWorkspace } = useWorkspace(); const environments = currentWorkspace?.environments || []; const selectedEnvironment = watch("environment"); + const { subscription } = useSubscription(); const { mutateAsync: createSecretImport } = useCreateSecretImport(); @@ -67,6 +70,11 @@ export const CreateSecretImportForm = ({ isReplication }: TFormSchema) => { try { + if (isReplication && !subscription?.secretReplication) { + onUpgradePlan(); + return; + } + await createSecretImport({ environment, projectId: workspaceId,