Skip to content

Commit 5ea7b89

Browse files
fix: [UIE-9737] - IAM Permissions performance improvements: Firewall entity assignment (#13153)
* Permission model switch * Added changeset: IAM Permissions performance improvements: Firewall entity assignment
1 parent df8ff8f commit 5ea7b89

File tree

4 files changed

+76
-45
lines changed

4 files changed

+76
-45
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Fixed
3+
---
4+
5+
IAM Permissions performance improvements: Firewall entity assignment ([#13153](https://github.com/linode/manager/pull/13153))

packages/manager/src/features/Firewalls/FirewallDetail/Devices/FirewallDeviceActionMenu.tsx

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,48 +6,31 @@ export interface ActionHandlers {
66
handleRemoveDevice: (device: FirewallDevice) => void;
77
}
88

9-
import { usePermissions } from 'src/features/IAM/hooks/usePermissions';
10-
119
import type { FirewallDevice } from '@linode/api-v4';
1210

1311
export interface FirewallDeviceActionMenuProps extends ActionHandlers {
1412
device: FirewallDevice;
1513
disabled: boolean;
14+
isLinodeUpdatable: boolean;
15+
isNodebalancerUpdatable: boolean;
16+
isPermissionsLoading: boolean;
1617
}
1718

1819
export const FirewallDeviceActionMenu = React.memo(
1920
(props: FirewallDeviceActionMenuProps) => {
20-
const { device, disabled, handleRemoveDevice } = props;
21+
const {
22+
device,
23+
disabled,
24+
handleRemoveDevice,
25+
isLinodeUpdatable,
26+
isNodebalancerUpdatable,
27+
isPermissionsLoading,
28+
} = props;
2129

2230
const { type } = device.entity;
2331

24-
const { data: linodePermissions, isLoading: isLinodePermissionsLoading } =
25-
usePermissions(
26-
'linode',
27-
['update_linode'],
28-
device?.entity.id,
29-
type !== 'nodebalancer'
30-
);
31-
32-
const {
33-
data: nodebalancerPermissions,
34-
isLoading: isNodebalancerPermissionsLoading,
35-
} = usePermissions(
36-
'nodebalancer',
37-
['update_nodebalancer'],
38-
device?.entity.id,
39-
type === 'nodebalancer'
40-
);
41-
4232
const disabledDueToPermissions =
43-
type === 'nodebalancer'
44-
? !nodebalancerPermissions?.update_nodebalancer
45-
: !linodePermissions?.update_linode;
46-
47-
const isPermissionsLoading =
48-
type === 'nodebalancer'
49-
? isNodebalancerPermissionsLoading
50-
: isLinodePermissionsLoading;
33+
type === 'nodebalancer' ? !isNodebalancerUpdatable : !isLinodeUpdatable;
5134

5235
return (
5336
<InlineMenuAction
@@ -59,7 +42,9 @@ export const FirewallDeviceActionMenu = React.memo(
5942
tooltip={
6043
disabledDueToPermissions
6144
? `You do not have permission to modify this ${type === 'nodebalancer' ? 'NodeBalancer' : 'Linode'}.`
62-
: undefined
45+
: disabled
46+
? 'You do not have permission to remove the device from this firewall.'
47+
: undefined
6348
}
6449
/>
6550
);

packages/manager/src/features/Firewalls/FirewallDetail/Devices/FirewallDeviceRow.test.tsx

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,32 @@ import userEvent from '@testing-library/user-event';
22
import * as React from 'react';
33

44
import { accountFactory, firewallDeviceFactory } from 'src/factories';
5-
import { http, HttpResponse, server } from 'src/mocks/testServer';
65
import { renderWithTheme } from 'src/utilities/testHelpers';
76

87
import { FirewallDeviceRow } from './FirewallDeviceRow';
98

109
import type { FirewallDeviceEntityType } from '@linode/api-v4';
1110

1211
const queryMocks = vi.hoisted(() => ({
13-
userPermissions: vi.fn(() => ({
14-
data: {
15-
update_linode: true,
16-
},
17-
})),
12+
useAccount: vi.fn().mockReturnValue({}),
1813
}));
1914

20-
vi.mock('src/features/IAM/hooks/usePermissions', () => ({
21-
usePermissions: queryMocks.userPermissions,
22-
}));
15+
vi.mock('@linode/queries', async () => {
16+
const actual = await vi.importActual('@linode/queries');
17+
return {
18+
...actual,
19+
useAccount: queryMocks.useAccount,
20+
};
21+
});
2322

2423
const props = {
2524
device: firewallDeviceFactory.build(),
2625
disabled: false,
2726
handleRemoveDevice: vi.fn(),
2827
isLinodeRelatedDevice: true,
28+
isLinodeUpdatable: true,
29+
isNodebalancerUpdatable: true,
30+
isPermissionsLoading: false,
2931
};
3032

3133
const INTERFACE_TEXT = 'Configuration Profile Interface';
@@ -36,11 +38,11 @@ describe('FirewallDeviceRow', () => {
3638
capabilities: ['Linode Interfaces'],
3739
});
3840

39-
server.use(
40-
http.get('*/v4*/account', () => {
41-
return HttpResponse.json(account);
42-
})
43-
);
41+
queryMocks.useAccount.mockReturnValue({
42+
data: account,
43+
isLoading: false,
44+
error: null,
45+
});
4446

4547
const { getAllByRole, getByText, findByText } = renderWithTheme(
4648
<FirewallDeviceRow {...props} />,

packages/manager/src/features/Firewalls/FirewallDetail/Devices/FirewallDeviceTable.tsx

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { TableContentWrapper } from 'src/components/TableContentWrapper/TableCon
99
import { TableHead } from 'src/components/TableHead';
1010
import { TableRow } from 'src/components/TableRow';
1111
import { TableSortCell } from 'src/components/TableSortCell';
12+
import { useGetAllUserEntitiesByPermission } from 'src/features/IAM/hooks/useGetAllUserEntitiesByPermission';
1213
import { useOrderV2 } from 'src/hooks/useOrderV2';
1314
import { usePaginationV2 } from 'src/hooks/usePaginationV2';
1415
import { getAPIErrorOrDefault } from 'src/utilities/errorUtils';
@@ -47,6 +48,26 @@ export const FirewallDeviceTable = React.memo(
4748
isLoading,
4849
} = useAllFirewallDevicesQuery(firewallId);
4950

51+
const {
52+
data: updatableLinodes = [],
53+
isLoading: isLinodePermissionsLoading,
54+
error: linodePermissionsError,
55+
} = useGetAllUserEntitiesByPermission({
56+
entityType: 'linode',
57+
permission: 'update_linode',
58+
enabled: type === 'linode',
59+
});
60+
61+
const {
62+
data: updatableNodebalancers = [],
63+
isLoading: isNodebalancerPermissionsLoading,
64+
error: nodebalancerPermissionsError,
65+
} = useGetAllUserEntitiesByPermission({
66+
entityType: 'nodebalancer',
67+
permission: 'update_nodebalancer',
68+
enabled: type === 'nodebalancer',
69+
});
70+
5071
const devices =
5172
allDevices?.filter((device) =>
5273
type === 'linode' && isLinodeInterfacesEnabled
@@ -82,12 +103,20 @@ export const FirewallDeviceTable = React.memo(
82103

83104
const isLinodeRelatedDevice = type === 'linode';
84105

106+
const permissionsError =
107+
linodePermissionsError || nodebalancerPermissionsError;
108+
85109
const _error = error
86110
? getAPIErrorOrDefault(
87111
error,
88112
`Unable to retrieve ${formattedTypes[deviceType]}s`
89113
)
90-
: undefined;
114+
: permissionsError
115+
? getAPIErrorOrDefault(
116+
permissionsError,
117+
`Unable to retrieve ${formattedTypes[deviceType]}s`
118+
)
119+
: undefined;
91120

92121
const ariaLabel = `List of ${formattedTypes[deviceType]}s attached to this firewall`;
93122

@@ -152,6 +181,16 @@ export const FirewallDeviceTable = React.memo(
152181
disabled={disabled}
153182
handleRemoveDevice={handleRemoveDevice}
154183
isLinodeRelatedDevice={isLinodeRelatedDevice}
184+
isLinodeUpdatable={updatableLinodes?.some(
185+
(linode) => linode.id === thisDevice.entity.id
186+
)}
187+
isNodebalancerUpdatable={updatableNodebalancers?.some(
188+
(nodebalancer) => nodebalancer.id === thisDevice.entity.id
189+
)}
190+
isPermissionsLoading={
191+
isLinodePermissionsLoading ||
192+
isNodebalancerPermissionsLoading
193+
}
155194
key={`device-row-${thisDevice.id}`}
156195
/>
157196
))}

0 commit comments

Comments
 (0)