From 45f0c0e1366dbc37b107c79103a1de7ac3aac43e Mon Sep 17 00:00:00 2001 From: Anastasiia Alekseenko Date: Tue, 2 Sep 2025 14:42:11 +0200 Subject: [PATCH 1/2] feat: [UIE-9142] - IAM RBAC: perm check nodebalancer summary tab --- .../NodeBalancerDetail/NodeBalancerDetail.tsx | 15 ++++---- .../NodeBalancerSummary/SummaryPanel.test.tsx | 36 +++++++++++++++++++ .../NodeBalancerSummary/SummaryPanel.tsx | 14 ++++---- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerDetail.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerDetail.tsx index cefd263bc45..6f6850b45d8 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerDetail.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerDetail.tsx @@ -13,7 +13,7 @@ import { TabPanels } from 'src/components/Tabs/TabPanels'; import { Tabs } from 'src/components/Tabs/Tabs'; import { TanStackTabLinkList } from 'src/components/Tabs/TanStackTabLinkList'; import { getRestrictedResourceText } from 'src/features/Account/utils'; -import { useIsResourceRestricted } from 'src/hooks/useIsResourceRestricted'; +import { usePermissions } from 'src/features/IAM/hooks/usePermissions'; import { useTabs } from 'src/hooks/useTabs'; import { getErrorMap } from 'src/utilities/errorUtils'; @@ -38,11 +38,11 @@ export const NodeBalancerDetail = () => { isLoading, } = useNodeBalancerQuery(Number(id), Boolean(id)); - const isNodeBalancerReadOnly = useIsResourceRestricted({ - grantLevel: 'read_only', - grantType: 'nodebalancer', - id: nodebalancer?.id, - }); + const { data: permissions } = usePermissions( + 'nodebalancer', + ['update_nodebalancer'], + nodebalancer?.id + ); const { handleTabChange, tabIndex, tabs } = useTabs([ { @@ -90,13 +90,14 @@ export const NodeBalancerDetail = () => { }, pathname: `/nodebalancers/${nodebalancer.label}`, }} + disabledBreadcrumbEditButton={!permissions.update_nodebalancer} docsLabel="Docs" docsLink="https://techdocs.akamai.com/cloud-computing/docs/getting-started-with-nodebalancers" spacingBottom={4} title={nodebalancer.label} /> {errorMap.none && } - {isNodeBalancerReadOnly && ( + {!permissions.update_nodebalancer && ( ({ .fn() .mockReturnValue({ data: undefined }), useParams: vi.fn().mockReturnValue({}), + userPermissions: vi.fn(() => ({ + data: { + update_nodebalancer: false, + }, + })), +})); + +vi.mock('src/features/IAM/hooks/usePermissions', () => ({ + usePermissions: queryMocks.userPermissions, })); vi.mock('@tanstack/react-router', async () => { @@ -188,4 +197,31 @@ describe('SummaryPanel', () => { ); }); }); + + it('should disable "Add a tag" if user does not have permission', () => { + const { getByText } = renderWithTheme(, { + flags: { nodebalancerVpc: true }, + }); + + // Tags panel + expect(getByText('Tags')).toBeVisible(); + expect(getByText('Add a tag')).toBeVisible(); + expect(getByText('Add a tag')).toHaveAttribute('aria-disabled', 'true'); + }); + + it('should enable "Add a tag" if user has permission', () => { + queryMocks.userPermissions.mockReturnValue({ + data: { + update_nodebalancer: true, + }, + }); + const { getByText } = renderWithTheme(, { + flags: { nodebalancerVpc: true }, + }); + + // Tags panel + expect(getByText('Tags')).toBeVisible(); + expect(getByText('Add a tag')).toBeVisible(); + expect(getByText('Add a tag')).not.toHaveAttribute('aria-disabled', 'true'); + }); }); diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx index 00acc92ff86..e2003220d02 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx @@ -15,9 +15,9 @@ import * as React from 'react'; import { Link } from 'src/components/Link'; import { TagCell } from 'src/components/TagCell/TagCell'; +import { usePermissions } from 'src/features/IAM/hooks/usePermissions'; import { useKubernetesBetaEndpoint } from 'src/features/Kubernetes/kubeUtils'; import { IPAddress } from 'src/features/Linodes/LinodesLanding/IPAddress'; -import { useIsResourceRestricted } from 'src/hooks/useIsResourceRestricted'; import { useKubernetesClusterQuery } from 'src/queries/kubernetes'; import { useIsNodebalancerVPCEnabled } from '../../utils'; @@ -42,11 +42,11 @@ export const SummaryPanel = () => { ); const displayFirewallLink = !!attachedFirewallData?.data?.length; - const isNodeBalancerReadOnly = useIsResourceRestricted({ - grantLevel: 'read_only', - grantType: 'nodebalancer', - id: nodebalancer?.id, - }); + const { data: permissions } = usePermissions( + 'nodebalancer', + ['update_nodebalancer'], + nodebalancer?.id + ); const flags = useIsNodebalancerVPCEnabled(); @@ -265,7 +265,7 @@ export const SummaryPanel = () => { Tags updateNodeBalancer({ tags })} From ba4251f8b4821f5bbbf8afd22dd54bca1bc8b399 Mon Sep 17 00:00:00 2001 From: Anastasiia Alekseenko Date: Tue, 2 Sep 2025 14:47:18 +0200 Subject: [PATCH 2/2] Added changeset: IAM RBAC: Implements IAM RBAC permissions for NodeBalancer summary tab --- packages/manager/.changeset/pr-12790-added-1756817237984.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/manager/.changeset/pr-12790-added-1756817237984.md diff --git a/packages/manager/.changeset/pr-12790-added-1756817237984.md b/packages/manager/.changeset/pr-12790-added-1756817237984.md new file mode 100644 index 00000000000..ca8c00ac97b --- /dev/null +++ b/packages/manager/.changeset/pr-12790-added-1756817237984.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Added +--- + +IAM RBAC: Implements IAM RBAC permissions for NodeBalancer summary tab ([#12790](https://github.com/linode/manager/pull/12790))