mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +01:00
task: Use fine-grained project permissions in frontend (#5974)
Connected to [#5932](https://github.com/Unleash/unleash/pull/5932) - This starts using the new permissions in addition to the old UPDATE_PROJECT permission. That way, if you're happy with UPDATE_PROJECT, you don't need to change. However, you can now add more fine grained permissions for both READ and WRITE operations.
This commit is contained in:
parent
b7483e8989
commit
8256c2eaf2
@ -8,7 +8,7 @@ import {
|
|||||||
} from 'hooks/useHasAccess';
|
} from 'hooks/useHasAccess';
|
||||||
|
|
||||||
interface IPermissionSwitchProps extends SwitchProps {
|
interface IPermissionSwitchProps extends SwitchProps {
|
||||||
permission: string;
|
permission: string | string[];
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
@ -4,7 +4,10 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
|||||||
import { Alert } from '@mui/material';
|
import { Alert } from '@mui/material';
|
||||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||||
import AccessContext from 'contexts/AccessContext';
|
import AccessContext from 'contexts/AccessContext';
|
||||||
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
import {
|
||||||
|
PROJECT_CHANGE_REQUEST_READ,
|
||||||
|
UPDATE_PROJECT,
|
||||||
|
} from 'component/providers/AccessProvider/permissions';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { usePageTitle } from 'hooks/usePageTitle';
|
import { usePageTitle } from 'hooks/usePageTitle';
|
||||||
import { useProjectNameOrId } from 'hooks/api/getters/useProject/useProject';
|
import { useProjectNameOrId } from 'hooks/api/getters/useProject/useProject';
|
||||||
@ -36,7 +39,7 @@ export const ChangeRequestConfiguration = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAccess(UPDATE_PROJECT, projectId)) {
|
if (!hasAccess([UPDATE_PROJECT, PROJECT_CHANGE_REQUEST_READ], projectId)) {
|
||||||
return (
|
return (
|
||||||
<PageContent
|
<PageContent
|
||||||
header={<PageHeader title='Change request configuration' />}
|
header={<PageHeader title='Change request configuration' />}
|
||||||
|
@ -30,6 +30,7 @@ import { KeyboardArrowDownOutlined } from '@mui/icons-material';
|
|||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import AccessContext from 'contexts/AccessContext';
|
import AccessContext from 'contexts/AccessContext';
|
||||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||||
|
import { PROJECT_CHANGE_REQUEST_WRITE } from '../../../../providers/AccessProvider/permissions';
|
||||||
|
|
||||||
const StyledBox = styled(Box)(({ theme }) => ({
|
const StyledBox = styled(Box)(({ theme }) => ({
|
||||||
padding: theme.spacing(1),
|
padding: theme.spacing(1),
|
||||||
@ -159,7 +160,10 @@ export const ChangeRequestTable: VFC = () => {
|
|||||||
}}
|
}}
|
||||||
disabled={
|
disabled={
|
||||||
!hasAccess(
|
!hasAccess(
|
||||||
|
[
|
||||||
UPDATE_PROJECT,
|
UPDATE_PROJECT,
|
||||||
|
PROJECT_CHANGE_REQUEST_WRITE,
|
||||||
|
],
|
||||||
projectId,
|
projectId,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -188,7 +192,10 @@ export const ChangeRequestTable: VFC = () => {
|
|||||||
<PermissionSwitch
|
<PermissionSwitch
|
||||||
checked={value}
|
checked={value}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
permission={UPDATE_PROJECT}
|
permission={[
|
||||||
|
UPDATE_PROJECT,
|
||||||
|
PROJECT_CHANGE_REQUEST_WRITE,
|
||||||
|
]}
|
||||||
inputProps={{ 'aria-label': original.environment }}
|
inputProps={{ 'aria-label': original.environment }}
|
||||||
onClick={onRowChange(
|
onClick={onRowChange(
|
||||||
original.environment,
|
original.environment,
|
||||||
|
@ -4,7 +4,10 @@ import AccessContext from 'contexts/AccessContext';
|
|||||||
import { usePageTitle } from 'hooks/usePageTitle';
|
import { usePageTitle } from 'hooks/usePageTitle';
|
||||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||||
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
import {
|
||||||
|
PROJECT_DEFAULT_STRATEGY_READ,
|
||||||
|
UPDATE_PROJECT,
|
||||||
|
} from 'component/providers/AccessProvider/permissions';
|
||||||
import { Alert, styled } from '@mui/material';
|
import { Alert, styled } from '@mui/material';
|
||||||
import ProjectEnvironment from './ProjectEnvironment/ProjectEnvironment';
|
import ProjectEnvironment from './ProjectEnvironment/ProjectEnvironment';
|
||||||
import { Route, Routes, useNavigate } from 'react-router-dom';
|
import { Route, Routes, useNavigate } from 'react-router-dom';
|
||||||
@ -25,7 +28,9 @@ export const ProjectDefaultStrategySettings = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
usePageTitle(`Project default strategy configuration – ${projectName}`);
|
usePageTitle(`Project default strategy configuration – ${projectName}`);
|
||||||
|
|
||||||
if (!hasAccess(UPDATE_PROJECT, projectId)) {
|
if (
|
||||||
|
!hasAccess([UPDATE_PROJECT, PROJECT_DEFAULT_STRATEGY_READ], projectId)
|
||||||
|
) {
|
||||||
return (
|
return (
|
||||||
<PageContent
|
<PageContent
|
||||||
header={<PageHeader title='Default Strategy configuration' />}
|
header={<PageHeader title='Default Strategy configuration' />}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
import {
|
||||||
|
PROJECT_SETTINGS_WRITE,
|
||||||
|
UPDATE_PROJECT,
|
||||||
|
} from 'component/providers/AccessProvider/permissions';
|
||||||
import useProject from 'hooks/api/getters/useProject/useProject';
|
import useProject from 'hooks/api/getters/useProject/useProject';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
@ -26,7 +29,10 @@ const EditProject = () => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const accessDeniedAlert = !hasAccess(UPDATE_PROJECT, id) && (
|
const accessDeniedAlert = !hasAccess(
|
||||||
|
[UPDATE_PROJECT, PROJECT_SETTINGS_WRITE],
|
||||||
|
id,
|
||||||
|
) && (
|
||||||
<Alert severity='error' sx={{ mb: 4 }}>
|
<Alert severity='error' sx={{ mb: 4 }}>
|
||||||
You do not have the required permissions to edit this project.
|
You do not have the required permissions to edit this project.
|
||||||
</Alert>
|
</Alert>
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||||
import ProjectForm from '../../../ProjectForm/ProjectForm';
|
import ProjectForm from '../../../ProjectForm/ProjectForm';
|
||||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||||
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
import {
|
||||||
|
PROJECT_SETTINGS_WRITE,
|
||||||
|
UPDATE_PROJECT,
|
||||||
|
} from 'component/providers/AccessProvider/permissions';
|
||||||
import useProjectForm, {
|
import useProjectForm, {
|
||||||
DEFAULT_PROJECT_STICKINESS,
|
DEFAULT_PROJECT_STICKINESS,
|
||||||
} from '../../../hooks/useProjectForm';
|
} from '../../../hooks/useProjectForm';
|
||||||
@ -138,7 +141,10 @@ export const UpdateProject = ({ project }: IUpdateProject) => {
|
|||||||
>
|
>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
type='submit'
|
type='submit'
|
||||||
permission={UPDATE_PROJECT}
|
permission={[
|
||||||
|
UPDATE_PROJECT,
|
||||||
|
PROJECT_SETTINGS_WRITE,
|
||||||
|
]}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
data-testid={EDIT_PROJECT_BTN}
|
data-testid={EDIT_PROJECT_BTN}
|
||||||
>
|
>
|
||||||
|
@ -3,7 +3,10 @@ import { PageContent } from 'component/common/PageContent/PageContent';
|
|||||||
import { Alert } from '@mui/material';
|
import { Alert } from '@mui/material';
|
||||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||||
import AccessContext from 'contexts/AccessContext';
|
import AccessContext from 'contexts/AccessContext';
|
||||||
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
import {
|
||||||
|
PROJECT_SETTINGS_READ,
|
||||||
|
UPDATE_PROJECT,
|
||||||
|
} from 'component/providers/AccessProvider/permissions';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { usePageTitle } from 'hooks/usePageTitle';
|
import { usePageTitle } from 'hooks/usePageTitle';
|
||||||
import EditProject from './EditProject/EditProject';
|
import EditProject from './EditProject/EditProject';
|
||||||
@ -29,7 +32,7 @@ export const Settings = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAccess(UPDATE_PROJECT, projectId)) {
|
if (!hasAccess([UPDATE_PROJECT, PROJECT_SETTINGS_READ], projectId)) {
|
||||||
return (
|
return (
|
||||||
<PageContent header={<PageHeader title='Access' />}>
|
<PageContent header={<PageHeader title='Access' />}>
|
||||||
<Alert severity='error'>
|
<Alert severity='error'>
|
||||||
|
@ -4,7 +4,10 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
|||||||
import { Alert } from '@mui/material';
|
import { Alert } from '@mui/material';
|
||||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||||
import AccessContext from 'contexts/AccessContext';
|
import AccessContext from 'contexts/AccessContext';
|
||||||
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
import {
|
||||||
|
PROJECT_USER_ACCESS_READ,
|
||||||
|
UPDATE_PROJECT,
|
||||||
|
} from 'component/providers/AccessProvider/permissions';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { usePageTitle } from 'hooks/usePageTitle';
|
import { usePageTitle } from 'hooks/usePageTitle';
|
||||||
import { ProjectAccessTable } from 'component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable';
|
import { ProjectAccessTable } from 'component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable';
|
||||||
@ -29,7 +32,7 @@ export const ProjectAccess = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAccess(UPDATE_PROJECT, projectId)) {
|
if (!hasAccess([UPDATE_PROJECT, PROJECT_USER_ACCESS_READ], projectId)) {
|
||||||
return (
|
return (
|
||||||
<PageContent header={<PageHeader title='Access' />}>
|
<PageContent header={<PageHeader title='Access' />}>
|
||||||
<Alert severity='error'>
|
<Alert severity='error'>
|
||||||
|
@ -9,7 +9,10 @@ import useProjectAccess, {
|
|||||||
IProjectAccess,
|
IProjectAccess,
|
||||||
} from 'hooks/api/getters/useProjectAccess/useProjectAccess';
|
} from 'hooks/api/getters/useProjectAccess/useProjectAccess';
|
||||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||||
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
import {
|
||||||
|
PROJECT_USER_ACCESS_WRITE,
|
||||||
|
UPDATE_PROJECT,
|
||||||
|
} from 'component/providers/AccessProvider/permissions';
|
||||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||||
import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
|
import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
|
||||||
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
||||||
@ -212,7 +215,10 @@ export const ProjectAccessTable: VFC = () => {
|
|||||||
<PermissionIconButton
|
<PermissionIconButton
|
||||||
data-testid={PA_EDIT_BUTTON_ID}
|
data-testid={PA_EDIT_BUTTON_ID}
|
||||||
component={Link}
|
component={Link}
|
||||||
permission={UPDATE_PROJECT}
|
permission={[
|
||||||
|
UPDATE_PROJECT,
|
||||||
|
PROJECT_USER_ACCESS_WRITE,
|
||||||
|
]}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
to={`edit/${
|
to={`edit/${
|
||||||
row.type === ENTITY_TYPE.GROUP
|
row.type === ENTITY_TYPE.GROUP
|
||||||
@ -231,7 +237,10 @@ export const ProjectAccessTable: VFC = () => {
|
|||||||
</PermissionIconButton>
|
</PermissionIconButton>
|
||||||
<PermissionIconButton
|
<PermissionIconButton
|
||||||
data-testid={PA_REMOVE_BUTTON_ID}
|
data-testid={PA_REMOVE_BUTTON_ID}
|
||||||
permission={UPDATE_PROJECT}
|
permission={[
|
||||||
|
UPDATE_PROJECT,
|
||||||
|
PROJECT_USER_ACCESS_WRITE,
|
||||||
|
]}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedRow(row);
|
setSelectedRow(row);
|
||||||
@ -411,7 +420,10 @@ export const ProjectAccessTable: VFC = () => {
|
|||||||
onClick={() => navigate('create')}
|
onClick={() => navigate('create')}
|
||||||
maxWidth='700px'
|
maxWidth='700px'
|
||||||
Icon={Add}
|
Icon={Add}
|
||||||
permission={UPDATE_PROJECT}
|
permission={[
|
||||||
|
UPDATE_PROJECT,
|
||||||
|
PROJECT_USER_ACCESS_WRITE,
|
||||||
|
]}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
data-testid={PA_ASSIGN_BUTTON_ID}
|
data-testid={PA_ASSIGN_BUTTON_ID}
|
||||||
>
|
>
|
||||||
|
@ -39,3 +39,12 @@ export const READ_PROJECT_API_TOKEN = 'READ_PROJECT_API_TOKEN';
|
|||||||
export const CREATE_PROJECT_API_TOKEN = 'CREATE_PROJECT_API_TOKEN';
|
export const CREATE_PROJECT_API_TOKEN = 'CREATE_PROJECT_API_TOKEN';
|
||||||
export const DELETE_PROJECT_API_TOKEN = 'DELETE_PROJECT_API_TOKEN';
|
export const DELETE_PROJECT_API_TOKEN = 'DELETE_PROJECT_API_TOKEN';
|
||||||
export const UPDATE_PROJECT_SEGMENT = 'UPDATE_PROJECT_SEGMENT';
|
export const UPDATE_PROJECT_SEGMENT = 'UPDATE_PROJECT_SEGMENT';
|
||||||
|
|
||||||
|
export const PROJECT_USER_ACCESS_READ = 'PROJECT_USER_ACCESS_READ';
|
||||||
|
export const PROJECT_DEFAULT_STRATEGY_READ = 'PROJECT_DEFAULT_STRATEGY_READ';
|
||||||
|
export const PROJECT_CHANGE_REQUEST_READ = 'PROJECT_CHANGE_REQUEST_READ';
|
||||||
|
export const PROJECT_SETTINGS_READ = 'PROJECT_SETTINGS_READ';
|
||||||
|
export const PROJECT_USER_ACCESS_WRITE = 'PROJECT_USER_ACCESS_WRITE';
|
||||||
|
export const PROJECT_DEFAULT_STRATEGY_WRITE = 'PROJECT_DEFAULT_STRATEGY_WRITE';
|
||||||
|
export const PROJECT_CHANGE_REQUEST_WRITE = 'PROJECT_CHANGE_REQUEST_WRITE';
|
||||||
|
export const PROJECT_SETTINGS_WRITE = 'PROJECT_SETTINGS_WRITE';
|
||||||
|
Loading…
Reference in New Issue
Block a user