mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-27 10:26:09 +00:00
feat(ui): update global settings page with new roles and policy (#7002)
* feat(ui): update global settings page with new roles and policy * Modify the permission util and remove access prop from create bot and user page * Fix typo * Fix console errors * fix unit tests * Add getEntityPermission to provider * Remove admin and auth disable check from global setting * Add check for edit description. Co-authored-by: Sachin Chaurasiya <sachinchaurasiyachotey87@gmail.com>
This commit is contained in:
parent
15c83e3f2d
commit
cc0449e506
@ -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<ResourcePermission>(
|
||||
`/permissions/${resource}/${entityId}`
|
||||
);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
@ -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<AddWebhookProps> = ({
|
||||
const [generatingSecret, setGeneratingSecret] = useState<boolean>(false);
|
||||
const [isDelete, setIsDelete] = useState<boolean>(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<AddWebhookProps> = ({
|
||||
};
|
||||
|
||||
const getDeleteButton = () => {
|
||||
return allowAccess ? (
|
||||
return (
|
||||
<>
|
||||
{deleteState === 'waiting' ? (
|
||||
<Button
|
||||
@ -271,24 +306,32 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
<Loader size="small" type="default" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={classNames({
|
||||
'tw-opacity-40': !allowAccess,
|
||||
})}
|
||||
data-testid="delete-webhook"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="text"
|
||||
onClick={() => setIsDelete(true)}>
|
||||
Delete
|
||||
</Button>
|
||||
<Tooltip
|
||||
placement="left"
|
||||
title={
|
||||
deleteWebhookPermission ? 'Delete' : NO_PERMISSION_FOR_ACTION
|
||||
}>
|
||||
<Button
|
||||
data-testid="delete-webhook"
|
||||
disabled={!deleteWebhookPermission}
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="text"
|
||||
onClick={() => setIsDelete(true)}>
|
||||
Delete
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
) : null;
|
||||
);
|
||||
};
|
||||
|
||||
const getSaveButton = () => {
|
||||
return allowAccess ? (
|
||||
const savePermission =
|
||||
(mode === 'add' && addWebhookPermission) ||
|
||||
(mode === 'edit' && editWebhookPermission);
|
||||
|
||||
return (
|
||||
<>
|
||||
{saveState === 'waiting' ? (
|
||||
<Button
|
||||
@ -309,20 +352,23 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
<FontAwesomeIcon icon="check" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={classNames('tw-w-16 tw-h-10', {
|
||||
'tw-opacity-40': !allowAccess,
|
||||
})}
|
||||
data-testid="save-webhook"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained"
|
||||
onClick={handleSave}>
|
||||
Save
|
||||
</Button>
|
||||
<Tooltip
|
||||
placement="left"
|
||||
title={savePermission ? 'Save' : NO_PERMISSION_FOR_ACTION}>
|
||||
<Button
|
||||
className={classNames('tw-w-16 tw-h-10')}
|
||||
data-testid="save-webhook"
|
||||
disabled={!savePermission}
|
||||
size="regular"
|
||||
theme="primary"
|
||||
variant="contained"
|
||||
onClick={handleSave}>
|
||||
Save
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
) : null;
|
||||
);
|
||||
};
|
||||
|
||||
const fetchRightPanel = useCallback(() => {
|
||||
@ -631,7 +677,7 @@ const AddWebhook: FunctionComponent<AddWebhookProps> = ({
|
||||
</div>
|
||||
)}
|
||||
</Field>
|
||||
{data && isDelete && (
|
||||
{data && isDelete && deleteWebhookPermission && (
|
||||
<ConfirmationModal
|
||||
bodyText={`You want to delete webhook ${data.name} permanently? This action cannot be reverted.`}
|
||||
cancelText="Cancel"
|
||||
|
@ -34,7 +34,7 @@ import { Operation } from '../../generated/entity/policies/accessControl/rule';
|
||||
import { JWTTokenExpiry, User } from '../../generated/entity/teams/user';
|
||||
import { EntityReference } from '../../generated/type/entityReference';
|
||||
import { getEntityName, requiredField } from '../../utils/CommonUtils';
|
||||
import { checkPemission } from '../../utils/PermissionsUtils';
|
||||
import { checkPermission } from '../../utils/PermissionsUtils';
|
||||
import { getSettingPath } from '../../utils/RouterUtils';
|
||||
import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
@ -77,18 +77,18 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
const [generateToken, setGenerateToken] = useState<boolean>(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
|
||||
|
@ -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', () => {
|
||||
|
@ -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<number>(INITIAL_PAGING_VALUE);
|
||||
|
||||
const deletePermission = checkPemission(
|
||||
const deletePermission = checkPermission(
|
||||
Operation.Delete,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
@ -103,11 +104,7 @@ const BotListV1 = ({ showDeleted }: BotListV1Props) => {
|
||||
<Space align="center" size={8}>
|
||||
<Tooltip
|
||||
placement="bottom"
|
||||
title={
|
||||
deletePermission
|
||||
? 'Delete'
|
||||
: 'You do not have permissions to perform this action.'
|
||||
}>
|
||||
title={deletePermission ? 'Delete' : NO_PERMISSION_FOR_ACTION}>
|
||||
<Button
|
||||
disabled={!deletePermission}
|
||||
icon={
|
||||
|
@ -36,7 +36,6 @@ import TeamsSelectable from '../TeamsSelectable/TeamsSelectable';
|
||||
import { CreateUserProps } from './CreateUser.interface';
|
||||
|
||||
const CreateUser = ({
|
||||
allowAccess,
|
||||
roles,
|
||||
saveState = 'initial',
|
||||
onCancel,
|
||||
@ -81,10 +80,6 @@ const CreateUser = ({
|
||||
const handleValidation = (
|
||||
event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
|
||||
) => {
|
||||
if (!allowAccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = event.target.value;
|
||||
const eleName = event.target.name;
|
||||
|
||||
@ -188,7 +183,7 @@ const CreateUser = ({
|
||||
* @returns Button
|
||||
*/
|
||||
const getSaveButton = () => {
|
||||
return allowAccess ? (
|
||||
return (
|
||||
<>
|
||||
{saveState === 'waiting' ? (
|
||||
<Button
|
||||
@ -210,9 +205,7 @@ const CreateUser = ({
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
className={classNames('tw-w-16 tw-h-10', {
|
||||
'tw-opacity-40': !allowAccess,
|
||||
})}
|
||||
className={classNames('tw-w-16 tw-h-10')}
|
||||
data-testid="save-user"
|
||||
size="regular"
|
||||
theme="primary"
|
||||
@ -222,7 +215,7 @@ const CreateUser = ({
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
) : null;
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -310,10 +303,8 @@ const CreateUser = ({
|
||||
className={classNames('toggle-switch', { open: isAdmin })}
|
||||
data-testid="admin"
|
||||
onClick={() => {
|
||||
if (allowAccess) {
|
||||
setIsAdmin((prev) => !prev);
|
||||
setIsBot(false);
|
||||
}
|
||||
setIsAdmin((prev) => !prev);
|
||||
setIsBot(false);
|
||||
}}>
|
||||
<div className="switch" />
|
||||
</div>
|
||||
@ -324,10 +315,8 @@ const CreateUser = ({
|
||||
className={classNames('toggle-switch', { open: isBot })}
|
||||
data-testid="bot"
|
||||
onClick={() => {
|
||||
if (allowAccess) {
|
||||
setIsBot((prev) => !prev);
|
||||
setIsAdmin(false);
|
||||
}
|
||||
setIsBot((prev) => !prev);
|
||||
setIsAdmin(false);
|
||||
}}>
|
||||
<div className="switch" />
|
||||
</div>
|
||||
|
@ -16,7 +16,6 @@ import { CreateUser } from '../../generated/api/teams/createUser';
|
||||
import { Role } from '../../generated/entity/teams/role';
|
||||
|
||||
export interface CreateUserProps {
|
||||
allowAccess: boolean;
|
||||
saveState?: LoadingState;
|
||||
roles: Array<Role>;
|
||||
onSave: (data: CreateUser) => void;
|
||||
|
@ -37,7 +37,6 @@ jest.mock('../common/rich-text-editor/RichTextEditor', () => {
|
||||
});
|
||||
|
||||
const propsValue: CreateUserProps = {
|
||||
allowAccess: true,
|
||||
saveState: 'initial',
|
||||
roles: [],
|
||||
forceBot: false,
|
||||
|
@ -16,23 +16,29 @@ import { ItemType } from 'antd/lib/menu/hooks/useItems';
|
||||
import { camelCase } from 'lodash';
|
||||
import React from 'react';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
|
||||
import { GLOBAL_SETTINGS_MENU } from '../../constants/globalSettings.constants';
|
||||
import { useAuth } from '../../hooks/authHooks';
|
||||
import { Operation } from '../../generated/entity/policies/accessControl/rule';
|
||||
import { getGlobalSettingMenus } from '../../utils/GlobalSettingsUtils';
|
||||
import { checkPermission } from '../../utils/PermissionsUtils';
|
||||
import { getSettingPath } from '../../utils/RouterUtils';
|
||||
import { usePermissionProvider } from '../PermissionProvider/PermissionProvider';
|
||||
import { ResourceEntity } from '../PermissionProvider/PermissionProvider.interface';
|
||||
|
||||
const GlobalSettingLeftPanel = () => {
|
||||
const { tab, settingCategory } = useParams<{ [key: string]: string }>();
|
||||
const { isAdminUser } = useAuth();
|
||||
const { isAuthDisabled } = useAuthContext();
|
||||
|
||||
const isHasAccess = isAdminUser || isAuthDisabled;
|
||||
const { permissions } = usePermissionProvider();
|
||||
|
||||
const viewAllPermission = checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.ALL,
|
||||
permissions
|
||||
);
|
||||
|
||||
const history = useHistory();
|
||||
const items: ItemType[] = GLOBAL_SETTINGS_MENU.filter(({ isProtected }) => {
|
||||
if (isHasAccess) {
|
||||
return isHasAccess;
|
||||
if (viewAllPermission) {
|
||||
return viewAllPermission;
|
||||
}
|
||||
|
||||
return !isProtected;
|
||||
@ -44,7 +50,7 @@ const GlobalSettingLeftPanel = () => {
|
||||
'',
|
||||
items,
|
||||
'group',
|
||||
isHasAccess
|
||||
viewAllPermission
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1,3 +1,17 @@
|
||||
/*
|
||||
* Copyright 2021 Collate
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AxiosError } from 'axios';
|
||||
import { Operation } from '../../generated/entity/policies/accessControl/resourcePermission';
|
||||
|
||||
export type UIPermission = {
|
||||
@ -13,35 +27,47 @@ export enum ResourceEntity {
|
||||
BOT = 'bot',
|
||||
CHART = 'chart',
|
||||
DASHBOARD = 'dashboard',
|
||||
DASHBOARDSERVICE = 'dashboardService',
|
||||
DASHBOARD_SERVICE = 'dashboardService',
|
||||
DATABASE = 'database',
|
||||
DATABASESCHEMA = 'databaseSchema',
|
||||
DATABASESERVICE = 'databaseService',
|
||||
DATABASE_SCHEMA = 'databaseSchema',
|
||||
DATABASE_SERVICE = 'databaseService',
|
||||
EVENTS = 'events',
|
||||
FEED = 'feed',
|
||||
GLOSSARY = 'glossary',
|
||||
GLOSSARYTERM = 'glossaryTerm',
|
||||
INGESTIONPIPELINE = 'ingestionPipeline',
|
||||
GLOSSARY_TERM = 'glossaryTerm',
|
||||
INGESTION_PIPELINE = 'ingestionPipeline',
|
||||
LOCATION = 'location',
|
||||
MESSAGINGSERVICE = 'messagingService',
|
||||
MESSAGING_SERVICE = 'messagingService',
|
||||
METRICS = 'metrics',
|
||||
MLMODEL = 'mlmodel',
|
||||
MLMODELSERVICE = 'mlmodelService',
|
||||
ML_MODEL = 'mlmodel',
|
||||
ML_MODEL_SERVICE = 'mlmodelService',
|
||||
PIPELINE = 'pipeline',
|
||||
PIPELINESERVICE = 'pipelineService',
|
||||
PIPELINE_SERVICE = 'pipelineService',
|
||||
POLICY = 'policy',
|
||||
REPORT = 'report',
|
||||
ROLE = 'role',
|
||||
STORAGESERVICE = 'storageService',
|
||||
STORAGE_SERVICE = 'storageService',
|
||||
TABLE = 'table',
|
||||
TAG = 'tag',
|
||||
TAGCATEGORY = 'tagCategory',
|
||||
TAG_CATEGORY = 'tagCategory',
|
||||
TEAM = 'team',
|
||||
TESTCASE = 'testCase',
|
||||
TESTDEFINITION = 'testDefinition',
|
||||
TESTSUITE = 'testSuite',
|
||||
TEST_CASE = 'testCase',
|
||||
TEST_DEFINITION = 'testDefinition',
|
||||
TEST_SUITE = 'testSuite',
|
||||
TOPIC = 'topic',
|
||||
TYPE = 'type',
|
||||
USER = 'user',
|
||||
WEBHOOK = 'webhook',
|
||||
}
|
||||
|
||||
export interface PermissionContextType {
|
||||
permissions: UIPermission;
|
||||
getEntityPermission: (
|
||||
resource: ResourceEntity,
|
||||
entityId: string
|
||||
) => Promise<OperationPermission | AxiosError>;
|
||||
}
|
||||
|
||||
export interface EntityPermissionMap {
|
||||
[key: string]: OperationPermission;
|
||||
}
|
||||
|
@ -24,18 +24,27 @@ import React, {
|
||||
} from 'react';
|
||||
import AppState from '../../AppState';
|
||||
import { getLoggedInUserPermissions } from '../../axiosAPIs/miscAPI';
|
||||
import { getUIPermission } from '../../utils/PermissionsUtils';
|
||||
import { getEntityPermissionById } from '../../axiosAPIs/rolesAPIV1';
|
||||
import {
|
||||
getOperationPermissions,
|
||||
getUIPermission,
|
||||
} from '../../utils/PermissionsUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import { UIPermission } from './PermissionProvider.interface';
|
||||
import {
|
||||
EntityPermissionMap,
|
||||
PermissionContextType,
|
||||
ResourceEntity,
|
||||
UIPermission,
|
||||
} from './PermissionProvider.interface';
|
||||
|
||||
/**
|
||||
* Permission Context
|
||||
* Returns ResourcePermission List for loggedIn User
|
||||
* @returns PermissionMap
|
||||
*/
|
||||
export const PermissionContext = createContext<{
|
||||
permissions: UIPermission;
|
||||
}>({ permissions: {} as UIPermission });
|
||||
export const PermissionContext = createContext<PermissionContextType>(
|
||||
{} as PermissionContextType
|
||||
);
|
||||
|
||||
interface PermissionProviderProps {
|
||||
children: ReactNode;
|
||||
@ -51,6 +60,9 @@ const PermissionProvider: FC<PermissionProviderProps> = ({ children }) => {
|
||||
{} as UIPermission
|
||||
);
|
||||
|
||||
const [entitiesPermission, setEntitiesPermission] =
|
||||
useState<EntityPermissionMap>({} as EntityPermissionMap);
|
||||
|
||||
// Update current user details of AppState change
|
||||
const currentUser = useMemo(() => {
|
||||
return AppState.getCurrentUserDetails();
|
||||
@ -68,6 +80,29 @@ const PermissionProvider: FC<PermissionProviderProps> = ({ children }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchEntityPermission = async (
|
||||
resource: ResourceEntity,
|
||||
entityId: string
|
||||
) => {
|
||||
try {
|
||||
const entityPermission = entitiesPermission[entityId];
|
||||
if (entityPermission) {
|
||||
return entityPermission;
|
||||
} else {
|
||||
const response = await getEntityPermissionById(resource, entityId);
|
||||
const operationPermission = getOperationPermissions(response);
|
||||
setEntitiesPermission((prev) => ({
|
||||
...prev,
|
||||
[entityId]: operationPermission,
|
||||
}));
|
||||
|
||||
return operationPermission;
|
||||
}
|
||||
} catch (error) {
|
||||
return error as AxiosError;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
/**
|
||||
* Only fetch permission if user is logged In
|
||||
@ -78,7 +113,11 @@ const PermissionProvider: FC<PermissionProviderProps> = ({ children }) => {
|
||||
}, [currentUser]);
|
||||
|
||||
return (
|
||||
<PermissionContext.Provider value={{ permissions }}>
|
||||
<PermissionContext.Provider
|
||||
value={{
|
||||
permissions,
|
||||
getEntityPermission: fetchEntityPermission,
|
||||
}}>
|
||||
{children}
|
||||
</PermissionContext.Provider>
|
||||
);
|
||||
|
@ -11,8 +11,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Card, Col, Row } from 'antd';
|
||||
import React, { Fragment } from 'react';
|
||||
import { Card, Col, Row, Tooltip } from 'antd';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { Fragment, useMemo } from 'react';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
|
||||
import {
|
||||
@ -20,26 +21,32 @@ import {
|
||||
PAGE_SIZE,
|
||||
TITLE_FOR_NON_ADMIN_ACTION,
|
||||
} from '../../constants/constants';
|
||||
import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil';
|
||||
import {
|
||||
NoDataFoundPlaceHolder,
|
||||
servicesDisplayName,
|
||||
} from '../../constants/services.const';
|
||||
import { ServiceCategory } from '../../enums/service.enum';
|
||||
import { Operation } from '../../generated/entity/policies/policy';
|
||||
import { Paging } from '../../generated/type/paging';
|
||||
import { useAuth } from '../../hooks/authHooks';
|
||||
import { ServicesType } from '../../interface/service.interface';
|
||||
import {
|
||||
getEntityName,
|
||||
getServiceLogo,
|
||||
showPagination,
|
||||
} from '../../utils/CommonUtils';
|
||||
import { checkPermission } from '../../utils/PermissionsUtils';
|
||||
import { getAddServicePath } from '../../utils/RouterUtils';
|
||||
import { getOptionalFields } from '../../utils/ServiceUtils';
|
||||
import {
|
||||
getOptionalFields,
|
||||
getResourceEntityFromServiceCategory,
|
||||
} from '../../utils/ServiceUtils';
|
||||
import { Button } from '../buttons/Button/Button';
|
||||
import NextPrevious from '../common/next-previous/NextPrevious';
|
||||
import NonAdminAction from '../common/non-admin-action/NonAdminAction';
|
||||
import RichTextEditorPreviewer from '../common/rich-text-editor/RichTextEditorPreviewer';
|
||||
import { leftPanelAntCardStyle } from '../containers/PageLayout';
|
||||
import { usePermissionProvider } from '../PermissionProvider/PermissionProvider';
|
||||
|
||||
interface ServicesProps {
|
||||
serviceData: ServicesType[];
|
||||
@ -56,33 +63,49 @@ const Services = ({
|
||||
currentPage,
|
||||
onPageChange,
|
||||
}: ServicesProps) => {
|
||||
const { isAdminUser } = useAuth();
|
||||
const { isAuthDisabled } = useAuthContext();
|
||||
const history = useHistory();
|
||||
const handleAddServiceClick = () => {
|
||||
history.push(getAddServicePath(serviceName));
|
||||
};
|
||||
|
||||
const { permissions } = usePermissionProvider();
|
||||
|
||||
const addServicePermission = useMemo(
|
||||
() =>
|
||||
!isEmpty(permissions) &&
|
||||
checkPermission(
|
||||
Operation.Create,
|
||||
getResourceEntityFromServiceCategory(serviceName),
|
||||
permissions
|
||||
),
|
||||
[permissions, serviceName]
|
||||
);
|
||||
|
||||
return (
|
||||
<Row data-testid="services-container">
|
||||
{serviceData.length ? (
|
||||
<Fragment>
|
||||
<Col span={24}>
|
||||
<div className="tw-flex tw-justify-end" data-testid="header">
|
||||
<NonAdminAction
|
||||
position="bottom"
|
||||
title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||
<Tooltip
|
||||
placement="left"
|
||||
title={
|
||||
addServicePermission
|
||||
? 'Add Service'
|
||||
: NO_PERMISSION_FOR_ACTION
|
||||
}>
|
||||
<Button
|
||||
className="tw-h-8 tw-rounded tw-mb-2"
|
||||
data-testid="add-new-service-button"
|
||||
disabled={!isAdminUser && !isAuthDisabled}
|
||||
disabled={!addServicePermission && !isAuthDisabled}
|
||||
size="small"
|
||||
theme="primary"
|
||||
variant="contained"
|
||||
onClick={handleAddServiceClick}>
|
||||
Add New Service
|
||||
</Button>
|
||||
</NonAdminAction>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
|
@ -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<WebhooksV1Props> = ({
|
||||
onStatusFilter,
|
||||
currentPage,
|
||||
}) => {
|
||||
const { isAuthDisabled, isAdminUser } = useAuth();
|
||||
const [filteredData, setFilteredData] = useState<Array<Webhook>>(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<WebhooksV1Props> = ({
|
||||
<ErrorPlaceHolder>
|
||||
<p className="tw-text-center">{message}</p>
|
||||
<p className="tw-text-center">
|
||||
<NonAdminAction
|
||||
position="bottom"
|
||||
title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||
<Tooltip
|
||||
placement="left"
|
||||
title={
|
||||
addWebhookPermission ? 'Add Webhook' : NO_PERMISSION_FOR_ACTION
|
||||
}>
|
||||
<Button
|
||||
className={classNames('tw-h-8 tw-rounded tw-my-3', {
|
||||
'tw-opacity-40': !isAdminUser && !isAuthDisabled,
|
||||
})}
|
||||
className={classNames('tw-h-8 tw-rounded tw-my-3')}
|
||||
data-testid="add-webhook-button"
|
||||
disabled={!addWebhookPermission}
|
||||
size="small"
|
||||
theme="primary"
|
||||
variant="contained"
|
||||
onClick={onAddWebhook}>
|
||||
Add {webhookType === WebhookType.Slack ? 'Slack' : 'Webhook'}
|
||||
</Button>
|
||||
</NonAdminAction>
|
||||
</Tooltip>
|
||||
</p>
|
||||
</ErrorPlaceHolder>
|
||||
);
|
||||
@ -131,14 +140,17 @@ const WebhooksV1: FC<WebhooksV1Props> = ({
|
||||
className="tw-w-full tw-justify-end"
|
||||
size={16}>
|
||||
{filteredData.length > 0 && (
|
||||
<NonAdminAction
|
||||
position="bottom"
|
||||
title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||
<Tooltip
|
||||
placement="left"
|
||||
title={
|
||||
addWebhookPermission
|
||||
? 'Add Webhook'
|
||||
: NO_PERMISSION_FOR_ACTION
|
||||
}>
|
||||
<Button
|
||||
className={classNames('tw-h-8 tw-rounded ', {
|
||||
'tw-opacity-40': !isAdminUser && !isAuthDisabled,
|
||||
})}
|
||||
className={classNames('tw-h-8 tw-rounded ')}
|
||||
data-testid="add-webhook-button"
|
||||
disabled={!addWebhookPermission}
|
||||
size="small"
|
||||
theme="primary"
|
||||
variant="contained"
|
||||
@ -146,7 +158,7 @@ const WebhooksV1: FC<WebhooksV1Props> = ({
|
||||
Add{' '}
|
||||
{webhookType === WebhookType.Slack ? 'Slack' : 'Webhook'}
|
||||
</Button>
|
||||
</NonAdminAction>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Space>
|
||||
</Col>
|
||||
|
@ -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';
|
||||
|
@ -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 (
|
||||
<Row gutter={[16, 16]}>
|
||||
{viewAllPermission ? (
|
||||
<>
|
||||
<Col flex={1} />
|
||||
<Col>
|
||||
<Space size={16}>
|
||||
<Space align="end" size={5}>
|
||||
<Switch
|
||||
checked={showDeleted}
|
||||
id="switch-deleted"
|
||||
size="small"
|
||||
onClick={handleShowDeleted}
|
||||
/>
|
||||
<label htmlFor="switch-deleted">Show deleted</label>
|
||||
</Space>
|
||||
<Col flex={1} />
|
||||
<Col>
|
||||
<Space size={16}>
|
||||
<Space align="end" size={5}>
|
||||
<Switch
|
||||
checked={showDeleted}
|
||||
id="switch-deleted"
|
||||
size="small"
|
||||
onClick={handleShowDeleted}
|
||||
/>
|
||||
<label htmlFor="switch-deleted">Show deleted</label>
|
||||
</Space>
|
||||
|
||||
{createPermission && (
|
||||
<Button type="primary" onClick={handleAddBotClick}>
|
||||
Add Bot
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<BotListV1 showDeleted={showDeleted} />
|
||||
</Col>
|
||||
</>
|
||||
) : (
|
||||
<Empty description="You do not have permission to view data" />
|
||||
)}
|
||||
{createPermission && (
|
||||
<Button type="primary" onClick={handleAddBotClick}>
|
||||
Add Bot
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<BotListV1 showDeleted={showDeleted} />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
@ -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<Array<Role>>([]);
|
||||
@ -128,7 +124,6 @@ const CreateUserPage = () => {
|
||||
<PageContainerV1>
|
||||
<div className="tw-self-center">
|
||||
<CreateUserComponent
|
||||
allowAccess={isAdminUser || isAuthDisabled}
|
||||
forceBot={Boolean(bot)}
|
||||
roles={roles}
|
||||
saveState={status}
|
||||
|
@ -11,10 +11,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Col, Row } from 'antd';
|
||||
import { Col, Empty, Row } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { compare } from 'fast-json-patch';
|
||||
import { isUndefined } from 'lodash';
|
||||
import { isEmpty, isUndefined } from 'lodash';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { getTypeByFQN, updateType } from '../../axiosAPIs/metadataTypeAPI';
|
||||
@ -23,11 +23,16 @@ import ErrorPlaceHolder from '../../components/common/error-with-placeholder/Err
|
||||
import TabsPane from '../../components/common/TabsPane/TabsPane';
|
||||
import { CustomPropertyTable } from '../../components/CustomEntityDetail/CustomPropertyTable';
|
||||
import Loader from '../../components/Loader/Loader';
|
||||
import { usePermissionProvider } from '../../components/PermissionProvider/PermissionProvider';
|
||||
import SchemaEditor from '../../components/schema-editor/SchemaEditor';
|
||||
import { getAddCustomPropertyPath } from '../../constants/constants';
|
||||
import { customAttributesPath } from '../../constants/globalSettings.constants';
|
||||
import { NO_PERMISSION_TO_VIEW } from '../../constants/HelperTextUtil';
|
||||
import { Operation } from '../../generated/entity/policies/policy';
|
||||
import { Type } from '../../generated/entity/type';
|
||||
import jsonData from '../../jsons/en';
|
||||
import { getResourceEntityFromCustomProperty } from '../../utils/CustomPropertyUtils';
|
||||
import { checkPermission } from '../../utils/PermissionsUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import './CustomPropertiesPageV1.less';
|
||||
|
||||
@ -44,6 +49,19 @@ const CustomEntityDetailV1 = () => {
|
||||
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 ? (
|
||||
<Row
|
||||
className="tw-my-2"
|
||||
data-testid="custom-entity-container"
|
||||
@ -162,6 +180,12 @@ const CustomEntityDetailV1 = () => {
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
) : (
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Empty description={NO_PERMISSION_TO_VIEW} />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -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<Policy>({} as Policy);
|
||||
const [isLoading, setLoading] = useState<boolean>(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)}
|
||||
|
@ -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<PolicyListProps> = ({ policies, fetchPolicies }) => {
|
||||
const [selectedPolicy, setSelectedPolicy] = useState<Policy>();
|
||||
|
||||
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<Policy> = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
@ -71,27 +94,39 @@ const PoliciesList: FC<PolicyListProps> = ({ policies, fetchPolicies }) => {
|
||||
|
||||
return record.roles?.length ? (
|
||||
<Space wrap data-testid="role-link" size={4}>
|
||||
{record.roles.slice(0, LIST_CAP).map((role) => (
|
||||
<Link
|
||||
key={uniqueId()}
|
||||
to={getRoleWithFqnPath(role.fullyQualifiedName || '')}>
|
||||
{getEntityName(role)}
|
||||
</Link>
|
||||
))}
|
||||
{record.roles.slice(0, LIST_CAP).map((role) =>
|
||||
viewRolePermission ? (
|
||||
<Link
|
||||
key={uniqueId()}
|
||||
to={getRoleWithFqnPath(role.fullyQualifiedName || '')}>
|
||||
{getEntityName(role)}
|
||||
</Link>
|
||||
) : (
|
||||
<Tooltip title={NO_PERMISSION_TO_VIEW}>
|
||||
{getEntityName(role)}
|
||||
</Tooltip>
|
||||
)
|
||||
)}
|
||||
{hasMore && (
|
||||
<Popover
|
||||
className="tw-cursor-pointer"
|
||||
content={
|
||||
<Space wrap size={4}>
|
||||
{record.roles.slice(LIST_CAP).map((role) => (
|
||||
<Link
|
||||
key={uniqueId()}
|
||||
to={getRoleWithFqnPath(
|
||||
role.fullyQualifiedName || ''
|
||||
)}>
|
||||
{getEntityName(role)}
|
||||
</Link>
|
||||
))}
|
||||
{record.roles.slice(LIST_CAP).map((role) =>
|
||||
viewRolePermission ? (
|
||||
<Link
|
||||
key={uniqueId()}
|
||||
to={getRoleWithFqnPath(
|
||||
role.fullyQualifiedName || ''
|
||||
)}>
|
||||
{getEntityName(role)}
|
||||
</Link>
|
||||
) : (
|
||||
<Tooltip title={NO_PERMISSION_TO_VIEW}>
|
||||
{getEntityName(role)}
|
||||
</Tooltip>
|
||||
)
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
overlayClassName="tw-w-40 tw-text-center"
|
||||
@ -114,12 +149,19 @@ const PoliciesList: FC<PolicyListProps> = ({ policies, fetchPolicies }) => {
|
||||
key: 'actions',
|
||||
render: (_, record) => {
|
||||
return (
|
||||
<Button
|
||||
data-testid={`delete-action-${getEntityName(record)}`}
|
||||
type="text"
|
||||
onClick={() => setSelectedPolicy(record)}>
|
||||
<SVGIcons alt="delete" icon={Icons.DELETE} width="18px" />
|
||||
</Button>
|
||||
<Tooltip
|
||||
placement="left"
|
||||
title={
|
||||
deletePolicyPermission ? 'Delete' : NO_PERMISSION_FOR_ACTION
|
||||
}>
|
||||
<Button
|
||||
data-testid={`delete-action-${getEntityName(record)}`}
|
||||
disabled={!deletePolicyPermission}
|
||||
type="text"
|
||||
onClick={() => setSelectedPolicy(record)}>
|
||||
<SVGIcons alt="delete" icon={Icons.DELETE} width="18px" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -135,7 +177,7 @@ const PoliciesList: FC<PolicyListProps> = ({ policies, fetchPolicies }) => {
|
||||
pagination={false}
|
||||
size="middle"
|
||||
/>
|
||||
{selectedPolicy && (
|
||||
{selectedPolicy && deletePolicyPermission && (
|
||||
<DeleteWidgetModal
|
||||
afterDeleteAction={fetchPolicies}
|
||||
allowSoftDelete={false}
|
||||
|
@ -11,20 +11,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Col, Row, Space } from 'antd';
|
||||
import { Button, Col, Row, Space, Tooltip } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { getPolicies } from '../../../axiosAPIs/rolesAPIV1';
|
||||
import NextPrevious from '../../../components/common/next-previous/NextPrevious';
|
||||
import Loader from '../../../components/Loader/Loader';
|
||||
import { usePermissionProvider } from '../../../components/PermissionProvider/PermissionProvider';
|
||||
import { ResourceEntity } from '../../../components/PermissionProvider/PermissionProvider.interface';
|
||||
import {
|
||||
INITIAL_PAGING_VALUE,
|
||||
PAGE_SIZE,
|
||||
ROUTES,
|
||||
} from '../../../constants/constants';
|
||||
import { Policy } from '../../../generated/entity/policies/policy';
|
||||
import { NO_PERMISSION_FOR_ACTION } from '../../../constants/HelperTextUtil';
|
||||
import { Operation, Policy } from '../../../generated/entity/policies/policy';
|
||||
import { Paging } from '../../../generated/type/paging';
|
||||
import { checkPermission } from '../../../utils/PermissionsUtils';
|
||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||
import PoliciesList from './PoliciesList';
|
||||
import './PoliciesList.less';
|
||||
@ -36,6 +41,15 @@ const PoliciesListPage = () => {
|
||||
const [paging, setPaging] = useState<Paging>();
|
||||
const [currentPage, setCurrentPage] = useState<number>(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 = () => {
|
||||
<Row className="policies-list-container" gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<Space align="center" className="tw-w-full tw-justify-end" size={16}>
|
||||
<Button
|
||||
data-testid="add-policy"
|
||||
type="primary"
|
||||
onClick={handleAddPolicy}>
|
||||
Add Policy
|
||||
</Button>
|
||||
<Tooltip
|
||||
placement="left"
|
||||
title={
|
||||
addPolicyPermission ? 'Add Policy' : NO_PERMISSION_FOR_ACTION
|
||||
}>
|
||||
<Button
|
||||
data-testid="add-policy"
|
||||
disabled={!addPolicyPermission}
|
||||
type="primary"
|
||||
onClick={handleAddPolicy}>
|
||||
Add Policy
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
|
@ -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<Role>({} 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)}
|
||||
|
@ -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<RolesListProps> = ({ roles, fetchRoles }) => {
|
||||
const [selectedRole, setSelectedRole] = useState<Role>();
|
||||
|
||||
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<Role> = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
@ -72,27 +95,39 @@ const RolesList: FC<RolesListProps> = ({ roles, fetchRoles }) => {
|
||||
|
||||
return record.policies?.length ? (
|
||||
<Space wrap data-testid="policy-link" size={4}>
|
||||
{record.policies.slice(0, LIST_CAP).map((policy) => (
|
||||
<Link
|
||||
key={uniqueId()}
|
||||
to={getPolicyWithFqnPath(policy.fullyQualifiedName || '')}>
|
||||
{getEntityName(policy)}
|
||||
</Link>
|
||||
))}
|
||||
{record.policies.slice(0, LIST_CAP).map((policy) =>
|
||||
viewPolicyPermission ? (
|
||||
<Link
|
||||
key={uniqueId()}
|
||||
to={getPolicyWithFqnPath(policy.fullyQualifiedName || '')}>
|
||||
{getEntityName(policy)}
|
||||
</Link>
|
||||
) : (
|
||||
<Tooltip title={NO_PERMISSION_TO_VIEW}>
|
||||
{getEntityName(policy)}
|
||||
</Tooltip>
|
||||
)
|
||||
)}
|
||||
{hasMore && (
|
||||
<Popover
|
||||
className="tw-cursor-pointer"
|
||||
content={
|
||||
<Space wrap size={4}>
|
||||
{record.policies.slice(LIST_CAP).map((policy) => (
|
||||
<Link
|
||||
key={uniqueId()}
|
||||
to={getPolicyWithFqnPath(
|
||||
policy.fullyQualifiedName || ''
|
||||
)}>
|
||||
{getEntityName(policy)}
|
||||
</Link>
|
||||
))}
|
||||
{record.policies.slice(LIST_CAP).map((policy) =>
|
||||
viewPolicyPermission ? (
|
||||
<Link
|
||||
key={uniqueId()}
|
||||
to={getPolicyWithFqnPath(
|
||||
policy.fullyQualifiedName || ''
|
||||
)}>
|
||||
{getEntityName(policy)}
|
||||
</Link>
|
||||
) : (
|
||||
<Tooltip title={NO_PERMISSION_TO_VIEW}>
|
||||
{getEntityName(policy)}
|
||||
</Tooltip>
|
||||
)
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
overlayClassName="tw-w-40 tw-text-center"
|
||||
@ -115,12 +150,21 @@ const RolesList: FC<RolesListProps> = ({ roles, fetchRoles }) => {
|
||||
key: 'actions',
|
||||
render: (_, record) => {
|
||||
return (
|
||||
<Button
|
||||
data-testid={`delete-action-${getEntityName(record)}`}
|
||||
type="text"
|
||||
onClick={() => setSelectedRole(record)}>
|
||||
<SVGIcons alt="delete" icon={Icons.DELETE} width="18px" />
|
||||
</Button>
|
||||
<Tooltip
|
||||
placement="left"
|
||||
title={
|
||||
deleteRolePermission ? 'Delete' : NO_PERMISSION_FOR_ACTION
|
||||
}>
|
||||
<Button
|
||||
data-testid={`delete-action-${getEntityName(record)}`}
|
||||
disabled={!deleteRolePermission}
|
||||
icon={
|
||||
<SVGIcons alt="delete" icon={Icons.DELETE} width="18px" />
|
||||
}
|
||||
type="text"
|
||||
onClick={() => setSelectedRole(record)}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
@ -42,7 +42,27 @@ jest.mock('./RolesList', () =>
|
||||
jest.fn().mockReturnValue(<div data-testid="roles-list">RolesList</div>)
|
||||
);
|
||||
|
||||
describe('Test Roled List Page', () => {
|
||||
jest.mock('../../../utils/PermissionsUtils', () => ({
|
||||
checkPermission: jest.fn().mockReturnValue(true),
|
||||
}));
|
||||
|
||||
jest.mock('../../../components/PermissionProvider/PermissionProvider', () => ({
|
||||
usePermissionProvider: jest.fn().mockReturnValue({
|
||||
permissions: {
|
||||
role: {
|
||||
Create: true,
|
||||
Delete: true,
|
||||
ViewAll: true,
|
||||
EditAll: true,
|
||||
EditDescription: true,
|
||||
EditDisplayName: true,
|
||||
EditCustomFields: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('Test Roles List Page', () => {
|
||||
it('Should render the list component', async () => {
|
||||
render(<RolesListPage />);
|
||||
|
||||
|
@ -11,20 +11,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Col, Row, Space } from 'antd';
|
||||
import { Button, Col, Row, Space, Tooltip } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { getRoles } from '../../../axiosAPIs/rolesAPIV1';
|
||||
import NextPrevious from '../../../components/common/next-previous/NextPrevious';
|
||||
import Loader from '../../../components/Loader/Loader';
|
||||
import { usePermissionProvider } from '../../../components/PermissionProvider/PermissionProvider';
|
||||
import { ResourceEntity } from '../../../components/PermissionProvider/PermissionProvider.interface';
|
||||
import {
|
||||
INITIAL_PAGING_VALUE,
|
||||
PAGE_SIZE,
|
||||
ROUTES,
|
||||
} from '../../../constants/constants';
|
||||
import { NO_PERMISSION_FOR_ACTION } from '../../../constants/HelperTextUtil';
|
||||
import { Operation } from '../../../generated/entity/policies/policy';
|
||||
import { Role } from '../../../generated/entity/teams/role';
|
||||
import { Paging } from '../../../generated/type/paging';
|
||||
import { checkPermission } from '../../../utils/PermissionsUtils';
|
||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||
import RolesList from './RolesList';
|
||||
import './RolesList.less';
|
||||
@ -37,6 +43,15 @@ const RolesListPage = () => {
|
||||
const [paging, setPaging] = useState<Paging>();
|
||||
const [currentPage, setCurrentPage] = useState<number>(INITIAL_PAGING_VALUE);
|
||||
|
||||
const { permissions } = usePermissionProvider();
|
||||
|
||||
const addRolePermission = useMemo(() => {
|
||||
return (
|
||||
!isEmpty(permissions) &&
|
||||
checkPermission(Operation.Create, ResourceEntity.ROLE, permissions)
|
||||
);
|
||||
}, [permissions]);
|
||||
|
||||
const fetchRoles = async (paging?: Paging) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
@ -77,9 +92,17 @@ const RolesListPage = () => {
|
||||
gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<Space align="center" className="tw-w-full tw-justify-end" size={16}>
|
||||
<Button data-testid="add-role" type="primary" onClick={handleAddRole}>
|
||||
Add Role
|
||||
</Button>
|
||||
<Tooltip
|
||||
placement="left"
|
||||
title={addRolePermission ? 'Add Role' : NO_PERMISSION_FOR_ACTION}>
|
||||
<Button
|
||||
data-testid="add-role"
|
||||
disabled={!addRolePermission}
|
||||
type="primary"
|
||||
onClick={handleAddRole}>
|
||||
Add Role
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
|
@ -887,7 +887,7 @@ const ServicePage: FunctionComponent = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Row className="tw-my-4" gutter={[16, 16]}>
|
||||
{isLoading ? (
|
||||
<Loader />
|
||||
) : isError ? (
|
||||
|
@ -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<number>(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 <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
return viewAllPermission ? (
|
||||
<Services
|
||||
currentPage={currentPage}
|
||||
paging={paging}
|
||||
@ -86,6 +106,12 @@ const ServicesPage = () => {
|
||||
serviceName={serviceName}
|
||||
onPageChange={handlePageChange}
|
||||
/>
|
||||
) : (
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Empty description={NO_PERMISSION_TO_VIEW} />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -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 (
|
||||
<Switch>
|
||||
<Route exact component={MyDataPage} path={ROUTES.MY_DATA} />
|
||||
@ -208,11 +212,21 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={AddIngestionPage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.Create,
|
||||
ResourceEntity.INGESTION_PIPELINE,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.ADD_INGESTION}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={EditIngestionPage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.EditAll,
|
||||
ResourceEntity.INGESTION_PIPELINE,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.EDIT_INGESTION}
|
||||
/>
|
||||
<Route exact component={SignupPage} path={ROUTES.SIGNUP}>
|
||||
@ -290,47 +304,92 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={AddGlossaryPage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.Create,
|
||||
ResourceEntity.GLOSSARY,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.ADD_GLOSSARY}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={AddGlossaryTermPage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.Create,
|
||||
ResourceEntity.GLOSSARY_TERM,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.ADD_GLOSSARY_TERMS_CHILD}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={AddGlossaryTermPage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.Create,
|
||||
ResourceEntity.GLOSSARY_TERM,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.ADD_GLOSSARY_TERMS}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={AddWebhookPage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.Create,
|
||||
ResourceEntity.WEBHOOK,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.ADD_WEBHOOK_WITH_TYPE}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={AddWebhookPage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.Create,
|
||||
ResourceEntity.WEBHOOK,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.ADD_WEBHOOK}
|
||||
/>
|
||||
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={CreateUserPage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.Create,
|
||||
ResourceEntity.USER,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.CREATE_USER}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={CreateUserPage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.Create,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.CREATE_USER_WITH_BOT}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={BotDetailsPage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.BOTS_PROFILE}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={AddCustomProperty}
|
||||
hasPermission={checkPermission(
|
||||
Operation.Create,
|
||||
ResourceEntity.TYPE,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.ADD_CUSTOM_PROPERTY}
|
||||
/>
|
||||
<Route
|
||||
@ -352,21 +411,41 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={AddRolePage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.Create,
|
||||
ResourceEntity.ROLE,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.ADD_ROLE}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={AddPolicyPage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.Create,
|
||||
ResourceEntity.POLICY,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.ADD_POLICY}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={AddRulePage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.Create,
|
||||
ResourceEntity.POLICY,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.ADD_POLICY_RULE}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={EditRulePage}
|
||||
hasPermission={checkPermission(
|
||||
Operation.EditAll,
|
||||
ResourceEntity.POLICY,
|
||||
permissions
|
||||
)}
|
||||
path={ROUTES.EDIT_POLICY_RULE}
|
||||
/>
|
||||
|
||||
|
@ -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 = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={RolesListPage}
|
||||
hasPermission={checkPemission(
|
||||
hasPermission={checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.ROLE,
|
||||
permissions
|
||||
@ -117,7 +117,7 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={RolesDetailPage}
|
||||
hasPermission={checkPemission(
|
||||
hasPermission={checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.ROLE,
|
||||
permissions
|
||||
@ -135,7 +135,7 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={PoliciesListPage}
|
||||
hasPermission={checkPemission(
|
||||
hasPermission={checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.POLICY,
|
||||
permissions
|
||||
@ -148,7 +148,7 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={PoliciesDetailPage}
|
||||
hasPermission={checkPemission(
|
||||
hasPermission={checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.POLICY,
|
||||
permissions
|
||||
@ -162,7 +162,7 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={UserListPageV1}
|
||||
hasPermission={checkPemission(
|
||||
hasPermission={checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.USER,
|
||||
permissions
|
||||
@ -173,7 +173,7 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={WebhooksPageV1}
|
||||
hasPermission={checkPemission(
|
||||
hasPermission={checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.WEBHOOK,
|
||||
permissions
|
||||
@ -186,7 +186,7 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={BotsPageV1}
|
||||
hasPermission={checkPemission(
|
||||
hasPermission={checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
@ -200,7 +200,7 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={SlackSettingsPage}
|
||||
hasPermission={checkPemission(
|
||||
hasPermission={checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.WEBHOOK,
|
||||
permissions
|
||||
@ -220,7 +220,7 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={CustomPropertiesPageV1}
|
||||
hasPermission={checkPemission(
|
||||
hasPermission={checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.ALL,
|
||||
permissions
|
||||
|
@ -29,3 +29,4 @@
|
||||
|
||||
@import url('./components/table.less');
|
||||
@import url('./components/toggle-switch.less');
|
||||
@import url('./components/button.less');
|
||||
|
@ -0,0 +1,7 @@
|
||||
.ant-btn {
|
||||
&:disabled {
|
||||
img {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { ResourceEntity } from '../components/PermissionProvider/PermissionProvider.interface';
|
||||
|
||||
export const getResourceEntityFromCustomProperty = (property: string) => {
|
||||
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;
|
||||
};
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user