diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/local_development.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/local_development.yml
index cc53d8c7..759ab010 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/local_development.yml
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/local_development.yml
@@ -17,6 +17,8 @@ groups:
users: [jason@sartography.com, kevin@sartography.com]
group2:
users: [dan@sartography.com]
+ group3:
+ users: [jon@sartography.com]
permissions:
admin:
@@ -25,7 +27,7 @@ permissions:
uri: /*
basic:
- groups: [group2]
+ groups: [group2, group3]
actions: [all]
uri: BASIC
diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py
index de9aeddd..9fc7fe55 100644
--- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py
+++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_models_controller.py
@@ -158,7 +158,8 @@ def process_model_show(modified_process_model_identifier: str, include_file_refe
process_model.bpmn_version_control_identifier = current_git_revision
- available_actions = {}
+ # if the user got here then they can read the process model
+ available_actions = {"read": {"path": f"/process-models/{modified_process_model_identifier}", "method": "GET"}}
if GitService.check_for_publish_configs(raise_on_missing=False):
available_actions = {
"publish": {"path": f"/process-model-publish/{modified_process_model_identifier}", "method": "POST"}
diff --git a/spiffworkflow-frontend/src/components/ProcessBreadcrumb.tsx b/spiffworkflow-frontend/src/components/ProcessBreadcrumb.tsx
index e979f4b6..8a6db736 100644
--- a/spiffworkflow-frontend/src/components/ProcessBreadcrumb.tsx
+++ b/spiffworkflow-frontend/src/components/ProcessBreadcrumb.tsx
@@ -4,6 +4,8 @@ import { useEffect, useState } from 'react';
import { modifyProcessIdentifierForPathParam } from '../helpers';
import {
HotCrumbItem,
+ HotCrumbItemArray,
+ HotCrumbItemObject,
ProcessGroup,
ProcessGroupLite,
ProcessModel,
@@ -29,6 +31,7 @@ export default function ProcessBreadcrumb({ hotCrumbs }: OwnProps) {
entityToExplode as string
)}`,
successCallback: setProcessEntity,
+ onUnauthorized: () => {},
});
} else if (entityType === 'process-group-id') {
HttpService.makeCallToBackend({
@@ -36,6 +39,7 @@ export default function ProcessBreadcrumb({ hotCrumbs }: OwnProps) {
entityToExplode as string
)}`,
successCallback: setProcessEntity,
+ onUnauthorized: () => {},
});
} else {
setProcessEntity(entityToExplode as any);
@@ -47,74 +51,91 @@ export default function ProcessBreadcrumb({ hotCrumbs }: OwnProps) {
}
}, [setProcessEntity, hotCrumbs]);
+ const checkPermissions = (crumb: HotCrumbItemObject) => {
+ if (!crumb.checkPermission) {
+ return true;
+ }
+ return (
+ processEntity &&
+ 'actions' in processEntity &&
+ processEntity.actions &&
+ 'read' in processEntity.actions
+ );
+ };
+
// eslint-disable-next-line sonarjs/cognitive-complexity
const hotCrumbElement = () => {
if (hotCrumbs) {
- const leadingCrumbLinks = hotCrumbs.map((crumb: any) => {
- if (
- 'entityToExplode' in crumb &&
- processEntity &&
- processEntity.parent_groups
- ) {
- const breadcrumbs = processEntity.parent_groups.map(
- (parentGroup: ProcessGroupLite) => {
- const fullUrl = `/process-groups/${modifyProcessIdentifierForPathParam(
- parentGroup.id
+ const leadingCrumbLinks = hotCrumbs.map(
+ (crumb: HotCrumbItemArray | HotCrumbItemObject) => {
+ if (
+ 'entityToExplode' in crumb &&
+ processEntity &&
+ processEntity.parent_groups &&
+ checkPermissions(crumb)
+ ) {
+ const breadcrumbs = processEntity.parent_groups.map(
+ (parentGroup: ProcessGroupLite) => {
+ const fullUrl = `/process-groups/${modifyProcessIdentifierForPathParam(
+ parentGroup.id
+ )}`;
+ return (
+
+ {parentGroup.display_name}
+
+ );
+ }
+ );
+
+ if (crumb.linkLastItem) {
+ let apiBase = '/process-groups';
+ let dataQaTag = '';
+ if (crumb.entityType.startsWith('process-model')) {
+ apiBase = '/process-models';
+ dataQaTag = 'process-model-breadcrumb-link';
+ }
+ const fullUrl = `${apiBase}/${modifyProcessIdentifierForPathParam(
+ processEntity.id
)}`;
- return (
-
- {parentGroup.display_name}
+ breadcrumbs.push(
+
+ {processEntity.display_name}
+
+ );
+ } else {
+ breadcrumbs.push(
+
+ {processEntity.display_name}
);
}
- );
-
- if (crumb.linkLastItem) {
- let apiBase = '/process-groups';
- let dataQaTag = '';
- if (crumb.entityType.startsWith('process-model')) {
- apiBase = '/process-models';
- dataQaTag = 'process-model-breadcrumb-link';
- }
- const fullUrl = `${apiBase}/${modifyProcessIdentifierForPathParam(
- processEntity.id
- )}`;
- breadcrumbs.push(
-
- {processEntity.display_name}
-
- );
- } else {
- breadcrumbs.push(
-
- {processEntity.display_name}
-
- );
+ return breadcrumbs;
}
- return breadcrumbs;
+ if (Array.isArray(crumb)) {
+ const valueLabel = crumb[0];
+ const url = crumb[1];
+ if (!url && valueLabel) {
+ return (
+
+ {valueLabel}
+
+ );
+ }
+ if (url && valueLabel) {
+ return (
+
+ {valueLabel}
+
+ );
+ }
+ }
+ return null;
}
- const valueLabel = crumb[0];
- const url = crumb[1];
- if (!url && valueLabel) {
- return (
-
- {valueLabel}
-
- );
- }
- if (url && valueLabel) {
- return (
-
- {valueLabel}
-
- );
- }
- return null;
- });
+ );
return {leadingCrumbLinks};
}
return null;
diff --git a/spiffworkflow-frontend/src/hooks/PermissionService.tsx b/spiffworkflow-frontend/src/hooks/PermissionService.tsx
index fad496a7..017ea4cb 100644
--- a/spiffworkflow-frontend/src/hooks/PermissionService.tsx
+++ b/spiffworkflow-frontend/src/hooks/PermissionService.tsx
@@ -6,6 +6,20 @@ import { AbilityContext } from '../contexts/Can';
import { PermissionCheckResponseBody, PermissionsToCheck } from '../interfaces';
import HttpService from '../services/HttpService';
+export const checkPermissions = (
+ permissionsToCheck: PermissionsToCheck,
+ successCallback: Function
+) => {
+ if (Object.keys(permissionsToCheck).length !== 0) {
+ HttpService.makeCallToBackend({
+ path: `/permissions-check`,
+ httpMethod: 'POST',
+ successCallback,
+ postBody: { requests_to_check: permissionsToCheck },
+ });
+ }
+};
+
export const usePermissionFetcher = (
permissionsToCheck: PermissionsToCheck
) => {
@@ -37,14 +51,7 @@ export const usePermissionFetcher = (
ability.update(rules);
setPermissionsLoaded(true);
};
- if (Object.keys(permissionsToCheck).length !== 0) {
- HttpService.makeCallToBackend({
- path: `/permissions-check`,
- httpMethod: 'POST',
- successCallback: processPermissionResult,
- postBody: { requests_to_check: permissionsToCheck },
- });
- }
+ checkPermissions(permissionsToCheck, processPermissionResult);
});
return { ability, permissionsLoaded };
diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts
index 97a14baa..da3e2142 100644
--- a/spiffworkflow-frontend/src/interfaces.ts
+++ b/spiffworkflow-frontend/src/interfaces.ts
@@ -299,6 +299,7 @@ export interface HotCrumbItemObject {
entityToExplode: ProcessModel | ProcessGroup | string;
entityType: string;
linkLastItem?: boolean;
+ checkPermission?: boolean;
}
export type HotCrumbItemArray = [displayValue: string, url?: string];
diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx
index 2263ceb2..227aa817 100644
--- a/spiffworkflow-frontend/src/routes/TaskShow.tsx
+++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx
@@ -411,11 +411,18 @@ export default function TaskShow() {
pageElements.push(