diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml index 30f315846..d192a7dea 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml @@ -129,37 +129,6 @@ permissions: uri: /processes - manage-procurement-admin: - groups: ["Project Lead"] - users: [] - allowed_permissions: [create, read, update, delete] - uri: /process-groups/manage-procurement:* - manage-procurement-admin-slash: - groups: ["Project Lead"] - users: [] - allowed_permissions: [create, read, update, delete] - uri: /process-groups/manage-procurement/* - manage-procurement-admin-models: - groups: ["Project Lead"] - users: [] - allowed_permissions: [create, read, update, delete] - uri: /process-models/manage-procurement:* - manage-procurement-admin-models-slash: - groups: ["Project Lead"] - users: [] - allowed_permissions: [create, read, update, delete] - uri: /process-models/manage-procurement/* - manage-procurement-admin-instances: - groups: ["Project Lead"] - users: [] - allowed_permissions: [create, read, update, delete] - uri: /process-instances/manage-procurement:* - manage-procurement-admin-instances-slash: - groups: ["Project Lead"] - users: [] - allowed_permissions: [create, read, update, delete] - uri: /process-instances/manage-procurement/* - finance-admin: groups: ["Finance Team"] users: [] diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml index fac5cf300..d51b10f9b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml @@ -48,12 +48,6 @@ groups: lead1 ] - core-contributor: - users: - [ - core, - harmeet, - ] test: users: [natalia] @@ -64,25 +58,7 @@ permissions: allowed_permissions: [create, read, update, delete] uri: /* - tasks-crud: - groups: [everybody] - users: [] - allowed_permissions: [create, read, update, delete] - uri: /tasks/* - - service-tasks: - groups: [everybody] - users: [] - allowed_permissions: [read] - uri: /service-tasks - user-groups-for-current-user: - groups: [everybody] - users: [] - allowed_permissions: [read] - uri: /user-groups/for-current-user - - - # read all for everybody + # open system defaults for everybody read-all-process-groups: groups: [everybody] users: [] @@ -93,6 +69,8 @@ permissions: users: [] allowed_permissions: [read] uri: /process-models/* + + # basic perms for everybody read-all-process-instances-for-me: groups: [everybody] users: [] @@ -108,39 +86,23 @@ permissions: users: [] allowed_permissions: [read] uri: /processes + service-tasks: + groups: [everybody] + users: [] + allowed_permissions: [read] + uri: /service-tasks + tasks-crud: + groups: [everybody] + users: [] + allowed_permissions: [create, read, update, delete] + uri: /tasks/* + user-groups-for-current-user: + groups: [everybody] + users: [] + allowed_permissions: [read] + uri: /user-groups/for-current-user - manage-procurement-admin: - groups: ["Project Lead"] - users: [] - allowed_permissions: [create, read, update, delete] - uri: /process-groups/manage-procurement:* - manage-procurement-admin-slash: - groups: ["Project Lead"] - users: [] - allowed_permissions: [create, read, update, delete] - uri: /process-groups/manage-procurement/* - manage-procurement-admin-models: - groups: ["Project Lead"] - users: [] - allowed_permissions: [create, read, update, delete] - uri: /process-models/manage-procurement:* - manage-procurement-admin-models-slash: - groups: ["Project Lead"] - users: [] - allowed_permissions: [create, read, update, delete] - uri: /process-models/manage-procurement/* - manage-procurement-admin-instances: - groups: ["Project Lead"] - users: [] - allowed_permissions: [create, read, update, delete] - uri: /process-instances/manage-procurement:* - manage-procurement-admin-instances-slash: - groups: ["Project Lead"] - users: [] - allowed_permissions: [create, read, update, delete] - uri: /process-instances/manage-procurement/* - finance-admin: groups: ["Finance Team"] users: [] @@ -148,23 +110,37 @@ permissions: uri: /process-groups/manage-procurement:procurement:* manage-revenue-streams-instances: - groups: ["core-contributor", "demo"] + groups: ["demo"] users: [] - allowed_permissions: [create, read] + allowed_permissions: [create] uri: /process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* - manage-procurement-invoice-instances: - groups: ["core-contributor", "demo"] + groups: ["demo"] users: [] - allowed_permissions: [create, read] + allowed_permissions: [create] uri: /process-instances/manage-procurement:procurement:core-contributor-invoice-management:* - manage-procurement-instances: - groups: ["core-contributor", "demo"] + groups: ["demo"] users: [] - allowed_permissions: [create, read] + allowed_permissions: [create] uri: /process-instances/manage-procurement:vendor-lifecycle-management:* + manage-revenue-streams-instances-for-me: + groups: ["demo"] + users: [] + allowed_permissions: [read] + uri: /process-instances/for-me/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* + manage-procurement-invoice-instances-for-me: + groups: ["demo"] + users: [] + allowed_permissions: [read] + uri: /process-instances/for-me/manage-procurement:procurement:core-contributor-invoice-management:* + manage-procurement-instances-for-me: + groups: ["demo"] + users: [] + allowed_permissions: [read] + uri: /process-instances/for-me/manage-procurement:vendor-lifecycle-management:* + create-test-instances: groups: ["test"] users: [] diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py index 52ea3d826..ac6ac4c52 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py @@ -1,4 +1,5 @@ """Authorization_service.""" +from dataclasses import dataclass import inspect import re from hashlib import sha256 @@ -46,6 +47,21 @@ class UserDoesNotHaveAccessToTaskError(Exception): """UserDoesNotHaveAccessToTaskError.""" +@dataclass +class PermissionToAssign: + permission: str + target_uri: str + + +PATH_SEGMENTS_FOR_PERMISSION_ALL = [ + '/logs', + '/process-instances', + '/process-instance-suspend', + '/process-instance-terminate', + '/task-data', +] + + class AuthorizationService: """Determine whether a user has permission to perform their request.""" @@ -514,6 +530,93 @@ class AuthorizationService: # this cannot be None so ignore mypy return user_model # type: ignore + @classmethod + def get_permissions_to_assign(cls, permission: str, process_related_path_segment: str, target_uris: list[str]) -> list[PermissionToAssign]: + permissions = [permission] + if permission == "all": + permissions = ['create', 'read', 'update', 'delete'] + + permissions_to_assign: list[PermissionToAssign] = [] + + # we were thinking that if you can start an instance, you ought to be able to view your own instances. + if permission == "start": + target_uri = f"/process-instances/{process_related_path_segment}" + permissions_to_assign.append(PermissionToAssign(permission='create', target_uri=target_uri)) + target_uri = f"/process-instances/for-me/{process_related_path_segment}" + permissions_to_assign.append(PermissionToAssign(permission='read', target_uri=target_uri)) + + else: + if permission == 'all': + for path_segment in PATH_SEGMENTS_FOR_PERMISSION_ALL: + target_uris.append(f"{path_segment}/{process_related_path_segment}") + + for target_uri in target_uris: + for permission in permissions: + permissions_to_assign.append(PermissionToAssign(permission=permission, target_uri=target_uri)) + + return permissions_to_assign + + @classmethod + def explode_permissions(cls, permission: str, target: str) -> list[PermissionToAssign]: + """Explodes given permissions to and returns list of PermissionToAssign objects. + + These can be used to then iterate through and inserted into the database. + Target Macros: + PG:[process_group_identifier] + * affects given process-group and all sub process-groups and process-models + PM:[process_model_identifier] + * affects given process-model + BASIC + * Basic access to complete tasks and use the site + + Permission Macros: + all - create, read, update, delete + start - create process-instances (aka instantiate or start a process-model) + """ + permissions_to_assign: list[PermissionToAssign] = [] + if target.startswith("PG:"): + process_group_identifier = target.removeprefix("PG:").replace(":", "/") + process_related_path_segment = f"{process_group_identifier}/*" + target_uris = [] + 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, process_related_path_segment, target_uris) + + elif target.startswith("PM:"): + process_model_identifier = target.removeprefix("PM:").replace(":", "/") + process_related_path_segment = f"{process_model_identifier}/*" + target_uris = [] + 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, process_related_path_segment, target_uris) + + 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/*")) + else: + permissions_to_assign.append(PermissionToAssign(permission=permission, target_uri=target)) + + return permissions_to_assign + + @classmethod + def add_permission_from_uri_or_macro(cls, group_identifier: str, permission: str, target: str) -> None: + """Add_permission_from_uri_or_macro.""" + group = GroupService.find_or_create_group(group_identifier) + permissions_to_assign = cls.explode_permissions(permission, target) + for permission_to_assign in permissions_to_assign: + permission_target = AuthorizationService.find_or_create_permission_target(permission_to_assign.target_uri) + AuthorizationService.create_permission_for_principal( + group.principal, permission_target, permission_to_assign.permission + ) + class KeycloakAuthorization: """Interface with Keycloak server.""" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py index 3427b47bb..0dec5e44d 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py @@ -17,7 +17,8 @@ from spiffworkflow_backend.models.task import MultiInstanceType from spiffworkflow_backend.models.task import Task from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.authorization_service import AuthorizationService -from spiffworkflow_backend.services.git_service import GitService, GitCommandError +from spiffworkflow_backend.services.git_service import GitCommandError +from spiffworkflow_backend.services.git_service import GitService from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) @@ -38,7 +39,7 @@ class ProcessInstanceService: """Get_process_instance_from_spec.""" try: current_git_revision = GitService.get_current_revision() - except GitCommandError as ge: + except GitCommandError: current_git_revision = "" process_instance_model = ProcessInstanceModel( status=ProcessInstanceStatus.not_started.value, diff --git a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx index 03879d818..a6fd282f9 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx @@ -56,6 +56,8 @@ export default function ProcessModelEditDiagram() { const [processSearchEventBus, setProcessSearchEventBus] = useState(null); const [processSearchElement, setProcessSearchElement] = useState(null); const [processes, setProcesses] = useState([]); + const [displaySaveFileMessage, setDisplaySaveFileMessage] = + useState(false); const handleShowMarkdownEditor = () => setShowMarkdownEditor(true); @@ -157,6 +159,7 @@ export default function ProcessModelEditDiagram() { }; const navigateToProcessModelFile = (_result: any) => { + setDisplaySaveFileMessage(true); if (!params.file_name) { const fileNameWithExtension = `${newFileName}.${searchParams.get( 'file_type' @@ -167,9 +170,8 @@ export default function ProcessModelEditDiagram() { } }; - const [displaySaveFileMessage, setDisplaySaveFileMessage] = - useState(false); const saveDiagram = (bpmnXML: any, fileName = params.file_name) => { + setDisplaySaveFileMessage(false); setErrorMessage(null); setBpmnXmlForDiagramRendering(bpmnXML); @@ -204,7 +206,6 @@ export default function ProcessModelEditDiagram() { // after saving the file, make sure we null out newFileName // so it does not get used over the params setNewFileName(''); - setDisplaySaveFileMessage(true); }; const onDeleteFile = (fileName = params.file_name) => {