Add support for identity-based pricing

This commit is contained in:
Tuan Dang
2024-06-19 13:59:47 -07:00
parent afee158b95
commit d0f1cad98c
5 changed files with 89 additions and 7 deletions
@@ -437,6 +437,21 @@ 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) {
+35 -2
View File
@@ -19,11 +19,44 @@ 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 doc?.[0].count; return Number(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" });
} }
}; };
return { countOfOrgMembers }; const countOrgUsersAndIdentities = async (orgId: string | null, tx?: Knex) => {
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 Identities" });
}
};
return { countOfOrgMembers, countOrgUsersAndIdentities };
}; };
@@ -205,16 +205,22 @@ 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 count = await licenseDAL.countOfOrgMembers(orgId); const quantity = 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: count quantity,
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);
await licenseServerOnPremApi.request.patch(`/api/license/v1/license`, { usedSeats }); const usedIdentitySeats = await licenseDAL.countOrgUsersAndIdentities(null);
await licenseServerOnPremApi.request.patch(`/api/license/v1/license`, {
usedSeats,
usedIdentitySeats
});
} }
await refreshPlan(orgId); await refreshPlan(orgId);
}; };
@@ -377,6 +377,21 @@ 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) {
@@ -17,7 +17,7 @@ type TIdentityServiceFactoryDep = {
identityDAL: TIdentityDALFactory; identityDAL: TIdentityDALFactory;
identityOrgMembershipDAL: TIdentityOrgDALFactory; identityOrgMembershipDAL: TIdentityOrgDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRole">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRole">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
}; };
export type TIdentityServiceFactory = ReturnType<typeof identityServiceFactory>; export type TIdentityServiceFactory = ReturnType<typeof identityServiceFactory>;
@@ -25,7 +25,8 @@ 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,
@@ -47,6 +48,14 @@ 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(
@@ -60,6 +69,7 @@ export const identityServiceFactory = ({
); );
return newIdentity; return newIdentity;
}); });
await licenseService.updateSubscriptionOrgMemberCount(orgId);
return identity; return identity;
}; };
@@ -152,6 +162,9 @@ 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 };
}; };