Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

filter high level groups and action groups by cluster and index #1482

Merged
merged 11 commits into from
Jul 11, 2023
23 changes: 18 additions & 5 deletions public/apps/configuration/panels/role-edit/role-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import {
} from './tenant-panel';
import { RoleIndexPermissionStateClass, RoleTenantPermissionStateClass } from './types';
import { buildHashUrl, buildUrl } from '../../utils/url-builder';
import { ComboBoxOptions, ResourceType, Action } from '../../types';
import { ComboBoxOptions, ResourceType, Action, ActionGroupItem } from '../../types';
import {
useToastState,
createUnknownErrorToast,
Expand Down Expand Up @@ -106,12 +106,12 @@ export function RoleEdit(props: RoleEditDeps) {
}
}, [addToast, props.action, props.coreStart.http, props.sourceRoleName]);

const [actionGroups, setActionGroups] = useState<string[]>([]);
const [actionGroups, setActionGroups] = useState<Array<[string, ActionGroupItem]>>([]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for my understanding, why are we needing this ActionGroupItem, or can we use the original Action here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If i'm understanding your question correctly I need the whole item here in order to tell whether it is a cluster/index one later on in order to filter them accordingly

React.useEffect(() => {
const fetchActionGroupNames = async () => {
try {
const actionGroupsObject = await fetchActionGroups(props.coreStart.http);
setActionGroups(Object.keys(actionGroupsObject));
setActionGroups(Object.entries(actionGroupsObject));
} catch (e) {
addToast(createUnknownErrorToast('actionGroup', 'load data'));
console.error(e);
Expand Down Expand Up @@ -167,12 +167,25 @@ export function RoleEdit(props: RoleEditDeps) {
const clusterWisePermissionOptions = [
{
label: 'Permission groups',
options: actionGroups.map(stringToComboBoxOption),
options: actionGroups
.filter((actionGroup) => actionGroup[1].type === 'cluster')
.map((actionGroup) => actionGroup[0])
.map(stringToComboBoxOption),
},
{
label: 'Cluster permissions',
options: CLUSTER_PERMISSIONS.map(stringToComboBoxOption),
},
];

const indexWisePermissionOptions = [
{
label: 'Permission groups',
options: actionGroups
.filter((actionGroup) => actionGroup[1].type === 'index')
.map((actionGroup) => actionGroup[0])
.map(stringToComboBoxOption),
},
{
label: 'Index permissions',
options: INDEX_PERMISSIONS.map(stringToComboBoxOption),
Expand Down Expand Up @@ -219,7 +232,7 @@ export function RoleEdit(props: RoleEditDeps) {
<IndexPermissionPanel
state={roleIndexPermission}
setState={setRoleIndexPermission}
optionUniverse={clusterWisePermissionOptions}
optionUniverse={indexWisePermissionOptions}
/>
<EuiSpacer size="m" />
<TenantPanel
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright OpenSearch Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

import React from 'react';
import { ClusterPermissionPanel } from '../cluster-permission-panel';
import { RoleEdit } from '../role-edit';
import { ActionGroupItem } from '../../../types';
import { fetchActionGroups } from '../../../utils/action-groups-utils';

import { render, waitFor } from '@testing-library/react';

import { act } from 'react-dom/test-utils';
import { IndexPermissionPanel } from '../index-permission-panel';
import { CLUSTER_PERMISSIONS, INDEX_PERMISSIONS } from '../../../constants';

jest.mock('../../../utils/role-detail-utils', () => ({
getRoleDetail: jest.fn().mockReturnValue({
cluster_permissions: [],
index_permissions: [],
tenant_permissions: [],
reserved: false,
}),
updateRole: jest.fn(),
}));
jest.mock('../../../utils/action-groups-utils');

jest.mock('../cluster-permission-panel', () => ({
ClusterPermissionPanel: jest.fn(() => null) as jest.Mock,
}));

jest.mock('../index-permission-panel', () => ({
IndexPermissionPanel: jest.fn(() => null) as jest.Mock,
}));

describe('Role edit filtering', () => {
const sampleSourceRole = 'role';
const mockCoreStart = {
http: 1,
};

(fetchActionGroups as jest.Mock).mockResolvedValue({
data_access: {
reserved: true,
hidden: false,
allowed_actions: ['indices:data/*', 'crud'],
type: 'index',
description: 'Allow all read/write operations on data',
static: true,
},
cluster_manage_pipelines: {
reserved: true,
hidden: false,
allowed_actions: ['cluster:admin/ingest/pipeline/*'],
type: 'cluster',
description: 'Manage pipelines',
static: true,
},
});

it('basic cluster permission panel rendering', async () => {
const action = 'create';
const buildBreadcrumbs = jest.fn();

render(
<RoleEdit
action={action}
sourceRoleName={sampleSourceRole}
buildBreadcrumbs={buildBreadcrumbs}
coreStart={mockCoreStart as any}
navigation={{} as any}
params={{} as any}
config={{} as any}
/>
);

await act(async () => {
await waitFor(() => {
expect(ClusterPermissionPanel).toHaveBeenCalled();
});
});

const lastCallArgs =
ClusterPermissionPanel.mock.calls[ClusterPermissionPanel.mock.calls.length - 1];
const [props] = lastCallArgs;

// Cluster Permission Panel props is filtered to action groups with type cluster, and only the cluster permission constants
expect(props.optionUniverse).toEqual([
{
label: 'Permission groups',
options: [
{
label: 'cluster_manage_pipelines',
},
],
},
{
label: 'Cluster permissions',
options: CLUSTER_PERMISSIONS.map((x) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much more robust and less lines of code :)

return { label: x };
}),
},
]);
});

it('basic index permission panel rendering', async () => {
const action = 'create';
const buildBreadcrumbs = jest.fn();

render(
<RoleEdit
action={action}
sourceRoleName={sampleSourceRole}
buildBreadcrumbs={buildBreadcrumbs}
coreStart={mockCoreStart as any}
navigation={{} as any}
params={{} as any}
config={{} as any}
/>
);

await act(async () => {
await waitFor(() => {
expect(IndexPermissionPanel).toHaveBeenCalled();
});
});

const lastCallArgs =
IndexPermissionPanel.mock.calls[IndexPermissionPanel.mock.calls.length - 1];
const [props] = lastCallArgs;

// Index Permission Panel props is filtered to action groups with type index, and only the index permission constants
expect(props.optionUniverse).toEqual([
{
label: 'Permission groups',
options: [
{
label: 'data_access',
},
],
},
{
label: 'Index permissions',
options: INDEX_PERMISSIONS.map((x) => {
return { label: x };
}),
},
]);
});
});
Loading