Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-12689-fixed-1755093499594.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Fixed
---

IAM RBAC: IP Addresses actions disabled for account_linode_admin role ([#12689](https://github.com/linode/manager/pull/12689))
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
import { screen, waitForElementToBeRemoved } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';

import { ipAddressFactory } from 'src/factories/networking';
import { mockMatchMedia, renderWithTheme } from 'src/utilities/testHelpers';

import { LinodeIPAddresses } from './LinodeIPAddresses';
import { listIPv6InRange } from './LinodeIPAddressRow';
import { createType, ipResponseToDisplayRows } from './utils';

import type { LinodeIPsResponse } from '@linode/api-v4/lib/linodes';
const loadingTestId = 'circle-progress';
const queryMocks = vi.hoisted(() => ({
userPermissions: vi.fn(() => ({
data: {
update_linode: true,
},
})),
}));

vi.mock('src/features/IAM/hooks/usePermissions', () => ({
usePermissions: queryMocks.userPermissions,
}));

beforeAll(() => mockMatchMedia());
describe('listIPv6InRange utility function', () => {
const ipv4List = ipAddressFactory.buildList(4);
const ipv6Range = ipAddressFactory.build({
Expand Down Expand Up @@ -113,3 +132,53 @@ describe('createType utility function', () => {
expect(createType(ipv6, 'Link Local')).toBe('Link Local – IPv6');
});
});

describe('LinodeIPAddresses', () => {
it('should disable "Add an IP Address" button if the user does not have update_linode permission', async () => {
queryMocks.userPermissions.mockReturnValue({
data: {
update_linode: false,
},
});

const { queryByTestId } = await renderWithTheme(
<LinodeIPAddresses linodeID={2} />
);

const loadingState = queryByTestId(loadingTestId);
if (loadingState) {
await waitForElementToBeRemoved(loadingState);
}

const menuButton = screen.getByLabelText(/Linode IP Address Actions/i);
await userEvent.click(menuButton);

const ipTransferBtn = screen.getByTestId('Add an IP Address');
expect(ipTransferBtn).toBeInTheDocument();
expect(ipTransferBtn).toHaveAttribute('aria-disabled', 'true');
});

it('should enable "Add an IP Address" button if the user has update_linode permission', async () => {
queryMocks.userPermissions.mockReturnValue({
data: {
update_linode: true,
},
});

const { queryByTestId } = await renderWithTheme(
<LinodeIPAddresses linodeID={2} />
);

const loadingState = queryByTestId(loadingTestId);
if (loadingState) {
await waitForElementToBeRemoved(loadingState);
}

const menuButton = screen.getByLabelText(/Linode IP Address Actions/i);
await userEvent.click(menuButton);

const ipTransferBtn = screen.getByTestId('Add an IP Address');
expect(ipTransferBtn).toBeInTheDocument();
expect(ipTransferBtn).not.toHaveAttribute('aria-disabled', 'true');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
import { TableHead } from 'src/components/TableHead';
import { TableRow } from 'src/components/TableRow';
import { TableSortCell } from 'src/components/TableSortCell';
import { usePermissions } from 'src/features/IAM/hooks/usePermissions';
import { useDetermineUnreachableIPs } from 'src/hooks/useDetermineUnreachableIPs';
import { useIsResourceRestricted } from 'src/hooks/useIsResourceRestricted';
import { useOrderV2 } from 'src/hooks/useOrderV2';
import { useIsLinodeInterfacesEnabled } from 'src/utilities/linodes';

Expand Down Expand Up @@ -65,12 +65,12 @@
linode?.region ?? ''
);

const isLinodesGrantReadOnly = useIsResourceRestricted({
grantLevel: 'read_only',
grantType: 'linode',
id: linodeID,
});

// TODO: Update to check share_ips, assign_ips, update_ip_rdns, and allocate_linode_ip_address permissions once available

Check warning on line 68 in packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐶 Complete the task associated to this "TODO" comment. Raw Output: {"ruleId":"sonarjs/todo-tag","severity":1,"message":"Complete the task associated to this \"TODO\" comment.","line":68,"column":6,"nodeType":null,"messageId":"completeTODO","endLine":68,"endColumn":10}
const { data: permissions } = usePermissions(
'linode',
['update_linode'],
linodeID
);
const isLinodeInterface = linode?.interface_generation === 'linode';

const { isUnreachablePublicIPv4, isUnreachablePublicIPv6, interfaceWithVPC } =
Expand Down Expand Up @@ -191,19 +191,22 @@
...(showAddIPButton
? [
{
disabled: isLinodesGrantReadOnly,
// TODO: change to allocate_linode_ip_address permission

Check warning on line 194 in packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐶 Complete the task associated to this "TODO" comment. Raw Output: {"ruleId":"sonarjs/todo-tag","severity":1,"message":"Complete the task associated to this \"TODO\" comment.","line":194,"column":26,"nodeType":null,"messageId":"completeTODO","endLine":194,"endColumn":30}
disabled: !permissions.update_linode,
onClick: () => setIsAddDrawerOpen(true),
title: 'Add an IP Address',
},
]
: []),
{
disabled: isLinodesGrantReadOnly,
// TODO: change to assign_ips permission

Check warning on line 202 in packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐶 Complete the task associated to this "TODO" comment. Raw Output: {"ruleId":"sonarjs/todo-tag","severity":1,"message":"Complete the task associated to this \"TODO\" comment.","line":202,"column":20,"nodeType":null,"messageId":"completeTODO","endLine":202,"endColumn":24}
disabled: !permissions.update_linode,
onClick: () => setIsTransferDialogOpen(true),
title: 'IP Transfer',
},
{
disabled: isLinodesGrantReadOnly,
// TODO: change to share_ips permission

Check warning on line 208 in packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐶 Complete the task associated to this "TODO" comment. Raw Output: {"ruleId":"sonarjs/todo-tag","severity":1,"message":"Complete the task associated to this \"TODO\" comment.","line":208,"column":20,"nodeType":null,"messageId":"completeTODO","endLine":208,"endColumn":24}
disabled: !permissions.update_linode,
onClick: () => setIsShareDialogOpen(true),
title: 'IP Sharing',
},
Expand All @@ -214,22 +217,25 @@
<Stack direction="row" spacing={1}>
<Button
buttonType="secondary"
disabled={isLinodesGrantReadOnly}
// TODO: change to assign_ips permission

Check warning on line 220 in packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐶 Complete the task associated to this "TODO" comment. Raw Output: {"ruleId":"sonarjs/todo-tag","severity":1,"message":"Complete the task associated to this \"TODO\" comment.","line":220,"column":18,"nodeType":null,"messageId":"completeTODO","endLine":220,"endColumn":22}
disabled={!permissions.update_linode}
onClick={() => setIsTransferDialogOpen(true)}
>
IP Transfer
</Button>
<Button
buttonType="secondary"
disabled={isLinodesGrantReadOnly}
// TODO: change to share_ips permission

Check warning on line 228 in packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐶 Complete the task associated to this "TODO" comment. Raw Output: {"ruleId":"sonarjs/todo-tag","severity":1,"message":"Complete the task associated to this \"TODO\" comment.","line":228,"column":18,"nodeType":null,"messageId":"completeTODO","endLine":228,"endColumn":22}
disabled={!permissions.update_linode}
onClick={() => setIsShareDialogOpen(true)}
>
IP Sharing
</Button>
{showAddIPButton && (
<Button
buttonType="primary"
disabled={isLinodesGrantReadOnly}
// TODO: change to allocate_linode_ip_address permission

Check warning on line 237 in packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐶 Complete the task associated to this "TODO" comment. Raw Output: {"ruleId":"sonarjs/todo-tag","severity":1,"message":"Complete the task associated to this \"TODO\" comment.","line":237,"column":20,"nodeType":null,"messageId":"completeTODO","endLine":237,"endColumn":24}
disabled={!permissions.update_linode}
onClick={() => setIsAddDrawerOpen(true)}
>
Add an IP Address
Expand Down Expand Up @@ -272,7 +278,8 @@
isUnreachablePublicIPv6={isUnreachablePublicIPv6}
key={`${ipDisplay.address}-${ipDisplay.type}`}
linodeId={linodeID}
readOnly={isLinodesGrantReadOnly}
// TODO: change to update_ip_rdns permission

Check warning on line 281 in packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐶 Complete the task associated to this "TODO" comment. Raw Output: {"ruleId":"sonarjs/todo-tag","severity":1,"message":"Complete the task associated to this \"TODO\" comment.","line":281,"column":18,"nodeType":null,"messageId":"completeTODO","endLine":281,"endColumn":22}
readOnly={!permissions.update_linode}
/>
))}
</TableBody>
Expand Down Expand Up @@ -309,19 +316,22 @@
linodeIsInDistributedRegion={linodeIsInDistributedRegion}
onClose={() => setIsAddDrawerOpen(false)}
open={isAddDrawerOpen}
readOnly={isLinodesGrantReadOnly}
// TODO: change to allocate_linode_ip_address permission

Check warning on line 319 in packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐶 Complete the task associated to this "TODO" comment. Raw Output: {"ruleId":"sonarjs/todo-tag","severity":1,"message":"Complete the task associated to this \"TODO\" comment.","line":319,"column":12,"nodeType":null,"messageId":"completeTODO","endLine":319,"endColumn":16}
readOnly={!permissions.update_linode}
/>
<IPTransfer
linodeId={linodeID}
onClose={() => setIsTransferDialogOpen(false)}
open={isTransferDialogOpen}
readOnly={isLinodesGrantReadOnly}
// TODO: change to assign_ips permission

Check warning on line 326 in packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐶 Complete the task associated to this "TODO" comment. Raw Output: {"ruleId":"sonarjs/todo-tag","severity":1,"message":"Complete the task associated to this \"TODO\" comment.","line":326,"column":12,"nodeType":null,"messageId":"completeTODO","endLine":326,"endColumn":16}
readOnly={!permissions.update_linode}
/>
<IPSharing
linodeId={linodeID}
onClose={() => setIsShareDialogOpen(false)}
open={isShareDialogOpen}
readOnly={isLinodesGrantReadOnly}
readOnly={!permissions.update_linode}
// TODO: change to share_ips permission
/>
{selectedIP && (
<DeleteIPDialog
Expand Down
Loading