UN-2868 [FIX] Make shared workflows read-only for shared users and show the project name instead of its ID - #2273
UN-2868 [FIX] Make shared workflows read-only for shared users and show the project name instead of its ID#2273kirtimanmishrazipstack wants to merge 4 commits into
Conversation
…and org admins Workflow sub-resources were never gated when the sharing model landed. tool_instance_v2 was fixed; endpoint_v2 was missed, so any user a workflow was shared with could change its destination folder, database table or connector. Adds a reusable WorkflowOwnerMutationMixin next to is_workflow_mutator and applies it to WorkflowEndpointViewSet. Sharing -- direct, group or org-wide -- now grants read only; owners, co-owners, org admins and service accounts may still write. The UI now says so up front instead of failing on save: a shared user sees a read-only notice and greyed controls in the connector modal, tool settings, and the Prompt Studio project selector, with no Save button to press. The connector modal's Save now also flushes the HITL plugin's rules. It previously lit up for rule changes it could not save, then closed as if it had saved them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016eB6bLmieWVwUnmYH6H5WZ
…tead of its ID The workflow builder resolved the project name from exportedTools, which holds only the viewer's own exported projects. On a shared workflow the lookup missed and fell through to the raw function name, so shared users saw an ID where the owner saw a name. The tool instance already carries the display name (ToolInstanceSerializer sets it from the tool's properties), so fall back to that before falling back to the ID. The ID remains the last resort for a tool the registry can no longer resolve. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016eB6bLmieWVwUnmYH6H5WZ
|
| Filename | Overview |
|---|---|
| backend/permissions/permission.py | Adds a fail-closed workflow sub-resource mutation mixin; the previously reported unchecked create path is resolved. |
| backend/workflow_manager/endpoint_v2/views.py | Applies owner-level mutation permissions to endpoint updates while preserving shared-user reads. |
| frontend/src/components/agency/configure-connector-modal/ConfigureConnectorModal.jsx | Adds read-only presentation and coordinates endpoint and HITL saves without continuing after a failed endpoint update. |
| frontend/src/components/agency/agency/Agency.jsx | Disables Prompt Studio project changes for viewers and adds the tool-instance name fallback. |
| frontend/src/components/agency/tool-settings/ToolSettings.jsx | Prevents shared viewers from submitting tool-setting changes and presents the form as read-only. |
| frontend/src/hooks/useWorkflowCanEdit.js | Centralizes frontend workflow editability using workflow ownership and organization-admin state. |
| frontend/src/components/widgets/read-only-notice/ReadOnlyNotice.jsx | Introduces the shared view-only notice used by workflow configuration surfaces. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
U[Workflow user] --> UI[Workflow configuration UI]
UI --> C{Owner, co-owner, or org admin?}
C -- No --> RO[Show read-only notice and hide save controls]
C -- Yes --> M[Submit endpoint mutation]
M --> API[Workflow endpoint API]
API --> P{Owner, org admin, or service account?}
P -- No --> D[Return 403]
P -- Yes --> S[Persist endpoint configuration]
Reviews (3): Last reviewed commit: "UN-2868 [FIX] Refuse form submits from v..." | Re-trigger Greptile
… save Addresses review feedback plus two defects found alongside it. - perform_create skipped authorization entirely when the payload carried no workflow, then saved anyway. It now fails closed: every viewset using this mixin has a required workflow field, so a payload without one cannot be authorised. - handleValidateAndSubmit swallowed its own error, so a failed endpoint write still fell through to the HITL write and "Save and Close" dismissed the modal. Both writes now report success and the modal stays open on failure. - The HITL write only runs when its form is actually dirty; otherwise every connector save would have written a rule row too. - A view-only user can no longer be asked whether to save unsaved changes on close. They cannot have made any. - ReadOnlyNotice used defaultProps, which React 19 ignores on function components, so its default message never applied. Uses a default parameter. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016eB6bLmieWVwUnmYH6H5WZ
|
@greptile-apps Both findings fixed in 06198cc, plus three more found while tracing the second one. Partial save / false close — valid, and the sharper of the two. if (!(await handleValidateAndSubmit(formDataConfig))) {
return false;
}Create guard fails open — fixed, replied inline. Found alongside these:
The companion cloud PR (#1751) also fixes the dirty flag that made this reachable: the HITL form was dirty from the moment it opened, because the baseline was either Re-verified the permission matrix against real shared-workflow data after the change — owner and admin-not-owner write, shared viewer 403, non-shared 404, reads unaffected on both database and API destinations. |
|
Thanks for the follow-up. The partial-save/false-close path and the create permission guard are addressed, along with the HITL dirty-write, view-only close prompt, and React 19 |
|
@greptile-apps review Both findings from the previous pass are addressed in 06198cc, which landed after that review was generated. |
The read-only styling suppresses pointer events but leaves fields focusable, so a keyboard user could tab into one and press Enter. That fired a request the backend rejects, producing exactly the confusing error this ticket is about. Both RJSF forms now refuse to submit when the workflow is not editable, so the request is never made. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016eB6bLmieWVwUnmYH6H5WZ
Frontend Lint Report (Biome)✅ All checks passed! No linting or formatting issues found. |
|
Unstract test resultsPer-group results
Critical paths
|



What
If a workflow is shared with you, you can now look at how it is set up but you cannot change it. Before, anyone it was shared with could quietly rewrite the owner's settings.
What they could change and no longer can:
The screen also says so up front. A shared user now sees a "Shared with you — view only" line, greyed out fields and no Save button, instead of a normal-looking form that fails once they press Save.
Separately, shared users were shown the Prompt Studio project's internal ID where the owner saw its name. They now see the name.
Owners, co-owners and organization admins are unaffected.
Closes all four defects in the ticket: 1 (HITL, with the cloud PR), 2 (Change Prompt), 3 (tool settings error), 4 (project ID).
Why
Reported by a customer. Sharing a workflow was meant to let colleagues run and watch it, not reconfigure it — someone could point another team's workflow at a different output folder without meaning to.
When sharing was rebuilt around owners and co-owners, the pieces inside a workflow were never included. Tool instances were fixed then; endpoints were missed.
How
WorkflowOwnerMutationMixininbackend/permissions/permission.py, beside theis_workflow_mutator/IsParentWorkflowOwnerit delegates to. Handsupdate/partial_update/destroytoIsParentWorkflowOwnerand guardscreateexplicitly, since DRF never runs object permissions on a collection-level action.WorkflowEndpointViewSet. Its queryset already spanned shared workflows, so a shared user gets 403 rather than 404.useWorkflowCanEdithook readsis_owner(already on the workflow payload) orsessionDetails.isAdmin. Read straight from the workflow store byAgency,ConfigureConnectorModalandToolSettings— no prop drilling, no changed component signatures.ReadOnlyNoticewidget and a.uneditableclass that greys a whole region, used instead of per-inputdisabledbecause those regions hold third-party widgets (RJSF, the HITL query builder) with no single disabled prop. The tab bar sits outside it so viewers can still switch tabs.ConfigureConnectorModal's Save now also flushes the HITL plugin's rules through a ref, and is shown in API mode too. It previously lit up for rule changes it could not save, then closed as though it had saved them.exportedToolsholds only the viewer's own projects. It now falls back to the tool instance'sname, already set byToolInstanceSerializer.to_representation, before falling back to the ID.Can this PR break any existing features. If yes, please list possible items. If no, please explain why.
Database Migrations
Env Config
Relevant Docs
Related Issues or PRs
WorkflowOwnerMutationMixinfrom here.created_by. That field is audit-only since the co-owner work, so its check would have locked co-owners out.Dependencies Versions
Notes on Testing
Exercised on both a database and an API destination through DRF's request factory, inside a rolled-back transaction. Frontend build passes.
Screenshots
Checklist
I have read and understood the Contribution Guidelines.
🤖 Generated with Claude Code
https://claude.ai/code/session_016eB6bLmieWVwUnmYH6H5WZ