diff --git a/plugins/orchestrator-backend/src/service/router.ts b/plugins/orchestrator-backend/src/service/router.ts index 7c499f398af..742679fdf96 100644 --- a/plugins/orchestrator-backend/src/service/router.ts +++ b/plugins/orchestrator-backend/src/service/router.ts @@ -9,6 +9,7 @@ import { import type { Config } from '@backstage/config'; import type { DiscoveryApi } from '@backstage/core-plugin-api'; import { + AuthorizePermissionRequest, AuthorizePermissionResponse, AuthorizeResult, BasicPermission, @@ -29,18 +30,14 @@ import { Filter, openApiDocument, orchestratorPermissions, - orchestratorWorkflowExecutePermission, - orchestratorWorkflowExecuteSpecificPermission, - orchestratorWorkflowInstanceAbortPermission, - orchestratorWorkflowInstanceAbortSpecificPermission, - orchestratorWorkflowInstanceReadPermission, - orchestratorWorkflowInstanceReadSpecificPermission, - orchestratorWorkflowInstancesReadPermission, - orchestratorWorkflowReadPermission, - orchestratorWorkflowReadSpecificPermission, - orchestratorWorkflowsReadPermission, + orchestratorWorkflowPermission, + orchestratorWorkflowSpecificPermission, + orchestratorWorkflowUsePermission, + orchestratorWorkflowUseSpecificPermission, + ProcessInstanceListResultDTO, QUERY_PARAM_BUSINESS_KEY, QUERY_PARAM_INCLUDE_ASSESSMENT, + WorkflowOverviewListResultDTO, } from '@janus-idp/backstage-plugin-orchestrator-common'; import { UnauthorizedError } from '@janus-idp/backstage-plugin-rbac-common'; @@ -93,6 +90,89 @@ const authorize = async ( ); }; +const getAuthorizedWorkflowIds = async ( + request: HttpRequest, + permissionsSvc: PermissionsService, + httpAuth: HttpAuthService, + workflowIds: string[], +): Promise => { + const credentials = await httpAuth.credentials(request); + const genericWorkflowPermissionDecision = await permissionsSvc.authorize( + [{ permission: orchestratorWorkflowPermission }], + { + credentials, + }, + ); + + if (genericWorkflowPermissionDecision[0].result === AuthorizeResult.ALLOW) { + // The user can see all workflows + return workflowIds.map(_ => true); + } + + const specificWorkflowRequests: AuthorizePermissionRequest[] = + workflowIds.map(workflowId => ({ + permission: orchestratorWorkflowSpecificPermission(workflowId), + })); + + const decisions = await permissionsSvc.authorize(specificWorkflowRequests, { + credentials, + }); + + return decisions.map(d => d.result === AuthorizeResult.ALLOW); +}; + +const filterAuthorizedWorkflows = async ( + request: HttpRequest, + permissionsSvc: PermissionsService, + httpAuth: HttpAuthService, + workflows: WorkflowOverviewListResultDTO, +): Promise => { + if (!workflows.overviews) { + return workflows; + } + + const authorizedWorkflowIds = await getAuthorizedWorkflowIds( + request, + permissionsSvc, + httpAuth, + workflows.overviews.map(w => w.workflowId), + ); + + const filtered = { + ...workflows, + overviews: workflows.overviews.filter( + (_, idx) => authorizedWorkflowIds[idx], + ), + }; + + return filtered; +}; + +const filterAuthorizedInstances = async ( + request: HttpRequest, + permissionsSvc: PermissionsService, + httpAuth: HttpAuthService, + instances: ProcessInstanceListResultDTO, +): Promise => { + if (!instances.items) { + return instances; + } + + const authorizedWorkflowIds = await getAuthorizedWorkflowIds( + request, + permissionsSvc, + httpAuth, + instances.items.map(instance => instance.processId), + ); + + const filtered = { + ...instances, + items: instances.items.filter((_, idx) => authorizedWorkflowIds[idx]), + }; + + return filtered; +}; + export async function createBackendRouter( options: RouterOptions, ): Promise { @@ -312,22 +392,25 @@ function setupInternalRoutes( request: req, message: `Received request to '${endpoint}' endpoint`, }); - const decision = await authorize( - req, - [orchestratorWorkflowsReadPermission], - permissions, - httpAuth, - ); - if (decision.result === AuthorizeResult.DENY) { - manageDenyAuthorization(endpointName, endpoint, req); + + try { + // TODO: use pagination only if the generic orchestratorWorkflowPermission is in place + const result = await routerApi.v2.getWorkflowsOverview( + buildPagination(req), + getRequestFilters(req), + ); + + const workflows = await filterAuthorizedWorkflows( + req, + permissions, + httpAuth, + result, + ); + res.json(workflows); + } catch (error) { + auditLogRequestError(error, endpointName, endpoint, req); + next(error); } - return routerApi.v2 - .getWorkflowsOverview(buildPagination(req), getRequestFilters(req)) - .then(result => res.json(result)) - .catch(error => { - auditLogRequestError(error, endpointName, endpoint, req); - next(error); - }); }, ); @@ -351,8 +434,8 @@ function setupInternalRoutes( const decision = await authorize( _req, [ - orchestratorWorkflowReadPermission, - orchestratorWorkflowReadSpecificPermission(workflowId), + orchestratorWorkflowPermission, + orchestratorWorkflowSpecificPermission(workflowId), ], permissions, httpAuth, @@ -391,8 +474,8 @@ function setupInternalRoutes( const decision = await authorize( req, [ - orchestratorWorkflowExecutePermission, - orchestratorWorkflowExecuteSpecificPermission(workflowId), + orchestratorWorkflowUsePermission, + orchestratorWorkflowUseSpecificPermission(workflowId), ], permissions, httpAuth, @@ -438,7 +521,10 @@ function setupInternalRoutes( const decision = await authorize( req, - orchestratorWorkflowExecutePermission, + [ + orchestratorWorkflowUsePermission, + orchestratorWorkflowUseSpecificPermission(workflowId), + ], permissions, httpAuth, ); @@ -476,8 +562,8 @@ function setupInternalRoutes( const decision = await authorize( _req, [ - orchestratorWorkflowReadPermission, - orchestratorWorkflowReadSpecificPermission(workflowId), + orchestratorWorkflowPermission, + orchestratorWorkflowSpecificPermission(workflowId), ], permissions, httpAuth, @@ -510,15 +596,8 @@ function setupInternalRoutes( request: _req, message: `Received request to '${endpoint}' endpoint`, }); - const decision = await authorize( - _req, - [orchestratorWorkflowInstancesReadPermission], - permissions, - httpAuth, - ); - if (decision.result === AuthorizeResult.DENY) { - manageDenyAuthorization(endpointName, endpoint, _req); - } + // Anyone is authorized to call this endpoint + return routerApi.v2 .getWorkflowStatuses() .then(result => res.status(200).json(result)) @@ -549,8 +628,8 @@ function setupInternalRoutes( const decision = await authorize( req, [ - orchestratorWorkflowReadPermission, - orchestratorWorkflowReadSpecificPermission(workflowId), + orchestratorWorkflowPermission, + orchestratorWorkflowSpecificPermission(workflowId), ], permissions, httpAuth, @@ -670,7 +749,10 @@ function setupInternalRoutes( const decision = await authorize( req, - [orchestratorWorkflowInstancesReadPermission], + [ + orchestratorWorkflowPermission, + orchestratorWorkflowSpecificPermission(workflowId), + ], permissions, httpAuth, ); @@ -703,22 +785,23 @@ function setupInternalRoutes( message: `Received request to '${endpoint}' endpoint`, }); - const decision = await authorize( - req, - [orchestratorWorkflowInstancesReadPermission], - permissions, - httpAuth, - ); - if (decision.result === AuthorizeResult.DENY) { - manageDenyAuthorization(endpointName, endpoint, req); + try { + // TODO: use pagination only if the generic orchestratorWorkflowPermission is in place + const result = await routerApi.v2.getInstances( + buildPagination(req), + getRequestFilters(req), + ); + const instances = await filterAuthorizedInstances( + req, + permissions, + httpAuth, + result, + ); + res.json(instances); + } catch (error) { + auditLogRequestError(error, endpointName, endpoint, req); + next(error); } - return routerApi.v2 - .getInstances(buildPagination(req), getRequestFilters(req)) - .then(result => res.json(result)) - .catch(error => { - auditLogRequestError(error, endpointName, endpoint, req); - next(error); - }); }, ); @@ -752,12 +835,11 @@ function setupInternalRoutes( const workflowId = assessedInstance.instance.processId; - // we need to authorize after retrieval to know the workflowId const decision = await authorize( _req, [ - orchestratorWorkflowInstanceReadPermission, - orchestratorWorkflowInstanceReadSpecificPermission(workflowId), + orchestratorWorkflowPermission, + orchestratorWorkflowSpecificPermission(workflowId), ], permissions, httpAuth, @@ -766,11 +848,10 @@ function setupInternalRoutes( manageDenyAuthorization(endpointName, endpoint, _req); } - return res.status(200).json(assessedInstance); + res.status(200).json(assessedInstance); } catch (error) { auditLogRequestError(error, endpointName, endpoint, _req); next(error); - throw error; } }, ); @@ -792,25 +873,29 @@ function setupInternalRoutes( message: `Received request to '${endpoint}' endpoint`, }); - const decision = await authorize( - _req, - [ - orchestratorWorkflowInstanceAbortPermission, - // TODO: get workflowId and use orchestratorWorkflowInstanceAbortSpecificPermission(workflowId) - ], - permissions, - httpAuth, - ); - if (decision.result === AuthorizeResult.DENY) { - manageDenyAuthorization(endpointName, endpoint, _req); + try { + const assessedInstance = await routerApi.v2.getInstanceById(instanceId); + const workflowId = assessedInstance.instance.processId; + + const decision = await authorize( + _req, + [ + orchestratorWorkflowUsePermission, + orchestratorWorkflowUseSpecificPermission(workflowId), + ], + permissions, + httpAuth, + ); + if (decision.result === AuthorizeResult.DENY) { + manageDenyAuthorization(endpointName, endpoint, _req); + } + + const result = await routerApi.v2.abortWorkflow(instanceId); + res.status(200).json(result); + } catch (error) { + auditLogRequestError(error, endpointName, endpoint, _req); + next(error); } - return routerApi.v2 - .abortWorkflow(instanceId) - .then(result => res.json(result)) - .catch(error => { - auditLogRequestError(error, endpointName, endpoint, _req); - next(error); - }); }, ); } diff --git a/plugins/orchestrator-common/src/permissions.ts b/plugins/orchestrator-common/src/permissions.ts index 3f3a86a89bb..df427c7bb02 100644 --- a/plugins/orchestrator-common/src/permissions.ts +++ b/plugins/orchestrator-common/src/permissions.ts @@ -1,89 +1,29 @@ import { createPermission } from '@backstage/plugin-permission-common'; -export const orchestratorWorkflowInstancesReadPermission = createPermission({ - name: 'orchestrator.workflowInstances.read', +export const orchestratorWorkflowPermission = createPermission({ + name: 'orchestrator.workflow', attributes: { action: 'read', }, }); -export const orchestratorWorkflowInstanceReadPermission = createPermission({ - name: 'orchestrator.workflowInstance.read', - attributes: { - action: 'read', - }, -}); - -/** - * @param workflowId Mind this is workflowId and not instanceId - */ -export const orchestratorWorkflowInstanceReadSpecificPermission = ( - workflowId: string, -) => - createPermission({ - name: `orchestrator.workflowInstance.read.${workflowId}`, - attributes: { - action: 'read', - }, - }); - -export const orchestratorWorkflowsReadPermission = createPermission({ - name: 'orchestrator.workflows.read', - attributes: { - action: 'read', - }, -}); - -export const orchestratorWorkflowReadPermission = createPermission({ - name: 'orchestrator.workflow.read', - attributes: { - action: 'read', - }, -}); - -export const orchestratorWorkflowReadSpecificPermission = ( - workflowId: string, -) => +export const orchestratorWorkflowSpecificPermission = (workflowId: string) => createPermission({ - name: `orchestrator.workflow.read.${workflowId}`, + name: `orchestrator.workflow.${workflowId}`, attributes: { action: 'read', }, }); -export const orchestratorWorkflowExecutePermission = createPermission({ - name: 'orchestrator.workflow.execute', - attributes: {}, -}); - -export const orchestratorWorkflowExecuteSpecificPermission = ( - workflowId: string, -) => - createPermission({ - name: `orchestrator.workflow.execute.${workflowId}`, - attributes: {}, - }); - -export const orchestratorWorkflowInstanceAbortPermission = createPermission({ - name: 'orchestrator.workflowInstance.abort', +export const orchestratorWorkflowUsePermission = createPermission({ + name: 'orchestrator.workflow.use', attributes: {}, }); -/** - * @param workflowId Mind this is workflowId and not instanceId - */ -export const orchestratorWorkflowInstanceAbortSpecificPermission = ( - workflowId: string, -) => +export const orchestratorWorkflowUseSpecificPermission = (workflowId: string) => createPermission({ - name: `orchestrator.workflowInstance.abort.${workflowId}`, + name: `orchestrator.workflow.use.${workflowId}`, attributes: {}, }); -export const orchestratorPermissions = [ - orchestratorWorkflowReadPermission, - orchestratorWorkflowExecutePermission, - orchestratorWorkflowInstancesReadPermission, - orchestratorWorkflowInstanceReadPermission, - orchestratorWorkflowInstanceAbortPermission, -]; +export const orchestratorPermissions = [orchestratorWorkflowPermission]; diff --git a/plugins/orchestrator/docs/Permissions.md b/plugins/orchestrator/docs/Permissions.md index c80e6a8f74c..008cf8ad35d 100644 --- a/plugins/orchestrator/docs/Permissions.md +++ b/plugins/orchestrator/docs/Permissions.md @@ -3,21 +3,15 @@ the RBAC plugin. The result is control over what users can see or execute. ## Orchestrator Permissions -| Name | Resource Type | Policy | Description | Requirements | -| -------------------------------------------------- | -------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------- | ------------ | -| orchestrator.workflowInstances.read | named resource | read | Allows the user to read **all of orchestrator workflow runs** (workflow instances) | | -| orchestrator.workflowInstance.read | named resource | read | Allows the user to read the details of **any single** workflow instance | | -| orchestrator.workflowInstance.read.[`workflowId`] | named resource | read | Allows the user to read the details of `workflowId`-workflow's instances (replace the `[workflowId]` by a single workflow ID) | | -| orchestrator.workflowInstance.abort | named resource | use | Allows the user to abort any workflow instance | | -| orchestrator.workflowInstance.abort.[`workflowId`] | named resource | use | Allows the user to abort a single workflow instance | | -| orchestrator.workflows.read | named resource | read | Allows the user to read **all of workflows** (but not their instances) | | -| orchestrator.workflow.read | named resource | read | Allows the user to read the workflow definitions | | -| orchestrator.workflow.read.[`workflowId`] | named resource | read | Allows the user to read the details of a single workflow definition | | -| orchestrator.workflow.execute | named resource | use | Allows the user to execute a workflow | | -| orchestrator.workflow.execute.[`workflowId`] | named resource | use | Allows the user to execute a single workflow | | +| Name | Resource Type | Policy | Description | Requirements | +| ----------------------------------------- | -------------- | ------ | ------------------------------------------------------------------------------------- | ------------ | +| orchestrator.workflow.read | named resource | read | Allows the user to list and read _any_ workflow definition and their instances (runs) | | +| orchestrator.workflow.read.[`workflowId`] | named resource | read | Allows the user to list and read the details of a _single_ workflow definition | | +| orchestrator.workflow.use | named resource | read | Allows the user to run or abort _any_ workflow | | +| orchestrator.workflow.use.[`workflowId`] | named resource | read | Allows the user to run or abort the _single_ workflow | | The user is permitted to do an action if either the generic permission or the specific one allows it. -In other words, it is not possible to grant generic `orchestrator.workflowInstance.read` and then selectively disable it for a specific workflow via `orchestrator.workflowInstance.read.[workflowId]` with `deny`. +In other words, it is not possible to grant generic `orchestrator.workflowInstance.read` and then selectively disable it for a specific workflow via `orchestrator.workflow.use.[workflowId]` with `deny`. ## Policy File @@ -25,38 +19,30 @@ To get started with policies, we recommend defining 2 roles and assigning them t As an example, mind the following [policy file](./rbac-policy.csv). -Since the `guest` user has the `default/workflowViewer` role, it can: +Since the `guest` user has the `default/workflowUser` role, it can: -- view the list of workflows (`orchestrator.workflows.read`) -- view any workflow details (`orchestrator.workflow.read`) -- view the list of all instances (`orchestrator.workflowInstances.read`) -- view any instance (`orchestrator.workflowInstance.read`) -- execute just the `yamlgreet` workflow but not any other (`orchestrator.workflow.execute.yamlgreet`) +- list subset of workflows (specific `orchestrator.workflow.yamlgreet`) +- view workflow details and their instances of selected workflow (`orchestrator.workflow.yamlgreet`) +- execute or abort the `yamlgreet` and `wait-or-error` workflows but not any other (`orchestrator.workflow.use.yamlgreet`) -The users of the `workflowAdmin` role have full permissions. +Namely, the `default/workflowUser` role can not see the list of _all_ workflows or execute other workflows than explicitly stated. -```csv -p, role:default/workflowViewer, orchestrator.workflows.read, read, allow -p, role:default/workflowViewer, orchestrator.workflow.read, read, allow -p, role:default/workflowViewer, orchestrator.workflowInstances.read, read, allow -p, role:default/workflowViewer, orchestrator.workflowInstance.read, read, allow +The users of the `default/workflowAdmin` role have full permissions (can list, read and execute any workflow). -p, role:default/workflowViewer, orchestrator.workflow.execute.yamlgreet, use, allow +```csv +p, role:default/workflowUser, orchestrator.workflow.yamlgreet, read, allow +p, role:default/workflowUser, orchestrator.workflow.wait-or-error, read, allow -p, role:default/workflowAdmin, orchestrator.workflows.read, read, allow -p, role:default/workflowAdmin, orchestrator.workflow.read, read, allow -p, role:default/workflowAdmin, orchestrator.workflowInstance.abort, use, allow -p, role:default/workflowAdmin, orchestrator.workflowInstances.read, read, allow -p, role:default/workflowAdmin, orchestrator.workflowInstance.read, read, allow +p, role:default/workflowUser, orchestrator.workflow.use.yamlgreet, use, allow -p, role:default/workflowAdmin, orchestrator.workflow.execute, use, allow +p, role:default/workflowAdmin, orchestrator.workflow, read, allow +p, role:default/workflowAdmin, orchestrator.workflow.use, use, allow -g, user:default/guest, role:default/workflowViewer -g, user:default/myOrgUser, role:default/workflowAdmin -g, group:default/platformAdmins, role:default/worflowAdmin +g, user:development/guest, role:default/workflowUser +g, user:default/mareklibra, role:default/workflowAdmin ``` -See https://casbin.org/docs/rbac for more information about casbin rules +See https://casbin.org/docs/rbac for more information about casbin rules. ## Enable permissions diff --git a/plugins/orchestrator/docs/rbac-policy.csv b/plugins/orchestrator/docs/rbac-policy.csv index 1fbc6efc353..d81938d8d73 100644 --- a/plugins/orchestrator/docs/rbac-policy.csv +++ b/plugins/orchestrator/docs/rbac-policy.csv @@ -1,23 +1,13 @@ -p, role:default/workflowViewer, orchestrator.workflows.read, read, allow -p, role:default/workflowViewer, orchestrator.workflow.read, read, allow +p, role:default/workflowUser, orchestrator.workflow.yamlgreet, read, allow +p, role:default/workflowUser, orchestrator.workflow.wait-or-error, read, allow -p, role:default/workflowViewer, orchestrator.workflowInstances.read, read, allow -#p, role:default/workflowViewer, orchestrator.workflowInstance.read, read, allow -p, role:default/workflowViewer, orchestrator.workflowInstance.read.yamlgreet, read, allow +##p, role:default/workflowUser, orchestrator.workflow.use, use, allow +p, role:default/workflowUser, orchestrator.workflow.use.yamlgreet, use, allow +##p, role:default/workflowUser, orchestrator.workflow.use.wait-or-error, use, allow -#p, role:default/workflowViewer, orchestrator.workflow.execute, use, allow -p, role:default/workflowViewer, orchestrator.workflow.execute.yamlgreet, use, allow -#p, role:default/workflowViewer, orchestrator.workflow.execute.wait-or-error, use, allow +p, role:default/workflowAdmin, orchestrator.workflow, read, allow +p, role:default/workflowAdmin, orchestrator.workflow.use, use, allow -p, role:default/workflowAdmin, orchestrator.workflows.read, read, allow -p, role:default/workflowAdmin, orchestrator.workflow.read, read, allow -p, role:default/workflowAdmin, orchestrator.workflowInstance.abort, use, allow -p, role:default/workflowAdmin, orchestrator.workflowInstances.read, read, allow -p, role:default/workflowAdmin, orchestrator.workflowInstance.read, read, allow - -#p, role:default/workflowAdmin, orchestrator.workflow.execute, use, allow -p, role:default/workflowAdmin, orchestrator.workflow.execute.yamlgreet, use, allow - -g, user:development/guest, role:default/workflowViewer +g, user:development/guest, role:default/workflowUser g, user:default/rgolangh, role:default/workflowAdmin g, user:default/mareklibra, role:default/workflowAdmin diff --git a/plugins/orchestrator/src/components/WorkflowDefinitionViewerPage/WorkflowDefinitionViewerPage.tsx b/plugins/orchestrator/src/components/WorkflowDefinitionViewerPage/WorkflowDefinitionViewerPage.tsx index e0397af2362..785ac3a103d 100644 --- a/plugins/orchestrator/src/components/WorkflowDefinitionViewerPage/WorkflowDefinitionViewerPage.tsx +++ b/plugins/orchestrator/src/components/WorkflowDefinitionViewerPage/WorkflowDefinitionViewerPage.tsx @@ -13,12 +13,12 @@ import { Button, Grid, Tooltip } from '@material-ui/core'; import { Skeleton } from '@material-ui/lab'; import { - orchestratorWorkflowExecutePermission, - orchestratorWorkflowExecuteSpecificPermission, + orchestratorWorkflowUsePermission, + orchestratorWorkflowUseSpecificPermission, } from '@janus-idp/backstage-plugin-orchestrator-common'; import { orchestratorApiRef } from '../../api'; -import { usePermissionArray } from '../../hooks/usePermissionArray'; +import { usePermissionArrayDecision } from '../../hooks/usePermissionArray'; import { executeWorkflowRouteRef, workflowDefinitionsRouteRef, @@ -31,10 +31,11 @@ export const WorkflowDefinitionViewerPage = () => { const { workflowId, format } = useRouteRefParams(workflowDefinitionsRouteRef); const orchestratorApi = useApi(orchestratorApiRef); - const { loading: loadingPermission, allowed: canRun } = usePermissionArray([ - orchestratorWorkflowExecutePermission, - orchestratorWorkflowExecuteSpecificPermission(workflowId), - ]); + const { loading: loadingPermission, allowed: canRun } = + usePermissionArrayDecision([ + orchestratorWorkflowUsePermission, + orchestratorWorkflowUseSpecificPermission(workflowId), + ]); const { value: workflowOverviewDTO, loading, diff --git a/plugins/orchestrator/src/components/WorkflowInstancePage.tsx b/plugins/orchestrator/src/components/WorkflowInstancePage.tsx index dee01466d3c..656ba1787ea 100644 --- a/plugins/orchestrator/src/components/WorkflowInstancePage.tsx +++ b/plugins/orchestrator/src/components/WorkflowInstancePage.tsx @@ -17,10 +17,8 @@ import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; import { AssessedProcessInstanceDTO, - orchestratorWorkflowExecutePermission, - orchestratorWorkflowExecuteSpecificPermission, - orchestratorWorkflowInstanceAbortPermission, - orchestratorWorkflowInstanceAbortSpecificPermission, + orchestratorWorkflowUsePermission, + orchestratorWorkflowUseSpecificPermission, ProcessInstanceStatusDTO, QUERY_PARAM_ASSESSMENT_INSTANCE_ID, QUERY_PARAM_INSTANCE_ID, @@ -28,7 +26,7 @@ import { import { orchestratorApiRef } from '../api'; import { SHORT_REFRESH_INTERVAL } from '../constants'; -import { usePermissionArray } from '../hooks/usePermissionArray'; +import { usePermissionArrayDecision } from '../hooks/usePermissionArray'; import usePolling from '../hooks/usePolling'; import { executeWorkflowRouteRef, workflowInstanceRouteRef } from '../routes'; import { isNonNullable } from '../utils/TypeGuards'; @@ -131,22 +129,13 @@ export const WorkflowInstancePage = ({ ); const workflowId = value?.instance?.processId; - const permittedToExecute = usePermissionArray( + const permittedToUse = usePermissionArrayDecision( workflowId ? [ - orchestratorWorkflowExecutePermission, - orchestratorWorkflowExecuteSpecificPermission(workflowId), + orchestratorWorkflowUsePermission, + orchestratorWorkflowUseSpecificPermission(workflowId), ] - : [orchestratorWorkflowExecutePermission], - ); - - const permittedToAbort = usePermissionArray( - workflowId - ? [ - orchestratorWorkflowInstanceAbortPermission, - orchestratorWorkflowInstanceAbortSpecificPermission(workflowId), - ] - : [orchestratorWorkflowInstanceAbortPermission], + : [orchestratorWorkflowUsePermission], ); const canAbort = @@ -234,11 +223,11 @@ export const WorkflowInstancePage = ({