feat: added license check for dynamic secret

This commit is contained in:
Akhil Mohan
2024-03-26 14:35:19 +05:30
parent 8a7774f9ac
commit c882da2e1a
5 changed files with 45 additions and 5 deletions
@@ -15,6 +15,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
membersUsed: 0, membersUsed: 0,
environmentLimit: null, environmentLimit: null,
environmentsUsed: 0, environmentsUsed: 0,
dynamicSecret: false,
secretVersioning: true, secretVersioning: true,
pitRecovery: false, pitRecovery: false,
ipAllowlisting: false, ipAllowlisting: false,
@@ -27,6 +27,7 @@ export type TFeatureSet = {
tier: -1; tier: -1;
workspaceLimit: null; workspaceLimit: null;
workspacesUsed: 0; workspacesUsed: 0;
dynamicSecret: false;
memberLimit: null; memberLimit: null;
membersUsed: 0; membersUsed: 0;
environmentLimit: null; environmentLimit: null;
@@ -2,6 +2,7 @@ import { ForbiddenError, subject } from "@casl/ability";
import ms from "ms"; import ms from "ms";
import { SecretKeyEncoding } from "@app/db/schemas"; import { SecretKeyEncoding } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
@@ -28,6 +29,7 @@ type TDynamicSecretLeaseServiceFactoryDep = {
dynamicSecretDAL: Pick<TDynamicSecretDALFactory, "findOne">; dynamicSecretDAL: Pick<TDynamicSecretDALFactory, "findOne">;
dynamicSecretProviders: Record<DynamicSecretProviders, TDynamicProviderFns>; dynamicSecretProviders: Record<DynamicSecretProviders, TDynamicProviderFns>;
dynamicSecretQueueService: TDynamicSecretLeaseQueueServiceFactory; dynamicSecretQueueService: TDynamicSecretLeaseQueueServiceFactory;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">; folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">; projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
@@ -42,7 +44,8 @@ export const dynamicSecretLeaseServiceFactory = ({
folderDAL, folderDAL,
permissionService, permissionService,
dynamicSecretQueueService, dynamicSecretQueueService,
projectDAL projectDAL,
licenseService
}: TDynamicSecretLeaseServiceFactoryDep) => { }: TDynamicSecretLeaseServiceFactoryDep) => {
const create = async ({ const create = async ({
environmentSlug, environmentSlug,
@@ -72,6 +75,13 @@ export const dynamicSecretLeaseServiceFactory = ({
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
); );
const plan = await licenseService.getPlan(actorOrgId);
if (!plan?.dynamicSecret) {
throw new BadRequestError({
message: "Failed to create lease due to plan restriction. Upgrade plan to create dynamic secret."
});
}
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" }); if (!folder) throw new BadRequestError({ message: "Folder not found" });
@@ -138,6 +148,13 @@ export const dynamicSecretLeaseServiceFactory = ({
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
); );
const plan = await licenseService.getPlan(actorOrgId);
if (!plan?.dynamicSecret) {
throw new BadRequestError({
message: "Failed to renew lease due to plan restriction. Upgrade plan to create dynamic secret."
});
}
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" }); if (!folder) throw new BadRequestError({ message: "Folder not found" });
@@ -1,6 +1,7 @@
import { ForbiddenError, subject } from "@casl/ability"; import { ForbiddenError, subject } from "@casl/ability";
import { SecretKeyEncoding } from "@app/db/schemas"; import { SecretKeyEncoding } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption"; import { infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
@@ -29,6 +30,7 @@ type TDynamicSecretServiceFactoryDep = {
TDynamicSecretLeaseQueueServiceFactory, TDynamicSecretLeaseQueueServiceFactory,
"pruneDynamicSecret" | "unsetLeaseRevocation" "pruneDynamicSecret" | "unsetLeaseRevocation"
>; >;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">; folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath">;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">; projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
@@ -39,6 +41,7 @@ export type TDynamicSecretServiceFactory = ReturnType<typeof dynamicSecretServic
export const dynamicSecretServiceFactory = ({ export const dynamicSecretServiceFactory = ({
dynamicSecretDAL, dynamicSecretDAL,
dynamicSecretLeaseDAL, dynamicSecretLeaseDAL,
licenseService,
folderDAL, folderDAL,
dynamicSecretProviders, dynamicSecretProviders,
permissionService, permissionService,
@@ -74,6 +77,13 @@ export const dynamicSecretServiceFactory = ({
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
); );
const plan = await licenseService.getPlan(actorOrgId);
if (!plan?.dynamicSecret) {
throw new BadRequestError({
message: "Failed to create dynamic secret due to plan restriction. Upgrade plan to create dynamic secret."
});
}
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" }); if (!folder) throw new BadRequestError({ message: "Folder not found" });
@@ -135,6 +145,13 @@ export const dynamicSecretServiceFactory = ({
subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path }) subject(ProjectPermissionSub.Secrets, { environment: environmentSlug, secretPath: path })
); );
const plan = await licenseService.getPlan(actorOrgId);
if (!plan?.dynamicSecret) {
throw new BadRequestError({
message: "Failed to update dynamic secret due to plan restriction. Upgrade plan to create dynamic secret."
});
}
const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path); const folder = await folderDAL.findBySecretPath(projectId, environmentSlug, path);
if (!folder) throw new BadRequestError({ message: "Folder not found" }); if (!folder) throw new BadRequestError({ message: "Folder not found" });
@@ -360,8 +360,12 @@ export const ActionBar = ({
<Button <Button
leftIcon={<FontAwesomeIcon icon={faFingerprint} className="pr-2" />} leftIcon={<FontAwesomeIcon icon={faFingerprint} className="pr-2" />}
onClick={() => { onClick={() => {
handlePopUpOpen("addDynamicSecret"); if (subscription && subscription.dynamicSecret) {
handlePopUpClose("misc"); handlePopUpOpen("addDynamicSecret");
handlePopUpClose("misc");
return;
}
handlePopUpOpen("upgradePlan");
}} }}
isDisabled={!isAllowed} isDisabled={!isAllowed}
variant="outline_bg" variant="outline_bg"
@@ -471,8 +475,8 @@ export const ActionBar = ({
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)} onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text={ text={
subscription.slug === null subscription.slug === null
? "You can perform point-in-time recovery under an Enterprise license" ? "You can perform this action under an Enterprise license"
: "You can perform point-in-time recovery if you switch to Infisical's Team plan" : "You can perform this action if you switch to Infisical's Team plan"
} }
/> />
)} )}