Merge pull request #5118 from Infisical/chore/removal-ff

feat: removed feature flag for ai and pam
This commit is contained in:
Akhil Mohan
2026-01-07 14:08:13 +05:30
committed by GitHub
15 changed files with 184 additions and 394 deletions

View File

@@ -27,7 +27,6 @@ import { AiMcpServerCredentialMode } from "../ai-mcp-server/ai-mcp-server-enum";
import { TAiMcpServerServiceFactory } from "../ai-mcp-server/ai-mcp-server-service";
import { TAiMcpServerToolDALFactory } from "../ai-mcp-server/ai-mcp-server-tool-dal";
import { TAiMcpServerUserCredentialDALFactory } from "../ai-mcp-server/ai-mcp-server-user-credential-dal";
import { TLicenseServiceFactory } from "../license/license-service";
import { TPermissionServiceFactory } from "../permission/permission-service-types";
import { ProjectPermissionMcpEndpointActions, ProjectPermissionSub } from "../permission/project-permission";
import { TAiMcpEndpointDALFactory } from "./ai-mcp-endpoint-dal";
@@ -73,7 +72,6 @@ type TAiMcpEndpointServiceFactoryDep = {
authTokenService: Pick<TAuthTokenServiceFactory, "getUserTokenSessionById">;
userDAL: TUserDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
};
// OAuth schemas for parsing cached data
@@ -130,8 +128,7 @@ export const aiMcpEndpointServiceFactory = ({
keyStore,
authTokenService,
userDAL,
permissionService,
licenseService
permissionService
}: TAiMcpEndpointServiceFactoryDep) => {
const interactWithMcp = async ({
endpointId,
@@ -375,13 +372,6 @@ export const aiMcpEndpointServiceFactory = ({
actorAuthMethod,
actorOrgId
}: TCreateAiMcpEndpointDTO) => {
const orgLicensePlan = await licenseService.getPlan(actorOrgId);
if (!orgLicensePlan.ai) {
throw new BadRequestError({
message: "AI operation failed due to organization plan restrictions."
});
}
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,

View File

@@ -20,7 +20,6 @@ import { ActorType, AuthMethod } from "@app/services/auth/auth-type";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { TLicenseServiceFactory } from "../license/license-service";
import { TPermissionServiceFactory } from "../permission/permission-service-types";
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
import { TAiMcpServerDALFactory } from "./ai-mcp-server-dal";
@@ -53,7 +52,6 @@ type TAiMcpServerServiceFactoryDep = {
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
keyStore: Pick<TKeyStoreFactory, "getItem" | "setItemWithExpiry" | "deleteItem">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
};
export type TAiMcpServerServiceFactory = ReturnType<typeof aiMcpServerServiceFactory>;
@@ -152,8 +150,7 @@ export const aiMcpServerServiceFactory = ({
aiMcpServerUserCredentialDAL,
kmsService,
keyStore,
permissionService,
licenseService
permissionService
}: TAiMcpServerServiceFactoryDep) => {
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-redundant-type-constituents */
const fetchMcpTools = async (serverUrl: string, accessToken: string): Promise<TMcpTool[]> => {
@@ -549,13 +546,6 @@ export const aiMcpServerServiceFactory = ({
actorAuthMethod,
actorOrgId
}: TCreateAiMcpServerDTO) => {
const orgLicensePlan = await licenseService.getPlan(actorOrgId);
if (!orgLicensePlan.ai) {
throw new BadRequestError({
message: "AI operation failed due to organization plan restrictions."
});
}
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,

View File

@@ -112,9 +112,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
fips: false,
eventSubscriptions: false,
machineIdentityAuthTemplates: false,
pkiLegacyTemplates: false,
pam: false,
ai: false
pkiLegacyTemplates: false
});
export const setupLicenseRequestWithStore = (

View File

@@ -92,8 +92,6 @@ export type TFeatureSet = {
pkiLegacyTemplates: false;
fips: false;
eventSubscriptions: false;
pam: false;
ai: false;
};
export type TOrgPlansTableDTO = {

View File

@@ -52,7 +52,6 @@ import { TUserDALFactory } from "@app/services/user/user-dal";
import { EventType, TAuditLogServiceFactory } from "../audit-log/audit-log-types";
import { TGatewayV2ServiceFactory } from "../gateway-v2/gateway-v2-service";
import { TLicenseServiceFactory } from "../license/license-service";
import { TPamFolderDALFactory } from "../pam-folder/pam-folder-dal";
import { getFullPamFolderPath } from "../pam-folder/pam-folder-fns";
import { TPamResourceDALFactory } from "../pam-resource/pam-resource-dal";
@@ -78,7 +77,6 @@ type TPamAccountServiceFactoryDep = {
projectDAL: TProjectDALFactory;
orgDAL: TOrgDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
gatewayV2Service: Pick<
TGatewayV2ServiceFactory,
@@ -107,7 +105,6 @@ export const pamAccountServiceFactory = ({
orgDAL,
userDAL,
permissionService,
licenseService,
kmsService,
gatewayV2Service,
auditLogService,
@@ -128,13 +125,6 @@ export const pamAccountServiceFactory = ({
}: TCreateAccountDTO,
actor: OrgServiceActor
) => {
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
if (!orgLicensePlan.pam) {
throw new BadRequestError({
message: "PAM operation failed due to organization plan restrictions."
});
}
if (rotationEnabled && (rotationIntervalSeconds === undefined || rotationIntervalSeconds === null)) {
throw new BadRequestError({
message: "Rotation interval must be defined when rotation is enabled."
@@ -248,13 +238,6 @@ export const pamAccountServiceFactory = ({
}: TUpdateAccountDTO,
actor: OrgServiceActor
) => {
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
if (!orgLicensePlan.pam) {
throw new BadRequestError({
message: "PAM operation failed due to organization plan restrictions."
});
}
const account = await pamAccountDAL.findById(accountId);
if (!account) throw new NotFoundError({ message: `Account with ID '${accountId}' not found` });
@@ -591,13 +574,6 @@ export const pamAccountServiceFactory = ({
}: TAccessAccountDTO,
actor: OrgServiceActor
) => {
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
if (!orgLicensePlan.pam) {
throw new BadRequestError({
message: "PAM operation failed due to organization plan restrictions."
});
}
const pathSegments: string[] = accountPath.split("/").filter(Boolean);
if (pathSegments.length === 0) {
throw new BadRequestError({ message: "Invalid accountPath. Path must contain at least the account name." });
@@ -940,13 +916,6 @@ export const pamAccountServiceFactory = ({
};
const getSessionCredentials = async (sessionId: string, actor: OrgServiceActor) => {
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
if (!orgLicensePlan.pam) {
throw new BadRequestError({
message: "PAM operation failed due to organization plan restrictions."
});
}
// To be hit by gateways only
if (actor.type !== ActorType.IDENTITY) {
throw new ForbiddenRequestError({ message: "Only gateways can perform this action" });

View File

@@ -7,31 +7,18 @@ import { DatabaseErrorCode } from "@app/lib/error-codes";
import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors";
import { OrgServiceActor } from "@app/lib/types";
import { TLicenseServiceFactory } from "../license/license-service";
import { TPamFolderDALFactory } from "./pam-folder-dal";
import { TCreateFolderDTO, TUpdateFolderDTO } from "./pam-folder-types";
type TPamFolderServiceFactoryDep = {
pamFolderDAL: TPamFolderDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
};
export type TPamFolderServiceFactory = ReturnType<typeof pamFolderServiceFactory>;
export const pamFolderServiceFactory = ({
pamFolderDAL,
permissionService,
licenseService
}: TPamFolderServiceFactoryDep) => {
export const pamFolderServiceFactory = ({ pamFolderDAL, permissionService }: TPamFolderServiceFactoryDep) => {
const createFolder = async ({ name, description, parentId, projectId }: TCreateFolderDTO, actor: OrgServiceActor) => {
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
if (!orgLicensePlan.pam) {
throw new BadRequestError({
message: "PAM operation failed due to organization plan restrictions."
});
}
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
actorAuthMethod: actor.authMethod,
@@ -72,13 +59,6 @@ export const pamFolderServiceFactory = ({
};
const updateFolder = async ({ id, name, description }: TUpdateFolderDTO, actor: OrgServiceActor) => {
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
if (!orgLicensePlan.pam) {
throw new BadRequestError({
message: "PAM operation failed due to organization plan restrictions."
});
}
const folder = await pamFolderDAL.findById(id);
if (!folder) throw new NotFoundError({ message: `Folder with ID '${id}' not found` });

View File

@@ -12,7 +12,6 @@ import { OrgServiceActor } from "@app/lib/types";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TGatewayV2ServiceFactory } from "../gateway-v2/gateway-v2-service";
import { TLicenseServiceFactory } from "../license/license-service";
import { decryptAccountCredentials, encryptAccountCredentials } from "../pam-account/pam-account-fns";
import { TPamResourceDALFactory } from "./pam-resource-dal";
import { PamResource } from "./pam-resource-enums";
@@ -31,7 +30,6 @@ import { TSSHResourceMetadata } from "./ssh/ssh-resource-types";
type TPamResourceServiceFactoryDep = {
pamResourceDAL: TPamResourceDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
gatewayV2Service: Pick<
TGatewayV2ServiceFactory,
@@ -44,7 +42,6 @@ export type TPamResourceServiceFactory = ReturnType<typeof pamResourceServiceFac
export const pamResourceServiceFactory = ({
pamResourceDAL,
permissionService,
licenseService,
kmsService,
gatewayV2Service
}: TPamResourceServiceFactoryDep) => {
@@ -76,13 +73,6 @@ export const pamResourceServiceFactory = ({
{ resourceType, connectionDetails, gatewayId, name, projectId, rotationAccountCredentials }: TCreateResourceDTO,
actor: OrgServiceActor
) => {
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
if (!orgLicensePlan.pam) {
throw new BadRequestError({
message: "PAM operation failed due to organization plan restrictions."
});
}
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
actorAuthMethod: actor.authMethod,
@@ -137,13 +127,6 @@ export const pamResourceServiceFactory = ({
{ connectionDetails, resourceId, name, rotationAccountCredentials }: TUpdateResourceDTO,
actor: OrgServiceActor
) => {
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
if (!orgLicensePlan.pam) {
throw new BadRequestError({
message: "PAM operation failed due to organization plan restrictions."
});
}
const resource = await pamResourceDAL.findById(resourceId);
if (!resource) throw new NotFoundError({ message: `Resource with ID '${resourceId}' not found` });

View File

@@ -9,7 +9,6 @@ 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 { TLicenseServiceFactory } from "../license/license-service";
import { OrgPermissionGatewayActions, OrgPermissionSubjects } from "../permission/org-permission";
import { ProjectPermissionPamSessionActions, ProjectPermissionSub } from "../permission/project-permission";
import { TPamSessionDALFactory } from "./pam-session-dal";
@@ -21,7 +20,6 @@ type TPamSessionServiceFactoryDep = {
pamSessionDAL: TPamSessionDALFactory;
projectDAL: TProjectDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
};
@@ -31,7 +29,6 @@ export const pamSessionServiceFactory = ({
pamSessionDAL,
projectDAL,
permissionService,
licenseService,
kmsService
}: TPamSessionServiceFactoryDep) => {
// Helper to check and update expired sessions when viewing session details (redundancy for scheduled job)
@@ -111,13 +108,6 @@ export const pamSessionServiceFactory = ({
};
const updateLogsById = async ({ sessionId, logs }: TUpdateSessionLogsDTO, actor: OrgServiceActor) => {
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
if (!orgLicensePlan.pam) {
throw new BadRequestError({
message: "PAM operation failed due to organization plan restrictions."
});
}
// To be hit by gateways only
if (actor.type !== ActorType.IDENTITY) {
throw new ForbiddenRequestError({ message: "Only gateways can perform this action" });

View File

@@ -2450,14 +2450,12 @@ export const registerRoutes = async (
const pamFolderService = pamFolderServiceFactory({
pamFolderDAL,
permissionService,
licenseService
permissionService
});
const pamResourceService = pamResourceServiceFactory({
pamResourceDAL,
permissionService,
licenseService,
kmsService,
gatewayV2Service
});
@@ -2479,7 +2477,6 @@ export const registerRoutes = async (
pamAccountDAL,
gatewayV2Service,
kmsService,
licenseService,
pamFolderDAL,
pamResourceDAL,
pamSessionDAL,
@@ -2505,7 +2502,6 @@ export const registerRoutes = async (
pamSessionDAL,
projectDAL,
permissionService,
licenseService,
kmsService
});
@@ -2515,8 +2511,7 @@ export const registerRoutes = async (
aiMcpServerUserCredentialDAL,
kmsService,
keyStore,
permissionService,
licenseService
permissionService
});
const aiMcpActivityLogService = aiMcpActivityLogServiceFactory({
@@ -2537,8 +2532,7 @@ export const registerRoutes = async (
authTokenService: tokenService,
aiMcpActivityLogService,
userDAL,
permissionService,
licenseService
permissionService
});
const migrationService = externalMigrationServiceFactory({

View File

@@ -62,6 +62,4 @@ export type SubscriptionPlan = {
cardDeclinedReason?: string;
cardDeclinedDays?: number;
machineIdentityAuthTemplates: boolean;
pam: boolean;
ai: boolean;
};

View File

@@ -1,112 +1,87 @@
import { useEffect } from "react";
import { Link, Outlet, useLocation } from "@tanstack/react-router";
import { motion } from "framer-motion";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { Tab, TabList, Tabs } from "@app/components/v2";
import { useOrganization, useProject, useProjectPermission, useSubscription } from "@app/context";
import { usePopUp } from "@app/hooks";
import { useOrganization, useProject, useProjectPermission } from "@app/context";
import { AssumePrivilegeModeBanner } from "../ProjectLayout/components/AssumePrivilegeModeBanner";
export const AILayout = () => {
const { currentOrg } = useOrganization();
const { currentProject } = useProject();
const { subscription } = useSubscription();
const { assumedPrivilegeDetails } = useProjectPermission();
const location = useLocation();
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"]);
useEffect(() => {
if (subscription && !subscription.ai) {
handlePopUpOpen("upgradePlan", {
description:
"Your current plan does not provide access to Infisical AI. To unlock this feature, please upgrade to Infisical Enterprise plan.",
isEnterpriseFeature: true
});
}
}, [subscription]);
return (
<>
<div className="dark flex h-full w-full flex-col overflow-x-hidden bg-mineshaft-900">
<div className="border-y border-t-project/10 border-b-project/5 bg-gradient-to-b from-project/[0.075] to-project/[0.025] px-4 pt-0.5">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
animate={{ x: 0 }}
exit={{ x: -150 }}
transition={{ duration: 0.2 }}
className="px-4"
>
<nav className="w-full">
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/organizations/$orgId/projects/ai/$projectId/overview"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>MCP</Tab>}
</Link>
<Link
to="/organizations/$orgId/projects/ai/$projectId/access-management"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab
value={
isActive ||
location.pathname.match(/\/groups\/|\/identities\/|\/members\/|\/roles\//)
? "selected"
: ""
}
>
Access Control
</Tab>
)}
</Link>
<Link
to="/organizations/$orgId/projects/ai/$projectId/audit-logs"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/organizations/$orgId/projects/ai/$projectId/settings"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Settings</Tab>}
</Link>
</TabList>
</Tabs>
</nav>
</motion.div>
</div>
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
<div className="flex-1 overflow-x-hidden overflow-y-auto bg-bunker-800 px-12 pt-10 pb-4">
<Outlet />
</div>
<div className="dark flex h-full w-full flex-col overflow-x-hidden bg-mineshaft-900">
<div className="border-y border-t-project/10 border-b-project/5 bg-gradient-to-b from-project/[0.075] to-project/[0.025] px-4 pt-0.5">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
animate={{ x: 0 }}
exit={{ x: -150 }}
transition={{ duration: 0.2 }}
className="px-4"
>
<nav className="w-full">
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/organizations/$orgId/projects/ai/$projectId/overview"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>MCP</Tab>}
</Link>
<Link
to="/organizations/$orgId/projects/ai/$projectId/access-management"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab
value={
isActive ||
location.pathname.match(/\/groups\/|\/identities\/|\/members\/|\/roles\//)
? "selected"
: ""
}
>
Access Control
</Tab>
)}
</Link>
<Link
to="/organizations/$orgId/projects/ai/$projectId/audit-logs"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/organizations/$orgId/projects/ai/$projectId/settings"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Settings</Tab>}
</Link>
</TabList>
</Tabs>
</nav>
</motion.div>
</div>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => {
handlePopUpToggle("upgradePlan", isOpen);
}}
text={popUp.upgradePlan.data?.description}
isEnterpriseFeature={popUp.upgradePlan.data?.isEnterpriseFeature}
/>
</>
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
<div className="flex-1 overflow-x-hidden overflow-y-auto bg-bunker-800 px-12 pt-10 pb-4">
<Outlet />
</div>
</div>
);
};

View File

@@ -1,147 +1,122 @@
import { useEffect } from "react";
import { Link, Outlet, useLocation } from "@tanstack/react-router";
import { motion } from "framer-motion";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { Tab, TabList, Tabs } from "@app/components/v2";
import { useOrganization, useProject, useProjectPermission, useSubscription } from "@app/context";
import { usePopUp } from "@app/hooks";
import { useOrganization, useProject, useProjectPermission } from "@app/context";
import { AssumePrivilegeModeBanner } from "../ProjectLayout/components/AssumePrivilegeModeBanner";
export const PamLayout = () => {
const { currentProject } = useProject();
const { currentOrg } = useOrganization();
const { subscription } = useSubscription();
const { assumedPrivilegeDetails } = useProjectPermission();
const location = useLocation();
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"]);
useEffect(() => {
if (subscription && !subscription.pam) {
handlePopUpOpen("upgradePlan", {
description:
"Your current plan does not provide access to Infisical PAM. To unlock this feature, please upgrade to Infisical Enterprise plan.",
isEnterpriseFeature: true
});
}
}, [subscription]);
return (
<>
<div className="dark flex h-full w-full flex-col overflow-x-hidden bg-mineshaft-900">
<div className="border-y border-t-project/10 border-b-project/5 bg-gradient-to-b from-project/[0.075] to-project/[0.025] px-4 pt-0.5">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
animate={{ x: 0 }}
exit={{ x: -150 }}
transition={{ duration: 0.2 }}
className="px-4"
>
<nav className="w-full">
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/organizations/$orgId/projects/pam/$projectId/accounts"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Accounts</Tab>}
</Link>
<Link
to="/organizations/$orgId/projects/pam/$projectId/resources"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Resources</Tab>}
</Link>
<Link
to="/organizations/$orgId/projects/pam/$projectId/sessions"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Sessions</Tab>}
</Link>
<Link
to="/organizations/$orgId/projects/pam/$projectId/approvals"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab
value={
isActive || location.pathname.match(/\/approvals\/|\/i/) ? "selected" : ""
}
>
Approvals
</Tab>
)}
</Link>
<Link
to="/organizations/$orgId/projects/pam/$projectId/access-management"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab
value={
isActive ||
location.pathname.match(/\/groups\/|\/identities\/|\/members\/|\/roles\//)
? "selected"
: ""
}
>
Access Control
</Tab>
)}
</Link>
<Link
to="/organizations/$orgId/projects/pam/$projectId/audit-logs"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/organizations/$orgId/projects/pam/$projectId/settings"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Settings</Tab>}
</Link>
</TabList>
</Tabs>
</nav>
</motion.div>
</div>
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
<div className="flex-1 overflow-x-hidden overflow-y-auto bg-bunker-800 px-12 pt-10 pb-4">
<Outlet />
</div>
<div className="dark flex h-full w-full flex-col overflow-x-hidden bg-mineshaft-900">
<div className="border-y border-t-project/10 border-b-project/5 bg-gradient-to-b from-project/[0.075] to-project/[0.025] px-4 pt-0.5">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
animate={{ x: 0 }}
exit={{ x: -150 }}
transition={{ duration: 0.2 }}
className="px-4"
>
<nav className="w-full">
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/organizations/$orgId/projects/pam/$projectId/accounts"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Accounts</Tab>}
</Link>
<Link
to="/organizations/$orgId/projects/pam/$projectId/resources"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Resources</Tab>}
</Link>
<Link
to="/organizations/$orgId/projects/pam/$projectId/sessions"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Sessions</Tab>}
</Link>
<Link
to="/organizations/$orgId/projects/pam/$projectId/approvals"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab
value={
isActive || location.pathname.match(/\/approvals\/|\/i/) ? "selected" : ""
}
>
Approvals
</Tab>
)}
</Link>
<Link
to="/organizations/$orgId/projects/pam/$projectId/access-management"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab
value={
isActive ||
location.pathname.match(/\/groups\/|\/identities\/|\/members\/|\/roles\//)
? "selected"
: ""
}
>
Access Control
</Tab>
)}
</Link>
<Link
to="/organizations/$orgId/projects/pam/$projectId/audit-logs"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/organizations/$orgId/projects/pam/$projectId/settings"
params={{
orgId: currentOrg.id,
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Settings</Tab>}
</Link>
</TabList>
</Tabs>
</nav>
</motion.div>
</div>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => {
handlePopUpToggle("upgradePlan", isOpen);
}}
text={popUp.upgradePlan.data?.description}
isEnterpriseFeature={popUp.upgradePlan.data?.isEnterpriseFeature}
/>
</>
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
<div className="flex-1 overflow-x-hidden overflow-y-auto bg-bunker-800 px-12 pt-10 pb-4">
<Outlet />
</div>
</div>
);
};

View File

@@ -2,16 +2,10 @@ import { useState } from "react";
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions";
import { Button, DeleteActionModal } from "@app/components/v2";
import {
ProjectPermissionMcpEndpointActions,
ProjectPermissionSub,
useSubscription
} from "@app/context";
import { usePopUp } from "@app/hooks";
import { ProjectPermissionMcpEndpointActions, ProjectPermissionSub } from "@app/context";
import { TAiMcpEndpoint, useDeleteAiMcpEndpoint } from "@app/hooks/api";
import { AddMCPEndpointModal } from "./AddMCPEndpointModal";
@@ -24,18 +18,9 @@ export const MCPEndpointsTab = () => {
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [selectedEndpoint, setSelectedEndpoint] = useState<TAiMcpEndpoint | null>(null);
const { subscription } = useSubscription();
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"]);
const deleteEndpoint = useDeleteAiMcpEndpoint();
const handleCreateEndpoint = () => {
if (subscription && !subscription.ai) {
handlePopUpOpen("upgradePlan", {
text: "Your current plan does not include access to Infisical AI. To unlock this feature, please upgrade to Infisical Enterprise plan.",
isEnterpriseFeature: true
});
return;
}
setIsCreateModalOpen(true);
};
@@ -130,13 +115,6 @@ export const MCPEndpointsTab = () => {
onDeleteApproved={handleDeleteConfirm}
/>
)}
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text={popUp.upgradePlan.data?.text}
isEnterpriseFeature={popUp.upgradePlan.data?.isEnterpriseFeature}
/>
</div>
);
};

View File

@@ -2,12 +2,10 @@ import { useState } from "react";
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions";
import { Button, DeleteActionModal } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useSubscription } from "@app/context";
import { usePopUp } from "@app/hooks";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { TAiMcpServer, useDeleteAiMcpServer } from "@app/hooks/api";
import { AddMCPServerModal } from "./AddMCPServerModal";
@@ -20,18 +18,9 @@ export const MCPServersTab = () => {
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [selectedServer, setSelectedServer] = useState<TAiMcpServer | null>(null);
const { subscription } = useSubscription();
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"]);
const deleteServer = useDeleteAiMcpServer();
const handleAddServer = () => {
if (subscription && !subscription.ai) {
handlePopUpOpen("upgradePlan", {
text: "Your current plan does not include access to Infisical AI. To unlock this feature, please upgrade to Infisical Enterprise plan.",
isEnterpriseFeature: true
});
return;
}
setIsAddModalOpen(true);
};
@@ -123,13 +112,6 @@ export const MCPServersTab = () => {
onDeleteApproved={handleDeleteConfirm}
/>
)}
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text={popUp.upgradePlan.data?.text}
isEnterpriseFeature={popUp.upgradePlan.data?.isEnterpriseFeature}
/>
</div>
);
};

View File

@@ -4,7 +4,6 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { EmptyState, Input, Pagination, Spinner, Tooltip } from "@app/components/v2";
import { useSubscription } from "@app/context";
import { usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
import {
PAM_RESOURCE_TYPE_MAP,
@@ -18,7 +17,6 @@ type Props = {
export const ResourceTypeSelect = ({ onSelect }: Props) => {
const { isPending, data: resourceOptions } = useListPamResourceOptions();
const { subscription } = useSubscription();
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
const appendedResourceOptions = useMemo(() => {
@@ -65,14 +63,6 @@ export const ResourceTypeSelect = ({ onSelect }: Props) => {
});
const handleResourceSelect = (resource: PamResourceType) => {
if (!subscription.pam) {
handlePopUpOpen("upgradePlan", {
text: "Your current plan does not include access to Infisical PAM. To unlock this feature, please upgrade to Infisical Enterprise plan.",
isEnterpriseFeature: true
});
return;
}
// We temporarily show a special license modal for these because we will have to write some code to complete the integration
if (
resource === PamResourceType.RDP ||