diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index 79a218ee0..72f1ced65 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -976,7 +976,7 @@ paths: items: $ref: "#/components/schemas/Task" - /process-instance/{process_instance_id}/tasks: + /process-instances/{modified_process_model_id}/{process_instance_id}/tasks: parameters: - name: process_instance_id in: path diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml index d5504b23a..618a9719f 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml @@ -63,6 +63,12 @@ permissions: allowed_permissions: [read] uri: /v1.0/process-groups/* + process-instance-list: + groups: [everybody] + users: [] + allowed_permissions: [read] + uri: /v1.0/process-instances + # TODO: all uris should really have the same structure finance-admin-group: groups: ["Finance Team"] @@ -81,3 +87,9 @@ permissions: users: [] allowed_permissions: [read] uri: /* + + invoice-approval-tasks-read: + groups: ["Finance Team"] + users: [] + allowed_permissions: [read] + uri: /v1.0/process-instances/category_number_one:lanes/* diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index aa4cc588b..c56668c48 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1141,7 +1141,7 @@ def get_tasks( def process_instance_task_list( - process_instance_id: int, all_tasks: bool = False, spiff_step: int = 0 + modified_process_model_id: str, process_instance_id: int, all_tasks: bool = False, spiff_step: int = 0 ) -> flask.wrappers.Response: """Process_instance_task_list.""" process_instance = find_process_instance_by_id_or_raise(process_instance_id) @@ -1204,6 +1204,7 @@ def task_show(process_instance_id: int, task_id: str) -> flask.wrappers.Response task = ProcessInstanceService.spiff_task_to_api_task(spiff_task) task.data = spiff_task.data task.process_model_display_name = process_model.display_name + task.process_model_identifier = process_model.id process_model_with_form = process_model if task.type == "User Task": diff --git a/spiffworkflow-frontend/src/App.tsx b/spiffworkflow-frontend/src/App.tsx index d7e4866f2..deb38410d 100644 --- a/spiffworkflow-frontend/src/App.tsx +++ b/spiffworkflow-frontend/src/App.tsx @@ -60,7 +60,7 @@ export default function App() { {errorTag} - } /> + } /> } /> } /> diff --git a/spiffworkflow-frontend/src/hooks/PermissionService.tsx b/spiffworkflow-frontend/src/hooks/PermissionService.tsx index be39b9f9c..e8a40b962 100644 --- a/spiffworkflow-frontend/src/hooks/PermissionService.tsx +++ b/spiffworkflow-frontend/src/hooks/PermissionService.tsx @@ -1,3 +1,5 @@ +// We may need to update usage of Ability when we update. +// They say they are going to rename PureAbility to Ability and remove the old class. import { AbilityBuilder, Ability } from '@casl/ability'; import { useContext, useEffect } from 'react'; import { AbilityContext } from '../contexts/Can'; @@ -11,6 +13,7 @@ export const usePermissionFetcher = ( useEffect(() => { const processPermissionResult = (result: PermissionCheckResponseBody) => { + const oldRules = ability.rules; const { can, cannot, rules } = new AbilityBuilder(Ability); Object.keys(result.results).forEach((url: string) => { const permissionVerbResults = result.results[url]; @@ -23,14 +26,21 @@ export const usePermissionFetcher = ( } }); }); + oldRules.forEach((oldRule: any) => { + if (oldRule.inverted) { + cannot(oldRule.action, oldRule.subject); + } else { + can(oldRule.action, oldRule.subject); + } + }); ability.update(rules); }; + HttpService.makeCallToBackend({ path: `/permissions-check`, httpMethod: 'POST', successCallback: processPermissionResult, postBody: { requests_to_check: permissionsToCheck }, - // failureCallback: setErrorMessage, }); }); diff --git a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx index c86f1e31c..c00cb286e 100644 --- a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx +++ b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx @@ -3,14 +3,15 @@ import { useParams } from 'react-router-dom'; export const useUriListForPermissions = () => { const params = useParams(); const targetUris = { - processGroupListPath: `/v1.0/process-groups`, + messageInstanceListPath: '/v1.0/messages', + processGroupListPath: '/v1.0/process-groups', processGroupShowPath: `/v1.0/process-groups/${params.process_group_id}`, + processInstanceActionPath: `/v1.0/process-models/${params.process_model_id}/process-instances`, + processInstanceListPath: '/v1.0/process-instances', 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`, + processModelShowPath: `/v1.0/process-models/${params.process_model_id}`, }; return { targetUris }; diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx index 263869ea6..b6de2efc7 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx @@ -23,6 +23,7 @@ import { Stack, // @ts-ignore } from '@carbon/react'; +import { Can } from '@casl/react'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import HttpService from '../services/HttpService'; import ReactDiagramEditor from '../components/ReactDiagramEditor'; @@ -32,6 +33,9 @@ import { } from '../helpers'; import ButtonWithConfirmation from '../components/ButtonWithConfirmation'; import ErrorContext from '../contexts/ErrorContext'; +import { useUriListForPermissions } from '../hooks/UriListForPermissions'; +import { PermissionsToCheck } from '../interfaces'; +import { usePermissionFetcher } from '../hooks/PermissionService'; export default function ProcessInstanceShow() { const navigate = useNavigate(); @@ -50,6 +54,12 @@ export default function ProcessInstanceShow() { ); const modifiedProcessModelId = params.process_model_id; + const { targetUris } = useUriListForPermissions(); + const permissionRequestData: PermissionsToCheck = { + [targetUris.messageInstanceListPath]: ['GET'], + }; + const { ability } = usePermissionFetcher(permissionRequestData); + const navigateToProcessInstances = (_result: any) => { navigate( `/admin/process-instances?process_model_identifier=${unModifiedProcessModelId}` @@ -63,12 +73,12 @@ export default function ProcessInstanceShow() { }); if (typeof params.spiff_step === 'undefined') HttpService.makeCallToBackend({ - path: `/process-instance/${params.process_instance_id}/tasks?all_tasks=true`, + path: `/process-instances/${modifiedProcessModelId}/${params.process_instance_id}/tasks?all_tasks=true`, successCallback: setTasks, }); else HttpService.makeCallToBackend({ - path: `/process-instance/${params.process_instance_id}/tasks?all_tasks=true&spiff_step=${params.spiff_step}`, + path: `/process-instances/${modifiedProcessModelId}/${params.process_instance_id}/tasks?all_tasks=true&spiff_step=${params.spiff_step}`, successCallback: setTasks, }); }, [params, modifiedProcessModelId]); @@ -245,14 +255,20 @@ export default function ProcessInstanceShow() { > Logs - + + diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx index 50a088078..096f408dc 100644 --- a/spiffworkflow-frontend/src/routes/TaskShow.tsx +++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx @@ -8,6 +8,7 @@ import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import HttpService from '../services/HttpService'; import ErrorContext from '../contexts/ErrorContext'; +import { modifyProcessModelPath } from '../helpers'; export default function TaskShow() { const [task, setTask] = useState(null); @@ -18,16 +19,22 @@ export default function TaskShow() { const setErrorMessage = (useContext as any)(ErrorContext)[1]; useEffect(() => { + const processResult = (result: any) => { + setTask(result); + HttpService.makeCallToBackend({ + path: `/process-instances/${modifyProcessModelPath( + result.process_model_identifier + )}/${params.process_instance_id}/tasks`, + successCallback: setUserTasks, + }); + }; + HttpService.makeCallToBackend({ path: `/tasks/${params.process_instance_id}/${params.task_id}`, - successCallback: setTask, + successCallback: processResult, // This causes the page to continuously reload // failureCallback: setErrorMessage, }); - HttpService.makeCallToBackend({ - path: `/process-instance/${params.process_instance_id}/tasks`, - successCallback: setUserTasks, - }); }, [params]); const processSubmitResult = (result: any) => {