diff --git a/backend/src/ee/services/license/license-service.ts b/backend/src/ee/services/license/license-service.ts index cfc42d038..e2cf09bb1 100644 --- a/backend/src/ee/services/license/license-service.ts +++ b/backend/src/ee/services/license/license-service.ts @@ -17,7 +17,7 @@ import { TIdentityOrgDALFactory } from "@app/services/identity/identity-org-dal" import { TOrgDALFactory } from "@app/services/org/org-dal"; import { TProjectDALFactory } from "@app/services/project/project-dal"; -import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission"; +import { OrgPermissionBillingActions, OrgPermissionSubjects } from "../permission/org-permission"; import { TPermissionServiceFactory } from "../permission/permission-service"; import { BillingPlanRows, BillingPlanTableHead } from "./licence-enums"; import { TLicenseDALFactory } from "./license-dal"; @@ -288,7 +288,7 @@ export const licenseServiceFactory = ({ billingCycle }: TOrgPlansTableDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing); const { data } = await licenseServerCloudApi.request.get( `/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}` ); @@ -310,8 +310,10 @@ export const licenseServiceFactory = ({ success_url }: TStartOrgTrialDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan( + OrgPermissionBillingActions.ManageBilling, + OrgPermissionSubjects.Billing + ); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -338,8 +340,10 @@ export const licenseServiceFactory = ({ actorOrgId }: TCreateOrgPortalSession) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan( + OrgPermissionBillingActions.ManageBilling, + OrgPermissionSubjects.Billing + ); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -385,7 +389,7 @@ export const licenseServiceFactory = ({ const getOrgBillingInfo = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -413,7 +417,7 @@ export const licenseServiceFactory = ({ // returns org current plan feature table const getOrgPlanTable = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -484,7 +488,7 @@ export const licenseServiceFactory = ({ const getOrgBillingDetails = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -509,7 +513,10 @@ export const licenseServiceFactory = ({ email }: TUpdateOrgBillingDetailsDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan( + OrgPermissionBillingActions.ManageBilling, + OrgPermissionSubjects.Billing + ); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -529,7 +536,7 @@ export const licenseServiceFactory = ({ const getOrgPmtMethods = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgPmtMethodsDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -556,7 +563,10 @@ export const licenseServiceFactory = ({ cancel_url }: TAddOrgPmtMethodDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan( + OrgPermissionBillingActions.ManageBilling, + OrgPermissionSubjects.Billing + ); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -585,7 +595,10 @@ export const licenseServiceFactory = ({ pmtMethodId }: TDelOrgPmtMethodDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan( + OrgPermissionBillingActions.ManageBilling, + OrgPermissionSubjects.Billing + ); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -602,7 +615,7 @@ export const licenseServiceFactory = ({ const getOrgTaxIds = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgTaxIdDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -620,7 +633,10 @@ export const licenseServiceFactory = ({ const addOrgTaxId = async ({ actorId, actor, actorAuthMethod, actorOrgId, orgId, type, value }: TAddOrgTaxIdDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan( + OrgPermissionBillingActions.ManageBilling, + OrgPermissionSubjects.Billing + ); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -641,7 +657,10 @@ export const licenseServiceFactory = ({ const delOrgTaxId = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId, taxId }: TDelOrgTaxIdDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan( + OrgPermissionBillingActions.ManageBilling, + OrgPermissionSubjects.Billing + ); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -658,7 +677,7 @@ export const licenseServiceFactory = ({ const getOrgTaxInvoices = async ({ actorId, actor, actorOrgId, actorAuthMethod, orgId }: TOrgInvoiceDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { @@ -675,7 +694,7 @@ export const licenseServiceFactory = ({ const getOrgLicenses = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TOrgLicensesDTO) => { const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing); const organization = await orgDAL.findOrgById(orgId); if (!organization) { diff --git a/backend/src/ee/services/permission/org-permission.ts b/backend/src/ee/services/permission/org-permission.ts index 612914bcc..f0fe73d71 100644 --- a/backend/src/ee/services/permission/org-permission.ts +++ b/backend/src/ee/services/permission/org-permission.ts @@ -67,6 +67,11 @@ export enum OrgPermissionGroupActions { RemoveMembers = "remove-members" } +export enum OrgPermissionBillingActions { + Read = "read", + ManageBilling = "manage-billing" +} + export enum OrgPermissionSubjects { Workspace = "workspace", Role = "role", @@ -107,7 +112,7 @@ export type OrgPermissionSet = | [OrgPermissionActions, OrgPermissionSubjects.Ldap] | [OrgPermissionGroupActions, OrgPermissionSubjects.Groups] | [OrgPermissionActions, OrgPermissionSubjects.SecretScanning] - | [OrgPermissionActions, OrgPermissionSubjects.Billing] + | [OrgPermissionBillingActions, OrgPermissionSubjects.Billing] | [OrgPermissionIdentityActions, OrgPermissionSubjects.Identity] | [OrgPermissionActions, OrgPermissionSubjects.Kms] | [OrgPermissionActions, OrgPermissionSubjects.AuditLogs] @@ -298,10 +303,8 @@ const buildAdminPermission = () => { can(OrgPermissionGroupActions.AddMembers, OrgPermissionSubjects.Groups); can(OrgPermissionGroupActions.RemoveMembers, OrgPermissionSubjects.Groups); - can(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); - can(OrgPermissionActions.Create, OrgPermissionSubjects.Billing); - can(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing); - can(OrgPermissionActions.Delete, OrgPermissionSubjects.Billing); + can(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing); + can(OrgPermissionBillingActions.ManageBilling, OrgPermissionSubjects.Billing); can(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity); can(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity); @@ -362,7 +365,7 @@ const buildMemberPermission = () => { can(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups); can(OrgPermissionActions.Read, OrgPermissionSubjects.Role); can(OrgPermissionActions.Read, OrgPermissionSubjects.Settings); - can(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); + can(OrgPermissionBillingActions.Read, OrgPermissionSubjects.Billing); can(OrgPermissionActions.Read, OrgPermissionSubjects.IncidentAccount); can(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning); diff --git a/docs/internals/permissions/organization-permissions.mdx b/docs/internals/permissions/organization-permissions.mdx index 6de3bd6fe..80c843851 100644 --- a/docs/internals/permissions/organization-permissions.mdx +++ b/docs/internals/permissions/organization-permissions.mdx @@ -142,12 +142,10 @@ Below is a comprehensive list of all available organization-level subjects and t #### Subject: `billing` -| Action | Description | -| -------- | ------------------------------------------------ | -| `read` | View billing information and subscription status | -| `create` | Set up new payment methods or subscriptions | -| `edit` | Modify billing details or subscription plans | -| `delete` | Remove payment methods or cancel subscriptions | +| Action | Description | +| ---------------- | ------------------------------------------------ | +| `read` | View billing information and subscription status | +| `manage-billing` | Manage billing details and subscription plans | ### Templates & Automation diff --git a/frontend/src/context/OrgPermissionContext/index.tsx b/frontend/src/context/OrgPermissionContext/index.tsx index acbbf3902..fccd53935 100644 --- a/frontend/src/context/OrgPermissionContext/index.tsx +++ b/frontend/src/context/OrgPermissionContext/index.tsx @@ -2,6 +2,7 @@ export { useOrgPermission } from "./OrgPermissionContext"; export type { TOrgPermission } from "./types"; export { OrgPermissionActions, + OrgPermissionBillingActions, OrgPermissionGroupActions, OrgPermissionIdentityActions, OrgPermissionSubjects diff --git a/frontend/src/context/OrgPermissionContext/types.ts b/frontend/src/context/OrgPermissionContext/types.ts index a4bd202bf..59446eb07 100644 --- a/frontend/src/context/OrgPermissionContext/types.ts +++ b/frontend/src/context/OrgPermissionContext/types.ts @@ -7,6 +7,11 @@ export enum OrgPermissionActions { Delete = "delete" } +export enum OrgPermissionBillingActions { + Read = "read", + ManageBilling = "manage-billing" +} + export enum OrgGatewayPermissionActions { // is there a better word for this. This mean can an identity be a gateway CreateGateways = "create-gateways", @@ -100,7 +105,7 @@ export type OrgPermissionSet = | [OrgPermissionActions, OrgPermissionSubjects.Ldap] | [OrgPermissionGroupActions, OrgPermissionSubjects.Groups] | [OrgPermissionActions, OrgPermissionSubjects.SecretScanning] - | [OrgPermissionActions, OrgPermissionSubjects.Billing] + | [OrgPermissionBillingActions, OrgPermissionSubjects.Billing] | [OrgPermissionActions, OrgPermissionSubjects.Kms] | [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole] | [OrgPermissionActions, OrgPermissionSubjects.AuditLogs] diff --git a/frontend/src/context/index.tsx b/frontend/src/context/index.tsx index 8b64ee58a..c19c990dd 100644 --- a/frontend/src/context/index.tsx +++ b/frontend/src/context/index.tsx @@ -2,6 +2,7 @@ export { useOrganization } from "./OrganizationContext"; export type { TOrgPermission } from "./OrgPermissionContext"; export { OrgPermissionActions, + OrgPermissionBillingActions, OrgPermissionGroupActions, OrgPermissionIdentityActions, OrgPermissionSubjects, diff --git a/frontend/src/pages/organization/BillingPage/BillingPage.tsx b/frontend/src/pages/organization/BillingPage/BillingPage.tsx index 0ffe5abbb..d0b71cc21 100644 --- a/frontend/src/pages/organization/BillingPage/BillingPage.tsx +++ b/frontend/src/pages/organization/BillingPage/BillingPage.tsx @@ -2,7 +2,7 @@ import { Helmet } from "react-helmet"; import { useTranslation } from "react-i18next"; import { OrgPermissionCan } from "@app/components/permissions"; -import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; +import { OrgPermissionBillingActions, OrgPermissionSubjects } from "@app/context"; import { BillingTabGroup } from "./components"; @@ -24,7 +24,7 @@ export const BillingPage = () => { diff --git a/frontend/src/pages/organization/BillingPage/components/BillingCloudTab/PreviewSection.tsx b/frontend/src/pages/organization/BillingPage/components/BillingCloudTab/PreviewSection.tsx index 5741722bd..98309edbd 100644 --- a/frontend/src/pages/organization/BillingPage/components/BillingCloudTab/PreviewSection.tsx +++ b/frontend/src/pages/organization/BillingPage/components/BillingCloudTab/PreviewSection.tsx @@ -4,7 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { OrgPermissionCan } from "@app/components/permissions"; import { Button } from "@app/components/v2"; import { - OrgPermissionActions, + OrgPermissionBillingActions, OrgPermissionSubjects, useOrganization, useSubscription @@ -112,7 +112,7 @@ export const PreviewSection = () => { Get unlimited members, projects, RBAC, smart alerts, and so much more.

- + {(isAllowed) => (