added some permissions to the process model show page w/ burnettk

This commit is contained in:
jasquat 2022-11-15 17:35:16 -05:00
parent b5d2109b89
commit c57ae77721
9 changed files with 179 additions and 119 deletions

View File

@ -36,6 +36,7 @@ module.exports = {
],
'react/react-in-jsx-scope': 'off',
'react/require-default-props': 'off',
'import/prefer-default-export': 'off',
'no-unused-vars': [
'error',
{

View File

@ -24,7 +24,7 @@ export default function App() {
[errorMessage]
);
const ability = defineAbility((can: any) => {});
const ability = defineAbility(() => {});
let errorTag = null;
if (errorMessage) {

View File

@ -74,10 +74,7 @@ export default function ProcessModelForm({
if (hasErrors) {
return;
}
let path = `/process-models`;
if (mode === 'edit') {
path = `/process-models/${modifiedProcessModelPath}`;
}
const path = `/process-models/${modifiedProcessModelPath}`;
let httpMethod = 'POST';
if (mode === 'edit') {
httpMethod = 'PUT';

View File

@ -52,10 +52,14 @@ import TouchModule from 'diagram-js/lib/navigation/touch';
// @ts-expect-error TS(7016) FIXME
import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll';
import { Can } from '@casl/react';
import HttpService from '../services/HttpService';
import ButtonWithConfirmation from './ButtonWithConfirmation';
import { makeid } from '../helpers';
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
import { PermissionsToCheck } from '../interfaces';
import { usePermissionFetcher } from '../hooks/PermissionService';
type OwnProps = {
processModelId: string;
@ -107,6 +111,13 @@ export default function ReactDiagramEditor({
const alreadyImportedXmlRef = useRef(false);
const { targetUris } = useUriListForPermissions();
const permissionRequestData: PermissionsToCheck = {
[targetUris.processModelShowPath]: ['PUT'],
[targetUris.processModelFileShowPath]: ['POST', 'GET', 'PUT', 'DELETE'],
};
const { ability } = usePermissionFetcher(permissionRequestData);
useEffect(() => {
if (diagramModelerState) {
return;
@ -517,20 +528,40 @@ export default function ReactDiagramEditor({
if (diagramType !== 'readonly') {
return (
<>
<Button onClick={handleSave} variant="danger">
Save
</Button>
{fileName && (
<ButtonWithConfirmation
description={`Delete file ${fileName}?`}
onConfirmation={handleDelete}
buttonLabel="Delete"
/>
)}
{onSetPrimaryFile && (
<Button onClick={handleSetPrimaryFile}>Set as primary file</Button>
)}
<Button onClick={downloadXmlFile}>Download xml</Button>
<Can
I="PUT"
a={targetUris.processModelFileShowPath}
ability={ability}
>
<Button onClick={handleSave}>Save</Button>
</Can>
<Can
I="DELETE"
a={targetUris.processModelFileShowPath}
ability={ability}
>
{fileName && (
<ButtonWithConfirmation
description={`Delete file ${fileName}?`}
onConfirmation={handleDelete}
buttonLabel="Delete"
/>
)}
</Can>
<Can I="PUT" a={targetUris.processModelShowPath} ability={ability}>
{onSetPrimaryFile && (
<Button onClick={handleSetPrimaryFile}>
Set as primary file
</Button>
)}
</Can>
<Can
I="GET"
a={targetUris.processModelFileShowPath}
ability={ability}
>
<Button onClick={downloadXmlFile}>Download xml</Button>
</Can>
</>
);
}

View File

@ -1,5 +1,5 @@
import { createContext } from 'react';
import { AbilityBuilder, Ability } from '@casl/ability';
import { Ability } from '@casl/ability';
import { createContextualCan } from '@casl/react';
export const AbilityContext = createContext(new Ability());

View File

@ -12,19 +12,17 @@ export const usePermissionFetcher = (
useEffect(() => {
const processPermissionResult = (result: PermissionCheckResponseBody) => {
const { can, cannot, rules } = new AbilityBuilder(Ability);
for (const [url, permissionVerbResults] of Object.entries(
result.results
)) {
for (const [permissionVerb, hasPermission] of Object.entries(
permissionVerbResults
)) {
Object.keys(result.results).forEach((url: string) => {
const permissionVerbResults = result.results[url];
Object.keys(permissionVerbResults).forEach((permissionVerb: string) => {
const hasPermission = permissionVerbResults[permissionVerb];
if (hasPermission) {
can(permissionVerb, url);
} else {
cannot(permissionVerb, url);
}
}
}
});
});
ability.update(rules);
};
HttpService.makeCallToBackend({

View File

@ -5,8 +5,10 @@ export const useUriListForPermissions = () => {
const targetUris = {
processGroupListPath: `/v1.0/process-groups`,
processGroupShowPath: `/v1.0/process-groups/${params.process_group_id}`,
processModelListPath: `/v1.0/process-models`,
processModelCreatePath: `/v1.0/process-models/${params.process_group_id}`,
processModelShowPath: `/v1.0/process-models/${params.process_model_id}`,
processModelFileCreatePath: `/v1.0/process-models/${params.process_model_id}/files`,
processModelFileShowPath: `/v1.0/process-models/${params.process_model_id}/files/${params.file_name}`,
processInstanceListPath: `/v1.0/process-instances`,
processInstanceActionPath: `/v1.0/process-models/${params.process_model_id}/process-instances`,
};

View File

@ -35,6 +35,8 @@ export default function ProcessGroupShow() {
const { targetUris } = useUriListForPermissions();
const permissionRequestData: PermissionsToCheck = {
[targetUris.processGroupListPath]: ['POST'],
[targetUris.processGroupShowPath]: ['PUT'],
[targetUris.processModelCreatePath]: ['POST'],
};
const { ability } = usePermissionFetcher(permissionRequestData);
@ -159,23 +161,29 @@ export default function ProcessGroupShow() {
<Stack orientation="horizontal" gap={3}>
<Can I="POST" a={targetUris.processGroupListPath} ability={ability}>
<Button
kind="secondary"
href={`/admin/process-groups/new?parentGroupId=${processGroup.id}`}
>
Add a process group
</Button>
</Can>
<Button
href={`/admin/process-models/${modifiedProcessGroupId}/new`}
<Can
I="POST"
a={targetUris.processModelCreatePath}
ability={ability}
>
Add a process model
</Button>
<Button
href={`/admin/process-groups/${modifiedProcessGroupId}/edit`}
variant="secondary"
>
Edit process group
</Button>
<Button
href={`/admin/process-models/${modifiedProcessGroupId}/new`}
>
Add a process model
</Button>
</Can>
<Can I="PUT" a={targetUris.processGroupShowPath} ability={ability}>
<Button
href={`/admin/process-groups/${modifiedProcessGroupId}/edit`}
>
Edit process group
</Button>
</Can>
</Stack>
<br />
<br />

View File

@ -106,9 +106,10 @@ export default function ProcessModelShow() {
const { targetUris } = useUriListForPermissions();
const permissionRequestData: PermissionsToCheck = {
[targetUris.processModelShowPath]: ['GET', 'PUT'],
[targetUris.processModelShowPath]: ['PUT'],
[targetUris.processInstanceListPath]: ['GET'],
[targetUris.processInstanceActionPath]: ['POST'],
[targetUris.processModelFileCreatePath]: ['POST', 'GET', 'DELETE'],
};
const { ability } = usePermissionFetcher(permissionRequestData);
@ -263,50 +264,62 @@ export default function ProcessModelShow() {
) => {
const elements = [];
elements.push(
<Button
kind="ghost"
renderIcon={Edit}
iconDescription="Edit File"
hasIconOnly
size="lg"
data-qa={`edit-file-${processModelFile.name.replace('.', '-')}`}
onClick={() => navigateToFileEdit(processModelFile)}
/>
<Can I="GET" a={targetUris.processModelFileCreatePath} ability={ability}>
<Button
kind="ghost"
renderIcon={Edit}
iconDescription="Edit File"
hasIconOnly
size="lg"
data-qa={`edit-file-${processModelFile.name.replace('.', '-')}`}
onClick={() => navigateToFileEdit(processModelFile)}
/>
</Can>
);
elements.push(
<Button
kind="ghost"
renderIcon={Download}
iconDescription="Download File"
hasIconOnly
size="lg"
onClick={() => downloadFile(processModelFile.name)}
/>
<Can I="GET" a={targetUris.processModelFileCreatePath} ability={ability}>
<Button
kind="ghost"
renderIcon={Download}
iconDescription="Download File"
hasIconOnly
size="lg"
onClick={() => downloadFile(processModelFile.name)}
/>
</Can>
);
elements.push(
<ButtonWithConfirmation
kind="ghost"
renderIcon={TrashCan}
iconDescription="Delete File"
hasIconOnly
description={`Delete file: ${processModelFile.name}`}
onConfirmation={() => {
onDeleteFile(processModelFile.name);
}}
confirmButtonLabel="Delete"
/>
<Can
I="DELETE"
a={targetUris.processModelFileCreatePath}
ability={ability}
>
<ButtonWithConfirmation
kind="ghost"
renderIcon={TrashCan}
iconDescription="Delete File"
hasIconOnly
description={`Delete file: ${processModelFile.name}`}
onConfirmation={() => {
onDeleteFile(processModelFile.name);
}}
confirmButtonLabel="Delete"
/>
</Can>
);
if (processModelFile.name.match(/\.bpmn$/) && !isPrimaryBpmnFile) {
elements.push(
<Button
kind="ghost"
renderIcon={Favorite}
iconDescription="Set As Primary File"
hasIconOnly
size="lg"
onClick={() => onSetPrimaryFile(processModelFile.name)}
/>
<Can I="PUT" a={targetUris.processModelShowPath} ability={ability}>
<Button
kind="ghost"
renderIcon={Favorite}
iconDescription="Set As Primary File"
hasIconOnly
size="lg"
onClick={() => onSetPrimaryFile(processModelFile.name)}
/>
</Can>
);
}
return elements;
@ -337,7 +350,11 @@ export default function ProcessModelShow() {
let fileLink = null;
const fileUrl = profileModelFileEditUrl(processModelFile);
if (fileUrl) {
fileLink = <Link to={fileUrl}>{processModelFile.name}</Link>;
if (ability.can('GET', targetUris.processModelFileCreatePath)) {
fileLink = <Link to={fileUrl}>{processModelFile.name}</Link>;
} else {
fileLink = <span>{processModelFile.name}</span>;
}
}
constructedTag = (
<TableRow key={processModelFile.name}>
@ -442,47 +459,53 @@ export default function ProcessModelShow() {
</Stack>
}
>
<ButtonSet>
<Button
renderIcon={Upload}
data-qa="upload-file-button"
onClick={() => setShowFileUploadModal(true)}
size="sm"
kind=""
className="button-white-background"
>
Upload File
</Button>
<Button
renderIcon={Add}
href={`/admin/process-models/${modifiedProcessModelId}/files?file_type=bpmn`}
size="sm"
>
New BPMN File
</Button>
<Button
renderIcon={Add}
href={`/admin/process-models/${modifiedProcessModelId}/files?file_type=dmn`}
size="sm"
>
New DMN File
</Button>
<Button
renderIcon={Add}
href={`/admin/process-models/${modifiedProcessModelId}/form?file_ext=json`}
size="sm"
>
New JSON File
</Button>
<Button
renderIcon={Add}
href={`/admin/process-models/${modifiedProcessModelId}/form?file_ext=md`}
size="sm"
>
New Markdown File
</Button>
</ButtonSet>
<br />
<Can
I="POST"
a={targetUris.processModelFileCreatePath}
ability={ability}
>
<ButtonSet>
<Button
renderIcon={Upload}
data-qa="upload-file-button"
onClick={() => setShowFileUploadModal(true)}
size="sm"
kind=""
className="button-white-background"
>
Upload File
</Button>
<Button
renderIcon={Add}
href={`/admin/process-models/${modifiedProcessModelId}/files?file_type=bpmn`}
size="sm"
>
New BPMN File
</Button>
<Button
renderIcon={Add}
href={`/admin/process-models/${modifiedProcessModelId}/files?file_type=dmn`}
size="sm"
>
New DMN File
</Button>
<Button
renderIcon={Add}
href={`/admin/process-models/${modifiedProcessModelId}/form?file_ext=json`}
size="sm"
>
New JSON File
</Button>
<Button
renderIcon={Add}
href={`/admin/process-models/${modifiedProcessModelId}/form?file_ext=md`}
size="sm"
>
New Markdown File
</Button>
</ButtonSet>
<br />
</Can>
{processModelFileList()}
</AccordionItem>
</Accordion>