mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-31 12:39:01 +00:00
* Fix (#6971) UI : Add Permission Provider * Fix description unit test * Fix unit tests * Addressing review comments * Add checkPermission util * Add permission for protected route * Clean up * Fix rule condition validation issue
This commit is contained in:
parent
5cefe1bfbb
commit
1081254f7b
@ -31,6 +31,7 @@ import 'react-toastify/dist/ReactToastify.min.css';
|
||||
import { AuthProvider } from './authentication/auth-provider/AuthProvider';
|
||||
import Appbar from './components/app-bar/Appbar';
|
||||
import GlobalSearchProvider from './components/GlobalSearchProvider/GlobalSearchProvider';
|
||||
import PermissionProvider from './components/PermissionProvider/PermissionProvider';
|
||||
import WebSocketProvider from './components/web-scoket/web-scoket.provider';
|
||||
import { toastOptions } from './constants/toast.constants';
|
||||
import ErrorBoundry from './ErrorBoundry/ErrorBoundry';
|
||||
@ -56,12 +57,14 @@ const App: FunctionComponent = () => {
|
||||
<Router>
|
||||
<ErrorBoundry>
|
||||
<AuthProvider childComponentType={AppRouter}>
|
||||
<WebSocketProvider>
|
||||
<GlobalSearchProvider>
|
||||
<Appbar />
|
||||
<AppRouter />
|
||||
</GlobalSearchProvider>
|
||||
</WebSocketProvider>
|
||||
<PermissionProvider>
|
||||
<WebSocketProvider>
|
||||
<GlobalSearchProvider>
|
||||
<Appbar />
|
||||
<AppRouter />
|
||||
</GlobalSearchProvider>
|
||||
</WebSocketProvider>
|
||||
</PermissionProvider>
|
||||
</AuthProvider>
|
||||
</ErrorBoundry>
|
||||
</Router>
|
||||
|
@ -30,9 +30,11 @@ import {
|
||||
GlobalSettingOptions,
|
||||
GlobalSettingsMenuCategory,
|
||||
} from '../../constants/globalSettings.constants';
|
||||
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 { getSettingPath } from '../../utils/RouterUtils';
|
||||
import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
@ -43,6 +45,8 @@ import { reactSingleSelectCustomStyle } from '../common/react-select-component/r
|
||||
import TitleBreadcrumb from '../common/title-breadcrumb/title-breadcrumb.component';
|
||||
import PageLayout, { leftPanelAntCardStyle } from '../containers/PageLayout';
|
||||
import ConfirmationModal from '../Modals/ConfirmationModal/ConfirmationModal';
|
||||
import { usePermissionProvider } from '../PermissionProvider/PermissionProvider';
|
||||
import { ResourceEntity } from '../PermissionProvider/PermissionProvider.interface';
|
||||
import { UserDetails } from '../Users/Users.interface';
|
||||
|
||||
interface BotsDetailProp extends HTMLAttributes<HTMLDivElement> {
|
||||
@ -61,6 +65,7 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
updateBotsDetails,
|
||||
revokeTokenHandler,
|
||||
}) => {
|
||||
const { permissions } = usePermissionProvider();
|
||||
const [displayName, setDisplayName] = useState(botsData.displayName);
|
||||
const [isDisplayNameEdit, setIsDisplayNameEdit] = useState(false);
|
||||
const [isDescriptionEdit, setIsDescriptionEdit] = useState(false);
|
||||
@ -72,6 +77,23 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
const [generateToken, setGenerateToken] = useState<boolean>(false);
|
||||
const [selectedExpiry, setSelectedExpiry] = useState('7');
|
||||
|
||||
const editAllPermission = checkPemission(
|
||||
Operation.EditAll,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
);
|
||||
const displayNamePermission = checkPemission(
|
||||
Operation.EditDisplayName,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
);
|
||||
|
||||
const descriptionPermission = checkPemission(
|
||||
Operation.EditDescription,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
);
|
||||
|
||||
const getJWTTokenExpiryOptions = () => {
|
||||
return Object.keys(JWTTokenExpiry).map((expiry) => {
|
||||
const expiryValue = JWTTokenExpiry[expiry as keyof typeof JWTTokenExpiry];
|
||||
@ -209,13 +231,19 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
Add display name
|
||||
</span>
|
||||
)}
|
||||
|
||||
<button
|
||||
className="tw-ml-2 focus:tw-outline-none"
|
||||
data-testid="edit-displayName"
|
||||
onClick={() => setIsDisplayNameEdit(true)}>
|
||||
<SVGIcons alt="edit" icon="icon-edit" title="Edit" width="16px" />
|
||||
</button>
|
||||
{(displayNamePermission || editAllPermission) && (
|
||||
<button
|
||||
className="tw-ml-2 focus:tw-outline-none"
|
||||
data-testid="edit-displayName"
|
||||
onClick={() => setIsDisplayNameEdit(true)}>
|
||||
<SVGIcons
|
||||
alt="edit"
|
||||
icon="icon-edit"
|
||||
title="Edit"
|
||||
width="16px"
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
@ -226,9 +254,9 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
return (
|
||||
<div className="tw--ml-5">
|
||||
<Description
|
||||
hasEditAccess
|
||||
description={botsData.description || ''}
|
||||
entityName={getEntityName(botsData as unknown as EntityReference)}
|
||||
hasEditAccess={descriptionPermission || editAllPermission}
|
||||
isEdit={isDescriptionEdit}
|
||||
onCancel={() => setIsDescriptionEdit(false)}
|
||||
onDescriptionEdit={() => setIsDescriptionEdit(true)}
|
||||
@ -407,7 +435,7 @@ const BotDetails: FC<BotsDetailProp> = ({
|
||||
<h6 className="tw-mb-2 tw-self-center">
|
||||
{generateToken ? 'Generate JWT token' : 'JWT Token'}
|
||||
</h6>
|
||||
{!generateToken ? (
|
||||
{!generateToken && editAllPermission ? (
|
||||
<div className="tw-flex">
|
||||
<Button
|
||||
data-testid="generate-token"
|
||||
|
@ -68,6 +68,26 @@ const mockProp = {
|
||||
updateBotsDetails,
|
||||
};
|
||||
|
||||
jest.mock('../PermissionProvider/PermissionProvider', () => ({
|
||||
usePermissionProvider: jest.fn().mockReturnValue({
|
||||
permissions: {
|
||||
bot: {
|
||||
Create: true,
|
||||
Delete: true,
|
||||
ViewAll: true,
|
||||
EditAll: true,
|
||||
EditDescription: true,
|
||||
EditDisplayName: true,
|
||||
EditCustomFields: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../utils/PermissionsUtils', () => ({
|
||||
checkPemission: jest.fn().mockReturnValue(true),
|
||||
}));
|
||||
|
||||
jest.mock('../../axiosAPIs/userAPI', () => {
|
||||
return {
|
||||
generateUserToken: jest
|
||||
|
@ -24,22 +24,33 @@ import {
|
||||
} from '../../constants/constants';
|
||||
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 SVGIcons, { Icons } from '../../utils/SvgUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import DeleteWidgetModal from '../common/DeleteWidget/DeleteWidgetModal';
|
||||
import NextPrevious from '../common/next-previous/NextPrevious';
|
||||
import Loader from '../Loader/Loader';
|
||||
import { usePermissionProvider } from '../PermissionProvider/PermissionProvider';
|
||||
import { ResourceEntity } from '../PermissionProvider/PermissionProvider.interface';
|
||||
import { BotListV1Props } from './BotListV1.interfaces';
|
||||
|
||||
const BotListV1 = ({ showDeleted }: BotListV1Props) => {
|
||||
const { permissions } = usePermissionProvider();
|
||||
const [botUsers, setBotUsers] = useState<Bot[]>([]);
|
||||
const [paging, setPaging] = useState<Paging>({} as Paging);
|
||||
const [selectedUser, setSelectedUser] = useState<Bot>();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [currentPage, setCurrentPage] = useState<number>(INITIAL_PAGING_VALUE);
|
||||
|
||||
const deletePermission = checkPemission(
|
||||
Operation.Delete,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param after - Pagination value if passed data will be fetched post cursor value
|
||||
@ -90,8 +101,15 @@ const BotListV1 = ({ showDeleted }: BotListV1Props) => {
|
||||
width: 90,
|
||||
render: (_, record) => (
|
||||
<Space align="center" size={8}>
|
||||
<Tooltip placement="bottom" title="Delete">
|
||||
<Tooltip
|
||||
placement="bottom"
|
||||
title={
|
||||
deletePermission
|
||||
? 'Delete'
|
||||
: 'You do not have permissions to perform this action.'
|
||||
}>
|
||||
<Button
|
||||
disabled={!deletePermission}
|
||||
icon={
|
||||
<SVGIcons
|
||||
alt="Delete"
|
||||
|
@ -0,0 +1,47 @@
|
||||
import { Operation } from '../../generated/entity/policies/accessControl/resourcePermission';
|
||||
|
||||
export type UIPermission = {
|
||||
[key in ResourceEntity]: OperationPermission;
|
||||
};
|
||||
|
||||
export type OperationPermission = {
|
||||
[key in Operation]: boolean;
|
||||
};
|
||||
|
||||
export enum ResourceEntity {
|
||||
ALL = 'all',
|
||||
BOT = 'bot',
|
||||
CHART = 'chart',
|
||||
DASHBOARD = 'dashboard',
|
||||
DASHBOARDSERVICE = 'dashboardService',
|
||||
DATABASE = 'database',
|
||||
DATABASESCHEMA = 'databaseSchema',
|
||||
DATABASESERVICE = 'databaseService',
|
||||
EVENTS = 'events',
|
||||
FEED = 'feed',
|
||||
GLOSSARY = 'glossary',
|
||||
GLOSSARYTERM = 'glossaryTerm',
|
||||
INGESTIONPIPELINE = 'ingestionPipeline',
|
||||
LOCATION = 'location',
|
||||
MESSAGINGSERVICE = 'messagingService',
|
||||
METRICS = 'metrics',
|
||||
MLMODEL = 'mlmodel',
|
||||
MLMODELSERVICE = 'mlmodelService',
|
||||
PIPELINE = 'pipeline',
|
||||
PIPELINESERVICE = 'pipelineService',
|
||||
POLICY = 'policy',
|
||||
REPORT = 'report',
|
||||
ROLE = 'role',
|
||||
STORAGESERVICE = 'storageService',
|
||||
TABLE = 'table',
|
||||
TAG = 'tag',
|
||||
TAGCATEGORY = 'tagCategory',
|
||||
TEAM = 'team',
|
||||
TESTCASE = 'testCase',
|
||||
TESTDEFINITION = 'testDefinition',
|
||||
TESTSUITE = 'testSuite',
|
||||
TOPIC = 'topic',
|
||||
TYPE = 'type',
|
||||
USER = 'user',
|
||||
WEBHOOK = 'webhook',
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 { observer } from 'mobx-react';
|
||||
import React, {
|
||||
createContext,
|
||||
FC,
|
||||
ReactNode,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import AppState from '../../AppState';
|
||||
import { getLoggedInUserPermissions } from '../../axiosAPIs/miscAPI';
|
||||
import { getUIPermission } from '../../utils/PermissionsUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import { UIPermission } from './PermissionProvider.interface';
|
||||
|
||||
/**
|
||||
* Permission Context
|
||||
* Returns ResourcePermission List for loggedIn User
|
||||
* @returns PermissionMap
|
||||
*/
|
||||
export const PermissionContext = createContext<{
|
||||
permissions: UIPermission;
|
||||
}>({ permissions: {} as UIPermission });
|
||||
|
||||
interface PermissionProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param children:ReactNode
|
||||
* @returns JSX
|
||||
*/
|
||||
const PermissionProvider: FC<PermissionProviderProps> = ({ children }) => {
|
||||
const [permissions, setPermissions] = useState<UIPermission>(
|
||||
{} as UIPermission
|
||||
);
|
||||
|
||||
// Update current user details of AppState change
|
||||
const currentUser = useMemo(() => {
|
||||
return AppState.getCurrentUserDetails();
|
||||
}, [AppState.userDetails, AppState.nonSecureUserDetails]);
|
||||
|
||||
/**
|
||||
* Fetch permission for logged in user
|
||||
*/
|
||||
const fetchLoggedInUserPermissions = async () => {
|
||||
try {
|
||||
const response = await getLoggedInUserPermissions();
|
||||
setPermissions(getUIPermission(response.data || []));
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
/**
|
||||
* Only fetch permission if user is logged In
|
||||
*/
|
||||
if (currentUser && currentUser.id) {
|
||||
fetchLoggedInUserPermissions();
|
||||
}
|
||||
}, [currentUser]);
|
||||
|
||||
return (
|
||||
<PermissionContext.Provider value={{ permissions }}>
|
||||
{children}
|
||||
</PermissionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const usePermissionProvider = () => useContext(PermissionContext);
|
||||
|
||||
export default observer(PermissionProvider);
|
@ -93,9 +93,12 @@ jest.mock('../non-admin-action/NonAdminAction', () => {
|
||||
|
||||
describe('Test Description Component', () => {
|
||||
it('Check if it has all child elements', async () => {
|
||||
const { container } = render(<Description {...mockDescriptionProp} />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
const { container } = render(
|
||||
<Description {...mockDescriptionProp} hasEditAccess />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const descriptionContainer = await findByTestId(container, 'description');
|
||||
const editDescriptionButton = await findByTestId(
|
||||
@ -236,4 +239,19 @@ describe('Test Description Component', () => {
|
||||
// should render requestDescription, as description thread and description are empty value
|
||||
expect(requestDescription).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Should not show edit button if hasEditAccess is false', async () => {
|
||||
const { container } = render(
|
||||
<Description {...mockDescriptionProp} hasEditAccess={false} />,
|
||||
{
|
||||
wrapper: MemoryRouter,
|
||||
}
|
||||
);
|
||||
|
||||
const descriptionContainer = await findByTestId(container, 'description');
|
||||
const editDescriptionButton = queryByTestId(container, 'edit-description');
|
||||
|
||||
expect(descriptionContainer).toBeInTheDocument();
|
||||
expect(editDescriptionButton).toBeNull();
|
||||
});
|
||||
});
|
||||
|
@ -17,15 +17,11 @@ import { isUndefined } from 'lodash';
|
||||
import { EntityFieldThreads } from 'Models';
|
||||
import React, { FC, Fragment } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useAuthContext } from '../../../authentication/auth-provider/AuthProvider';
|
||||
import { EntityField } from '../../../constants/feed.constants';
|
||||
import { EntityType } from '../../../enums/entity.enum';
|
||||
import { ThreadType } from '../../../generated/entity/feed/thread';
|
||||
import { Operation } from '../../../generated/entity/policies/accessControl/rule';
|
||||
import { useAuth } from '../../../hooks/authHooks';
|
||||
import { isTaskSupported } from '../../../utils/CommonUtils';
|
||||
import { getEntityFeedLink } from '../../../utils/EntityUtils';
|
||||
import { hasPemission } from '../../../utils/PermissionsUtils';
|
||||
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
|
||||
import {
|
||||
getRequestDescriptionPath,
|
||||
@ -54,9 +50,6 @@ const Description: FC<DescriptionProps> = ({
|
||||
}) => {
|
||||
const history = useHistory();
|
||||
|
||||
const { isAdminUser, userPermissions } = useAuth();
|
||||
const { isAuthDisabled } = useAuthContext();
|
||||
|
||||
const thread = entityFieldThreads?.[0];
|
||||
const tasks = entityFieldTasks?.[0];
|
||||
|
||||
@ -72,19 +65,6 @@ const Description: FC<DescriptionProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const checkPermission = () => {
|
||||
return (
|
||||
isAdminUser ||
|
||||
Boolean(hasEditAccess) ||
|
||||
hasPemission(
|
||||
Operation.EditDescription,
|
||||
entityType as EntityType,
|
||||
userPermissions
|
||||
) ||
|
||||
isAuthDisabled
|
||||
);
|
||||
};
|
||||
|
||||
const handleUpdate = () => {
|
||||
onDescriptionEdit && onDescriptionEdit();
|
||||
};
|
||||
@ -179,7 +159,7 @@ const Description: FC<DescriptionProps> = ({
|
||||
const DescriptionActions = () => {
|
||||
return !isReadOnly ? (
|
||||
<div className={classNames('tw-w-5 tw-min-w-max tw-flex tw--mt-1')}>
|
||||
{checkPermission() && (
|
||||
{hasEditAccess && (
|
||||
<button
|
||||
className="tw-w-7 tw-h-8 tw-flex-none focus:tw-outline-none"
|
||||
data-testid="edit-description"
|
||||
|
@ -11,13 +11,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Col, Row, Space, Switch } from 'antd';
|
||||
import { Button, Col, Empty, Row, Space, Switch } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import BotListV1 from '../../components/BotListV1/BotListV1.component';
|
||||
import { usePermissionProvider } from '../../components/PermissionProvider/PermissionProvider';
|
||||
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';
|
||||
|
||||
export const BotsPageV1 = () => {
|
||||
const { permissions } = usePermissionProvider();
|
||||
const history = useHistory();
|
||||
const [showDeleted, setShowDeleted] = useState(false);
|
||||
|
||||
@ -29,28 +34,49 @@ export const BotsPageV1 = () => {
|
||||
setShowDeleted(checked);
|
||||
};
|
||||
|
||||
const viewAllPermission = checkPemission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
);
|
||||
|
||||
const createPermission = checkPemission(
|
||||
Operation.Create,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
);
|
||||
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<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>
|
||||
<Button type="primary" onClick={handleAddBotClick}>
|
||||
Add Bot
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<BotListV1 showDeleted={showDeleted} />
|
||||
</Col>
|
||||
{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>
|
||||
|
||||
{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" />
|
||||
)}
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
@ -80,11 +80,12 @@ const AddPolicyPage = () => {
|
||||
};
|
||||
|
||||
const handleSumbit = async () => {
|
||||
const { condition, ...rest } = ruleData;
|
||||
const data: CreatePolicy = {
|
||||
name,
|
||||
description,
|
||||
policyType: PolicyType.AccessControl,
|
||||
rules: [ruleData],
|
||||
rules: [condition ? { ...rest, condition } : rest],
|
||||
};
|
||||
|
||||
try {
|
||||
|
@ -87,9 +87,10 @@ const AddRulePage = () => {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const { condition, ...rest } = ruleData;
|
||||
const patch = compare(policy, {
|
||||
...policy,
|
||||
rules: [...policy.rules, ruleData],
|
||||
rules: [...policy.rules, condition ? { ...rest, condition } : rest],
|
||||
});
|
||||
try {
|
||||
const data = await patchPolicy(patch, policy.id);
|
||||
|
@ -98,11 +98,14 @@ const EditRulePage = () => {
|
||||
const existingRules = policy.rules;
|
||||
const updatedRules = existingRules.map((rule) => {
|
||||
if (rule.name === ruleName) {
|
||||
return ruleData;
|
||||
const { condition, ...rest } = ruleData;
|
||||
|
||||
return condition ? { ...rest, condition } : rest;
|
||||
} else {
|
||||
return rule;
|
||||
}
|
||||
});
|
||||
|
||||
const patch = compare(policy, {
|
||||
...policy,
|
||||
rules: updatedRules,
|
||||
|
@ -17,11 +17,15 @@ import { useAuthContext } from '../authentication/auth-provider/AuthProvider';
|
||||
import { ROUTES } from '../constants/constants';
|
||||
import { useAuth } from '../hooks/authHooks';
|
||||
|
||||
const AdminProtectedRoute = (routeProps: RouteProps) => {
|
||||
interface AdminProtectedRouteProps extends RouteProps {
|
||||
hasPermission: boolean;
|
||||
}
|
||||
|
||||
const AdminProtectedRoute = (routeProps: AdminProtectedRouteProps) => {
|
||||
const { isAdminUser } = useAuth();
|
||||
const { isAuthDisabled, isAuthenticated } = useAuthContext();
|
||||
|
||||
if (isAuthDisabled || isAdminUser) {
|
||||
if (isAuthDisabled || isAdminUser || routeProps.hasPermission) {
|
||||
return <Route {...routeProps} />;
|
||||
} else if (isAuthenticated) {
|
||||
return <Redirect to={ROUTES.NOT_FOUND} />;
|
||||
|
@ -13,11 +13,15 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
import { usePermissionProvider } from '../components/PermissionProvider/PermissionProvider';
|
||||
import { ResourceEntity } from '../components/PermissionProvider/PermissionProvider.interface';
|
||||
import {
|
||||
GlobalSettingOptions,
|
||||
GlobalSettingsMenuCategory,
|
||||
} from '../constants/globalSettings.constants';
|
||||
import { Operation } from '../generated/entity/policies/policy';
|
||||
import TeamsPage from '../pages/teams/TeamsPage';
|
||||
import { checkPemission } from '../utils/PermissionsUtils';
|
||||
import { getSettingCategoryPath, getSettingPath } from '../utils/RouterUtils';
|
||||
import AdminProtectedRoute from './AdminProtectedRoute';
|
||||
import withSuspenseFallback from './withSuspenseFallback';
|
||||
@ -64,6 +68,8 @@ const SlackSettingsPage = withSuspenseFallback(
|
||||
);
|
||||
|
||||
const GlobalSettingRouter = () => {
|
||||
const { permissions } = usePermissionProvider();
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact path={getSettingPath()}>
|
||||
@ -97,6 +103,11 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={RolesListPage}
|
||||
hasPermission={checkPemission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.ROLE,
|
||||
permissions
|
||||
)}
|
||||
path={getSettingPath(
|
||||
GlobalSettingsMenuCategory.ACCESS,
|
||||
GlobalSettingOptions.ROLES
|
||||
@ -106,6 +117,11 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={RolesDetailPage}
|
||||
hasPermission={checkPemission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.ROLE,
|
||||
permissions
|
||||
)}
|
||||
path={getSettingPath(
|
||||
GlobalSettingsMenuCategory.ACCESS,
|
||||
GlobalSettingOptions.ROLES,
|
||||
@ -119,6 +135,11 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={PoliciesListPage}
|
||||
hasPermission={checkPemission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.POLICY,
|
||||
permissions
|
||||
)}
|
||||
path={getSettingPath(
|
||||
GlobalSettingsMenuCategory.ACCESS,
|
||||
GlobalSettingOptions.POLICIES
|
||||
@ -127,6 +148,11 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={PoliciesDetailPage}
|
||||
hasPermission={checkPemission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.POLICY,
|
||||
permissions
|
||||
)}
|
||||
path={getSettingPath(
|
||||
GlobalSettingsMenuCategory.ACCESS,
|
||||
GlobalSettingOptions.POLICIES,
|
||||
@ -136,12 +162,22 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={UserListPageV1}
|
||||
hasPermission={checkPemission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.USER,
|
||||
permissions
|
||||
)}
|
||||
path={getSettingCategoryPath(GlobalSettingsMenuCategory.MEMBERS)}
|
||||
/>
|
||||
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={WebhooksPageV1}
|
||||
hasPermission={checkPemission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.WEBHOOK,
|
||||
permissions
|
||||
)}
|
||||
path={getSettingPath(
|
||||
GlobalSettingsMenuCategory.INTEGRATIONS,
|
||||
GlobalSettingOptions.WEBHOOK
|
||||
@ -150,6 +186,11 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={BotsPageV1}
|
||||
hasPermission={checkPemission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.BOT,
|
||||
permissions
|
||||
)}
|
||||
path={getSettingPath(
|
||||
GlobalSettingsMenuCategory.INTEGRATIONS,
|
||||
GlobalSettingOptions.BOTS
|
||||
@ -159,6 +200,11 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={SlackSettingsPage}
|
||||
hasPermission={checkPemission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.WEBHOOK,
|
||||
permissions
|
||||
)}
|
||||
path={getSettingPath(
|
||||
GlobalSettingsMenuCategory.INTEGRATIONS,
|
||||
GlobalSettingOptions.SLACK
|
||||
@ -174,6 +220,11 @@ const GlobalSettingRouter = () => {
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={CustomPropertiesPageV1}
|
||||
hasPermission={checkPemission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.ALL,
|
||||
permissions
|
||||
)}
|
||||
path={getSettingCategoryPath(
|
||||
GlobalSettingsMenuCategory.CUSTOM_ATTRIBUTES
|
||||
)}
|
||||
|
@ -11,13 +11,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
OperationPermission,
|
||||
ResourceEntity,
|
||||
UIPermission,
|
||||
} from '../components/PermissionProvider/PermissionProvider.interface';
|
||||
import { EntityType } from '../enums/entity.enum';
|
||||
import {
|
||||
Access,
|
||||
Permission,
|
||||
ResourcePermission,
|
||||
} from '../generated/entity/policies/accessControl/resourcePermission';
|
||||
import { Operation } from '../generated/entity/policies/policy';
|
||||
|
||||
/**
|
||||
* TODO: Remove this method once we have new permission structure everywhere
|
||||
*/
|
||||
export const hasPemission = (
|
||||
operation: Operation,
|
||||
entityType: EntityType,
|
||||
@ -34,4 +43,64 @@ export const hasPemission = (
|
||||
return currentPermission?.access === Access.Allow;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param operation operation like Edit, Delete
|
||||
* @param resourceType Resource type like "bot", "table"
|
||||
* @param permissions UIPermission
|
||||
* @returns boolean - true/false
|
||||
*/
|
||||
export const checkPemission = (
|
||||
operation: Operation,
|
||||
resourceType: ResourceEntity,
|
||||
permissions: UIPermission
|
||||
) => {
|
||||
const allResource = permissions.all;
|
||||
const entityResource = permissions[resourceType];
|
||||
|
||||
/**
|
||||
* If allresource is present then check for permission and return it
|
||||
*/
|
||||
if (allResource) {
|
||||
return allResource.All || allResource[operation];
|
||||
}
|
||||
|
||||
return entityResource[operation];
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param permission ResourcePermission
|
||||
* @returns OperationPermission - {Operation:true/false}
|
||||
*/
|
||||
export const getOperationPermissions = (
|
||||
permission: ResourcePermission
|
||||
): OperationPermission => {
|
||||
return permission.permissions.reduce(
|
||||
(acc: OperationPermission, curr: Permission) => {
|
||||
return {
|
||||
...acc,
|
||||
[curr.operation as Operation]: curr.access === Access.Allow,
|
||||
};
|
||||
},
|
||||
{} as OperationPermission
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param permissions Take ResourcePermission list
|
||||
* @returns UIPermission
|
||||
*/
|
||||
export const getUIPermission = (
|
||||
permissions: ResourcePermission[]
|
||||
): UIPermission => {
|
||||
return permissions.reduce((acc: UIPermission, curr: ResourcePermission) => {
|
||||
return {
|
||||
...acc,
|
||||
[curr.resource as ResourceEntity]: getOperationPermissions(curr),
|
||||
};
|
||||
}, {} as UIPermission);
|
||||
};
|
||||
|
||||
export const LIST_CAP = 1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user