diff --git a/packages/manager/.changeset/pr-12534-upcoming-features-1752829040541.md b/packages/manager/.changeset/pr-12534-upcoming-features-1752829040541.md
new file mode 100644
index 00000000000..f114f4b1ecb
--- /dev/null
+++ b/packages/manager/.changeset/pr-12534-upcoming-features-1752829040541.md
@@ -0,0 +1,5 @@
+---
+"@linode/manager": Upcoming Features
+---
+
+Implement the new RBAC permission hook in Firewalls Rules flow ([#12534](https://github.com/linode/manager/pull/12534))
diff --git a/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRulesLanding.test.tsx b/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRulesLanding.test.tsx
new file mode 100644
index 00000000000..e05ab9304dd
--- /dev/null
+++ b/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRulesLanding.test.tsx
@@ -0,0 +1,191 @@
+import React from 'react';
+
+import { firewallRulesFactory } from 'src/factories/firewalls';
+import { renderWithTheme } from 'src/utilities/testHelpers';
+
+import { FirewallRulesLanding } from './FirewallRulesLanding';
+
+const queryMocks = vi.hoisted(() => ({
+ userPermissions: vi.fn(() => ({
+ permissions: {
+ update_firewall_rules: false,
+ },
+ })),
+ useNavigate: vi.fn(),
+ useLocation: vi.fn(),
+ useBlocker: vi.fn(() => ({
+ proceed: vi.fn(),
+ reset: vi.fn(),
+ status: 'unblocked',
+ })),
+}));
+
+vi.mock('src/features/IAM/hooks/usePermissions', () => ({
+ usePermissions: queryMocks.userPermissions,
+}));
+
+vi.mock('@tanstack/react-router', async () => {
+ const actual = await vi.importActual('@tanstack/react-router');
+ return {
+ ...actual,
+ useNavigate: queryMocks.useNavigate,
+ useLocation: vi.fn(() => ({
+ pathname: '/firewalls/1/rules',
+ })),
+ useBlocker: queryMocks.useBlocker,
+ };
+});
+
+const firewallRules = firewallRulesFactory.build();
+const getDisabledState = () =>
+ !queryMocks.userPermissions().permissions.update_firewall_rules;
+
+describe('FirewallRuleTable', () => {
+ it('should disable "Add AN Inbound Rule" button if the user does not have update_firewall_rules permission', () => {
+ const { getByRole } = renderWithTheme(
+
+ );
+
+ const addInboundBtn = getByRole('button', { name: 'Add an Inbound Rule' });
+
+ expect(addInboundBtn).toBeInTheDocument();
+ expect(addInboundBtn).toHaveAttribute('aria-disabled', 'true');
+ });
+
+ it('should disable "Add AN Outbound Rule" button if the user does not have update_firewall_rules permission', () => {
+ const { getByRole } = renderWithTheme(
+
+ );
+
+ const addOutboundBtn = getByRole('button', {
+ name: 'Add an Outbound Rule',
+ });
+ expect(addOutboundBtn).toBeInTheDocument();
+ expect(addOutboundBtn).toHaveAttribute('aria-disabled', 'true');
+ });
+
+ it('should disable "Save Changes" button if the user does not have update_firewall_rules permission', () => {
+ const { getByRole } = renderWithTheme(
+
+ );
+
+ const saveBtn = getByRole('button', { name: 'Save Changes' });
+
+ expect(saveBtn).toBeInTheDocument();
+ expect(saveBtn).toHaveAttribute('aria-disabled', 'true');
+ });
+
+ it('should disable menu buttons if the user does not have update_firewall_rules permission', () => {
+ const { getAllByRole } = renderWithTheme(
+
+ );
+
+ const editBtns = getAllByRole('button', {
+ name: 'Edit',
+ });
+ expect(editBtns[0]).toBeVisible();
+ expect(editBtns[0]).toHaveAttribute('aria-disabled', 'true');
+
+ const cloneBtns = getAllByRole('button', {
+ name: 'Clone',
+ });
+ expect(cloneBtns[0]).toBeVisible();
+ expect(cloneBtns[0]).toHaveAttribute('aria-disabled', 'true');
+
+ const deleteBtns = getAllByRole('button', {
+ name: 'Delete',
+ });
+ expect(deleteBtns[0]).toBeVisible();
+ expect(deleteBtns[0]).toHaveAttribute('aria-disabled', 'true');
+ });
+
+ it('should enable menu buttons if the user has update_firewall_rules permission', () => {
+ queryMocks.userPermissions.mockReturnValue({
+ permissions: {
+ update_firewall_rules: true,
+ },
+ });
+ const { getAllByRole } = renderWithTheme(
+
+ );
+
+ const editBtns = getAllByRole('button', {
+ name: 'Edit',
+ });
+ expect(editBtns[0]).toBeVisible();
+ expect(editBtns[0]).not.toHaveAttribute('aria-disabled', 'true');
+
+ const cloneBtns = getAllByRole('button', {
+ name: 'Clone',
+ });
+ expect(cloneBtns[0]).toBeVisible();
+ expect(cloneBtns[0]).not.toHaveAttribute('aria-disabled', 'true');
+
+ const deleteBtns = getAllByRole('button', {
+ name: 'Delete',
+ });
+ expect(deleteBtns[0]).toBeVisible();
+ expect(deleteBtns[0]).not.toHaveAttribute('aria-disabled', 'true');
+ });
+
+ it('should enable "Add AN Inbound Rule" button if the user has update_firewall_rules permission', () => {
+ queryMocks.userPermissions.mockReturnValue({
+ permissions: {
+ update_firewall_rules: true,
+ },
+ });
+ const { getByRole } = renderWithTheme(
+
+ );
+
+ const addInboundBtn = getByRole('button', { name: 'Add an Inbound Rule' });
+
+ expect(addInboundBtn).toBeInTheDocument();
+ expect(addInboundBtn).not.toHaveAttribute('aria-disabled', 'true');
+ });
+
+ it('should enable "Add AN Outbound Rule" button if the user has update_firewall_rules permission', () => {
+ queryMocks.userPermissions.mockReturnValue({
+ permissions: {
+ update_firewall_rules: true,
+ },
+ });
+ const { getByRole } = renderWithTheme(
+
+ );
+
+ const addOutboundBtn = getByRole('button', {
+ name: 'Add an Outbound Rule',
+ });
+ expect(addOutboundBtn).toBeInTheDocument();
+ expect(addOutboundBtn).not.toHaveAttribute('aria-disabled', 'true');
+ });
+});
diff --git a/packages/manager/src/features/Firewalls/FirewallDetail/index.tsx b/packages/manager/src/features/Firewalls/FirewallDetail/index.tsx
index 5e8bd07315b..72d6a4363b3 100644
--- a/packages/manager/src/features/Firewalls/FirewallDetail/index.tsx
+++ b/packages/manager/src/features/Firewalls/FirewallDetail/index.tsx
@@ -27,6 +27,7 @@ import { SafeTabPanel } from 'src/components/Tabs/SafeTabPanel';
import { TabPanels } from 'src/components/Tabs/TabPanels';
import { Tabs } from 'src/components/Tabs/Tabs';
import { TanStackTabLinkList } from 'src/components/Tabs/TanStackTabLinkList';
+import { usePermissions } from 'src/features/IAM/hooks/usePermissions';
import { useFlags } from 'src/hooks/useFlags';
import { useSecureVMNoticesEnabled } from 'src/hooks/useSecureVMNoticesEnabled';
import { useTabs } from 'src/hooks/useTabs';
@@ -82,6 +83,12 @@ export const FirewallDetail = () => {
grants
);
+ const { permissions } = usePermissions(
+ 'firewall',
+ ['update_firewall_rules'],
+ firewallId
+ );
+
const { data: allDevices } = useAllFirewallDevicesQuery(firewallId);
const { linodeCount, nodebalancerCount } = allDevices?.reduce(
@@ -230,7 +237,7 @@ export const FirewallDetail = () => {