Revert "Add support for Identity-Based Pricing"

This commit is contained in:
Maidul Islam
2024-06-25 18:03:07 -04:00
committed by GitHub
parent 514df55d67
commit e0c774c045
14 changed files with 21 additions and 149 deletions
-1
View File
@@ -5,4 +5,3 @@ frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/M
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:292 frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:292
docs/self-hosting/configuration/envars.mdx:generic-api-key:106 docs/self-hosting/configuration/envars.mdx:generic-api-key:106
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:451 frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:451
docs/mint.json:generic-api-key:651
@@ -450,21 +450,6 @@ export const ldapConfigServiceFactory = ({
} }
}); });
} else { } else {
const plan = await licenseService.getPlan(orgId);
if (plan?.memberLimit && plan.membersUsed >= plan.memberLimit) {
// limit imposed on number of members allowed / number of members used exceeds the number of members allowed
throw new BadRequestError({
message: "Failed to create new member via LDAP due to member limit reached. Upgrade plan to add more members."
});
}
if (plan?.identityLimit && plan.identitiesUsed >= plan.identityLimit) {
// limit imposed on number of identities allowed / number of identities used exceeds the number of identities allowed
throw new BadRequestError({
message: "Failed to create new member via LDAP due to member limit reached. Upgrade plan to add more members."
});
}
userAlias = await userDAL.transaction(async (tx) => { userAlias = await userDAL.transaction(async (tx) => {
let newUser: TUsers | undefined; let newUser: TUsers | undefined;
if (serverCfg.trustSamlEmails) { if (serverCfg.trustSamlEmails) {
@@ -7,8 +7,6 @@ export const getDefaultOnPremFeatures = () => {
workspacesUsed: 0, workspacesUsed: 0,
memberLimit: null, memberLimit: null,
membersUsed: 0, membersUsed: 0,
identityLimit: null,
identitiesUsed: 0,
environmentLimit: null, environmentLimit: null,
environmentsUsed: 0, environmentsUsed: 0,
secretVersioning: true, secretVersioning: true,
@@ -15,8 +15,6 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
membersUsed: 0, membersUsed: 0,
environmentLimit: null, environmentLimit: null,
environmentsUsed: 0, environmentsUsed: 0,
identityLimit: null,
identitiesUsed: 0,
dynamicSecret: false, dynamicSecret: false,
secretVersioning: true, secretVersioning: true,
pitRecovery: false, pitRecovery: false,
+2 -35
View File
@@ -19,44 +19,11 @@ export const licenseDALFactory = (db: TDbClient) => {
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`) .join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
.where(`${TableName.Users}.isGhost`, false) .where(`${TableName.Users}.isGhost`, false)
.count(); .count();
return Number(doc?.[0].count); return doc?.[0].count;
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "Count of Org Members" }); throw new DatabaseError({ error, name: "Count of Org Members" });
} }
}; };
const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => { return { countOfOrgMembers };
try {
// count org users
const userDoc = await (tx || db)(TableName.OrgMembership)
.where({ status: OrgMembershipStatus.Accepted })
.andWhere((bd) => {
if (orgId) {
void bd.where({ orgId });
}
})
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
.where(`${TableName.Users}.isGhost`, false)
.count();
const userCount = Number(userDoc?.[0].count);
// count org identities
const identityDoc = await (tx || db)(TableName.IdentityOrgMembership)
.where((bd) => {
if (orgId) {
void bd.where({ orgId });
}
})
.count();
const identityCount = Number(identityDoc?.[0].count);
return userCount + identityCount;
} catch (error) {
throw new DatabaseError({ error, name: "Count of Org Users + Identities" });
}
};
return { countOfOrgMembers, countOrgUsersAndIdentities };
}; };
@@ -155,7 +155,6 @@ export const licenseServiceFactory = ({
LICENSE_SERVER_CLOUD_PLAN_TTL, LICENSE_SERVER_CLOUD_PLAN_TTL,
JSON.stringify(currentPlan) JSON.stringify(currentPlan)
); );
return currentPlan; return currentPlan;
} }
} catch (error) { } catch (error) {
@@ -205,22 +204,16 @@ export const licenseServiceFactory = ({
const org = await orgDAL.findOrgById(orgId); const org = await orgDAL.findOrgById(orgId);
if (!org) throw new BadRequestError({ message: "Org not found" }); if (!org) throw new BadRequestError({ message: "Org not found" });
const quantity = await licenseDAL.countOfOrgMembers(orgId); const count = await licenseDAL.countOfOrgMembers(orgId);
const quantityIdentities = await licenseDAL.countOrgUsersAndIdentities(orgId);
if (org?.customerId) { if (org?.customerId) {
await licenseServerCloudApi.request.patch(`/api/license-server/v1/customers/${org.customerId}/cloud-plan`, { await licenseServerCloudApi.request.patch(`/api/license-server/v1/customers/${org.customerId}/cloud-plan`, {
quantity, quantity: count
quantityIdentities
}); });
} }
await keyStore.deleteItem(FEATURE_CACHE_KEY(orgId)); await keyStore.deleteItem(FEATURE_CACHE_KEY(orgId));
} else if (instanceType === InstanceType.EnterpriseOnPrem) { } else if (instanceType === InstanceType.EnterpriseOnPrem) {
const usedSeats = await licenseDAL.countOfOrgMembers(null); const usedSeats = await licenseDAL.countOfOrgMembers(null);
const usedIdentitySeats = await licenseDAL.countOrgUsersAndIdentities(null); await licenseServerOnPremApi.request.patch(`/api/license/v1/license`, { usedSeats });
await licenseServerOnPremApi.request.patch(`/api/license/v1/license`, {
usedSeats,
usedIdentitySeats
});
} }
await refreshPlan(orgId); await refreshPlan(orgId);
}; };
@@ -31,8 +31,6 @@ export type TFeatureSet = {
dynamicSecret: false; dynamicSecret: false;
memberLimit: null; memberLimit: null;
membersUsed: 0; membersUsed: 0;
identityLimit: null;
identitiesUsed: 0;
environmentLimit: null; environmentLimit: null;
environmentsUsed: 0; environmentsUsed: 0;
secretVersioning: true; secretVersioning: true;
@@ -380,21 +380,6 @@ export const samlConfigServiceFactory = ({
return foundUser; return foundUser;
}); });
} else { } else {
const plan = await licenseService.getPlan(orgId);
if (plan?.memberLimit && plan.membersUsed >= plan.memberLimit) {
// limit imposed on number of members allowed / number of members used exceeds the number of members allowed
throw new BadRequestError({
message: "Failed to create new member via SAML due to member limit reached. Upgrade plan to add more members."
});
}
if (plan?.identityLimit && plan.identitiesUsed >= plan.identityLimit) {
// limit imposed on number of identities allowed / number of identities used exceeds the number of identities allowed
throw new BadRequestError({
message: "Failed to create new member via SAML due to member limit reached. Upgrade plan to add more members."
});
}
user = await userDAL.transaction(async (tx) => { user = await userDAL.transaction(async (tx) => {
let newUser: TUsers | undefined; let newUser: TUsers | undefined;
if (serverCfg.trustSamlEmails) { if (serverCfg.trustSamlEmails) {
+1 -2
View File
@@ -806,8 +806,7 @@ export const registerRoutes = async (
const identityService = identityServiceFactory({ const identityService = identityServiceFactory({
permissionService, permissionService,
identityDAL, identityDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL
licenseService
}); });
const identityAccessTokenService = identityAccessTokenServiceFactory({ const identityAccessTokenService = identityAccessTokenServiceFactory({
identityAccessTokenDAL, identityAccessTokenDAL,
@@ -1,7 +1,6 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import { OrgMembershipRole, TableName, TOrgRoles } from "@app/db/schemas"; import { OrgMembershipRole, TableName, TOrgRoles } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { isAtLeastAsPrivileged } from "@app/lib/casl";
@@ -17,7 +16,6 @@ type TIdentityServiceFactoryDep = {
identityDAL: TIdentityDALFactory; identityDAL: TIdentityDALFactory;
identityOrgMembershipDAL: TIdentityOrgDALFactory; identityOrgMembershipDAL: TIdentityOrgDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRole">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRole">;
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
}; };
export type TIdentityServiceFactory = ReturnType<typeof identityServiceFactory>; export type TIdentityServiceFactory = ReturnType<typeof identityServiceFactory>;
@@ -25,8 +23,7 @@ export type TIdentityServiceFactory = ReturnType<typeof identityServiceFactory>;
export const identityServiceFactory = ({ export const identityServiceFactory = ({
identityDAL, identityDAL,
identityOrgMembershipDAL, identityOrgMembershipDAL,
permissionService, permissionService
licenseService
}: TIdentityServiceFactoryDep) => { }: TIdentityServiceFactoryDep) => {
const createIdentity = async ({ const createIdentity = async ({
name, name,
@@ -48,14 +45,6 @@ export const identityServiceFactory = ({
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, rolePermission); const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, rolePermission);
if (!hasRequiredPriviledges) throw new BadRequestError({ message: "Failed to create a more privileged identity" }); if (!hasRequiredPriviledges) throw new BadRequestError({ message: "Failed to create a more privileged identity" });
const plan = await licenseService.getPlan(orgId);
if (plan?.identityLimit && plan.identitiesUsed >= plan.identityLimit) {
// limit imposed on number of identities allowed / number of identities used exceeds the number of identities allowed
throw new BadRequestError({
message: "Failed to create identity due to identity limit reached. Upgrade plan to create more identities."
});
}
const identity = await identityDAL.transaction(async (tx) => { const identity = await identityDAL.transaction(async (tx) => {
const newIdentity = await identityDAL.create({ name }, tx); const newIdentity = await identityDAL.create({ name }, tx);
await identityOrgMembershipDAL.create( await identityOrgMembershipDAL.create(
@@ -69,7 +58,6 @@ export const identityServiceFactory = ({
); );
return newIdentity; return newIdentity;
}); });
await licenseService.updateSubscriptionOrgMemberCount(orgId);
return identity; return identity;
}; };
@@ -180,9 +168,6 @@ export const identityServiceFactory = ({
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" }); throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
const deletedIdentity = await identityDAL.deleteById(id); const deletedIdentity = await identityDAL.deleteById(id);
await licenseService.updateSubscriptionOrgMemberCount(identityOrgMembership.orgId);
return { ...deletedIdentity, orgId: identityOrgMembership.orgId }; return { ...deletedIdentity, orgId: identityOrgMembership.orgId };
}; };
+3 -10
View File
@@ -420,20 +420,13 @@ export const orgServiceFactory = ({
} }
const plan = await licenseService.getPlan(orgId); const plan = await licenseService.getPlan(orgId);
if (plan?.memberLimit && plan.membersUsed >= plan.memberLimit) { if (plan.memberLimit !== null && plan.membersUsed >= plan.memberLimit) {
// limit imposed on number of members allowed / number of members used exceeds the number of members allowed // case: limit imposed on number of members allowed
// case: number of members used exceeds the number of members allowed
throw new BadRequestError({ throw new BadRequestError({
message: "Failed to invite member due to member limit reached. Upgrade plan to invite more members." message: "Failed to invite member due to member limit reached. Upgrade plan to invite more members."
}); });
} }
if (plan?.identityLimit && plan.identitiesUsed >= plan.identityLimit) {
// limit imposed on number of identities allowed / number of identities used exceeds the number of identities allowed
throw new BadRequestError({
message: "Failed to invite member due to member limit reached. Upgrade plan to invite more members."
});
}
const invitee = await orgDAL.transaction(async (tx) => { const invitee = await orgDAL.transaction(async (tx) => {
const inviteeUser = await userDAL.findUserByUsername(inviteeEmail, tx); const inviteeUser = await userDAL.findUserByUsername(inviteeEmail, tx);
if (inviteeUser) { if (inviteeUser) {
@@ -2,8 +2,6 @@ export type SubscriptionPlan = {
id: string; id: string;
membersUsed: number; membersUsed: number;
memberLimit: number; memberLimit: number;
identitiesUsed: number;
identityLimit: number;
auditLogs: boolean; auditLogs: boolean;
dynamicSecret: boolean; dynamicSecret: boolean;
auditLogsRetentionDays: number; auditLogsRetentionDays: number;
@@ -4,13 +4,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions"; import { OrgPermissionCan } from "@app/components/permissions";
import { Button, DeleteActionModal, UpgradePlanModal } from "@app/components/v2"; import { Button, DeleteActionModal } from "@app/components/v2";
import { import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
OrgPermissionActions,
OrgPermissionSubjects,
useOrganization,
useSubscription
} from "@app/context";
import { withPermission } from "@app/hoc"; import { withPermission } from "@app/hoc";
import { useDeleteIdentity } from "@app/hooks/api"; import { useDeleteIdentity } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp"; import { usePopUp } from "@app/hooks/usePopUp";
@@ -22,10 +17,10 @@ import { IdentityUniversalAuthClientSecretModal } from "./IdentityUniversalAuthC
export const IdentitySection = withPermission( export const IdentitySection = withPermission(
() => { () => {
const { subscription } = useSubscription();
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id || ""; const orgId = currentOrg?.id || "";
const { mutateAsync: deleteMutateAsync } = useDeleteIdentity(); const { mutateAsync: deleteMutateAsync } = useDeleteIdentity();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"identity", "identity",
@@ -36,10 +31,6 @@ export const IdentitySection = withPermission(
"upgradePlan" "upgradePlan"
] as const); ] as const);
const isMoreIdentitiesAllowed = subscription?.identityLimit
? subscription.identitiesUsed < subscription.identityLimit
: true;
const onDeleteIdentitySubmit = async (identityId: string) => { const onDeleteIdentitySubmit = async (identityId: string) => {
try { try {
await deleteMutateAsync({ await deleteMutateAsync({
@@ -90,15 +81,7 @@ export const IdentitySection = withPermission(
colorSchema="primary" colorSchema="primary"
type="submit" type="submit"
leftIcon={<FontAwesomeIcon icon={faPlus} />} leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => { onClick={() => handlePopUpOpen("identity")}
if (!isMoreIdentitiesAllowed) {
handlePopUpOpen("upgradePlan", {
description: "You can add more identities if you upgrade your Infisical plan."
});
return;
}
handlePopUpOpen("identity");
}}
isDisabled={!isAllowed} isDisabled={!isAllowed}
> >
Create identity Create identity
@@ -135,11 +118,6 @@ export const IdentitySection = withPermission(
) )
} }
/> />
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text={(popUp.upgradePlan?.data as { description: string })?.description}
/>
</div> </div>
); );
}, },
@@ -23,6 +23,7 @@ import { AddOrgMemberModal } from "./AddOrgMemberModal";
import { OrgMembersTable } from "./OrgMembersTable"; import { OrgMembersTable } from "./OrgMembersTable";
export const OrgMembersSection = () => { export const OrgMembersSection = () => {
const { subscription } = useSubscription(); const { subscription } = useSubscription();
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const orgId = currentOrg?.id ?? ""; const orgId = currentOrg?.id ?? "";
@@ -38,13 +39,9 @@ export const OrgMembersSection = () => {
const { mutateAsync: deleteMutateAsync } = useDeleteOrgMembership(); const { mutateAsync: deleteMutateAsync } = useDeleteOrgMembership();
const isMoreUsersAllowed = subscription?.memberLimit const isMoreUsersNotAllowed = subscription?.memberLimit
? subscription.membersUsed < subscription.memberLimit ? subscription.membersUsed >= subscription.memberLimit
: true; : false;
const isMoreIdentitiesAllowed = subscription?.identityLimit
? subscription.identitiesUsed < subscription.identityLimit
: true;
const handleAddMemberModal = () => { const handleAddMemberModal = () => {
if (currentOrg?.authEnforced) { if (currentOrg?.authEnforced) {
@@ -55,14 +52,13 @@ export const OrgMembersSection = () => {
return; return;
} }
if (!isMoreUsersAllowed || !isMoreIdentitiesAllowed) { if (isMoreUsersNotAllowed) {
handlePopUpOpen("upgradePlan", { handlePopUpOpen("upgradePlan", {
description: "You can add more members if you upgrade your Infisical plan." description: "You can add more members if you upgrade your Infisical plan."
}); });
return; } else {
handlePopUpOpen("addMember");
} }
handlePopUpOpen("addMember");
}; };
const onRemoveMemberSubmit = async (orgMembershipId: string) => { const onRemoveMemberSubmit = async (orgMembershipId: string) => {