diff --git a/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/rolesAPIV1.ts b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/rolesAPIV1.ts index 7f4b340a166..8b5884ac393 100644 --- a/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/rolesAPIV1.ts +++ b/openmetadata-ui/src/main/resources/ui/src/axiosAPIs/rolesAPIV1.ts @@ -13,9 +13,11 @@ import { AxiosResponse } from 'axios'; import { Operation } from 'fast-json-patch'; +import { ResourceEntity } from '../components/PermissionProvider/PermissionProvider.interface'; import { CreatePolicy } from '../generated/api/policies/createPolicy'; import { CreateRole } from '../generated/api/teams/createRole'; import { ResourceDescriptor } from '../generated/entity/policies/accessControl/resourceDescriptor'; +import { ResourcePermission } from '../generated/entity/policies/accessControl/resourcePermission'; import { Policy } from '../generated/entity/policies/policy'; import { Role } from '../generated/entity/teams/role'; import { Function } from '../generated/type/function'; @@ -154,3 +156,14 @@ export const validateRuleCondition = async (condition: string) => { */ return response; }; + +export const getEntityPermissionById = async ( + resource: ResourceEntity, + entityId: string +) => { + const response = await APIClient.get( + `/permissions/${resource}/${entityId}` + ); + + return response.data; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AddWebhook/AddWebhook.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AddWebhook/AddWebhook.tsx index 8723e2d05e3..e44fc20b2ad 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AddWebhook/AddWebhook.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AddWebhook/AddWebhook.tsx @@ -13,12 +13,19 @@ import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Tooltip } from 'antd'; import { Store } from 'antd/lib/form/interface'; import classNames from 'classnames'; import cryptoRandomString from 'crypto-random-string-with-promisify-polyfill'; -import { cloneDeep, isEqual, isNil } from 'lodash'; +import { cloneDeep, isEmpty, isEqual, isNil } from 'lodash'; import { EditorContentRef } from 'Models'; -import React, { FunctionComponent, useCallback, useRef, useState } from 'react'; +import React, { + FunctionComponent, + useCallback, + useMemo, + useRef, + useState, +} from 'react'; import { ROUTES, TERM_ALL } from '../../constants/constants'; import { GlobalSettingOptions, @@ -27,6 +34,7 @@ import { import { CONFIGURE_SLACK_TEXT, CONFIGURE_WEBHOOK_TEXT, + NO_PERMISSION_FOR_ACTION, } from '../../constants/HelperTextUtil'; import { UrlEntityCharRegEx } from '../../constants/regex.constants'; import { FormSubmitType } from '../../enums/form.enum'; @@ -37,12 +45,14 @@ import { Filters, } from '../../generated/api/events/createWebhook'; import { WebhookType } from '../../generated/entity/events/webhook'; +import { Operation } from '../../generated/entity/policies/policy'; import { errorMsg, getSeparator, isValidUrl, requiredField, } from '../../utils/CommonUtils'; +import { checkPermission } from '../../utils/PermissionsUtils'; import { getSettingPath } from '../../utils/RouterUtils'; import SVGIcons, { Icons } from '../../utils/SvgUtils'; import { Button } from '../buttons/Button/Button'; @@ -52,6 +62,8 @@ import TitleBreadcrumb from '../common/title-breadcrumb/title-breadcrumb.compone import PageLayout from '../containers/PageLayout'; import Loader from '../Loader/Loader'; import ConfirmationModal from '../Modals/ConfirmationModal/ConfirmationModal'; +import { usePermissionProvider } from '../PermissionProvider/PermissionProvider'; +import { ResourceEntity } from '../PermissionProvider/PermissionProvider.interface'; import { AddWebhookProps } from './AddWebhook.interface'; import SelectComponent from './select-component'; import { @@ -163,6 +175,29 @@ const AddWebhook: FunctionComponent = ({ const [generatingSecret, setGeneratingSecret] = useState(false); const [isDelete, setIsDelete] = useState(false); + const { permissions } = usePermissionProvider(); + + const editWebhookPermission = useMemo( + () => + !isEmpty(permissions) && + checkPermission(Operation.EditAll, ResourceEntity.WEBHOOK, permissions), + [permissions] + ); + + const addWebhookPermission = useMemo( + () => + !isEmpty(permissions) && + checkPermission(Operation.Create, ResourceEntity.WEBHOOK, permissions), + [permissions] + ); + + const deleteWebhookPermission = useMemo( + () => + !isEmpty(permissions) && + checkPermission(Operation.Delete, ResourceEntity.WEBHOOK, permissions), + [permissions] + ); + const handleDelete = () => { if (data) { onDelete && onDelete(data.id); @@ -259,7 +294,7 @@ const AddWebhook: FunctionComponent = ({ }; const getDeleteButton = () => { - return allowAccess ? ( + return ( <> {deleteState === 'waiting' ? ( ) : ( - + + + )} - ) : null; + ); }; const getSaveButton = () => { - return allowAccess ? ( + const savePermission = + (mode === 'add' && addWebhookPermission) || + (mode === 'edit' && editWebhookPermission); + + return ( <> {saveState === 'waiting' ? ( ) : ( - + + + )} - ) : null; + ); }; const fetchRightPanel = useCallback(() => { @@ -631,7 +677,7 @@ const AddWebhook: FunctionComponent = ({ )} - {data && isDelete && ( + {data && isDelete && deleteWebhookPermission && ( = ({ const [generateToken, setGenerateToken] = useState(false); const [selectedExpiry, setSelectedExpiry] = useState('7'); - const editAllPermission = checkPemission( + const editAllPermission = checkPermission( Operation.EditAll, ResourceEntity.BOT, permissions ); - const displayNamePermission = checkPemission( + const displayNamePermission = checkPermission( Operation.EditDisplayName, ResourceEntity.BOT, permissions ); - const descriptionPermission = checkPemission( + const descriptionPermission = checkPermission( Operation.EditDescription, ResourceEntity.BOT, permissions diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BotDetails/BotDetails.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/BotDetails/BotDetails.test.tsx index 8477198f40e..8373c7ed51b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/BotDetails/BotDetails.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/BotDetails/BotDetails.test.tsx @@ -85,7 +85,7 @@ jest.mock('../PermissionProvider/PermissionProvider', () => ({ })); jest.mock('../../utils/PermissionsUtils', () => ({ - checkPemission: jest.fn().mockReturnValue(true), + checkPermission: jest.fn().mockReturnValue(true), })); jest.mock('../../axiosAPIs/userAPI', () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BotListV1/BotListV1.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/BotListV1/BotListV1.component.tsx index 9ff769a19cc..cf9a25c867b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/BotListV1/BotListV1.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/BotListV1/BotListV1.component.tsx @@ -22,12 +22,13 @@ import { INITIAL_PAGING_VALUE, PAGE_SIZE, } from '../../constants/constants'; +import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil'; import { EntityType } from '../../enums/entity.enum'; import { Bot } from '../../generated/entity/bot'; import { Operation } from '../../generated/entity/policies/accessControl/rule'; import { Include } from '../../generated/type/include'; import { Paging } from '../../generated/type/paging'; -import { checkPemission } from '../../utils/PermissionsUtils'; +import { checkPermission } from '../../utils/PermissionsUtils'; import SVGIcons, { Icons } from '../../utils/SvgUtils'; import { showErrorToast } from '../../utils/ToastUtils'; import DeleteWidgetModal from '../common/DeleteWidget/DeleteWidgetModal'; @@ -45,7 +46,7 @@ const BotListV1 = ({ showDeleted }: BotListV1Props) => { const [loading, setLoading] = useState(true); const [currentPage, setCurrentPage] = useState(INITIAL_PAGING_VALUE); - const deletePermission = checkPemission( + const deletePermission = checkPermission( Operation.Delete, ResourceEntity.BOT, permissions @@ -103,11 +104,7 @@ const BotListV1 = ({ showDeleted }: BotListV1Props) => { + title={deletePermission ? 'Delete' : NO_PERMISSION_FOR_ACTION}> - + diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Webhooks/WebhooksV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Webhooks/WebhooksV1.tsx index 10e7936d3be..047263ec853 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Webhooks/WebhooksV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Webhooks/WebhooksV1.tsx @@ -11,28 +11,28 @@ * limitations under the License. */ -import { Card, Col, Row, Select, Space } from 'antd'; +import { Card, Col, Row, Select, Space, Tooltip } from 'antd'; import classNames from 'classnames'; -import { isNil } from 'lodash'; +import { isEmpty, isNil } from 'lodash'; import React, { FC, useEffect, useMemo, useState } from 'react'; +import { PAGE_SIZE } from '../../constants/constants'; import { - PAGE_SIZE, - TITLE_FOR_NON_ADMIN_ACTION, -} from '../../constants/constants'; -import { + NO_PERMISSION_FOR_ACTION, SLACK_LISTING_TEXT, WEBHOOK_LISTING_TEXT, } from '../../constants/HelperTextUtil'; import { WebhookType } from '../../generated/api/events/createWebhook'; import { Webhook } from '../../generated/entity/events/webhook'; -import { useAuth } from '../../hooks/authHooks'; +import { Operation } from '../../generated/entity/policies/policy'; +import { checkPermission } from '../../utils/PermissionsUtils'; import { statuses } from '../AddWebhook/WebhookConstants'; import { Button } from '../buttons/Button/Button'; import ErrorPlaceHolder from '../common/error-with-placeholder/ErrorPlaceHolder'; import NextPrevious from '../common/next-previous/NextPrevious'; -import NonAdminAction from '../common/non-admin-action/NonAdminAction'; import WebhookDataCard from '../common/webhook-data-card/WebhookDataCard'; import { leftPanelAntCardStyle } from '../containers/PageLayout'; +import { usePermissionProvider } from '../PermissionProvider/PermissionProvider'; +import { ResourceEntity } from '../PermissionProvider/PermissionProvider.interface'; import { WebhooksV1Props } from './WebhooksV1.interface'; import './webhookV1.less'; @@ -47,9 +47,17 @@ const WebhooksV1: FC = ({ onStatusFilter, currentPage, }) => { - const { isAuthDisabled, isAdminUser } = useAuth(); const [filteredData, setFilteredData] = useState>(data); + const { permissions } = usePermissionProvider(); + + const addWebhookPermission = useMemo( + () => + !isEmpty(permissions) && + checkPermission(Operation.Create, ResourceEntity.WEBHOOK, permissions), + [permissions] + ); + const getFilteredWebhooks = () => { return selectedStatus.length ? data.filter( @@ -79,21 +87,22 @@ const WebhooksV1: FC = ({

{message}

- + - +

); @@ -131,14 +140,17 @@ const WebhooksV1: FC = ({ className="tw-w-full tw-justify-end" size={16}> {filteredData.length > 0 && ( - + - + )}
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/HelperTextUtil.ts b/openmetadata-ui/src/main/resources/ui/src/constants/HelperTextUtil.ts index f566285eaaf..c387971fa4c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/HelperTextUtil.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/HelperTextUtil.ts @@ -36,3 +36,8 @@ export const ADD_POLICY_TEXT = ` Policies are assigned to Teams. In OpenMetadata, a Policy is a collection of Rules, which define access based on certain conditions. We support rich SpEL (Spring Expression Language) based conditions. All the operations supported by an Entity are published. Use these fine grained operations to define the conditional Rules for each Policy. Create well-defined policies based on conditional rules to build rich access control roles. `; + +export const NO_PERMISSION_FOR_ACTION = + 'You do not have permissions to perform this action.'; + +export const NO_PERMISSION_TO_VIEW = 'You do not have permission to view data'; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/BotsPageV1/BotsPageV1.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/BotsPageV1/BotsPageV1.component.tsx index 094cbcee717..b8f7a76c612 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/BotsPageV1/BotsPageV1.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/BotsPageV1/BotsPageV1.component.tsx @@ -11,7 +11,7 @@ * limitations under the License. */ -import { Button, Col, Empty, Row, Space, Switch } from 'antd'; +import { Button, Col, Row, Space, Switch } from 'antd'; import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; import BotListV1 from '../../components/BotListV1/BotListV1.component'; @@ -19,7 +19,7 @@ import { usePermissionProvider } from '../../components/PermissionProvider/Permi import { ResourceEntity } from '../../components/PermissionProvider/PermissionProvider.interface'; import { getCreateUserPath } from '../../constants/constants'; import { Operation } from '../../generated/entity/policies/accessControl/rule'; -import { checkPemission } from '../../utils/PermissionsUtils'; +import { checkPermission } from '../../utils/PermissionsUtils'; export const BotsPageV1 = () => { const { permissions } = usePermissionProvider(); @@ -34,13 +34,7 @@ export const BotsPageV1 = () => { setShowDeleted(checked); }; - const viewAllPermission = checkPemission( - Operation.ViewAll, - ResourceEntity.BOT, - permissions - ); - - const createPermission = checkPemission( + const createPermission = checkPermission( Operation.Create, ResourceEntity.BOT, permissions @@ -48,35 +42,29 @@ export const BotsPageV1 = () => { return ( - {viewAllPermission ? ( - <> - - - - - - - + + + + + + + - {createPermission && ( - - )} - - - - - - - ) : ( - - )} + {createPermission && ( + + )} + + + + + ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CreateUserPage/CreateUserPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CreateUserPage/CreateUserPage.component.tsx index ed8591df879..6da1f00622a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/CreateUserPage/CreateUserPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/CreateUserPage/CreateUserPage.component.tsx @@ -17,7 +17,6 @@ import { LoadingState } from 'Models'; import React, { useEffect, useState } from 'react'; import { useHistory, useParams } from 'react-router-dom'; import AppState from '../../AppState'; -import { useAuthContext } from '../../authentication/auth-provider/AuthProvider'; import { createBot } from '../../axiosAPIs/botsAPI'; import { createUser } from '../../axiosAPIs/userAPI'; import PageContainerV1 from '../../components/containers/PageContainerV1'; @@ -30,14 +29,11 @@ import { EntityType } from '../../enums/entity.enum'; import { CreateUser } from '../../generated/api/teams/createUser'; import { Bot } from '../../generated/entity/bot'; import { Role } from '../../generated/entity/teams/role'; -import { useAuth } from '../../hooks/authHooks'; import jsonData from '../../jsons/en'; import { getSettingPath } from '../../utils/RouterUtils'; import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils'; const CreateUserPage = () => { - const { isAdminUser } = useAuth(); - const { isAuthDisabled } = useAuthContext(); const history = useHistory(); const [roles, setRoles] = useState>([]); @@ -128,7 +124,6 @@ const CreateUserPage = () => {
{ const tabAttributePath = customAttributesPath[tab as keyof typeof customAttributesPath]; + const { permissions } = usePermissionProvider(); + + const viewPermission = useMemo(() => { + return ( + !isEmpty(permissions) && + checkPermission( + Operation.ViewAll, + getResourceEntityFromCustomProperty(tab), + permissions + ) + ); + }, [permissions, tab]); + const fetchTypeDetail = async (typeFQN: string) => { setIsLoading(true); try { @@ -120,7 +138,7 @@ const CustomEntityDetailV1 = () => { ); } - return ( + return viewPermission ? ( { )} + ) : ( + + + + + ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesDetailPage/PoliciesDetailPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesDetailPage/PoliciesDetailPage.tsx index 5e1d37d2683..3b94610f373 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesDetailPage/PoliciesDetailPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesDetailPage/PoliciesDetailPage.tsx @@ -41,15 +41,18 @@ import Description from '../../../components/common/description/Description'; 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 { ResourceEntity } from '../../../components/PermissionProvider/PermissionProvider.interface'; import { GlobalSettingOptions, GlobalSettingsMenuCategory, } from '../../../constants/globalSettings.constants'; import { EntityType } from '../../../enums/entity.enum'; import { Rule } from '../../../generated/api/policies/createPolicy'; -import { Policy } from '../../../generated/entity/policies/policy'; +import { Operation, Policy } from '../../../generated/entity/policies/policy'; import { EntityReference } from '../../../generated/type/entityReference'; import { getEntityName } from '../../../utils/CommonUtils'; +import { checkPermission } from '../../../utils/PermissionsUtils'; import { getAddPolicyRulePath, getEditPolicyRulePath, @@ -147,6 +150,7 @@ const List = ({ const PoliciesDetailPage = () => { const history = useHistory(); const { fqn } = useParams<{ fqn: string }>(); + const { permissions } = usePermissionProvider(); const [policy, setPolicy] = useState({} as Policy); const [isLoading, setLoading] = useState(false); @@ -159,6 +163,17 @@ const PoliciesDetailPage = () => { GlobalSettingOptions.POLICIES ); + const editDescriptionPermission = useMemo(() => { + return ( + !isEmpty(permissions) && + checkPermission( + Operation.EditDescription, + ResourceEntity.ROLE, + permissions + ) + ); + }, [permissions]); + const breadcrumb = useMemo( () => [ { @@ -393,6 +408,7 @@ const PoliciesDetailPage = () => { entityFqn={policy.fullyQualifiedName} entityName={getEntityName(policy)} entityType={EntityType.POLICY} + hasEditAccess={editDescriptionPermission} isEdit={editDescription} onCancel={() => setEditDescription(false)} onDescriptionEdit={() => setEditDescription(true)} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesList.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesList.tsx index 6bcf295550a..a2ac00a245a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesList.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/PoliciesPage/PoliciesListPage/PoliciesList.tsx @@ -11,18 +11,24 @@ * limitations under the License. */ -import { Button, Popover, Space, Table, Tag } from 'antd'; +import { Button, Popover, Space, Table, Tag, Tooltip } from 'antd'; import { ColumnsType } from 'antd/lib/table'; -import { isUndefined, uniqueId } from 'lodash'; +import { isEmpty, isUndefined, uniqueId } from 'lodash'; import React, { FC, useMemo, useState } from 'react'; import { Link } from 'react-router-dom'; import DeleteWidgetModal from '../../../components/common/DeleteWidget/DeleteWidgetModal'; import RichTextEditorPreviewer from '../../../components/common/rich-text-editor/RichTextEditorPreviewer'; +import { usePermissionProvider } from '../../../components/PermissionProvider/PermissionProvider'; +import { ResourceEntity } from '../../../components/PermissionProvider/PermissionProvider.interface'; +import { + NO_PERMISSION_FOR_ACTION, + NO_PERMISSION_TO_VIEW, +} from '../../../constants/HelperTextUtil'; import { EntityType } from '../../../enums/entity.enum'; -import { Policy } from '../../../generated/entity/policies/policy'; +import { Operation, Policy } from '../../../generated/entity/policies/policy'; import { Paging } from '../../../generated/type/paging'; import { getEntityName } from '../../../utils/CommonUtils'; -import { LIST_CAP } from '../../../utils/PermissionsUtils'; +import { checkPermission, LIST_CAP } from '../../../utils/PermissionsUtils'; import { getPolicyWithFqnPath, getRoleWithFqnPath, @@ -36,6 +42,23 @@ interface PolicyListProps { const PoliciesList: FC = ({ policies, fetchPolicies }) => { const [selectedPolicy, setSelectedPolicy] = useState(); + + const { permissions } = usePermissionProvider(); + + const deletePolicyPermission = useMemo(() => { + return ( + !isEmpty(permissions) && + checkPermission(Operation.Delete, ResourceEntity.POLICY, permissions) + ); + }, [permissions]); + + const viewRolePermission = useMemo(() => { + return ( + !isEmpty(permissions) && + checkPermission(Operation.ViewAll, ResourceEntity.ROLE, permissions) + ); + }, [permissions]); + const columns: ColumnsType = useMemo(() => { return [ { @@ -71,27 +94,39 @@ const PoliciesList: FC = ({ policies, fetchPolicies }) => { return record.roles?.length ? ( - {record.roles.slice(0, LIST_CAP).map((role) => ( - - {getEntityName(role)} - - ))} + {record.roles.slice(0, LIST_CAP).map((role) => + viewRolePermission ? ( + + {getEntityName(role)} + + ) : ( + + {getEntityName(role)} + + ) + )} {hasMore && ( - {record.roles.slice(LIST_CAP).map((role) => ( - - {getEntityName(role)} - - ))} + {record.roles.slice(LIST_CAP).map((role) => + viewRolePermission ? ( + + {getEntityName(role)} + + ) : ( + + {getEntityName(role)} + + ) + )} } overlayClassName="tw-w-40 tw-text-center" @@ -114,12 +149,19 @@ const PoliciesList: FC = ({ policies, fetchPolicies }) => { key: 'actions', render: (_, record) => { return ( - + + + ); }, }, @@ -135,7 +177,7 @@ const PoliciesList: FC = ({ policies, fetchPolicies }) => { pagination={false} size="middle" /> - {selectedPolicy && ( + {selectedPolicy && deletePolicyPermission && ( { const [paging, setPaging] = useState(); const [currentPage, setCurrentPage] = useState(INITIAL_PAGING_VALUE); + const { permissions } = usePermissionProvider(); + + const addPolicyPermission = useMemo(() => { + return ( + !isEmpty(permissions) && + checkPermission(Operation.Create, ResourceEntity.POLICY, permissions) + ); + }, [permissions]); + const fetchPolicies = async (paging?: Paging) => { setIsLoading(true); try { @@ -73,12 +87,19 @@ const PoliciesListPage = () => { - + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesDetailPage/RolesDetailPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesDetailPage/RolesDetailPage.tsx index ff8408bc01b..fe31e018370 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesDetailPage/RolesDetailPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesDetailPage/RolesDetailPage.tsx @@ -26,14 +26,18 @@ import Description from '../../../components/common/description/Description'; 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 { ResourceEntity } from '../../../components/PermissionProvider/PermissionProvider.interface'; import { getUserPath } from '../../../constants/constants'; import { GlobalSettingOptions, GlobalSettingsMenuCategory, } from '../../../constants/globalSettings.constants'; import { EntityType } from '../../../enums/entity.enum'; +import { Operation } from '../../../generated/entity/policies/policy'; import { Role } from '../../../generated/entity/teams/role'; import { getEntityName } from '../../../utils/CommonUtils'; +import { checkPermission } from '../../../utils/PermissionsUtils'; import { getPolicyWithFqnPath, getSettingPath, @@ -139,6 +143,7 @@ const List = ({ const RolesDetailPage = () => { const history = useHistory(); + const { permissions } = usePermissionProvider(); const { fqn } = useParams<{ fqn: string }>(); const [role, setRole] = useState({} as Role); @@ -154,6 +159,17 @@ const RolesDetailPage = () => { GlobalSettingOptions.ROLES ); + const editDescriptionPermission = useMemo(() => { + return ( + !isEmpty(permissions) && + checkPermission( + Operation.EditDescription, + ResourceEntity.ROLE, + permissions + ) + ); + }, [permissions]); + const breadcrumb = useMemo( () => [ { @@ -319,6 +335,7 @@ const RolesDetailPage = () => { entityFqn={role.fullyQualifiedName} entityName={getEntityName(role)} entityType={EntityType.ROLE} + hasEditAccess={editDescriptionPermission} isEdit={editDescription} onCancel={() => setEditDescription(false)} onDescriptionEdit={() => setEditDescription(true)} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesList.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesList.tsx index 8e7780bca0d..ea2193e8794 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesList.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/RolesPage/RolesListPage/RolesList.tsx @@ -11,18 +11,25 @@ * limitations under the License. */ -import { Button, Popover, Space, Table, Tag } from 'antd'; +import { Button, Popover, Space, Table, Tag, Tooltip } from 'antd'; import { ColumnsType } from 'antd/lib/table'; -import { isUndefined, uniqueId } from 'lodash'; +import { isEmpty, isUndefined, uniqueId } from 'lodash'; import React, { FC, useMemo, useState } from 'react'; import { Link } from 'react-router-dom'; import DeleteWidgetModal from '../../../components/common/DeleteWidget/DeleteWidgetModal'; import RichTextEditorPreviewer from '../../../components/common/rich-text-editor/RichTextEditorPreviewer'; +import { usePermissionProvider } from '../../../components/PermissionProvider/PermissionProvider'; +import { ResourceEntity } from '../../../components/PermissionProvider/PermissionProvider.interface'; +import { + NO_PERMISSION_FOR_ACTION, + NO_PERMISSION_TO_VIEW, +} from '../../../constants/HelperTextUtil'; import { EntityType } from '../../../enums/entity.enum'; +import { Operation } from '../../../generated/entity/policies/policy'; import { Role } from '../../../generated/entity/teams/role'; import { Paging } from '../../../generated/type/paging'; import { getEntityName } from '../../../utils/CommonUtils'; -import { LIST_CAP } from '../../../utils/PermissionsUtils'; +import { checkPermission, LIST_CAP } from '../../../utils/PermissionsUtils'; import { getPolicyWithFqnPath, getRoleWithFqnPath, @@ -37,6 +44,22 @@ interface RolesListProps { const RolesList: FC = ({ roles, fetchRoles }) => { const [selectedRole, setSelectedRole] = useState(); + const { permissions } = usePermissionProvider(); + + const viewPolicyPermission = useMemo(() => { + return ( + !isEmpty(permissions) && + checkPermission(Operation.ViewAll, ResourceEntity.POLICY, permissions) + ); + }, [permissions]); + + const deleteRolePermission = useMemo(() => { + return ( + !isEmpty(permissions) && + checkPermission(Operation.Delete, ResourceEntity.ROLE, permissions) + ); + }, [permissions]); + const columns: ColumnsType = useMemo(() => { return [ { @@ -72,27 +95,39 @@ const RolesList: FC = ({ roles, fetchRoles }) => { return record.policies?.length ? ( - {record.policies.slice(0, LIST_CAP).map((policy) => ( - - {getEntityName(policy)} - - ))} + {record.policies.slice(0, LIST_CAP).map((policy) => + viewPolicyPermission ? ( + + {getEntityName(policy)} + + ) : ( + + {getEntityName(policy)} + + ) + )} {hasMore && ( - {record.policies.slice(LIST_CAP).map((policy) => ( - - {getEntityName(policy)} - - ))} + {record.policies.slice(LIST_CAP).map((policy) => + viewPolicyPermission ? ( + + {getEntityName(policy)} + + ) : ( + + {getEntityName(policy)} + + ) + )} } overlayClassName="tw-w-40 tw-text-center" @@ -115,12 +150,21 @@ const RolesList: FC = ({ roles, fetchRoles }) => { key: 'actions', render: (_, record) => { return ( - + + + + + diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx index b08840c4127..4ff6c654beb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx @@ -887,7 +887,7 @@ const ServicePage: FunctionComponent = () => { }; return ( - + {isLoading ? ( ) : isError ? ( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/services/ServicesPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/services/ServicesPage.tsx index 7d2961522eb..9402d376a42 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/services/ServicesPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/services/ServicesPage.tsx @@ -11,19 +11,26 @@ * limitations under the License. */ +import { Col, Empty, Row } from 'antd'; import { AxiosError } from 'axios'; +import { isEmpty } from 'lodash'; import { ServiceCategory } from 'Models'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; import { getServices } from '../../axiosAPIs/serviceAPI'; import Loader from '../../components/Loader/Loader'; +import { usePermissionProvider } from '../../components/PermissionProvider/PermissionProvider'; import Services from '../../components/Services/Services'; import { pagingObject } from '../../constants/constants'; +import { NO_PERMISSION_TO_VIEW } from '../../constants/HelperTextUtil'; import { SERVICE_CATEGORY } from '../../constants/services.const'; import { ServiceCategory as Category } from '../../enums/service.enum'; +import { Operation } from '../../generated/entity/policies/policy'; import { Paging } from '../../generated/type/paging'; import { ServicesType } from '../../interface/service.interface'; import jsonData from '../../jsons/en'; +import { checkPermission } from '../../utils/PermissionsUtils'; +import { getResourceEntityFromServiceCategory } from '../../utils/ServiceUtils'; import { showErrorToast } from '../../utils/ToastUtils'; const ServicesPage = () => { @@ -37,6 +44,19 @@ const ServicesPage = () => { ); const [currentPage, setCurrentPage] = useState(1); + const { permissions } = usePermissionProvider(); + + const viewAllPermission = useMemo(() => { + return ( + !isEmpty(permissions) && + checkPermission( + Operation.ViewAll, + getResourceEntityFromServiceCategory(tab), + permissions + ) + ); + }, [permissions]); + const getServiceDetails = async (type: string) => { setIsLoading(true); try { @@ -78,7 +98,7 @@ const ServicesPage = () => { return ; } - return ( + return viewAllPermission ? ( { serviceName={serviceName} onPageChange={handlePageChange} /> + ) : ( + + + + + ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx index 9d32e8d6d87..5c16e5d4ef7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx @@ -15,7 +15,12 @@ import { isEmpty } from 'lodash'; import React, { FunctionComponent } from 'react'; import { Redirect, Route, Switch } from 'react-router-dom'; import AppState from '../AppState'; +import { usePermissionProvider } from '../components/PermissionProvider/PermissionProvider'; +import { ResourceEntity } from '../components/PermissionProvider/PermissionProvider.interface'; import { ROUTES } from '../constants/constants'; +import { Operation } from '../generated/entity/policies/policy'; +import { checkPermission } from '../utils/PermissionsUtils'; +import AdminProtectedRoute from './AdminProtectedRoute'; import withSuspenseFallback from './withSuspenseFallback'; const GlobalSettingPage = withSuspenseFallback( @@ -70,9 +75,6 @@ const TourPageComponent = withSuspenseFallback( const UserPage = withSuspenseFallback( React.lazy(() => import('../pages/UserPage/UserPage.component')) ); -const AdminProtectedRoute = withSuspenseFallback( - React.lazy(() => import('./AdminProtectedRoute')) -); const AddGlossaryPage = withSuspenseFallback( React.lazy(() => import('../pages/AddGlossary/AddGlossaryPage.component')) @@ -190,6 +192,8 @@ const EditRulePage = withSuspenseFallback( ); const AuthenticatedAppRouter: FunctionComponent = () => { + const { permissions } = usePermissionProvider(); + return ( @@ -208,11 +212,21 @@ const AuthenticatedAppRouter: FunctionComponent = () => { @@ -290,47 +304,92 @@ const AuthenticatedAppRouter: FunctionComponent = () => { { diff --git a/openmetadata-ui/src/main/resources/ui/src/router/GlobalSettingRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/router/GlobalSettingRouter.tsx index 9878d3082cc..1a1b8245bd8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/router/GlobalSettingRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/router/GlobalSettingRouter.tsx @@ -21,7 +21,7 @@ import { } from '../constants/globalSettings.constants'; import { Operation } from '../generated/entity/policies/policy'; import TeamsPage from '../pages/teams/TeamsPage'; -import { checkPemission } from '../utils/PermissionsUtils'; +import { checkPermission } from '../utils/PermissionsUtils'; import { getSettingCategoryPath, getSettingPath } from '../utils/RouterUtils'; import AdminProtectedRoute from './AdminProtectedRoute'; import withSuspenseFallback from './withSuspenseFallback'; @@ -103,7 +103,7 @@ const GlobalSettingRouter = () => { { { { { { { { { { + switch (property) { + case 'tables': + return ResourceEntity.TABLE; + + case 'topics': + return ResourceEntity.TOPIC; + + case 'dashboards': + return ResourceEntity.DASHBOARD; + + case 'pipelines': + return ResourceEntity.PIPELINE; + + case 'mlModels': + return ResourceEntity.ML_MODEL; + } + + return ResourceEntity.TABLE; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/PermissionsUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/PermissionsUtils.ts index d5f4009080d..f760e4de3d5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/PermissionsUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/PermissionsUtils.ts @@ -11,6 +11,7 @@ * limitations under the License. */ +import AppState from '../AppState'; import { OperationPermission, ResourceEntity, @@ -25,6 +26,7 @@ import { import { Operation } from '../generated/entity/policies/policy'; /** + * @deprecated * TODO: Remove this method once we have new permission structure everywhere */ export const hasPemission = ( @@ -50,22 +52,26 @@ export const hasPemission = ( * @param permissions UIPermission * @returns boolean - true/false */ -export const checkPemission = ( +export const checkPermission = ( operation: Operation, resourceType: ResourceEntity, permissions: UIPermission ) => { - const allResource = permissions.all; - const entityResource = permissions[resourceType]; + const isAuthDisabled = AppState.authDisabled; + const allResource = permissions?.all; + const entityResource = permissions?.[resourceType]; + let hasPemission = isAuthDisabled; /** * If allresource is present then check for permission and return it */ - if (allResource) { - return allResource.All || allResource[operation]; + if (allResource && !hasPemission) { + hasPemission = allResource.All || allResource[operation]; } - return entityResource[operation]; + hasPemission = hasPemission || (entityResource && entityResource[operation]); + + return hasPemission; }; /** diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ServiceUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceUtils.tsx index 73f8406874b..11bca83aeb4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/ServiceUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceUtils.tsx @@ -22,6 +22,7 @@ import { } from 'Models'; import React from 'react'; import { getEntityCount } from '../axiosAPIs/miscAPI'; +import { ResourceEntity } from '../components/PermissionProvider/PermissionProvider.interface'; import { GlobalSettingOptions } from '../constants/globalSettings.constants'; import { addLineageIngestionGuide, @@ -743,3 +744,31 @@ export const getServiceRouteFromServiceType = (type: ServiceTypes) => { return GlobalSettingOptions.DATABASES; }; + +export const getResourceEntityFromServiceCategory = ( + category: string | ServiceCategory +) => { + switch (category) { + case 'dashboards': + case ServiceCategory.DASHBOARD_SERVICES: + return ResourceEntity.DASHBOARD_SERVICE; + + case 'databases': + case ServiceCategory.DATABASE_SERVICES: + return ResourceEntity.DATABASE_SERVICE; + + case 'mlModels': + case ServiceCategory.ML_MODAL_SERVICES: + return ResourceEntity.ML_MODEL_SERVICE; + + case 'messaging': + case ServiceCategory.MESSAGING_SERVICES: + return ResourceEntity.MESSAGING_SERVICE; + + case 'pipelines': + case ServiceCategory.PIPELINE_SERVICES: + return ResourceEntity.PIPELINE_SERVICE; + } + + return ResourceEntity.DATABASE_SERVICE; +};