mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-10-31 10:39:30 +00:00 
			
		
		
		
	* fix(#12954): restrict roles and policy in settings for admins only * chore: address comments
This commit is contained in:
		
							parent
							
								
									011eaf8ad5
								
							
						
					
					
						commit
						80c11ab4f5
					
				| @ -19,15 +19,16 @@ import { ROUTES } from '../../constants/constants'; | ||||
| import { useAuth } from '../../hooks/authHooks'; | ||||
| 
 | ||||
| interface AdminProtectedRouteProps extends RouteProps { | ||||
|   hasPermission: boolean; | ||||
|   hasPermission?: boolean; | ||||
| } | ||||
| 
 | ||||
| const AdminProtectedRoute = (routeProps: AdminProtectedRouteProps) => { | ||||
|   const { isAdminUser } = useAuth(); | ||||
|   const hasPermission = Boolean(routeProps.hasPermission); | ||||
| 
 | ||||
|   if (isAdminUser || routeProps.hasPermission) { | ||||
|   if (isAdminUser || hasPermission) { | ||||
|     return <Route {...routeProps} />; | ||||
|   } else if (!routeProps.hasPermission) { | ||||
|   } else if (!hasPermission) { | ||||
|     return <ErrorPlaceHolder type={ERROR_PLACEHOLDER_TYPE.PERMISSION} />; | ||||
|   } else { | ||||
|     return <Redirect to={ROUTES.SIGNIN} />; | ||||
|  | ||||
| @ -175,17 +175,13 @@ const GlobalSettingRouter = () => { | ||||
|       <AdminProtectedRoute | ||||
|         exact | ||||
|         component={RolesListPage} | ||||
|         hasPermission={userPermissions.hasViewPermissions( | ||||
|           ResourceEntity.ROLE, | ||||
|           permissions | ||||
|         )} | ||||
|         path={getSettingPath( | ||||
|           GlobalSettingsMenuCategory.ACCESS, | ||||
|           GlobalSettingOptions.ROLES | ||||
|         )} | ||||
|       /> | ||||
| 
 | ||||
|       <Route | ||||
|       <AdminProtectedRoute | ||||
|         exact | ||||
|         component={RolesDetailPage} | ||||
|         path={getSettingPath( | ||||
| @ -201,16 +197,12 @@ const GlobalSettingRouter = () => { | ||||
|       <AdminProtectedRoute | ||||
|         exact | ||||
|         component={PoliciesListPage} | ||||
|         hasPermission={userPermissions.hasViewPermissions( | ||||
|           ResourceEntity.POLICY, | ||||
|           permissions | ||||
|         )} | ||||
|         path={getSettingPath( | ||||
|           GlobalSettingsMenuCategory.ACCESS, | ||||
|           GlobalSettingOptions.POLICIES | ||||
|         )} | ||||
|       /> | ||||
|       <Route | ||||
|       <AdminProtectedRoute | ||||
|         exact | ||||
|         component={PoliciesDetailPage} | ||||
|         path={getSettingPath( | ||||
|  | ||||
| @ -57,20 +57,6 @@ jest.mock('components/Loader/Loader', () => | ||||
|   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', () => ({ | ||||
|   NO_PERMISSION_FOR_ACTION: '', | ||||
|   NO_PERMISSION_TO_VIEW: '', | ||||
| @ -80,18 +66,6 @@ jest.mock('../../../utils/CommonUtils', () => ({ | ||||
|   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', () => ({ | ||||
|   getAddPolicyRulePath: jest.fn(), | ||||
|   getEditPolicyRulePath: jest.fn(), | ||||
|  | ||||
| @ -22,7 +22,6 @@ import { | ||||
|   Row, | ||||
|   Space, | ||||
|   Tabs, | ||||
|   Tooltip, | ||||
|   Typography, | ||||
| } from 'antd'; | ||||
| 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 TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component'; | ||||
| 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 { isEmpty, isUndefined, startCase } from 'lodash'; | ||||
| 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 { Policy } from '../../../generated/entity/policies/policy'; | ||||
| import { EntityReference } from '../../../generated/type/entityReference'; | ||||
| import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils'; | ||||
| import { | ||||
|   getAddPolicyRulePath, | ||||
|   getEditPolicyRulePath, | ||||
| @ -78,7 +70,6 @@ const PoliciesDetailPage = () => { | ||||
|   const { t } = useTranslation(); | ||||
|   const history = useHistory(); | ||||
|   const { fqn } = useParams<{ fqn: string }>(); | ||||
|   const { getEntityPermissionByFqn } = usePermissionProvider(); | ||||
| 
 | ||||
|   const [policy, setPolicy] = useState<Policy>({} as Policy); | ||||
|   const [isLoading, setLoading] = useState<boolean>(false); | ||||
| @ -87,10 +78,6 @@ const PoliciesDetailPage = () => { | ||||
|   const [selectedEntity, setEntity] = | ||||
|     useState<{ attribute: Attribute; record: EntityReference }>(); | ||||
| 
 | ||||
|   const [policyPermission, setPolicyPermission] = useState<OperationPermission>( | ||||
|     DEFAULT_ENTITY_PERMISSION | ||||
|   ); | ||||
| 
 | ||||
|   const policiesPath = getSettingPath( | ||||
|     GlobalSettingsMenuCategory.ACCESS, | ||||
|     GlobalSettingOptions.POLICIES | ||||
| @ -110,21 +97,6 @@ const PoliciesDetailPage = () => { | ||||
|     [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 () => { | ||||
|     setLoading(true); | ||||
|     try { | ||||
| @ -256,7 +228,6 @@ const PoliciesDetailPage = () => { | ||||
|     (rule: Rule) => { | ||||
|       return ( | ||||
|         <Dropdown | ||||
|           disabled={!policyPermission.EditAll} | ||||
|           overlay={ | ||||
|             <Menu | ||||
|               items={[ | ||||
| @ -307,39 +278,25 @@ const PoliciesDetailPage = () => { | ||||
|           } | ||||
|           placement="bottomRight" | ||||
|           trigger={['click']}> | ||||
|           <Tooltip | ||||
|             title={ | ||||
|               policyPermission.EditAll | ||||
|                 ? t('label.manage-rule') | ||||
|                 : t('message.no-permission-for-action') | ||||
|             }> | ||||
|             <Button | ||||
|               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> | ||||
|           <Button | ||||
|             data-testid={`manage-button-${rule.name}`} | ||||
|             icon={<EllipsisOutlined className="text-grey-body" rotate={90} />} | ||||
|             size="small" | ||||
|             type="text" | ||||
|             onClick={(e) => { | ||||
|               e.stopPropagation(); | ||||
|             }} | ||||
|           /> | ||||
|         </Dropdown> | ||||
|       ); | ||||
|     }, | ||||
|     [policy, policyPermission] | ||||
|     [policy] | ||||
|   ); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     fetchPolicyPermission(); | ||||
|     fetchPolicy(); | ||||
|   }, [fqn]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (policyPermission.ViewAll || policyPermission.ViewBasic) { | ||||
|       fetchPolicy(); | ||||
|     } | ||||
|   }, [policyPermission, fqn]); | ||||
| 
 | ||||
|   if (isLoading) { | ||||
|     return <Loader />; | ||||
|   } | ||||
| @ -347,198 +304,178 @@ const PoliciesDetailPage = () => { | ||||
|   return ( | ||||
|     <div data-testid="policy-details-container"> | ||||
|       <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.rules) ? ( | ||||
|                     <ErrorPlaceHolder /> | ||||
|                   ) : ( | ||||
|                     <Space | ||||
|                       className="w-full tabpane-space" | ||||
|                       direction="vertical"> | ||||
|                       <Tooltip | ||||
|                         title={ | ||||
|                           policyPermission.EditAll | ||||
|                             ? t('label.add-entity', { | ||||
|                                 entity: t('label.rule'), | ||||
|                               }) | ||||
|                             : t('message.no-permission-for-action') | ||||
|                         }> | ||||
|                         <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> | ||||
|       <> | ||||
|         {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 type={ERROR_PLACEHOLDER_TYPE.PERMISSION} /> | ||||
|       )} | ||||
|           </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 | ||||
|               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 && ( | ||||
|         <Modal | ||||
|           centered | ||||
|  | ||||
| @ -60,20 +60,6 @@ jest.mock('../../../utils/RouterUtils', () => ({ | ||||
|   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', () => { | ||||
|   it('Should render the detail component', async () => { | ||||
|     render(<RolesDetailPage />); | ||||
|  | ||||
| @ -11,17 +11,12 @@ | ||||
|  *  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 Description from 'components/common/description/Description'; | ||||
| import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder'; | ||||
| import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component'; | ||||
| 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 { isEmpty, isUndefined } from 'lodash'; | ||||
| @ -39,7 +34,6 @@ import { | ||||
| import { EntityType } from '../../../enums/entity.enum'; | ||||
| import { Role } from '../../../generated/entity/teams/role'; | ||||
| import { EntityReference } from '../../../generated/type/entityReference'; | ||||
| import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils'; | ||||
| import { getSettingPath } from '../../../utils/RouterUtils'; | ||||
| import { showErrorToast } from '../../../utils/ToastUtils'; | ||||
| import AddAttributeModal from '../AddAttributeModal/AddAttributeModal'; | ||||
| @ -58,7 +52,6 @@ interface AddAttribute { | ||||
| const RolesDetailPage = () => { | ||||
|   const history = useHistory(); | ||||
|   const { t } = useTranslation(); | ||||
|   const { getEntityPermissionByFqn } = usePermissionProvider(); | ||||
|   const { fqn } = useParams<{ fqn: string }>(); | ||||
| 
 | ||||
|   const [role, setRole] = useState<Role>({} as Role); | ||||
| @ -70,10 +63,6 @@ const RolesDetailPage = () => { | ||||
| 
 | ||||
|   const [addAttribute, setAddAttribute] = useState<AddAttribute>(); | ||||
| 
 | ||||
|   const [rolePermission, setRolePermission] = useState<OperationPermission>( | ||||
|     DEFAULT_ENTITY_PERMISSION | ||||
|   ); | ||||
| 
 | ||||
|   const rolesPath = getSettingPath( | ||||
|     GlobalSettingsMenuCategory.ACCESS, | ||||
|     GlobalSettingOptions.ROLES | ||||
| @ -93,18 +82,6 @@ const RolesDetailPage = () => { | ||||
|     [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 () => { | ||||
|     setLoading(true); | ||||
|     try { | ||||
| @ -238,15 +215,9 @@ const RolesDetailPage = () => { | ||||
|   }; | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     fetchRolePermission(); | ||||
|     fetchRole(); | ||||
|   }, [fqn]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (rolePermission.ViewAll || rolePermission.ViewBasic) { | ||||
|       fetchRole(); | ||||
|     } | ||||
|   }, [rolePermission, fqn]); | ||||
| 
 | ||||
|   if (isLoading) { | ||||
|     return <Loader />; | ||||
|   } | ||||
| @ -254,112 +225,99 @@ const RolesDetailPage = () => { | ||||
|   return ( | ||||
|     <div data-testid="role-details-container"> | ||||
|       <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')}> | ||||
|                   <Space className="w-full" direction="vertical"> | ||||
|                     <Tooltip | ||||
|                       title={ | ||||
|                         rolePermission.EditAll | ||||
|                           ? t('label.add-entity', { | ||||
|                               entity: t('label.policy'), | ||||
|                             }) | ||||
|                           : t('message.no-permission-for-action') | ||||
|                       }> | ||||
|                       <Button | ||||
|                         data-testid="add-policy" | ||||
|                         disabled={!rolePermission.EditAll} | ||||
|                         type="primary" | ||||
|                         onClick={() => | ||||
|                           setAddAttribute({ | ||||
|                             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> | ||||
|       <> | ||||
|         {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 type={ERROR_PLACEHOLDER_TYPE.PERMISSION} /> | ||||
|       )} | ||||
|           </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 | ||||
|               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 && ( | ||||
|         <Modal | ||||
|           centered | ||||
|  | ||||
| @ -102,19 +102,13 @@ export const getGlobalSettingsMenuWithPermission = ( | ||||
|       items: [ | ||||
|         { | ||||
|           label: i18next.t('label.role-plural'), | ||||
|           isProtected: userPermissions.hasViewPermissions( | ||||
|             ResourceEntity.ROLE, | ||||
|             permissions | ||||
|           ), | ||||
|           isProtected: Boolean(isAdminUser), | ||||
|           key: 'access.roles', | ||||
|           icon: <RolesIcon className="side-panel-icons" />, | ||||
|         }, | ||||
|         { | ||||
|           label: i18next.t('label.policy-plural'), | ||||
|           isProtected: userPermissions.hasViewPermissions( | ||||
|             ResourceEntity.POLICY, | ||||
|             permissions | ||||
|           ), | ||||
|           isProtected: Boolean(isAdminUser), | ||||
|           key: 'access.policies', | ||||
|           icon: <PoliciesIcon className="side-panel-icons" />, | ||||
|         }, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Sachin Chaurasiya
						Sachin Chaurasiya