diff --git a/src/js/IAMClient.ts b/src/js/IAMClient.ts
index 5037c08b7..6f7c213bd 100644
--- a/src/js/IAMClient.ts
+++ b/src/js/IAMClient.ts
@@ -189,6 +189,14 @@ export default class IAMClient implements IAMClientInterface {
.promise();
}
+ getRole(roleName: string) {
+ return notFalsyTypeGuard(this.client)
+ .getRole({
+ RoleName: roleName,
+ })
+ .promise();
+ }
+
listOwnAccessKeys() {
return notFalsyTypeGuard(this.client).listAccessKeys().promise();
}
diff --git a/src/react/DataServiceRoleProvider.tsx b/src/react/DataServiceRoleProvider.tsx
index b31ec9256..143d3742c 100644
--- a/src/react/DataServiceRoleProvider.tsx
+++ b/src/react/DataServiceRoleProvider.tsx
@@ -97,6 +97,7 @@ export const useCurrentAccount = () => {
const DataServiceRoleProvider = ({
children,
+ inlineLoader = false,
/**
* DoNotChangePropsWithRedux is a static props.
* When set, it must not be changed, otherwise it will break the hook rules.
@@ -105,6 +106,7 @@ const DataServiceRoleProvider = ({
DoNotChangePropsWithRedux = true,
}: {
children: JSX.Element;
+ inlineLoader?: boolean;
DoNotChangePropsWithRedux?: boolean;
}) => {
const [role, setRoleState] = useState<{ roleArn: string }>({
@@ -181,7 +183,7 @@ const DataServiceRoleProvider = ({
if (role.roleArn && !assumedRole) {
//@ts-expect-error fix this when you are working on it
- return Loading...;
+ return inlineLoader ?
loading...
: Loading...;
}
if (DoNotChangePropsWithRedux) {
diff --git a/src/react/ui-elements/SelectAccountIAMRole.tsx b/src/react/ui-elements/SelectAccountIAMRole.tsx
index d813b750c..7d1ac58f1 100644
--- a/src/react/ui-elements/SelectAccountIAMRole.tsx
+++ b/src/react/ui-elements/SelectAccountIAMRole.tsx
@@ -1,9 +1,9 @@
-import { Stack } from '@scality/core-ui';
+import { Form, FormGroup, FormSection, Stack } from '@scality/core-ui';
import { Select } from '@scality/core-ui/dist/next';
import { IAM } from 'aws-sdk';
import { Bucket } from 'aws-sdk/clients/s3';
import { PropsWithChildren, useState } from 'react';
-import { useQuery, useQueryClient } from 'react-query';
+import { useMutation, useQuery, useQueryClient } from 'react-query';
import { MemoryRouter, Route, useHistory, useParams } from 'react-router-dom';
import DataServiceRoleProvider, {
useAssumedRole,
@@ -20,7 +20,7 @@ import {
} from '../next-architecture/ui/AccessibleAccountsAdapterProvider';
import { AccountsLocationsEndpointsAdapterProvider } from '../next-architecture/ui/AccountsLocationsEndpointsAdapterProvider';
import { getListRolesQuery } from '../queries';
-import { regexArn } from '../utils/hooks';
+import { SCALITY_INTERNAL_ROLES, regexArn } from '../utils/hooks';
class NoOpMetricsAdapter implements IMetricsAdapter {
async listBucketsLatestUsedCapacity(
@@ -128,7 +128,7 @@ const InternalProvider = ({
]}
>
-
+
void;
+ onChange: (
+ account: Account,
+ role: IAM.Role,
+ keycloakRoleName: string,
+ ) => void;
defaultValue?: { accountName: string; roleName: string };
hideAccountRoles?: { accountName: string; roleName: string }[];
+ menuPosition?: 'absolute' | 'fixed';
+ identityProviderUrl?: string;
+ filterOutInternalRoles?: boolean;
};
type SelectAccountIAMRoleWithAccountProps = SelectAccountIAMRoleProps & {
@@ -169,6 +176,12 @@ const SelectAccountIAMRoleWithAccount = (
const [role, setRole] = useState(null);
const assumedRole = useAssumedRole();
+ const getIAMRoleMutation = useMutation({
+ mutationFn: (roleName: string) => {
+ return IAMClient.getRole(roleName);
+ },
+ });
+
const accountName = account ? account.name : '';
const rolesQuery = getListRolesQuery(accountName, IAMClient);
const queryClient = useQueryClient();
@@ -197,66 +210,133 @@ const SelectAccountIAMRoleWithAccount = (
};
const roleQueryData = useQuery(listRolesQuery);
- const roles = filterRoles(
+ const allRolesExceptHiddenOnes = filterRoles(
accountName,
roleQueryData?.data?.Roles ?? [],
hideAccountRoles,
);
+ const roles = props.filterOutInternalRoles
+ ? allRolesExceptHiddenOnes.filter((role) => {
+ return (
+ SCALITY_INTERNAL_ROLES.includes(role.RoleName) ||
+ !role.Arn.includes('role/scality-internal')
+ );
+ })
+ : allRolesExceptHiddenOnes;
const isDefaultAccountSelected = account?.name === defaultValue?.accountName;
const defaultRole = isDefaultAccountSelected ? defaultValue?.roleName : null;
return (
-
-
+ setAccount(selectedAccount);
+ setRole(null);
+ queryClient.invalidateQueries(rolesQuery.queryKey);
+ }}
+ menuPosition={props.menuPosition}
+ placeholder="Select Account"
+ >
+ {accounts.map((account) => (
+
+ {account.name}
+
+ ))}
+
+ }
+ />
- {roles.length > 0 ? (
-
- ) : null}
-
+ content={
+ roles.length > 0 ? (
+
+ ) : (
+
+ )
+ }
+ />
+
+
);
};
@@ -282,6 +362,9 @@ export const _SelectAccountIAMRole = (props: SelectAccountIAMRoleProps) => {
defaultValue={defaultValue}
hideAccountRoles={hideAccountRoles}
onChange={onChange}
+ menuPosition={props.menuPosition}
+ filterOutInternalRoles={props.filterOutInternalRoles}
+ identityProviderUrl={props.identityProviderUrl}
/>
);
} else {
diff --git a/src/react/ui-elements/__tests__/SelectAccountIAMRole.test.tsx b/src/react/ui-elements/__tests__/SelectAccountIAMRole.test.tsx
index cb11ae4b9..5873f1b6e 100644
--- a/src/react/ui-elements/__tests__/SelectAccountIAMRole.test.tsx
+++ b/src/react/ui-elements/__tests__/SelectAccountIAMRole.test.tsx
@@ -209,6 +209,28 @@ const genFn = (getPayloadFn: jest.Mock) => {
}),
);
}
+
+ if (params.get('Action') === 'GetRole') {
+ return res(
+ ctx.xml(`
+
+
+
+ %7B%22Statement%22%3A%5B%7B%22Action%22%3A%22sts%3AAssumeRoleWithWebIdentity%22%2C%22Condition%22%3A%7B%22StringEquals%22%3A%7B%22keycloak%3Aroles%22%3A%2211112%3A%3ADataConsumer%22%7D%7D%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Federated%22%3A%22https%3A%2F%2Fui.pod-choco.local%2Fauth%2Frealms%2Fartesca%22%7D%7D%2C%7B%22Action%22%3A%22sts%3AAssumeRoleWithWebIdentity%22%2C%22Condition%22%3A%7B%22StringEquals%22%3A%7B%22keycloak%3Aroles%22%3A%2211112%3A%3ADataConsumer%22%7D%7D%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Federated%22%3A%22https%3A%2F%2F13.48.197.10%3A8443%2Fauth%2Frealms%2Fartesca%22%7D%7D%5D%2C%22Version%22%3A%222012-10-17%22%7D
+ Has S3 read and write accesses to 11112 S3 Buckets. Cannot create or delete S3 Buckets.
+ /scality-internal/
+ data-consumer-role
+ ZX41BHX0Z4JYLB711HLUKWL2BBVM5DFZ
+ arn:aws:iam::087998292579:role/scality-internal/data-consumer-role
+ 2024-05-06T14:09:38Z
+
+
+
+ 6273e485a54abdb41527
+
+ `),
+ );
+ }
});
};
@@ -307,7 +329,7 @@ describe('SelectAccountIAMRole', () => {
RoleName: 'backbeat-gc-1',
Tags: [],
};
- expect(onChange).toHaveBeenCalledWith(account, role);
+ expect(onChange).toHaveBeenCalledWith(account, role, '11112::DataConsumer');
});
it('test the change of account and role', async () => {
@@ -370,6 +392,7 @@ describe('SelectAccountIAMRole', () => {
RoleName: 'backbeat-gc-1',
Tags: [],
},
+ '11112::DataConsumer',
);
await userEvent.click(seletors.accountSelect());
@@ -533,8 +556,8 @@ describe('SelectAccountIAMRole', () => {
RoleName: 'yanjin-custom-role',
Tags: [],
},
+ '11112::DataConsumer',
);
- debug();
});
it('renders with default value', async () => {
@@ -615,4 +638,33 @@ describe('SelectAccountIAMRole', () => {
expect(screen.getByText(/no options/i)).toBeInTheDocument();
});
+
+ it('renders with hidden internal roles', async () => {
+ const getPayloadFn = jest.fn();
+ server.use(genFn(getPayloadFn));
+ const onChange = jest.fn();
+ render(
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(seletors.accountSelect()).toBeInTheDocument();
+ });
+
+ await userEvent.click(seletors.accountSelect());
+
+ await userEvent.click(seletors.selectOption(/no-bucket/i));
+
+ await waitFor(() => {
+ expect(seletors.roleSelect()).toBeInTheDocument();
+ });
+
+ await userEvent.click(seletors.roleSelect());
+
+ expect(
+ screen.queryAllByRole('option', { name: /backbeat-gc-1/i }),
+ ).toHaveLength(0);
+ });
});
diff --git a/src/react/utils/hooks.ts b/src/react/utils/hooks.ts
index 4aa24f0b5..588caed7a 100644
--- a/src/react/utils/hooks.ts
+++ b/src/react/utils/hooks.ts
@@ -154,10 +154,12 @@ export const regexArn =
export const STORAGE_MANAGER_ROLE = 'storage-manager-role';
export const STORAGE_ACCOUNT_OWNER_ROLE = 'storage-account-owner-role';
const DATA_CONSUMER_ROLE = 'data-consumer-role';
+const DATA_ACCESSOR_ROLE = 'data-accessor-role';
export const SCALITY_INTERNAL_ROLES = [
STORAGE_MANAGER_ROLE,
STORAGE_ACCOUNT_OWNER_ROLE,
DATA_CONSUMER_ROLE,
+ DATA_ACCESSOR_ROLE,
];
const reduxBasedEventDispatcher = () => {