Skip to content

Commit e01fac6

Browse files
upcoming: [UIE-9597] - IAM Parent/Child: Align proxy logic with delegate users (linode#13336)
* save progress * save progress * fix storage conventions * e2e fixes * SwitchAccount fix * last e2e * Added changeset: IAM Parent/Child: Align proxy logic with delegate users * post rebase fixes
1 parent fe206af commit e01fac6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+349
-238
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Upcoming Features
3+
---
4+
5+
IAM Parent/Child: Align proxy logic with delegate users ([#13336](https://github.com/linode/manager/pull/13336))

packages/manager/cypress/e2e/core/account/account-cancellation.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ import {
2929
import { accountFactory } from 'src/factories/account';
3030
import {
3131
CHILD_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT,
32+
DELEGATE_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT,
3233
PARENT_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT,
33-
PROXY_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT,
3434
} from 'src/features/Account/constants';
3535

3636
import type { CancelAccount } from '@linode/api-v4';
@@ -302,7 +302,7 @@ describe('Parent/Child account cancellation', () => {
302302
.trigger('mouseover');
303303
// Click the button first, then confirm the tooltip is shown.
304304
ui.tooltip
305-
.findByText(PROXY_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT)
305+
.findByText(DELEGATE_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT)
306306
.should('be.visible');
307307
});
308308
});

packages/manager/cypress/e2e/core/account/personal-access-tokens.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { ui } from 'support/ui';
1515
import { randomLabel, randomString } from 'support/util/random';
1616

1717
import { appTokenFactory } from 'src/factories/oauth';
18-
import { PROXY_USER_RESTRICTED_TOOLTIP_TEXT } from 'src/features/Account/constants';
18+
import { DELEGATE_USER_RESTRICTED_TOOLTIP_TEXT } from 'src/features/Account/constants';
1919

2020
import type { Token } from '@linode/api-v4';
2121

@@ -370,7 +370,7 @@ describe('Personal access tokens', () => {
370370
});
371371

372372
ui.tooltip
373-
.findByText(PROXY_USER_RESTRICTED_TOOLTIP_TEXT)
373+
.findByText(DELEGATE_USER_RESTRICTED_TOOLTIP_TEXT)
374374
.should('be.visible');
375375

376376
// Confirm that token has not been renamed, initiate revocation.
@@ -405,7 +405,7 @@ describe('Personal access tokens', () => {
405405
.click();
406406

407407
ui.tooltip
408-
.findByText(PROXY_USER_RESTRICTED_TOOLTIP_TEXT)
408+
.findByText(DELEGATE_USER_RESTRICTED_TOOLTIP_TEXT)
409409
.should('be.visible');
410410

411411
// Confirm that token is removed from list after revoking.

packages/manager/src/components/AvatarForProxy.tsx renamed to packages/manager/src/components/AvatarForDelegateUser.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import { Box } from '@linode/ui';
22
import { styled } from '@mui/material/styles';
33
import * as React from 'react';
44

5-
import ProxyUserIcon from 'src/assets/icons/parent-child.svg';
5+
import DelegateUserIcon from 'src/assets/icons/parent-child.svg';
66

77
interface Props {
88
height?: number;
99
width?: number;
1010
}
1111

12-
export const AvatarForProxy = ({ height = 34, width = 34 }: Props) => {
12+
export const AvatarForDelegateUser = ({ height = 34, width = 34 }: Props) => {
1313
return (
1414
<Box
1515
sx={(theme) => ({
@@ -32,14 +32,14 @@ export const AvatarForProxy = ({ height = 34, width = 34 }: Props) => {
3232
width: `calc(${width}px - 6px)`,
3333
})}
3434
>
35-
<StyledProxyUserIcon />
35+
<StyledDelegateUserIcon />
3636
</Box>
3737
</Box>
3838
);
3939
};
4040

41-
const StyledProxyUserIcon = styled(ProxyUserIcon, {
42-
label: 'styledProxyUserIcon',
41+
const StyledDelegateUserIcon = styled(DelegateUserIcon, {
42+
label: 'styledDelegateUserIcon',
4343
})(({ theme }) => ({
4444
bottom: 0,
4545
left: 0,

packages/manager/src/dev-tools/components/ExtraPresetProfileAndGrants.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ export const ExtraPresetProfileAndGrants = ({
248248
<option value="child">Child</option>
249249
<option value="parent">Parent</option>
250250
<option value="proxy">Proxy</option>
251+
<option value="delegate">Delegate</option>
251252
<option value="default">Default</option>
252253
</select>
253254
</label>

packages/manager/src/features/Account/AccountLanding.tsx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useAccount, useProfile } from '@linode/queries';
1+
import { useAccount } from '@linode/queries';
22
import {
33
Outlet,
44
useLocation,
@@ -23,6 +23,7 @@ import { useTabs } from 'src/hooks/useTabs';
2323
import { sendSwitchAccountEvent } from 'src/utilities/analytics/customEventAnalytics';
2424

2525
import { PlatformMaintenanceBanner } from '../../components/PlatformMaintenanceBanner/PlatformMaintenanceBanner';
26+
import { useDelegationRole } from '../IAM/hooks/useDelegationRole';
2627
import { useIsIAMDelegationEnabled } from '../IAM/hooks/useIsIAMEnabled';
2728
import { usePermissions } from '../IAM/hooks/usePermissions';
2829
import { SwitchAccountButton } from './SwitchAccountButton';
@@ -37,7 +38,12 @@ export const AccountLanding = () => {
3738
strict: false,
3839
});
3940
const { data: account } = useAccount();
40-
const { data: profile } = useProfile();
41+
const {
42+
isProxyOrDelegateUserType,
43+
isChildUserType,
44+
isParentUserType,
45+
profileUserType,
46+
} = useDelegationRole();
4147
const { limitsEvolution } = useFlags();
4248

4349
const { data: permissions } = usePermissions('account', [
@@ -48,21 +54,20 @@ export const AccountLanding = () => {
4854
const sessionContext = React.useContext(switchAccountSessionContext);
4955

5056
const isAkamaiAccount = account?.billing_source === 'akamai';
51-
const isProxyUser = profile?.user_type === 'proxy';
52-
const isChildUser = profile?.user_type === 'child';
53-
const isParentUser = profile?.user_type === 'parent';
5457

5558
const showQuotasTab = limitsEvolution?.enabled ?? false;
5659

57-
const isReadOnly = !permissions.make_billing_payment || isChildUser;
60+
const isReadOnly = !permissions.make_billing_payment || isChildUserType;
5861

5962
const isChildAccountAccessRestricted = useRestrictedGlobalGrantCheck({
6063
globalGrantType: 'child_account_access',
6164
});
6265

6366
const { isIAMDelegationEnabled } = useIsIAMDelegationEnabled();
6467

65-
const { isParentTokenExpired } = useIsParentTokenExpired({ isProxyUser });
68+
const { isParentTokenExpired } = useIsParentTokenExpired({
69+
isProxyOrDelegateUserType,
70+
});
6671

6772
const { tabs, handleTabChange, tabIndex, getTabIndex } = useTabs([
6873
{
@@ -124,8 +129,9 @@ export const AccountLanding = () => {
124129

125130
const isBillingTabSelected = getTabIndex('/account/billing') === tabIndex;
126131
const canSwitchBetweenParentOrProxyAccount = isIAMDelegationEnabled
127-
? isParentUser
128-
: (!isChildAccountAccessRestricted && isParentUser) || isProxyUser;
132+
? isParentUserType
133+
: (!isChildAccountAccessRestricted && isParentUserType) ||
134+
isProxyOrDelegateUserType;
129135

130136
const landingHeaderProps: LandingHeaderProps = {
131137
breadcrumbProps: {
@@ -134,7 +140,7 @@ export const AccountLanding = () => {
134140
buttonDataAttrs: {
135141
disabled: isReadOnly,
136142
tooltipText: getRestrictedResourceText({
137-
isChildUser,
143+
isChildUserType,
138144
resourceType: 'Account',
139145
}),
140146
},
@@ -181,7 +187,7 @@ export const AccountLanding = () => {
181187
<SwitchAccountDrawer
182188
onClose={() => setIsDrawerOpen(false)}
183189
open={isDrawerOpen}
184-
userType={profile?.user_type}
190+
userType={profileUserType}
185191
/>
186192
</React.Fragment>
187193
);

packages/manager/src/features/Account/AccountLogins.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useAccountLoginsQuery, useProfile } from '@linode/queries';
1+
import { useAccountLoginsQuery } from '@linode/queries';
22
import { Notice, Typography } from '@linode/ui';
33
import { Hidden } from '@linode/ui';
44
import * as React from 'react';
@@ -18,6 +18,7 @@ import { TableSortCell } from 'src/components/TableSortCell';
1818
import { useOrderV2 } from 'src/hooks/useOrderV2';
1919
import { usePaginationV2 } from 'src/hooks/usePaginationV2';
2020

21+
import { useDelegationRole } from '../IAM/hooks/useDelegationRole';
2122
import { usePermissions } from '../IAM/hooks/usePermissions';
2223
import AccountLoginsTableRow from './AccountLoginsTableRow';
2324
import { getRestrictedResourceText } from './utils';
@@ -74,8 +75,7 @@ const AccountLogins = () => {
7475
},
7576
filter
7677
);
77-
const { data: profile } = useProfile();
78-
const isChildUser = profile?.user_type === 'child';
78+
const { isChildUserType } = useDelegationRole();
7979
const canViewAccountLogins = permissions.list_account_logins;
8080

8181
const renderTableContent = () => {
@@ -172,7 +172,7 @@ const AccountLogins = () => {
172172
) : (
173173
<Notice
174174
text={getRestrictedResourceText({
175-
isChildUser,
175+
isChildUserType,
176176
resourceType: 'Account',
177177
})}
178178
variant="warning"

packages/manager/src/features/Account/CloseAccountSetting.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { renderWithTheme } from 'src/utilities/testHelpers';
77
import CloseAccountSetting from './CloseAccountSetting';
88
import {
99
CHILD_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT,
10+
DELEGATE_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT,
1011
PARENT_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT,
11-
PROXY_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT,
1212
} from './constants';
1313

1414
// Mock the useProfile hook to immediately return the expected data, circumventing the HTTP request and loading state.
@@ -105,7 +105,7 @@ describe('Close Account Settings', () => {
105105
expect(getByRole('tooltip')).toBeInTheDocument();
106106
});
107107

108-
expect(getByText(PROXY_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT)).toBeVisible();
108+
expect(getByText(DELEGATE_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT)).toBeVisible();
109109
expect(button).toHaveAttribute('aria-describedby', 'button-tooltip');
110110
expect(button).not.toHaveAttribute('disabled');
111111
expect(button).toHaveAttribute('aria-disabled', 'true');

packages/manager/src/features/Account/CloseAccountSetting.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import { usePermissions } from '../IAM/hooks/usePermissions';
66
import CloseAccountDialog from './CloseAccountDialog';
77
import {
88
CHILD_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT,
9+
DELEGATE_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT,
910
PARENT_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT,
10-
PROXY_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT,
1111
} from './constants';
1212

1313
export const CloseAccountSetting = () => {
@@ -17,7 +17,7 @@ export const CloseAccountSetting = () => {
1717

1818
const { data: permissions } = usePermissions('account', ['cancel_account']);
1919

20-
// Disable the Close Account button for users with a Parent/Proxy/Child user type.
20+
// Disable the Close Account button for users with a Parent/Proxy/Delegate/Child user type.
2121
const isCloseAccountDisabled = Boolean(profile?.user_type !== 'default');
2222

2323
let closeAccountButtonTooltipText;
@@ -28,8 +28,11 @@ export const CloseAccountSetting = () => {
2828
case 'child':
2929
closeAccountButtonTooltipText = CHILD_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT;
3030
break;
31+
case 'delegate':
32+
closeAccountButtonTooltipText = DELEGATE_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT;
33+
break;
3134
case 'proxy':
32-
closeAccountButtonTooltipText = PROXY_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT;
35+
closeAccountButtonTooltipText = DELEGATE_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT;
3336
break;
3437
default:
3538
closeAccountButtonTooltipText = PARENT_USER_CLOSE_ACCOUNT_TOOLTIP_TEXT;

packages/manager/src/features/Account/SwitchAccountDrawer.tsx

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ export const SwitchAccountDrawer = (props: Props) => {
4040
>([]);
4141
const [searchQuery, setSearchQuery] = React.useState<string>('');
4242
const { isIAMDelegationEnabled } = useIsIAMDelegationEnabled();
43-
const isProxyUser = userType === 'proxy';
43+
const isParentUserType = userType === 'parent';
44+
const isProxyUserType = userType === 'proxy';
45+
const isDelegateUserType = userType === 'delegate';
46+
const isProxyOrDelegateUserType = isProxyUserType || isDelegateUserType;
4447
const currentParentTokenWithBearer =
4548
getStorage('authentication/parent_token/token') ?? '';
4649
const currentTokenWithBearer = storage.authentication.token.get() ?? '';
@@ -73,12 +76,11 @@ export const SwitchAccountDrawer = (props: Props) => {
7376
} = useChildAccountsInfiniteQuery(
7477
{
7578
filter,
76-
headers:
77-
userType === 'proxy'
78-
? {
79-
Authorization: currentTokenWithBearer,
80-
}
81-
: undefined,
79+
headers: isProxyOrDelegateUserType
80+
? {
81+
Authorization: currentTokenWithBearer,
82+
}
83+
: undefined,
8284
},
8385
isIAMDelegationEnabled === false
8486
);
@@ -90,7 +92,7 @@ export const SwitchAccountDrawer = (props: Props) => {
9092
refetch: refetchAllChildAccounts,
9193
} = useAllListMyDelegatedChildAccountsQuery({
9294
params: {},
93-
enabled: isIAMDelegationEnabled,
95+
enabled: isIAMDelegationEnabled && isParentUserType,
9496
});
9597

9698
const refetchFn = isIAMDelegationEnabled
@@ -105,10 +107,11 @@ export const SwitchAccountDrawer = (props: Props) => {
105107
onClose,
106108
userType,
107109
}: HandleSwitchToChildAccountProps) => {
108-
const isProxyUser = userType === 'proxy';
110+
const isProxyOrDelegateUserType =
111+
userType === 'proxy' || userType === 'delegate';
109112

110113
try {
111-
if (isProxyUser) {
114+
if (isProxyOrDelegateUserType) {
112115
// Revoke proxy token before switching accounts.
113116
await revokeToken().catch(() => {
114117
/* Allow user account switching; tokens will expire naturally. */
@@ -121,14 +124,18 @@ export const SwitchAccountDrawer = (props: Props) => {
121124
const proxyToken = await createToken(euuid);
122125

123126
setTokenInLocalStorage({
124-
prefix: 'authentication/proxy_token',
127+
prefix: isProxyUserType
128+
? 'authentication/proxy_token'
129+
: 'authentication/delegate_token',
125130
token: {
126131
...proxyToken,
127132
token: `Bearer ${proxyToken.token}`,
128133
},
129134
});
130135

131-
updateCurrentToken({ userType: 'proxy' });
136+
updateCurrentToken({
137+
userType: isProxyUserType ? 'proxy' : 'delegate',
138+
});
132139
onClose(event);
133140
location.reload();
134141
} catch (error) {
@@ -153,19 +160,30 @@ export const SwitchAccountDrawer = (props: Props) => {
153160
// Flag to prevent multiple clicks on the switch account link.
154161
setSubmitting(true);
155162

156-
// Revoke proxy token before switching to parent account.
163+
// Revoke proxy or delegate token before switching to parent account.
157164
await revokeToken().catch(() => {
158165
/* Allow user account switching; tokens will expire naturally. */
159166
});
160167

161168
updateCurrentToken({ userType: 'parent' });
162169

163-
// Reset flag for proxy user to display success toast once.
164-
setStorage('is_proxy_user', 'false');
170+
// Reset flag for proxy or delegate user to display success toast once.
171+
if (isProxyUserType) {
172+
setStorage('is_proxy_user_type', 'false');
173+
} else if (isDelegateUserType) {
174+
setStorage('is_delegate_user_type', 'false');
175+
}
165176

166177
onClose();
167178
location.reload();
168-
}, [onClose, revokeToken, validateParentToken, updateCurrentToken]);
179+
}, [
180+
onClose,
181+
revokeToken,
182+
validateParentToken,
183+
updateCurrentToken,
184+
isProxyUserType,
185+
isDelegateUserType,
186+
]);
169187

170188
const [isSwitchingChildAccounts, setIsSwitchingChildAccounts] =
171189
useState<boolean>(false);
@@ -203,7 +221,7 @@ export const SwitchAccountDrawer = (props: Props) => {
203221
})}
204222
>
205223
Select an account to view and manage its settings and configurations
206-
{isProxyUser && (
224+
{isProxyOrDelegateUserType && (
207225
<>
208226
{' or '}
209227
<LinkButton
@@ -256,7 +274,9 @@ export const SwitchAccountDrawer = (props: Props) => {
256274
<ChildAccountList
257275
childAccounts={childAccounts}
258276
currentTokenWithBearer={
259-
isProxyUser ? currentParentTokenWithBearer : currentTokenWithBearer
277+
isProxyOrDelegateUserType
278+
? currentParentTokenWithBearer
279+
: currentTokenWithBearer
260280
}
261281
errors={{
262282
childAccountInfiniteError,

0 commit comments

Comments
 (0)