From 499a9562c34a6f452baa59f8ce99a434c224c63b Mon Sep 17 00:00:00 2001 From: jasquat Date: Thu, 5 Jan 2023 14:59:59 -0500 Subject: [PATCH 1/3] basic support to find a process instance by id w/ burnettk --- .../src/spiffworkflow_backend/api.yml | 21 +++ .../config/permissions/testing.yml | 6 + .../models/process_model.py | 8 + .../routes/process_instances_controller.py | 48 ++++-- .../services/authorization_service.py | 139 ++++++++++-------- .../helpers/base_test.py | 6 +- .../test_process_instances_controller.py | 51 +++++++ .../unit/test_authorization_service.py | 1 + spiffworkflow-frontend/src/helpers.test.tsx | 9 ++ spiffworkflow-frontend/src/helpers.tsx | 4 + .../src/routes/AdminRoutes.tsx | 5 + .../src/routes/ProcessInstanceFindById.tsx | 83 +++++++++++ .../src/routes/ProcessInstanceList.tsx | 9 ++ .../src/routes/ProcessModelShow.tsx | 2 +- 14 files changed, 312 insertions(+), 80 deletions(-) create mode 100644 spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_instances_controller.py create mode 100644 spiffworkflow-frontend/src/routes/ProcessInstanceFindById.tsx diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index d96de3dbc..6c720265c 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -946,6 +946,27 @@ paths: schema: $ref: "#/components/schemas/Workflow" + /process-instances/find-by-id/{process_instance_id}: + parameters: + - name: process_instance_id + in: path + required: true + description: The unique id of an existing process instance. + schema: + type: integer + get: + operationId: spiffworkflow_backend.routes.process_instances_controller.process_instance_find_by_id + summary: Find a process instance based on its id only + tags: + - Process Instances + responses: + "200": + description: One Process Instance + content: + application/json: + schema: + $ref: "#/components/schemas/Workflow" + /process-instances/{modified_process_model_identifier}/{process_instance_id}: parameters: - name: modified_process_model_identifier diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/testing.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/testing.yml index 79a137104..d3edf0a8a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/testing.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/testing.yml @@ -30,6 +30,12 @@ permissions: allowed_permissions: [read] uri: /* + process-instances-find-by-id: + groups: [everybody] + users: [] + allowed_permissions: [read] + uri: /process-instances/find-by-id/* + tasks-crud: groups: [everybody] users: [] diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py index c737b274b..5e0ba6ca0 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_model.py @@ -58,6 +58,14 @@ class ProcessModelInfo: """Id_for_file_path.""" return self.id.replace("/", os.sep) + @classmethod + def modify_process_identifier_for_path_param(cls, identifier: str) -> str: + """Identifier.""" + if "\\" in identifier: + raise Exception(f"Found backslash in identifier: {identifier}") + + return identifier.replace("/", ":") + class ProcessModelInfoSchema(Schema): """ProcessModelInfoSchema.""" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py index ed27f2b24..64e0f1330 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py @@ -1,5 +1,7 @@ """APIs for dealing with process groups, process models, and process instances.""" import json +from spiffworkflow_backend.services.authorization_service import AuthorizationService +from spiffworkflow_backend.models.process_model import ProcessModelInfo from typing import Any from typing import Dict from typing import Optional @@ -88,9 +90,7 @@ def process_instance_run( do_engine_steps: bool = True, ) -> flask.wrappers.Response: """Process_instance_run.""" - process_instance = ProcessInstanceService().get_process_instance( - process_instance_id - ) + process_instance = _find_process_instance_by_id_or_raise(process_instance_id) if process_instance.status != "not_started": raise ApiError( error_code="process_instance_not_runnable", @@ -138,9 +138,7 @@ def process_instance_terminate( modified_process_model_identifier: str, ) -> flask.wrappers.Response: """Process_instance_run.""" - process_instance = ProcessInstanceService().get_process_instance( - process_instance_id - ) + process_instance = _find_process_instance_by_id_or_raise(process_instance_id) processor = ProcessInstanceProcessor(process_instance) processor.terminate() return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") @@ -151,9 +149,7 @@ def process_instance_suspend( modified_process_model_identifier: str, ) -> flask.wrappers.Response: """Process_instance_suspend.""" - process_instance = ProcessInstanceService().get_process_instance( - process_instance_id - ) + process_instance = _find_process_instance_by_id_or_raise(process_instance_id) processor = ProcessInstanceProcessor(process_instance) processor.suspend() return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") @@ -164,9 +160,7 @@ def process_instance_resume( modified_process_model_identifier: str, ) -> flask.wrappers.Response: """Process_instance_resume.""" - process_instance = ProcessInstanceService().get_process_instance( - process_instance_id - ) + process_instance = _find_process_instance_by_id_or_raise(process_instance_id) processor = ProcessInstanceProcessor(process_instance) processor.resume() return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") @@ -575,14 +569,38 @@ def process_instance_reset( spiff_step: int = 0, ) -> flask.wrappers.Response: """Reset a process instance to a particular step.""" - process_instance = ProcessInstanceService().get_process_instance( - process_instance_id - ) + process_instance = _find_process_instance_by_id_or_raise(process_instance_id) processor = ProcessInstanceProcessor(process_instance) processor.reset_process(spiff_step) return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") +def process_instance_find_by_id( + process_instance_id: int, +) -> flask.wrappers.Response: + + process_instance = _find_process_instance_by_id_or_raise(process_instance_id) + modified_process_model_identifier = ProcessModelInfo.modify_process_identifier_for_path_param(process_instance.process_model_identifier) + process_instance_uri = f'/process-instances/{modified_process_model_identifier}/{process_instance.id}' + has_permission = AuthorizationService.user_has_permission( + user=g.user, + permission='read', + target_uri=process_instance_uri, + ) + + uri_type = None + if not has_permission: + process_instance = _find_process_instance_for_me_or_raise(process_instance_id) + uri_type = 'for-me' + + response_json = { + "process_instance": process_instance, + "uri_type": uri_type, + + } + return make_response(jsonify(response_json), 200) + + def _get_process_instance( modified_process_model_identifier: str, process_instance: ProcessInstanceModel, diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py index 69d19cb7f..ebc26aea5 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py @@ -624,6 +624,83 @@ class AuthorizationService: return permissions_to_assign + @classmethod + def set_basic_permissions(cls) -> list[PermissionToAssign]: + permissions_to_assign: list[PermissionToAssign] = [] + permissions_to_assign.append( + PermissionToAssign( + permission="read", target_uri="/process-instances/for-me" + ) + ) + permissions_to_assign.append( + PermissionToAssign(permission="read", target_uri="/processes") + ) + permissions_to_assign.append( + PermissionToAssign(permission="read", target_uri="/service-tasks") + ) + permissions_to_assign.append( + PermissionToAssign( + permission="read", target_uri="/user-groups/for-current-user" + ) + ) + permissions_to_assign.append( + PermissionToAssign( + permission="read", target_uri="/process-instances/find-by-id/*" + ) + ) + + for permission in ["create", "read", "update", "delete"]: + permissions_to_assign.append( + PermissionToAssign( + permission=permission, target_uri="/process-instances/reports/*" + ) + ) + permissions_to_assign.append( + PermissionToAssign(permission=permission, target_uri="/tasks/*") + ) + return permissions_to_assign + + @classmethod + def set_process_group_permissions(cls, target: str, permission_set: str) -> list[PermissionToAssign]: + permissions_to_assign: list[PermissionToAssign] = [] + process_group_identifier = ( + target.removeprefix("PG:").replace("/", ":").removeprefix(":") + ) + process_related_path_segment = f"{process_group_identifier}:*" + if process_group_identifier == "ALL": + process_related_path_segment = "*" + target_uris = [ + f"/process-groups/{process_related_path_segment}", + f"/process-models/{process_related_path_segment}", + ] + permissions_to_assign = ( + permissions_to_assign + + cls.get_permissions_to_assign( + permission_set, process_related_path_segment, target_uris + ) + ) + return permissions_to_assign + + @classmethod + def set_process_model_permissions(cls, target: str, permission_set: str) -> list[PermissionToAssign]: + permissions_to_assign: list[PermissionToAssign] = [] + process_model_identifier = ( + target.removeprefix("PM:").replace("/", ":").removeprefix(":") + ) + process_related_path_segment = f"{process_model_identifier}/*" + + if process_model_identifier == "ALL": + process_related_path_segment = "*" + + target_uris = [f"/process-models/{process_related_path_segment}"] + permissions_to_assign = ( + permissions_to_assign + + cls.get_permissions_to_assign( + permission_set, process_related_path_segment, target_uris + ) + ) + return permissions_to_assign + @classmethod def explode_permissions( cls, permission_set: str, target: str @@ -654,72 +731,16 @@ class AuthorizationService: permissions = ["create", "read", "update", "delete"] if target.startswith("PG:"): - process_group_identifier = ( - target.removeprefix("PG:").replace("/", ":").removeprefix(":") - ) - process_related_path_segment = f"{process_group_identifier}:*" - if process_group_identifier == "ALL": - process_related_path_segment = "*" - target_uris = [ - f"/process-groups/{process_related_path_segment}", - f"/process-models/{process_related_path_segment}", - ] - permissions_to_assign = ( - permissions_to_assign - + cls.get_permissions_to_assign( - permission_set, process_related_path_segment, target_uris - ) - ) - + permissions_to_assign += cls.set_process_group_permissions(target, permission_set) elif target.startswith("PM:"): - process_model_identifier = ( - target.removeprefix("PM:").replace("/", ":").removeprefix(":") - ) - process_related_path_segment = f"{process_model_identifier}/*" - - if process_model_identifier == "ALL": - process_related_path_segment = "*" - - target_uris = [f"/process-models/{process_related_path_segment}"] - permissions_to_assign = ( - permissions_to_assign - + cls.get_permissions_to_assign( - permission_set, process_related_path_segment, target_uris - ) - ) - + permissions_to_assign += cls.set_process_model_permissions(target, permission_set) elif permission_set == "start": raise InvalidPermissionError( "Permission 'start' is only available for macros PM and PG." ) elif target.startswith("BASIC"): - permissions_to_assign.append( - PermissionToAssign( - permission="read", target_uri="/process-instances/for-me" - ) - ) - permissions_to_assign.append( - PermissionToAssign(permission="read", target_uri="/processes") - ) - permissions_to_assign.append( - PermissionToAssign(permission="read", target_uri="/service-tasks") - ) - permissions_to_assign.append( - PermissionToAssign( - permission="read", target_uri="/user-groups/for-current-user" - ) - ) - - for permission in ["create", "read", "update", "delete"]: - permissions_to_assign.append( - PermissionToAssign( - permission=permission, target_uri="/process-instances/reports/*" - ) - ) - permissions_to_assign.append( - PermissionToAssign(permission=permission, target_uri="/tasks/*") - ) + permissions_to_assign += cls.set_basic_permissions() elif target == "ALL": for permission in permissions: permissions_to_assign.append( diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py index 47cf2d876..1f0e9dc1e 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py @@ -354,11 +354,7 @@ class BaseTest: assert has_permission is expected_result def modify_process_identifier_for_path_param(self, identifier: str) -> str: - """Identifier.""" - if "\\" in identifier: - raise Exception(f"Found backslash in identifier: {identifier}") - - return identifier.replace("/", ":") + return ProcessModelInfo.modify_process_identifier_for_path_param(identifier) def un_modify_modified_process_identifier_for_path_param( self, modified_identifier: str diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_instances_controller.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_instances_controller.py new file mode 100644 index 000000000..2577ff516 --- /dev/null +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_instances_controller.py @@ -0,0 +1,51 @@ +"""Test_users_controller.""" +from flask.app import Flask +from tests.spiffworkflow_backend.helpers.test_data import load_test_spec +from flask.testing import FlaskClient +from tests.spiffworkflow_backend.helpers.base_test import BaseTest + +from spiffworkflow_backend.models.user import UserModel + + +class TestProcessInstancesController(BaseTest): + def test_find_by_id( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_user_search_returns_a_user.""" + user_one = self.create_user_with_permission(username="user_one", target_uri="/process-instances/find-by-id/*") + user_two = self.create_user_with_permission(username="user_two", target_uri="/process-instances/find-by-id/*") + + process_model = load_test_spec( + process_model_id="group/sample", + bpmn_file_name="sample.bpmn", + process_model_source_directory="sample", + ) + process_instance = self.create_process_instance_from_process_model( + process_model=process_model, user=user_one + ) + + response = client.get( + f"/v1.0/process-instances/find-by-id/{process_instance.id}", + headers=self.logged_in_headers(user_one), + ) + assert response.status_code == 200 + assert response.json + assert response.json['id'] == process_instance.id + + response = client.get( + f"/v1.0/process-instances/find-by-id/{process_instance.id}", + headers=self.logged_in_headers(user_two), + ) + assert response.status_code == 400 + + response = client.get( + f"/v1.0/process-instances/find-by-id/{process_instance.id}", + headers=self.logged_in_headers(with_super_admin_user), + ) + assert response.status_code == 200 + assert response.json + assert response.json['id'] == process_instance.id diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py index 83ed7fd8e..9e7af5d0f 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py @@ -277,6 +277,7 @@ class TestAuthorizationService(BaseTest): ) -> None: """Test_explode_permissions_basic.""" expected_permissions = [ + ("/process-instances/find-by-id/*", "read"), ("/process-instances/for-me", "read"), ("/process-instances/reports/*", "create"), ("/process-instances/reports/*", "delete"), diff --git a/spiffworkflow-frontend/src/helpers.test.tsx b/spiffworkflow-frontend/src/helpers.test.tsx index 660f65f67..5a7889a65 100644 --- a/spiffworkflow-frontend/src/helpers.test.tsx +++ b/spiffworkflow-frontend/src/helpers.test.tsx @@ -1,5 +1,6 @@ import { convertSecondsToFormattedDateString, + isInteger, slugifyString, underscorizeString, } from './helpers'; @@ -20,3 +21,11 @@ test('it can keep the correct date when converting seconds to date', () => { const dateString = convertSecondsToFormattedDateString(1666325400); expect(dateString).toEqual('2022-10-21'); }); + +test('it can validate numeric values', () => { + expect(isInteger('11')).toEqual(true); + expect(isInteger('hey')).toEqual(false); + expect(isInteger(' ')).toEqual(false); + expect(isInteger('1 2')).toEqual(false); + expect(isInteger(2)).toEqual(true); +}); diff --git a/spiffworkflow-frontend/src/helpers.tsx b/spiffworkflow-frontend/src/helpers.tsx index d91f05439..b3edd7766 100644 --- a/spiffworkflow-frontend/src/helpers.tsx +++ b/spiffworkflow-frontend/src/helpers.tsx @@ -253,3 +253,7 @@ export const setErrorMessageSafely = ( errorMessageSetter({ message: newErrorMessageString }); return null; }; + +export const isInteger = (str: string | number) => { + return /^\d+$/.test(str.toString()); +}; diff --git a/spiffworkflow-frontend/src/routes/AdminRoutes.tsx b/spiffworkflow-frontend/src/routes/AdminRoutes.tsx index 2d61439bf..8d21a5b9d 100644 --- a/spiffworkflow-frontend/src/routes/AdminRoutes.tsx +++ b/spiffworkflow-frontend/src/routes/AdminRoutes.tsx @@ -23,6 +23,7 @@ import MessageInstanceList from './MessageInstanceList'; import Configuration from './Configuration'; import JsonSchemaFormBuilder from './JsonSchemaFormBuilder'; import ProcessModelNewExperimental from './ProcessModelNewExperimental'; +import ProcessInstanceFindById from './ProcessInstanceFindById'; export default function AdminRoutes() { const location = useLocation(); @@ -133,6 +134,10 @@ export default function AdminRoutes() { path="process-models/:process_model_id/form-builder" element={} /> + } + /> ); } diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceFindById.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceFindById.tsx new file mode 100644 index 000000000..c587ff3c0 --- /dev/null +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceFindById.tsx @@ -0,0 +1,83 @@ +import { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +// @ts-ignore +import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react'; +import { + isInteger, + modifyProcessIdentifierForPathParam, + slugifyString, +} from '../helpers'; +import HttpService from '../services/HttpService'; +import { ProcessGroup, ProcessInstance } from '../interfaces'; + +export default function ProcessInstanceFindById() { + const navigate = useNavigate(); + const [processInstanceId, setProcessInstanceId] = useState(''); + const [processInstanceIdValid, setProcessInstanceIdValid] = + useState(true); + + useEffect(() => {}, []); + + const handleProcessInstanceNavigation = (result: any) => { + const processInstance: ProcessInstance = result.process_instance; + let path = '/admin/process-instances/'; + if (result.uri_type === 'for-me') { + path += 'for-me/'; + } + path += `${modifyProcessIdentifierForPathParam( + processInstance.process_model_identifier + )}/${processInstance.id}`; + navigate(path); + }; + + const handleFormSubmission = (event: any) => { + event.preventDefault(); + + if (!processInstanceId) { + setProcessInstanceIdValid(false); + } + + if (processInstanceId && processInstanceIdValid) { + HttpService.makeCallToBackend({ + path: `/process-instances/find-by-id/${processInstanceId}`, + successCallback: handleProcessInstanceNavigation, + }); + } + }; + + const handleProcessInstanceIdChange = (event: any) => { + if (isInteger(event.target.value)) { + setProcessInstanceIdValid(true); + } else { + setProcessInstanceIdValid(false); + } + setProcessInstanceId(event.target.value); + }; + + const formElements = () => { + return ( + + ); + }; + + const formButtons = () => { + const buttons = []; + return {buttons}; + }; + + return ( +
+ + {formElements()} + {formButtons()} + +
+ ); +} diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx index a18f48c80..33af0c1fb 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx @@ -85,6 +85,15 @@ export default function ProcessInstanceList({ variant }: OwnProps) { All + { + navigate('/admin/process-instances/find-by-id'); + }} + > + Find By Id +
diff --git a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx index 9c1e4bef5..a46d02a25 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx @@ -100,7 +100,7 @@ export default function ProcessModelShow() { onClose={() => setProcessInstance(null)} > view From 618bbeb18a9b4eb7f00f298ec11557226f9c409c Mon Sep 17 00:00:00 2001 From: jasquat Date: Thu, 5 Jan 2023 17:29:28 -0500 Subject: [PATCH 2/3] added fix to SpiffWorkflow to deepcopy operation params before evaluating them w/ burnettk --- .../routes/process_instances_controller.py | 21 +++++++----- .../services/authorization_service.py | 33 +++++++++++-------- .../helpers/base_test.py | 1 + .../test_process_instances_controller.py | 16 ++++++--- .../src/routes/ProcessInstanceFindById.tsx | 8 ++--- 5 files changed, 46 insertions(+), 33 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py index 64e0f1330..5fbaecdf4 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py @@ -1,7 +1,5 @@ """APIs for dealing with process groups, process models, and process instances.""" import json -from spiffworkflow_backend.services.authorization_service import AuthorizationService -from spiffworkflow_backend.models.process_model import ProcessModelInfo from typing import Any from typing import Dict from typing import Optional @@ -33,6 +31,7 @@ from spiffworkflow_backend.models.process_instance_metadata import ( from spiffworkflow_backend.models.process_instance_report import ( ProcessInstanceReportModel, ) +from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.models.spec_reference import SpecReferenceNotFoundError from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel @@ -45,6 +44,7 @@ from spiffworkflow_backend.routes.process_api_blueprint import _get_process_mode from spiffworkflow_backend.routes.process_api_blueprint import ( _un_modify_modified_process_model_id, ) +from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.error_handling_service import ErrorHandlingService from spiffworkflow_backend.services.git_service import GitCommandError from spiffworkflow_backend.services.git_service import GitService @@ -578,25 +578,30 @@ def process_instance_reset( def process_instance_find_by_id( process_instance_id: int, ) -> flask.wrappers.Response: - + """Process_instance_find_by_id.""" process_instance = _find_process_instance_by_id_or_raise(process_instance_id) - modified_process_model_identifier = ProcessModelInfo.modify_process_identifier_for_path_param(process_instance.process_model_identifier) - process_instance_uri = f'/process-instances/{modified_process_model_identifier}/{process_instance.id}' + modified_process_model_identifier = ( + ProcessModelInfo.modify_process_identifier_for_path_param( + process_instance.process_model_identifier + ) + ) + process_instance_uri = ( + f"/process-instances/{modified_process_model_identifier}/{process_instance.id}" + ) has_permission = AuthorizationService.user_has_permission( user=g.user, - permission='read', + permission="read", target_uri=process_instance_uri, ) uri_type = None if not has_permission: process_instance = _find_process_instance_for_me_or_raise(process_instance_id) - uri_type = 'for-me' + uri_type = "for-me" response_json = { "process_instance": process_instance, "uri_type": uri_type, - } return make_response(jsonify(response_json), 200) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py index ebc26aea5..9abe25970 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py @@ -626,6 +626,7 @@ class AuthorizationService: @classmethod def set_basic_permissions(cls) -> list[PermissionToAssign]: + """Set_basic_permissions.""" permissions_to_assign: list[PermissionToAssign] = [] permissions_to_assign.append( PermissionToAssign( @@ -661,7 +662,10 @@ class AuthorizationService: return permissions_to_assign @classmethod - def set_process_group_permissions(cls, target: str, permission_set: str) -> list[PermissionToAssign]: + def set_process_group_permissions( + cls, target: str, permission_set: str + ) -> list[PermissionToAssign]: + """Set_process_group_permissions.""" permissions_to_assign: list[PermissionToAssign] = [] process_group_identifier = ( target.removeprefix("PG:").replace("/", ":").removeprefix(":") @@ -673,16 +677,16 @@ class AuthorizationService: f"/process-groups/{process_related_path_segment}", f"/process-models/{process_related_path_segment}", ] - permissions_to_assign = ( - permissions_to_assign - + cls.get_permissions_to_assign( - permission_set, process_related_path_segment, target_uris - ) + permissions_to_assign = permissions_to_assign + cls.get_permissions_to_assign( + permission_set, process_related_path_segment, target_uris ) return permissions_to_assign @classmethod - def set_process_model_permissions(cls, target: str, permission_set: str) -> list[PermissionToAssign]: + def set_process_model_permissions( + cls, target: str, permission_set: str + ) -> list[PermissionToAssign]: + """Set_process_model_permissions.""" permissions_to_assign: list[PermissionToAssign] = [] process_model_identifier = ( target.removeprefix("PM:").replace("/", ":").removeprefix(":") @@ -693,11 +697,8 @@ class AuthorizationService: process_related_path_segment = "*" target_uris = [f"/process-models/{process_related_path_segment}"] - permissions_to_assign = ( - permissions_to_assign - + cls.get_permissions_to_assign( - permission_set, process_related_path_segment, target_uris - ) + permissions_to_assign = permissions_to_assign + cls.get_permissions_to_assign( + permission_set, process_related_path_segment, target_uris ) return permissions_to_assign @@ -731,9 +732,13 @@ class AuthorizationService: permissions = ["create", "read", "update", "delete"] if target.startswith("PG:"): - permissions_to_assign += cls.set_process_group_permissions(target, permission_set) + permissions_to_assign += cls.set_process_group_permissions( + target, permission_set + ) elif target.startswith("PM:"): - permissions_to_assign += cls.set_process_model_permissions(target, permission_set) + permissions_to_assign += cls.set_process_model_permissions( + target, permission_set + ) elif permission_set == "start": raise InvalidPermissionError( "Permission 'start' is only available for macros PM and PG." diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py index 1f0e9dc1e..df62f5be1 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py @@ -354,6 +354,7 @@ class BaseTest: assert has_permission is expected_result def modify_process_identifier_for_path_param(self, identifier: str) -> str: + """Modify_process_identifier_for_path_param.""" return ProcessModelInfo.modify_process_identifier_for_path_param(identifier) def un_modify_modified_process_identifier_for_path_param( diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_instances_controller.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_instances_controller.py index 2577ff516..8c11e4e72 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_instances_controller.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_instances_controller.py @@ -1,13 +1,15 @@ """Test_users_controller.""" from flask.app import Flask -from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from flask.testing import FlaskClient from tests.spiffworkflow_backend.helpers.base_test import BaseTest +from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from spiffworkflow_backend.models.user import UserModel class TestProcessInstancesController(BaseTest): + """TestProcessInstancesController.""" + def test_find_by_id( self, app: Flask, @@ -16,8 +18,12 @@ class TestProcessInstancesController(BaseTest): with_super_admin_user: UserModel, ) -> None: """Test_user_search_returns_a_user.""" - user_one = self.create_user_with_permission(username="user_one", target_uri="/process-instances/find-by-id/*") - user_two = self.create_user_with_permission(username="user_two", target_uri="/process-instances/find-by-id/*") + user_one = self.create_user_with_permission( + username="user_one", target_uri="/process-instances/find-by-id/*" + ) + user_two = self.create_user_with_permission( + username="user_two", target_uri="/process-instances/find-by-id/*" + ) process_model = load_test_spec( process_model_id="group/sample", @@ -34,7 +40,7 @@ class TestProcessInstancesController(BaseTest): ) assert response.status_code == 200 assert response.json - assert response.json['id'] == process_instance.id + assert response.json["id"] == process_instance.id response = client.get( f"/v1.0/process-instances/find-by-id/{process_instance.id}", @@ -48,4 +54,4 @@ class TestProcessInstancesController(BaseTest): ) assert response.status_code == 200 assert response.json - assert response.json['id'] == process_instance.id + assert response.json["id"] == process_instance.id diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceFindById.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceFindById.tsx index c587ff3c0..e55520ef6 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceFindById.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceFindById.tsx @@ -2,13 +2,9 @@ import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; // @ts-ignore import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react'; -import { - isInteger, - modifyProcessIdentifierForPathParam, - slugifyString, -} from '../helpers'; +import { isInteger, modifyProcessIdentifierForPathParam } from '../helpers'; import HttpService from '../services/HttpService'; -import { ProcessGroup, ProcessInstance } from '../interfaces'; +import { ProcessInstance } from '../interfaces'; export default function ProcessInstanceFindById() { const navigate = useNavigate(); From fbdeddab4d117d7633429b489f58a6771283b41b Mon Sep 17 00:00:00 2001 From: jasquat Date: Thu, 5 Jan 2023 17:31:31 -0500 Subject: [PATCH 3/3] fixed test w/ burnettk --- .../integration/test_process_instances_controller.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_instances_controller.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_instances_controller.py index 8c11e4e72..e9c403a4e 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_instances_controller.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_instances_controller.py @@ -40,7 +40,9 @@ class TestProcessInstancesController(BaseTest): ) assert response.status_code == 200 assert response.json - assert response.json["id"] == process_instance.id + assert 'process_instance' in response.json + assert response.json['process_instance']["id"] == process_instance.id + assert response.json['uri_type'] == 'for-me' response = client.get( f"/v1.0/process-instances/find-by-id/{process_instance.id}", @@ -54,4 +56,6 @@ class TestProcessInstancesController(BaseTest): ) assert response.status_code == 200 assert response.json - assert response.json["id"] == process_instance.id + assert 'process_instance' in response.json + assert response.json['process_instance']["id"] == process_instance.id + assert response.json['uri_type'] is None