fix(#12954): restrict roles and policy in settings for admins only (#13073)

* fix(#12954): restrict roles and policy in settings for admins only

* chore: address comments
This commit is contained in:
Sachin Chaurasiya 2023-09-05 15:19:24 +05:30 committed by GitHub
parent 011eaf8ad5
commit 80c11ab4f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 282 additions and 440 deletions

View File

@ -19,15 +19,16 @@ import { ROUTES } from '../../constants/constants';
import { useAuth } from '../../hooks/authHooks'; import { useAuth } from '../../hooks/authHooks';
interface AdminProtectedRouteProps extends RouteProps { interface AdminProtectedRouteProps extends RouteProps {
hasPermission: boolean; hasPermission?: boolean;
} }
const AdminProtectedRoute = (routeProps: AdminProtectedRouteProps) => { const AdminProtectedRoute = (routeProps: AdminProtectedRouteProps) => {
const { isAdminUser } = useAuth(); const { isAdminUser } = useAuth();
const hasPermission = Boolean(routeProps.hasPermission);
if (isAdminUser || routeProps.hasPermission) { if (isAdminUser || hasPermission) {
return <Route {...routeProps} />; return <Route {...routeProps} />;
} else if (!routeProps.hasPermission) { } else if (!hasPermission) {
return <ErrorPlaceHolder type={ERROR_PLACEHOLDER_TYPE.PERMISSION} />; return <ErrorPlaceHolder type={ERROR_PLACEHOLDER_TYPE.PERMISSION} />;
} else { } else {
return <Redirect to={ROUTES.SIGNIN} />; return <Redirect to={ROUTES.SIGNIN} />;

View File

@ -175,17 +175,13 @@ const GlobalSettingRouter = () => {
<AdminProtectedRoute <AdminProtectedRoute
exact exact
component={RolesListPage} component={RolesListPage}
hasPermission={userPermissions.hasViewPermissions(
ResourceEntity.ROLE,
permissions
)}
path={getSettingPath( path={getSettingPath(
GlobalSettingsMenuCategory.ACCESS, GlobalSettingsMenuCategory.ACCESS,
GlobalSettingOptions.ROLES GlobalSettingOptions.ROLES
)} )}
/> />
<Route <AdminProtectedRoute
exact exact
component={RolesDetailPage} component={RolesDetailPage}
path={getSettingPath( path={getSettingPath(
@ -201,16 +197,12 @@ const GlobalSettingRouter = () => {
<AdminProtectedRoute <AdminProtectedRoute
exact exact
component={PoliciesListPage} component={PoliciesListPage}
hasPermission={userPermissions.hasViewPermissions(
ResourceEntity.POLICY,
permissions
)}
path={getSettingPath( path={getSettingPath(
GlobalSettingsMenuCategory.ACCESS, GlobalSettingsMenuCategory.ACCESS,
GlobalSettingOptions.POLICIES GlobalSettingOptions.POLICIES
)} )}
/> />
<Route <AdminProtectedRoute
exact exact
component={PoliciesDetailPage} component={PoliciesDetailPage}
path={getSettingPath( path={getSettingPath(

View File

@ -57,20 +57,6 @@ jest.mock('components/Loader/Loader', () =>
jest.fn().mockReturnValue(<div>Loader</div>) jest.fn().mockReturnValue(<div>Loader</div>)
); );
jest.mock('components/PermissionProvider/PermissionProvider', () => ({
usePermissionProvider: jest.fn().mockReturnValue({
getEntityPermissionByFqn: jest.fn().mockReturnValue({
Create: true,
Delete: true,
ViewAll: true,
EditAll: true,
EditDescription: true,
EditDisplayName: true,
EditCustomFields: true,
}),
}),
}));
jest.mock('../../../constants/HelperTextUtil', () => ({ jest.mock('../../../constants/HelperTextUtil', () => ({
NO_PERMISSION_FOR_ACTION: '', NO_PERMISSION_FOR_ACTION: '',
NO_PERMISSION_TO_VIEW: '', NO_PERMISSION_TO_VIEW: '',
@ -80,18 +66,6 @@ jest.mock('../../../utils/CommonUtils', () => ({
getEntityName: jest.fn().mockReturnValue(''), getEntityName: jest.fn().mockReturnValue(''),
})); }));
jest.mock('../../../utils/PermissionsUtils', () => ({
DEFAULT_ENTITY_PERMISSION: {
Create: true,
Delete: true,
ViewAll: true,
EditAll: true,
EditDescription: true,
EditDisplayName: true,
EditCustomFields: true,
},
}));
jest.mock('../../../utils/RouterUtils', () => ({ jest.mock('../../../utils/RouterUtils', () => ({
getAddPolicyRulePath: jest.fn(), getAddPolicyRulePath: jest.fn(),
getEditPolicyRulePath: jest.fn(), getEditPolicyRulePath: jest.fn(),

View File

@ -22,7 +22,6 @@ import {
Row, Row,
Space, Space,
Tabs, Tabs,
Tooltip,
Typography, Typography,
} from 'antd'; } from 'antd';
import { ReactComponent as EditIcon } from 'assets/svg/edit-new.svg'; import { ReactComponent as EditIcon } from 'assets/svg/edit-new.svg';
@ -32,12 +31,6 @@ import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlac
import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer'; import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer';
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component'; import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
import Loader from 'components/Loader/Loader'; import Loader from 'components/Loader/Loader';
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
import {
OperationPermission,
ResourceEntity,
} from 'components/PermissionProvider/PermissionProvider.interface';
import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum';
import { compare } from 'fast-json-patch'; import { compare } from 'fast-json-patch';
import { isEmpty, isUndefined, startCase } from 'lodash'; import { isEmpty, isUndefined, startCase } from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
@ -59,7 +52,6 @@ import { EntityType } from '../../../enums/entity.enum';
import { Rule } from '../../../generated/api/policies/createPolicy'; import { Rule } from '../../../generated/api/policies/createPolicy';
import { Policy } from '../../../generated/entity/policies/policy'; import { Policy } from '../../../generated/entity/policies/policy';
import { EntityReference } from '../../../generated/type/entityReference'; import { EntityReference } from '../../../generated/type/entityReference';
import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils';
import { import {
getAddPolicyRulePath, getAddPolicyRulePath,
getEditPolicyRulePath, getEditPolicyRulePath,
@ -78,7 +70,6 @@ const PoliciesDetailPage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const history = useHistory(); const history = useHistory();
const { fqn } = useParams<{ fqn: string }>(); const { fqn } = useParams<{ fqn: string }>();
const { getEntityPermissionByFqn } = usePermissionProvider();
const [policy, setPolicy] = useState<Policy>({} as Policy); const [policy, setPolicy] = useState<Policy>({} as Policy);
const [isLoading, setLoading] = useState<boolean>(false); const [isLoading, setLoading] = useState<boolean>(false);
@ -87,10 +78,6 @@ const PoliciesDetailPage = () => {
const [selectedEntity, setEntity] = const [selectedEntity, setEntity] =
useState<{ attribute: Attribute; record: EntityReference }>(); useState<{ attribute: Attribute; record: EntityReference }>();
const [policyPermission, setPolicyPermission] = useState<OperationPermission>(
DEFAULT_ENTITY_PERMISSION
);
const policiesPath = getSettingPath( const policiesPath = getSettingPath(
GlobalSettingsMenuCategory.ACCESS, GlobalSettingsMenuCategory.ACCESS,
GlobalSettingOptions.POLICIES GlobalSettingOptions.POLICIES
@ -110,21 +97,6 @@ const PoliciesDetailPage = () => {
[policy] [policy]
); );
const fetchPolicyPermission = async () => {
setLoading(true);
try {
const response = await getEntityPermissionByFqn(
ResourceEntity.POLICY,
fqn
);
setPolicyPermission(response);
} catch (error) {
showErrorToast(error as AxiosError);
} finally {
setLoading(false);
}
};
const fetchPolicy = async () => { const fetchPolicy = async () => {
setLoading(true); setLoading(true);
try { try {
@ -256,7 +228,6 @@ const PoliciesDetailPage = () => {
(rule: Rule) => { (rule: Rule) => {
return ( return (
<Dropdown <Dropdown
disabled={!policyPermission.EditAll}
overlay={ overlay={
<Menu <Menu
items={[ items={[
@ -307,39 +278,25 @@ const PoliciesDetailPage = () => {
} }
placement="bottomRight" placement="bottomRight"
trigger={['click']}> trigger={['click']}>
<Tooltip <Button
title={ data-testid={`manage-button-${rule.name}`}
policyPermission.EditAll icon={<EllipsisOutlined className="text-grey-body" rotate={90} />}
? t('label.manage-rule') size="small"
: t('message.no-permission-for-action') type="text"
}> onClick={(e) => {
<Button e.stopPropagation();
data-testid={`manage-button-${rule.name}`} }}
disabled={!policyPermission.EditAll} />
icon={<EllipsisOutlined className="text-grey-body" rotate={90} />}
size="small"
type="text"
onClick={(e) => {
e.stopPropagation();
}}
/>
</Tooltip>
</Dropdown> </Dropdown>
); );
}, },
[policy, policyPermission] [policy]
); );
useEffect(() => { useEffect(() => {
fetchPolicyPermission(); fetchPolicy();
}, [fqn]); }, [fqn]);
useEffect(() => {
if (policyPermission.ViewAll || policyPermission.ViewBasic) {
fetchPolicy();
}
}, [policyPermission, fqn]);
if (isLoading) { if (isLoading) {
return <Loader />; return <Loader />;
} }
@ -347,198 +304,178 @@ const PoliciesDetailPage = () => {
return ( return (
<div data-testid="policy-details-container"> <div data-testid="policy-details-container">
<TitleBreadcrumb titleLinks={breadcrumb} /> <TitleBreadcrumb titleLinks={breadcrumb} />
{policyPermission.ViewAll || policyPermission.ViewBasic ? (
<>
{isEmpty(policy) ? (
<ErrorPlaceHolder>
<div className="text-center">
<p>
{t('message.no-entity-found-for-name', {
entity: t('label.policy-lowercase'),
name: fqn,
})}
</p>
<Button
size="small"
type="primary"
onClick={() => history.push(policiesPath)}>
{t('label.go-back')}
</Button>
</div>
</ErrorPlaceHolder>
) : (
<div className="policies-detail" data-testid="policy-details">
<Typography.Title
className="m-b-0 m-t-xs"
data-testid="heading"
level={5}>
{getEntityName(policy)}
</Typography.Title>
<Description
className="m-b-md"
description={policy.description || ''}
entityFqn={policy.fullyQualifiedName}
entityName={getEntityName(policy)}
entityType={EntityType.POLICY}
hasEditAccess={
policyPermission.EditAll || policyPermission.EditDescription
}
isEdit={editDescription}
onCancel={() => setEditDescription(false)}
onDescriptionEdit={() => setEditDescription(true)}
onDescriptionUpdate={handleDescriptionUpdate}
/>
<Tabs defaultActiveKey="rules"> <>
<TabPane key="rules" tab={t('label.rule-plural')}> {isEmpty(policy) ? (
{isEmpty(policy.rules) ? ( <ErrorPlaceHolder>
<ErrorPlaceHolder /> <div className="text-center">
) : ( <p>
<Space {t('message.no-entity-found-for-name', {
className="w-full tabpane-space" entity: t('label.policy-lowercase'),
direction="vertical"> name: fqn,
<Tooltip })}
title={ </p>
policyPermission.EditAll <Button
? t('label.add-entity', { size="small"
entity: t('label.rule'), type="primary"
}) onClick={() => history.push(policiesPath)}>
: t('message.no-permission-for-action') {t('label.go-back')}
}> </Button>
<Button
data-testid="add-rule"
disabled={!policyPermission.EditAll}
type="primary"
onClick={() =>
history.push(getAddPolicyRulePath(fqn))
}>
{t('label.add-entity', {
entity: t('label.rule'),
})}
</Button>
</Tooltip>
<Space className="w-full" direction="vertical" size={20}>
{policy.rules.map((rule) => (
<Card
data-testid="rule-card"
key={rule.name || 'rule'}>
<Space
align="baseline"
className="w-full justify-between p-b-lg"
direction="horizontal">
<Typography.Text
className="font-medium text-base text-grey-body"
data-testid="rule-name">
{rule.name}
</Typography.Text>
{getRuleActionElement(rule)}
</Space>
<Space
className="w-full"
direction="vertical"
size={12}>
{rule.description && (
<Row data-testid="description">
<Col span={2}>
<Typography.Text className="text-grey-muted">
{`${t('label.description')}:`}
</Typography.Text>
</Col>
<Col span={22}>
<RichTextEditorPreviewer
markdown={rule.description || ''}
/>
</Col>
</Row>
)}
<Row data-testid="resources">
<Col span={2}>
<Typography.Text className="text-grey-muted m-b-0">
{`${t('label.resource-plural')}:`}
</Typography.Text>
</Col>
<Col span={22}>
<Typography.Text className="text-grey-body">
{rule.resources
?.map((resource) => startCase(resource))
?.join(', ')}
</Typography.Text>
</Col>
</Row>
<Row data-testid="operations">
<Col span={2}>
<Typography.Text className="text-grey-muted">
{`${t('label.operation-plural')}:`}
</Typography.Text>
</Col>
<Col span={22}>
<Typography.Text className="text-grey-body">
{rule.operations?.join(', ')}
</Typography.Text>
</Col>
</Row>
<Row data-testid="effect">
<Col span={2}>
<Typography.Text className="text-grey-muted">
{`${t('label.effect')}:`}
</Typography.Text>
</Col>
<Col span={22}>
<Typography.Text className="text-grey-body">
{startCase(rule.effect)}
</Typography.Text>
</Col>
</Row>
{rule.condition && (
<Row data-testid="condition">
<Col span={2}>
<Typography.Text className="text-grey-muted">
{`${t('label.condition')}:`}
</Typography.Text>
</Col>
<Col span={22}>
<code>{rule.condition}</code>
</Col>
</Row>
)}
</Space>
</Card>
))}
</Space>
</Space>
)}
</TabPane>
<TabPane key="roles" tab={t('label.role-plural')}>
<PoliciesDetailsList
hasAccess={policyPermission.EditAll}
list={policy.roles ?? []}
type="role"
onDelete={(record) =>
setEntity({ record, attribute: 'roles' })
}
/>
</TabPane>
<TabPane key="teams" tab={t('label.team-plural')}>
<PoliciesDetailsList
hasAccess={policyPermission.EditAll}
list={policy.teams ?? []}
type="team"
onDelete={(record) =>
setEntity({ record, attribute: 'teams' })
}
/>
</TabPane>
</Tabs>
</div> </div>
)} </ErrorPlaceHolder>
</> ) : (
) : ( <div className="policies-detail" data-testid="policy-details">
<ErrorPlaceHolder type={ERROR_PLACEHOLDER_TYPE.PERMISSION} /> <Typography.Title
)} className="m-b-0 m-t-xs"
data-testid="heading"
level={5}>
{getEntityName(policy)}
</Typography.Title>
<Description
hasEditAccess
className="m-b-md"
description={policy.description || ''}
entityFqn={policy.fullyQualifiedName}
entityName={getEntityName(policy)}
entityType={EntityType.POLICY}
isEdit={editDescription}
onCancel={() => setEditDescription(false)}
onDescriptionEdit={() => setEditDescription(true)}
onDescriptionUpdate={handleDescriptionUpdate}
/>
<Tabs defaultActiveKey="rules">
<TabPane key="rules" tab={t('label.rule-plural')}>
{isEmpty(policy.rules) ? (
<ErrorPlaceHolder />
) : (
<Space className="w-full tabpane-space" direction="vertical">
<Button
data-testid="add-rule"
type="primary"
onClick={() => history.push(getAddPolicyRulePath(fqn))}>
{t('label.add-entity', {
entity: t('label.rule'),
})}
</Button>
<Space className="w-full" direction="vertical" size={20}>
{policy.rules.map((rule) => (
<Card data-testid="rule-card" key={rule.name || 'rule'}>
<Space
align="baseline"
className="w-full justify-between p-b-lg"
direction="horizontal">
<Typography.Text
className="font-medium text-base text-grey-body"
data-testid="rule-name">
{rule.name}
</Typography.Text>
{getRuleActionElement(rule)}
</Space>
<Space
className="w-full"
direction="vertical"
size={12}>
{rule.description && (
<Row data-testid="description">
<Col span={2}>
<Typography.Text className="text-grey-muted">
{`${t('label.description')}:`}
</Typography.Text>
</Col>
<Col span={22}>
<RichTextEditorPreviewer
markdown={rule.description || ''}
/>
</Col>
</Row>
)}
<Row data-testid="resources">
<Col span={2}>
<Typography.Text className="text-grey-muted m-b-0">
{`${t('label.resource-plural')}:`}
</Typography.Text>
</Col>
<Col span={22}>
<Typography.Text className="text-grey-body">
{rule.resources
?.map((resource) => startCase(resource))
?.join(', ')}
</Typography.Text>
</Col>
</Row>
<Row data-testid="operations">
<Col span={2}>
<Typography.Text className="text-grey-muted">
{`${t('label.operation-plural')}:`}
</Typography.Text>
</Col>
<Col span={22}>
<Typography.Text className="text-grey-body">
{rule.operations?.join(', ')}
</Typography.Text>
</Col>
</Row>
<Row data-testid="effect">
<Col span={2}>
<Typography.Text className="text-grey-muted">
{`${t('label.effect')}:`}
</Typography.Text>
</Col>
<Col span={22}>
<Typography.Text className="text-grey-body">
{startCase(rule.effect)}
</Typography.Text>
</Col>
</Row>
{rule.condition && (
<Row data-testid="condition">
<Col span={2}>
<Typography.Text className="text-grey-muted">
{`${t('label.condition')}:`}
</Typography.Text>
</Col>
<Col span={22}>
<code>{rule.condition}</code>
</Col>
</Row>
)}
</Space>
</Card>
))}
</Space>
</Space>
)}
</TabPane>
<TabPane key="roles" tab={t('label.role-plural')}>
<PoliciesDetailsList
hasAccess
list={policy.roles ?? []}
type="role"
onDelete={(record) =>
setEntity({ record, attribute: 'roles' })
}
/>
</TabPane>
<TabPane key="teams" tab={t('label.team-plural')}>
<PoliciesDetailsList
hasAccess
list={policy.teams ?? []}
type="team"
onDelete={(record) =>
setEntity({ record, attribute: 'teams' })
}
/>
</TabPane>
</Tabs>
</div>
)}
</>
{selectedEntity && ( {selectedEntity && (
<Modal <Modal
centered centered

View File

@ -60,20 +60,6 @@ jest.mock('../../../utils/RouterUtils', () => ({
getTeamsWithFqnPath: jest.fn(), getTeamsWithFqnPath: jest.fn(),
})); }));
jest.mock('components/PermissionProvider/PermissionProvider', () => ({
usePermissionProvider: jest.fn().mockReturnValue({
getEntityPermissionByFqn: jest.fn().mockReturnValue({
Create: true,
Delete: true,
ViewAll: true,
EditAll: true,
EditDescription: true,
EditDisplayName: true,
EditCustomFields: true,
}),
}),
}));
describe('Test Roles Details Page', () => { describe('Test Roles Details Page', () => {
it('Should render the detail component', async () => { it('Should render the detail component', async () => {
render(<RolesDetailPage />); render(<RolesDetailPage />);

View File

@ -11,17 +11,12 @@
* limitations under the License. * limitations under the License.
*/ */
import { Button, Modal, Space, Tabs, Tooltip, Typography } from 'antd'; import { Button, Modal, Space, Tabs, Typography } from 'antd';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import Description from 'components/common/description/Description'; import Description from 'components/common/description/Description';
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder'; import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component'; import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
import Loader from 'components/Loader/Loader'; import Loader from 'components/Loader/Loader';
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
import {
OperationPermission,
ResourceEntity,
} from 'components/PermissionProvider/PermissionProvider.interface';
import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum'; import { ERROR_PLACEHOLDER_TYPE } from 'enums/common.enum';
import { compare } from 'fast-json-patch'; import { compare } from 'fast-json-patch';
import { isEmpty, isUndefined } from 'lodash'; import { isEmpty, isUndefined } from 'lodash';
@ -39,7 +34,6 @@ import {
import { EntityType } from '../../../enums/entity.enum'; import { EntityType } from '../../../enums/entity.enum';
import { Role } from '../../../generated/entity/teams/role'; import { Role } from '../../../generated/entity/teams/role';
import { EntityReference } from '../../../generated/type/entityReference'; import { EntityReference } from '../../../generated/type/entityReference';
import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils';
import { getSettingPath } from '../../../utils/RouterUtils'; import { getSettingPath } from '../../../utils/RouterUtils';
import { showErrorToast } from '../../../utils/ToastUtils'; import { showErrorToast } from '../../../utils/ToastUtils';
import AddAttributeModal from '../AddAttributeModal/AddAttributeModal'; import AddAttributeModal from '../AddAttributeModal/AddAttributeModal';
@ -58,7 +52,6 @@ interface AddAttribute {
const RolesDetailPage = () => { const RolesDetailPage = () => {
const history = useHistory(); const history = useHistory();
const { t } = useTranslation(); const { t } = useTranslation();
const { getEntityPermissionByFqn } = usePermissionProvider();
const { fqn } = useParams<{ fqn: string }>(); const { fqn } = useParams<{ fqn: string }>();
const [role, setRole] = useState<Role>({} as Role); const [role, setRole] = useState<Role>({} as Role);
@ -70,10 +63,6 @@ const RolesDetailPage = () => {
const [addAttribute, setAddAttribute] = useState<AddAttribute>(); const [addAttribute, setAddAttribute] = useState<AddAttribute>();
const [rolePermission, setRolePermission] = useState<OperationPermission>(
DEFAULT_ENTITY_PERMISSION
);
const rolesPath = getSettingPath( const rolesPath = getSettingPath(
GlobalSettingsMenuCategory.ACCESS, GlobalSettingsMenuCategory.ACCESS,
GlobalSettingOptions.ROLES GlobalSettingOptions.ROLES
@ -93,18 +82,6 @@ const RolesDetailPage = () => {
[role] [role]
); );
const fetchRolePermission = async () => {
setLoading(true);
try {
const response = await getEntityPermissionByFqn(ResourceEntity.ROLE, fqn);
setRolePermission(response);
} catch (error) {
showErrorToast(error as AxiosError);
} finally {
setLoading(false);
}
};
const fetchRole = async () => { const fetchRole = async () => {
setLoading(true); setLoading(true);
try { try {
@ -238,15 +215,9 @@ const RolesDetailPage = () => {
}; };
useEffect(() => { useEffect(() => {
fetchRolePermission(); fetchRole();
}, [fqn]); }, [fqn]);
useEffect(() => {
if (rolePermission.ViewAll || rolePermission.ViewBasic) {
fetchRole();
}
}, [rolePermission, fqn]);
if (isLoading) { if (isLoading) {
return <Loader />; return <Loader />;
} }
@ -254,112 +225,99 @@ const RolesDetailPage = () => {
return ( return (
<div data-testid="role-details-container"> <div data-testid="role-details-container">
<TitleBreadcrumb titleLinks={breadcrumb} /> <TitleBreadcrumb titleLinks={breadcrumb} />
{rolePermission.ViewAll || rolePermission.ViewBasic ? (
<>
{isEmpty(role) ? (
<ErrorPlaceHolder type={ERROR_PLACEHOLDER_TYPE.CUSTOM}>
<div className="text-center">
<p>
{t('message.no-entity-found-for-name', {
entity: t('label.role'),
name: fqn,
})}
</p>
<Button
ghost
className="m-t-sm"
type="primary"
onClick={() => history.push(rolesPath)}>
{t('label.go-back')}
</Button>
</div>
</ErrorPlaceHolder>
) : (
<div className="roles-detail" data-testid="role-details">
<Typography.Title
className="m-b-0 m-t-xs"
data-testid="heading"
level={5}>
{getEntityName(role)}
</Typography.Title>
<Description
className="m-b-md"
description={role.description || ''}
entityFqn={role.fullyQualifiedName}
entityName={getEntityName(role)}
entityType={EntityType.ROLE}
hasEditAccess={
rolePermission.EditAll || rolePermission.EditDescription
}
isEdit={editDescription}
onCancel={() => setEditDescription(false)}
onDescriptionEdit={() => setEditDescription(true)}
onDescriptionUpdate={handleDescriptionUpdate}
/>
<Tabs data-testid="tabs" defaultActiveKey="policies"> <>
<TabPane key="policies" tab={t('label.policy-plural')}> {isEmpty(role) ? (
<Space className="w-full" direction="vertical"> <ErrorPlaceHolder type={ERROR_PLACEHOLDER_TYPE.CUSTOM}>
<Tooltip <div className="text-center">
title={ <p>
rolePermission.EditAll {t('message.no-entity-found-for-name', {
? t('label.add-entity', { entity: t('label.role'),
entity: t('label.policy'), name: fqn,
}) })}
: t('message.no-permission-for-action') </p>
}> <Button
<Button ghost
data-testid="add-policy" className="m-t-sm"
disabled={!rolePermission.EditAll} type="primary"
type="primary" onClick={() => history.push(rolesPath)}>
onClick={() => {t('label.go-back')}
setAddAttribute({ </Button>
type: EntityType.POLICY,
selectedData: role.policies || [],
})
}>
{t('label.add-entity', {
entity: t('label.policy'),
})}
</Button>
</Tooltip>
<RolesDetailPageList
hasAccess={rolePermission.EditAll}
list={role.policies ?? []}
type="policy"
onDelete={(record) =>
setEntity({ record, attribute: 'policies' })
}
/>
</Space>
</TabPane>
<TabPane key="teams" tab={t('label.team-plural')}>
<RolesDetailPageList
hasAccess={rolePermission.EditAll}
list={role.teams ?? []}
type="team"
onDelete={(record) =>
setEntity({ record, attribute: 'teams' })
}
/>
</TabPane>
<TabPane key="users" tab={t('label.user-plural')}>
<RolesDetailPageList
hasAccess={rolePermission.EditAll}
list={role.users ?? []}
type="user"
onDelete={(record) =>
setEntity({ record, attribute: 'users' })
}
/>
</TabPane>
</Tabs>
</div> </div>
)} </ErrorPlaceHolder>
</> ) : (
) : ( <div className="roles-detail" data-testid="role-details">
<ErrorPlaceHolder type={ERROR_PLACEHOLDER_TYPE.PERMISSION} /> <Typography.Title
)} className="m-b-0 m-t-xs"
data-testid="heading"
level={5}>
{getEntityName(role)}
</Typography.Title>
<Description
hasEditAccess
className="m-b-md"
description={role.description || ''}
entityFqn={role.fullyQualifiedName}
entityName={getEntityName(role)}
entityType={EntityType.ROLE}
isEdit={editDescription}
onCancel={() => setEditDescription(false)}
onDescriptionEdit={() => setEditDescription(true)}
onDescriptionUpdate={handleDescriptionUpdate}
/>
<Tabs data-testid="tabs" defaultActiveKey="policies">
<TabPane key="policies" tab={t('label.policy-plural')}>
<Space className="w-full" direction="vertical">
<Button
data-testid="add-policy"
type="primary"
onClick={() =>
setAddAttribute({
type: EntityType.POLICY,
selectedData: role.policies || [],
})
}>
{t('label.add-entity', {
entity: t('label.policy'),
})}
</Button>
<RolesDetailPageList
hasAccess
list={role.policies ?? []}
type="policy"
onDelete={(record) =>
setEntity({ record, attribute: 'policies' })
}
/>
</Space>
</TabPane>
<TabPane key="teams" tab={t('label.team-plural')}>
<RolesDetailPageList
hasAccess
list={role.teams ?? []}
type="team"
onDelete={(record) =>
setEntity({ record, attribute: 'teams' })
}
/>
</TabPane>
<TabPane key="users" tab={t('label.user-plural')}>
<RolesDetailPageList
hasAccess
list={role.users ?? []}
type="user"
onDelete={(record) =>
setEntity({ record, attribute: 'users' })
}
/>
</TabPane>
</Tabs>
</div>
)}
</>
{selectedEntity && ( {selectedEntity && (
<Modal <Modal
centered centered

View File

@ -102,19 +102,13 @@ export const getGlobalSettingsMenuWithPermission = (
items: [ items: [
{ {
label: i18next.t('label.role-plural'), label: i18next.t('label.role-plural'),
isProtected: userPermissions.hasViewPermissions( isProtected: Boolean(isAdminUser),
ResourceEntity.ROLE,
permissions
),
key: 'access.roles', key: 'access.roles',
icon: <RolesIcon className="side-panel-icons" />, icon: <RolesIcon className="side-panel-icons" />,
}, },
{ {
label: i18next.t('label.policy-plural'), label: i18next.t('label.policy-plural'),
isProtected: userPermissions.hasViewPermissions( isProtected: Boolean(isAdminUser),
ResourceEntity.POLICY,
permissions
),
key: 'access.policies', key: 'access.policies',
icon: <PoliciesIcon className="side-panel-icons" />, icon: <PoliciesIcon className="side-panel-icons" />,
}, },