mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-01 13:13:10 +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 { AuthProvider } from './authentication/auth-provider/AuthProvider';
|
||||||
import Appbar from './components/app-bar/Appbar';
|
import Appbar from './components/app-bar/Appbar';
|
||||||
import GlobalSearchProvider from './components/GlobalSearchProvider/GlobalSearchProvider';
|
import GlobalSearchProvider from './components/GlobalSearchProvider/GlobalSearchProvider';
|
||||||
|
import PermissionProvider from './components/PermissionProvider/PermissionProvider';
|
||||||
import WebSocketProvider from './components/web-scoket/web-scoket.provider';
|
import WebSocketProvider from './components/web-scoket/web-scoket.provider';
|
||||||
import { toastOptions } from './constants/toast.constants';
|
import { toastOptions } from './constants/toast.constants';
|
||||||
import ErrorBoundry from './ErrorBoundry/ErrorBoundry';
|
import ErrorBoundry from './ErrorBoundry/ErrorBoundry';
|
||||||
@ -56,12 +57,14 @@ const App: FunctionComponent = () => {
|
|||||||
<Router>
|
<Router>
|
||||||
<ErrorBoundry>
|
<ErrorBoundry>
|
||||||
<AuthProvider childComponentType={AppRouter}>
|
<AuthProvider childComponentType={AppRouter}>
|
||||||
|
<PermissionProvider>
|
||||||
<WebSocketProvider>
|
<WebSocketProvider>
|
||||||
<GlobalSearchProvider>
|
<GlobalSearchProvider>
|
||||||
<Appbar />
|
<Appbar />
|
||||||
<AppRouter />
|
<AppRouter />
|
||||||
</GlobalSearchProvider>
|
</GlobalSearchProvider>
|
||||||
</WebSocketProvider>
|
</WebSocketProvider>
|
||||||
|
</PermissionProvider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</ErrorBoundry>
|
</ErrorBoundry>
|
||||||
</Router>
|
</Router>
|
||||||
|
@ -30,9 +30,11 @@ import {
|
|||||||
GlobalSettingOptions,
|
GlobalSettingOptions,
|
||||||
GlobalSettingsMenuCategory,
|
GlobalSettingsMenuCategory,
|
||||||
} from '../../constants/globalSettings.constants';
|
} from '../../constants/globalSettings.constants';
|
||||||
|
import { Operation } from '../../generated/entity/policies/accessControl/rule';
|
||||||
import { JWTTokenExpiry, User } from '../../generated/entity/teams/user';
|
import { JWTTokenExpiry, User } from '../../generated/entity/teams/user';
|
||||||
import { EntityReference } from '../../generated/type/entityReference';
|
import { EntityReference } from '../../generated/type/entityReference';
|
||||||
import { getEntityName, requiredField } from '../../utils/CommonUtils';
|
import { getEntityName, requiredField } from '../../utils/CommonUtils';
|
||||||
|
import { checkPemission } from '../../utils/PermissionsUtils';
|
||||||
import { getSettingPath } from '../../utils/RouterUtils';
|
import { getSettingPath } from '../../utils/RouterUtils';
|
||||||
import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
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 TitleBreadcrumb from '../common/title-breadcrumb/title-breadcrumb.component';
|
||||||
import PageLayout, { leftPanelAntCardStyle } from '../containers/PageLayout';
|
import PageLayout, { leftPanelAntCardStyle } from '../containers/PageLayout';
|
||||||
import ConfirmationModal from '../Modals/ConfirmationModal/ConfirmationModal';
|
import ConfirmationModal from '../Modals/ConfirmationModal/ConfirmationModal';
|
||||||
|
import { usePermissionProvider } from '../PermissionProvider/PermissionProvider';
|
||||||
|
import { ResourceEntity } from '../PermissionProvider/PermissionProvider.interface';
|
||||||
import { UserDetails } from '../Users/Users.interface';
|
import { UserDetails } from '../Users/Users.interface';
|
||||||
|
|
||||||
interface BotsDetailProp extends HTMLAttributes<HTMLDivElement> {
|
interface BotsDetailProp extends HTMLAttributes<HTMLDivElement> {
|
||||||
@ -61,6 +65,7 @@ const BotDetails: FC<BotsDetailProp> = ({
|
|||||||
updateBotsDetails,
|
updateBotsDetails,
|
||||||
revokeTokenHandler,
|
revokeTokenHandler,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { permissions } = usePermissionProvider();
|
||||||
const [displayName, setDisplayName] = useState(botsData.displayName);
|
const [displayName, setDisplayName] = useState(botsData.displayName);
|
||||||
const [isDisplayNameEdit, setIsDisplayNameEdit] = useState(false);
|
const [isDisplayNameEdit, setIsDisplayNameEdit] = useState(false);
|
||||||
const [isDescriptionEdit, setIsDescriptionEdit] = useState(false);
|
const [isDescriptionEdit, setIsDescriptionEdit] = useState(false);
|
||||||
@ -72,6 +77,23 @@ const BotDetails: FC<BotsDetailProp> = ({
|
|||||||
const [generateToken, setGenerateToken] = useState<boolean>(false);
|
const [generateToken, setGenerateToken] = useState<boolean>(false);
|
||||||
const [selectedExpiry, setSelectedExpiry] = useState('7');
|
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 = () => {
|
const getJWTTokenExpiryOptions = () => {
|
||||||
return Object.keys(JWTTokenExpiry).map((expiry) => {
|
return Object.keys(JWTTokenExpiry).map((expiry) => {
|
||||||
const expiryValue = JWTTokenExpiry[expiry as keyof typeof JWTTokenExpiry];
|
const expiryValue = JWTTokenExpiry[expiry as keyof typeof JWTTokenExpiry];
|
||||||
@ -209,13 +231,19 @@ const BotDetails: FC<BotsDetailProp> = ({
|
|||||||
Add display name
|
Add display name
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{(displayNamePermission || editAllPermission) && (
|
||||||
<button
|
<button
|
||||||
className="tw-ml-2 focus:tw-outline-none"
|
className="tw-ml-2 focus:tw-outline-none"
|
||||||
data-testid="edit-displayName"
|
data-testid="edit-displayName"
|
||||||
onClick={() => setIsDisplayNameEdit(true)}>
|
onClick={() => setIsDisplayNameEdit(true)}>
|
||||||
<SVGIcons alt="edit" icon="icon-edit" title="Edit" width="16px" />
|
<SVGIcons
|
||||||
|
alt="edit"
|
||||||
|
icon="icon-edit"
|
||||||
|
title="Edit"
|
||||||
|
width="16px"
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -226,9 +254,9 @@ const BotDetails: FC<BotsDetailProp> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="tw--ml-5">
|
<div className="tw--ml-5">
|
||||||
<Description
|
<Description
|
||||||
hasEditAccess
|
|
||||||
description={botsData.description || ''}
|
description={botsData.description || ''}
|
||||||
entityName={getEntityName(botsData as unknown as EntityReference)}
|
entityName={getEntityName(botsData as unknown as EntityReference)}
|
||||||
|
hasEditAccess={descriptionPermission || editAllPermission}
|
||||||
isEdit={isDescriptionEdit}
|
isEdit={isDescriptionEdit}
|
||||||
onCancel={() => setIsDescriptionEdit(false)}
|
onCancel={() => setIsDescriptionEdit(false)}
|
||||||
onDescriptionEdit={() => setIsDescriptionEdit(true)}
|
onDescriptionEdit={() => setIsDescriptionEdit(true)}
|
||||||
@ -407,7 +435,7 @@ const BotDetails: FC<BotsDetailProp> = ({
|
|||||||
<h6 className="tw-mb-2 tw-self-center">
|
<h6 className="tw-mb-2 tw-self-center">
|
||||||
{generateToken ? 'Generate JWT token' : 'JWT Token'}
|
{generateToken ? 'Generate JWT token' : 'JWT Token'}
|
||||||
</h6>
|
</h6>
|
||||||
{!generateToken ? (
|
{!generateToken && editAllPermission ? (
|
||||||
<div className="tw-flex">
|
<div className="tw-flex">
|
||||||
<Button
|
<Button
|
||||||
data-testid="generate-token"
|
data-testid="generate-token"
|
||||||
|
@ -68,6 +68,26 @@ const mockProp = {
|
|||||||
updateBotsDetails,
|
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', () => {
|
jest.mock('../../axiosAPIs/userAPI', () => {
|
||||||
return {
|
return {
|
||||||
generateUserToken: jest
|
generateUserToken: jest
|
||||||
|
@ -24,22 +24,33 @@ import {
|
|||||||
} from '../../constants/constants';
|
} from '../../constants/constants';
|
||||||
import { EntityType } from '../../enums/entity.enum';
|
import { EntityType } from '../../enums/entity.enum';
|
||||||
import { Bot } from '../../generated/entity/bot';
|
import { Bot } from '../../generated/entity/bot';
|
||||||
|
import { Operation } from '../../generated/entity/policies/accessControl/rule';
|
||||||
import { Include } from '../../generated/type/include';
|
import { Include } from '../../generated/type/include';
|
||||||
import { Paging } from '../../generated/type/paging';
|
import { Paging } from '../../generated/type/paging';
|
||||||
|
import { checkPemission } from '../../utils/PermissionsUtils';
|
||||||
import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
import { showErrorToast } from '../../utils/ToastUtils';
|
||||||
import DeleteWidgetModal from '../common/DeleteWidget/DeleteWidgetModal';
|
import DeleteWidgetModal from '../common/DeleteWidget/DeleteWidgetModal';
|
||||||
import NextPrevious from '../common/next-previous/NextPrevious';
|
import NextPrevious from '../common/next-previous/NextPrevious';
|
||||||
import Loader from '../Loader/Loader';
|
import Loader from '../Loader/Loader';
|
||||||
|
import { usePermissionProvider } from '../PermissionProvider/PermissionProvider';
|
||||||
|
import { ResourceEntity } from '../PermissionProvider/PermissionProvider.interface';
|
||||||
import { BotListV1Props } from './BotListV1.interfaces';
|
import { BotListV1Props } from './BotListV1.interfaces';
|
||||||
|
|
||||||
const BotListV1 = ({ showDeleted }: BotListV1Props) => {
|
const BotListV1 = ({ showDeleted }: BotListV1Props) => {
|
||||||
|
const { permissions } = usePermissionProvider();
|
||||||
const [botUsers, setBotUsers] = useState<Bot[]>([]);
|
const [botUsers, setBotUsers] = useState<Bot[]>([]);
|
||||||
const [paging, setPaging] = useState<Paging>({} as Paging);
|
const [paging, setPaging] = useState<Paging>({} as Paging);
|
||||||
const [selectedUser, setSelectedUser] = useState<Bot>();
|
const [selectedUser, setSelectedUser] = useState<Bot>();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [currentPage, setCurrentPage] = useState<number>(INITIAL_PAGING_VALUE);
|
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
|
* @param after - Pagination value if passed data will be fetched post cursor value
|
||||||
@ -90,8 +101,15 @@ const BotListV1 = ({ showDeleted }: BotListV1Props) => {
|
|||||||
width: 90,
|
width: 90,
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Space align="center" size={8}>
|
<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
|
<Button
|
||||||
|
disabled={!deletePermission}
|
||||||
icon={
|
icon={
|
||||||
<SVGIcons
|
<SVGIcons
|
||||||
alt="Delete"
|
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', () => {
|
describe('Test Description Component', () => {
|
||||||
it('Check if it has all child elements', async () => {
|
it('Check if it has all child elements', async () => {
|
||||||
const { container } = render(<Description {...mockDescriptionProp} />, {
|
const { container } = render(
|
||||||
|
<Description {...mockDescriptionProp} hasEditAccess />,
|
||||||
|
{
|
||||||
wrapper: MemoryRouter,
|
wrapper: MemoryRouter,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const descriptionContainer = await findByTestId(container, 'description');
|
const descriptionContainer = await findByTestId(container, 'description');
|
||||||
const editDescriptionButton = await findByTestId(
|
const editDescriptionButton = await findByTestId(
|
||||||
@ -236,4 +239,19 @@ describe('Test Description Component', () => {
|
|||||||
// should render requestDescription, as description thread and description are empty value
|
// should render requestDescription, as description thread and description are empty value
|
||||||
expect(requestDescription).toBeInTheDocument();
|
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 { EntityFieldThreads } from 'Models';
|
||||||
import React, { FC, Fragment } from 'react';
|
import React, { FC, Fragment } from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { useAuthContext } from '../../../authentication/auth-provider/AuthProvider';
|
|
||||||
import { EntityField } from '../../../constants/feed.constants';
|
import { EntityField } from '../../../constants/feed.constants';
|
||||||
import { EntityType } from '../../../enums/entity.enum';
|
import { EntityType } from '../../../enums/entity.enum';
|
||||||
import { ThreadType } from '../../../generated/entity/feed/thread';
|
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 { isTaskSupported } from '../../../utils/CommonUtils';
|
||||||
import { getEntityFeedLink } from '../../../utils/EntityUtils';
|
import { getEntityFeedLink } from '../../../utils/EntityUtils';
|
||||||
import { hasPemission } from '../../../utils/PermissionsUtils';
|
|
||||||
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
|
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
|
||||||
import {
|
import {
|
||||||
getRequestDescriptionPath,
|
getRequestDescriptionPath,
|
||||||
@ -54,9 +50,6 @@ const Description: FC<DescriptionProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const { isAdminUser, userPermissions } = useAuth();
|
|
||||||
const { isAuthDisabled } = useAuthContext();
|
|
||||||
|
|
||||||
const thread = entityFieldThreads?.[0];
|
const thread = entityFieldThreads?.[0];
|
||||||
const tasks = entityFieldTasks?.[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 = () => {
|
const handleUpdate = () => {
|
||||||
onDescriptionEdit && onDescriptionEdit();
|
onDescriptionEdit && onDescriptionEdit();
|
||||||
};
|
};
|
||||||
@ -179,7 +159,7 @@ const Description: FC<DescriptionProps> = ({
|
|||||||
const DescriptionActions = () => {
|
const DescriptionActions = () => {
|
||||||
return !isReadOnly ? (
|
return !isReadOnly ? (
|
||||||
<div className={classNames('tw-w-5 tw-min-w-max tw-flex tw--mt-1')}>
|
<div className={classNames('tw-w-5 tw-min-w-max tw-flex tw--mt-1')}>
|
||||||
{checkPermission() && (
|
{hasEditAccess && (
|
||||||
<button
|
<button
|
||||||
className="tw-w-7 tw-h-8 tw-flex-none focus:tw-outline-none"
|
className="tw-w-7 tw-h-8 tw-flex-none focus:tw-outline-none"
|
||||||
data-testid="edit-description"
|
data-testid="edit-description"
|
||||||
|
@ -11,13 +11,18 @@
|
|||||||
* limitations under the License.
|
* 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 React, { useState } from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import BotListV1 from '../../components/BotListV1/BotListV1.component';
|
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 { getCreateUserPath } from '../../constants/constants';
|
||||||
|
import { Operation } from '../../generated/entity/policies/accessControl/rule';
|
||||||
|
import { checkPemission } from '../../utils/PermissionsUtils';
|
||||||
|
|
||||||
export const BotsPageV1 = () => {
|
export const BotsPageV1 = () => {
|
||||||
|
const { permissions } = usePermissionProvider();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [showDeleted, setShowDeleted] = useState(false);
|
const [showDeleted, setShowDeleted] = useState(false);
|
||||||
|
|
||||||
@ -29,8 +34,22 @@ export const BotsPageV1 = () => {
|
|||||||
setShowDeleted(checked);
|
setShowDeleted(checked);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const viewAllPermission = checkPemission(
|
||||||
|
Operation.ViewAll,
|
||||||
|
ResourceEntity.BOT,
|
||||||
|
permissions
|
||||||
|
);
|
||||||
|
|
||||||
|
const createPermission = checkPemission(
|
||||||
|
Operation.Create,
|
||||||
|
ResourceEntity.BOT,
|
||||||
|
permissions
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
|
{viewAllPermission ? (
|
||||||
|
<>
|
||||||
<Col flex={1} />
|
<Col flex={1} />
|
||||||
<Col>
|
<Col>
|
||||||
<Space size={16}>
|
<Space size={16}>
|
||||||
@ -43,14 +62,21 @@ export const BotsPageV1 = () => {
|
|||||||
/>
|
/>
|
||||||
<label htmlFor="switch-deleted">Show deleted</label>
|
<label htmlFor="switch-deleted">Show deleted</label>
|
||||||
</Space>
|
</Space>
|
||||||
|
|
||||||
|
{createPermission && (
|
||||||
<Button type="primary" onClick={handleAddBotClick}>
|
<Button type="primary" onClick={handleAddBotClick}>
|
||||||
Add Bot
|
Add Bot
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<BotListV1 showDeleted={showDeleted} />
|
<BotListV1 showDeleted={showDeleted} />
|
||||||
</Col>
|
</Col>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Empty description="You do not have permission to view data" />
|
||||||
|
)}
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -80,11 +80,12 @@ const AddPolicyPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSumbit = async () => {
|
const handleSumbit = async () => {
|
||||||
|
const { condition, ...rest } = ruleData;
|
||||||
const data: CreatePolicy = {
|
const data: CreatePolicy = {
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
policyType: PolicyType.AccessControl,
|
policyType: PolicyType.AccessControl,
|
||||||
rules: [ruleData],
|
rules: [condition ? { ...rest, condition } : rest],
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -87,9 +87,10 @@ const AddRulePage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
|
const { condition, ...rest } = ruleData;
|
||||||
const patch = compare(policy, {
|
const patch = compare(policy, {
|
||||||
...policy,
|
...policy,
|
||||||
rules: [...policy.rules, ruleData],
|
rules: [...policy.rules, condition ? { ...rest, condition } : rest],
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
const data = await patchPolicy(patch, policy.id);
|
const data = await patchPolicy(patch, policy.id);
|
||||||
|
@ -98,11 +98,14 @@ const EditRulePage = () => {
|
|||||||
const existingRules = policy.rules;
|
const existingRules = policy.rules;
|
||||||
const updatedRules = existingRules.map((rule) => {
|
const updatedRules = existingRules.map((rule) => {
|
||||||
if (rule.name === ruleName) {
|
if (rule.name === ruleName) {
|
||||||
return ruleData;
|
const { condition, ...rest } = ruleData;
|
||||||
|
|
||||||
|
return condition ? { ...rest, condition } : rest;
|
||||||
} else {
|
} else {
|
||||||
return rule;
|
return rule;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const patch = compare(policy, {
|
const patch = compare(policy, {
|
||||||
...policy,
|
...policy,
|
||||||
rules: updatedRules,
|
rules: updatedRules,
|
||||||
|
@ -17,11 +17,15 @@ import { useAuthContext } from '../authentication/auth-provider/AuthProvider';
|
|||||||
import { ROUTES } from '../constants/constants';
|
import { ROUTES } from '../constants/constants';
|
||||||
import { useAuth } from '../hooks/authHooks';
|
import { useAuth } from '../hooks/authHooks';
|
||||||
|
|
||||||
const AdminProtectedRoute = (routeProps: RouteProps) => {
|
interface AdminProtectedRouteProps extends RouteProps {
|
||||||
|
hasPermission: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AdminProtectedRoute = (routeProps: AdminProtectedRouteProps) => {
|
||||||
const { isAdminUser } = useAuth();
|
const { isAdminUser } = useAuth();
|
||||||
const { isAuthDisabled, isAuthenticated } = useAuthContext();
|
const { isAuthDisabled, isAuthenticated } = useAuthContext();
|
||||||
|
|
||||||
if (isAuthDisabled || isAdminUser) {
|
if (isAuthDisabled || isAdminUser || routeProps.hasPermission) {
|
||||||
return <Route {...routeProps} />;
|
return <Route {...routeProps} />;
|
||||||
} else if (isAuthenticated) {
|
} else if (isAuthenticated) {
|
||||||
return <Redirect to={ROUTES.NOT_FOUND} />;
|
return <Redirect to={ROUTES.NOT_FOUND} />;
|
||||||
|
@ -13,11 +13,15 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||||
|
import { usePermissionProvider } from '../components/PermissionProvider/PermissionProvider';
|
||||||
|
import { ResourceEntity } from '../components/PermissionProvider/PermissionProvider.interface';
|
||||||
import {
|
import {
|
||||||
GlobalSettingOptions,
|
GlobalSettingOptions,
|
||||||
GlobalSettingsMenuCategory,
|
GlobalSettingsMenuCategory,
|
||||||
} from '../constants/globalSettings.constants';
|
} from '../constants/globalSettings.constants';
|
||||||
|
import { Operation } from '../generated/entity/policies/policy';
|
||||||
import TeamsPage from '../pages/teams/TeamsPage';
|
import TeamsPage from '../pages/teams/TeamsPage';
|
||||||
|
import { checkPemission } from '../utils/PermissionsUtils';
|
||||||
import { getSettingCategoryPath, getSettingPath } from '../utils/RouterUtils';
|
import { getSettingCategoryPath, getSettingPath } from '../utils/RouterUtils';
|
||||||
import AdminProtectedRoute from './AdminProtectedRoute';
|
import AdminProtectedRoute from './AdminProtectedRoute';
|
||||||
import withSuspenseFallback from './withSuspenseFallback';
|
import withSuspenseFallback from './withSuspenseFallback';
|
||||||
@ -64,6 +68,8 @@ const SlackSettingsPage = withSuspenseFallback(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const GlobalSettingRouter = () => {
|
const GlobalSettingRouter = () => {
|
||||||
|
const { permissions } = usePermissionProvider();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path={getSettingPath()}>
|
<Route exact path={getSettingPath()}>
|
||||||
@ -97,6 +103,11 @@ const GlobalSettingRouter = () => {
|
|||||||
<AdminProtectedRoute
|
<AdminProtectedRoute
|
||||||
exact
|
exact
|
||||||
component={RolesListPage}
|
component={RolesListPage}
|
||||||
|
hasPermission={checkPemission(
|
||||||
|
Operation.ViewAll,
|
||||||
|
ResourceEntity.ROLE,
|
||||||
|
permissions
|
||||||
|
)}
|
||||||
path={getSettingPath(
|
path={getSettingPath(
|
||||||
GlobalSettingsMenuCategory.ACCESS,
|
GlobalSettingsMenuCategory.ACCESS,
|
||||||
GlobalSettingOptions.ROLES
|
GlobalSettingOptions.ROLES
|
||||||
@ -106,6 +117,11 @@ const GlobalSettingRouter = () => {
|
|||||||
<AdminProtectedRoute
|
<AdminProtectedRoute
|
||||||
exact
|
exact
|
||||||
component={RolesDetailPage}
|
component={RolesDetailPage}
|
||||||
|
hasPermission={checkPemission(
|
||||||
|
Operation.ViewAll,
|
||||||
|
ResourceEntity.ROLE,
|
||||||
|
permissions
|
||||||
|
)}
|
||||||
path={getSettingPath(
|
path={getSettingPath(
|
||||||
GlobalSettingsMenuCategory.ACCESS,
|
GlobalSettingsMenuCategory.ACCESS,
|
||||||
GlobalSettingOptions.ROLES,
|
GlobalSettingOptions.ROLES,
|
||||||
@ -119,6 +135,11 @@ const GlobalSettingRouter = () => {
|
|||||||
<AdminProtectedRoute
|
<AdminProtectedRoute
|
||||||
exact
|
exact
|
||||||
component={PoliciesListPage}
|
component={PoliciesListPage}
|
||||||
|
hasPermission={checkPemission(
|
||||||
|
Operation.ViewAll,
|
||||||
|
ResourceEntity.POLICY,
|
||||||
|
permissions
|
||||||
|
)}
|
||||||
path={getSettingPath(
|
path={getSettingPath(
|
||||||
GlobalSettingsMenuCategory.ACCESS,
|
GlobalSettingsMenuCategory.ACCESS,
|
||||||
GlobalSettingOptions.POLICIES
|
GlobalSettingOptions.POLICIES
|
||||||
@ -127,6 +148,11 @@ const GlobalSettingRouter = () => {
|
|||||||
<AdminProtectedRoute
|
<AdminProtectedRoute
|
||||||
exact
|
exact
|
||||||
component={PoliciesDetailPage}
|
component={PoliciesDetailPage}
|
||||||
|
hasPermission={checkPemission(
|
||||||
|
Operation.ViewAll,
|
||||||
|
ResourceEntity.POLICY,
|
||||||
|
permissions
|
||||||
|
)}
|
||||||
path={getSettingPath(
|
path={getSettingPath(
|
||||||
GlobalSettingsMenuCategory.ACCESS,
|
GlobalSettingsMenuCategory.ACCESS,
|
||||||
GlobalSettingOptions.POLICIES,
|
GlobalSettingOptions.POLICIES,
|
||||||
@ -136,12 +162,22 @@ const GlobalSettingRouter = () => {
|
|||||||
<AdminProtectedRoute
|
<AdminProtectedRoute
|
||||||
exact
|
exact
|
||||||
component={UserListPageV1}
|
component={UserListPageV1}
|
||||||
|
hasPermission={checkPemission(
|
||||||
|
Operation.ViewAll,
|
||||||
|
ResourceEntity.USER,
|
||||||
|
permissions
|
||||||
|
)}
|
||||||
path={getSettingCategoryPath(GlobalSettingsMenuCategory.MEMBERS)}
|
path={getSettingCategoryPath(GlobalSettingsMenuCategory.MEMBERS)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AdminProtectedRoute
|
<AdminProtectedRoute
|
||||||
exact
|
exact
|
||||||
component={WebhooksPageV1}
|
component={WebhooksPageV1}
|
||||||
|
hasPermission={checkPemission(
|
||||||
|
Operation.ViewAll,
|
||||||
|
ResourceEntity.WEBHOOK,
|
||||||
|
permissions
|
||||||
|
)}
|
||||||
path={getSettingPath(
|
path={getSettingPath(
|
||||||
GlobalSettingsMenuCategory.INTEGRATIONS,
|
GlobalSettingsMenuCategory.INTEGRATIONS,
|
||||||
GlobalSettingOptions.WEBHOOK
|
GlobalSettingOptions.WEBHOOK
|
||||||
@ -150,6 +186,11 @@ const GlobalSettingRouter = () => {
|
|||||||
<AdminProtectedRoute
|
<AdminProtectedRoute
|
||||||
exact
|
exact
|
||||||
component={BotsPageV1}
|
component={BotsPageV1}
|
||||||
|
hasPermission={checkPemission(
|
||||||
|
Operation.ViewAll,
|
||||||
|
ResourceEntity.BOT,
|
||||||
|
permissions
|
||||||
|
)}
|
||||||
path={getSettingPath(
|
path={getSettingPath(
|
||||||
GlobalSettingsMenuCategory.INTEGRATIONS,
|
GlobalSettingsMenuCategory.INTEGRATIONS,
|
||||||
GlobalSettingOptions.BOTS
|
GlobalSettingOptions.BOTS
|
||||||
@ -159,6 +200,11 @@ const GlobalSettingRouter = () => {
|
|||||||
<AdminProtectedRoute
|
<AdminProtectedRoute
|
||||||
exact
|
exact
|
||||||
component={SlackSettingsPage}
|
component={SlackSettingsPage}
|
||||||
|
hasPermission={checkPemission(
|
||||||
|
Operation.ViewAll,
|
||||||
|
ResourceEntity.WEBHOOK,
|
||||||
|
permissions
|
||||||
|
)}
|
||||||
path={getSettingPath(
|
path={getSettingPath(
|
||||||
GlobalSettingsMenuCategory.INTEGRATIONS,
|
GlobalSettingsMenuCategory.INTEGRATIONS,
|
||||||
GlobalSettingOptions.SLACK
|
GlobalSettingOptions.SLACK
|
||||||
@ -174,6 +220,11 @@ const GlobalSettingRouter = () => {
|
|||||||
<AdminProtectedRoute
|
<AdminProtectedRoute
|
||||||
exact
|
exact
|
||||||
component={CustomPropertiesPageV1}
|
component={CustomPropertiesPageV1}
|
||||||
|
hasPermission={checkPemission(
|
||||||
|
Operation.ViewAll,
|
||||||
|
ResourceEntity.ALL,
|
||||||
|
permissions
|
||||||
|
)}
|
||||||
path={getSettingCategoryPath(
|
path={getSettingCategoryPath(
|
||||||
GlobalSettingsMenuCategory.CUSTOM_ATTRIBUTES
|
GlobalSettingsMenuCategory.CUSTOM_ATTRIBUTES
|
||||||
)}
|
)}
|
||||||
|
@ -11,13 +11,22 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
OperationPermission,
|
||||||
|
ResourceEntity,
|
||||||
|
UIPermission,
|
||||||
|
} from '../components/PermissionProvider/PermissionProvider.interface';
|
||||||
import { EntityType } from '../enums/entity.enum';
|
import { EntityType } from '../enums/entity.enum';
|
||||||
import {
|
import {
|
||||||
Access,
|
Access,
|
||||||
|
Permission,
|
||||||
ResourcePermission,
|
ResourcePermission,
|
||||||
} from '../generated/entity/policies/accessControl/resourcePermission';
|
} from '../generated/entity/policies/accessControl/resourcePermission';
|
||||||
import { Operation } from '../generated/entity/policies/policy';
|
import { Operation } from '../generated/entity/policies/policy';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Remove this method once we have new permission structure everywhere
|
||||||
|
*/
|
||||||
export const hasPemission = (
|
export const hasPemission = (
|
||||||
operation: Operation,
|
operation: Operation,
|
||||||
entityType: EntityType,
|
entityType: EntityType,
|
||||||
@ -34,4 +43,64 @@ export const hasPemission = (
|
|||||||
return currentPermission?.access === Access.Allow;
|
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;
|
export const LIST_CAP = 1;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user