diff --git a/backend/src/ee/services/license/license-types.ts b/backend/src/ee/services/license/license-types.ts index 381044f82..678e15fd4 100644 --- a/backend/src/ee/services/license/license-types.ts +++ b/backend/src/ee/services/license/license-types.ts @@ -48,6 +48,7 @@ export type TFeatureSet = { samlSSO: false; hsm: false; oidcSSO: false; + secretAccessInsights: false; scim: false; ldap: false; groups: false; diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index eafd3467f..b530ff304 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -1028,7 +1028,8 @@ export const registerRoutes = async ( secretApprovalRequestDAL, secretApprovalRequestSecretDAL, secretV2BridgeService, - secretApprovalRequestService + secretApprovalRequestService, + licenseService }); const secretSharingService = secretSharingServiceFactory({ diff --git a/backend/src/services/secret/secret-service.ts b/backend/src/services/secret/secret-service.ts index 99d07354b..286bba9ea 100644 --- a/backend/src/services/secret/secret-service.ts +++ b/backend/src/services/secret/secret-service.ts @@ -11,6 +11,7 @@ import { SecretsSchema, SecretType } 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 { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service"; @@ -114,6 +115,7 @@ type TSecretServiceFactoryDep = { TSecretApprovalRequestSecretDALFactory, "insertMany" | "insertApprovalSecretTags" >; + licenseService: Pick; }; export type TSecretServiceFactory = ReturnType; @@ -135,7 +137,8 @@ export const secretServiceFactory = ({ secretApprovalRequestDAL, secretApprovalRequestSecretDAL, secretV2BridgeService, - secretApprovalRequestService + secretApprovalRequestService, + licenseService }: TSecretServiceFactoryDep) => { const getSecretReference = async (projectId: string) => { // if bot key missing means e2e still exist @@ -1150,6 +1153,13 @@ export const secretServiceFactory = ({ const getSecretAccessList = async (dto: TGetSecretAccessListDTO) => { const { environment, secretPath, secretName, projectId } = dto; + const plan = await licenseService.getPlan(dto.actorOrgId); + if (!plan.secretAccessInsights) { + throw new BadRequestError({ + message: "Failed to fetch secret access list due to plan restriction. Upgrade your plan." + }); + } + const secret = await secretV2BridgeService.getSecretByName({ actor: dto.actor, actorId: dto.actorId, diff --git a/frontend/src/hooks/api/subscriptions/types.ts b/frontend/src/hooks/api/subscriptions/types.ts index b57a2d26f..5ad3c4221 100644 --- a/frontend/src/hooks/api/subscriptions/types.ts +++ b/frontend/src/hooks/api/subscriptions/types.ts @@ -23,6 +23,7 @@ export type SubscriptionPlan = { workspacesUsed: number; environmentLimit: number; samlSSO: boolean; + secretAccessInsights: boolean; hsm: boolean; oidcSSO: boolean; scim: boolean; diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx index 8d82d928b..6b0c8fe35 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx @@ -15,6 +15,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { Link } from "@tanstack/react-router"; import { format } from "date-fns"; +import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal"; import { createNotification } from "@app/components/notifications"; import { ProjectPermissionCan } from "@app/components/permissions"; import { @@ -41,7 +42,7 @@ import { useProjectPermission, useWorkspace } from "@app/context"; -import { useToggle } from "@app/hooks"; +import { usePopUp, useToggle } from "@app/hooks"; import { useGetSecretVersion } from "@app/hooks/api"; import { useGetSecretAccessList } from "@app/hooks/api/secrets/queries"; import { SecretV3RawSanitized, WsTag } from "@app/hooks/api/types"; @@ -93,6 +94,10 @@ export const SecretDetailSidebar = ({ values: secret }); + const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp([ + "secretAccessUpgradePlan" + ] as const); + const { permission } = useProjectPermission(); const { currentWorkspace } = useWorkspace(); @@ -200,6 +205,13 @@ export const SecretDetailSidebar = ({ } }} /> + + handlePopUpToggle("secretAccessUpgradePlan", isUpgradeModalOpen) + } + text="Secret access analysis is only available on Infisical's Pro plan and above." + /> { if (isOpen && isDirty) { @@ -500,8 +512,19 @@ export const SecretDetailSidebar = ({
Access List
+ {isPending && ( + + )} {!isPending && secretAccessList === undefined && ( - )}