UN-2868 [FIX] Make settings read-only and block deletion on resources shared with a user - #2273
UN-2868 [FIX] Make settings read-only and block deletion on resources shared with a user#2273kirtimanmishrazipstack wants to merge 12 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 create authorization gap is resolved. |
| backend/workflow_manager/endpoint_v2/views.py | Applies owner mutation permissions to workflow endpoint update and deletion operations. |
| frontend/src/components/agency/configure-connector-modal/ConfigureConnectorModal.jsx | Adds shared-viewer read-only behavior and correctly short-circuits combined saves after endpoint persistence failures. |
| frontend/src/helpers/resourceAccess.js | Centralizes frontend owner/admin editability decisions for shared resources. |
| frontend/src/components/widgets/resource-table/ResourceTable.jsx | Disables edit and delete actions for shared resource rows while preserving sharing. |
| frontend/src/components/widgets/card-grid-view/CardFieldComponents.jsx | Applies the same shared-resource action restrictions to card-based resource lists. |
| backend/prompt_studio/prompt_studio_core_v2/serializers.py | Exposes ownership state needed for Prompt Studio read-only presentation. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
U[Authenticated user] --> R[Shared resource]
R --> O{Owner, co-owner, org admin, or service account?}
O -->|Yes| M[Edit settings or delete]
O -->|No| V[View and use resource]
V --> S[Share onward where permitted]
V --> D[Mutation controls disabled]
M --> B[Backend owner mutation check]
Reviews (6): Last reviewed commit: "Merge branch 'main' into UN-2868-sharing..." | 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
…he user Sharing grants read only, but every resource list still offered Edit, Share, Delete and the enable/disable toggle to the people it was shared with. The backend refused them; the UI did not say so. All eight shareable resources render through two shared widgets, so the row actions are gated in one place each -- ResourceTable covers workflows, Prompt Studio, connectors, adapters, agentic projects and lookups; CardActionBox covers pipelines and API deployments. The two card kebabs mix read and write actions, so those are filtered per page: Manage Keys, Notifications and Clear File History go, while View Logs, File History, Sync Now, Code Snippets and Download Postman stay. Running and watching a shared pipeline is still allowed -- that is what sharing is for. The rule itself now lives in one helper, canEditResource, which useWorkflowCanEdit also delegates to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016eB6bLmieWVwUnmYH6H5WZ
The Prompt Studio editor is served by CustomToolSerializer, which never carried is_owner -- only the list serializer did. Without it the editor cannot tell a shared user from an owner, so its edit controls cannot be gated. canEditResource now treats a payload that has not arrived yet as editable. The backend refuses the write either way, and the alternative flashes a read-only view at the resource's own owner while the request is in flight. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016eB6bLmieWVwUnmYH6H5WZ
…d delete Sharing onward is allowed for someone a resource was shared with: they may pass access to a group they belong to, or to a user in the same organisation. ShareAuthorizationService enforces both rules per axis, which is why the share endpoint sits at IsOwnerOrSharedUserOrSharedToOrg rather than IsOwner. The previous commit hid the Share button along with Edit and Delete. Only the latter two should go. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016eB6bLmieWVwUnmYH6H5WZ
Settings hold the project's LLM profiles and adapter selections -- the credential-bearing part. A shared user can read them but not change them: the panel gets the read-only notice and its controls are inert. Prompts are deliberately untouched. Editing, running and deleting prompts is what a project is shared for; only the settings panel is restricted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016eB6bLmieWVwUnmYH6H5WZ
The scope is settings and deletion, nothing else. The pipeline and API deployment cards had also lost their enable/disable toggle, Manage Keys, Notifications and Clear File History for shared users, which goes further than intended. Both card configs are reverted. Only the Edit and Delete controls in the two shared list widgets stay gated; Share, the toggle and every kebab action are available again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016eB6bLmieWVwUnmYH6H5WZ
Hiding the two controls left a shared user with no idea they existed or why they were missing. They now stay on screen, greyed out, with a tooltip reading "Only the owner can change this". Same treatment in both list widgets so every resource looks the same. The rename pencil beside a project title follows the same rule: ToolNavBar takes an editTitleDisabled prop, and Prompt Studio passes it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016eB6bLmieWVwUnmYH6H5WZ
…rules The connector write showed "Configuration saved successfully" before the rule write ran. A rule failure after it left a green toast on screen next to a red one, reading as though everything had landed. The connector success message is now suppressed when a rule write follows, and the rule write reports the outcome for both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TaTtTMaDriVZgw6BR8HZ4G
Frontend Lint Report (Biome)✅ All checks passed! No linting or formatting issues found. |
|
Unstract test resultsPer-group results
Critical paths
|



What
Makes "shared with you" mean read-only in the places that matter, consistently across every shareable resource.
If something is shared with you, you can open it, use it and pass access on — but you cannot change its settings or delete it.
You cannot:
You still can:
The screens now say so up front instead of failing on Save. A shared user sees a "Shared with you — view only" line, greyed-out fields and no Save button. Edit and Delete stay visible everywhere but are greyed out, so it is obvious the action exists and is not yours to take, rather than silently missing.
Two smaller fixes ride along: shared users were shown a Prompt Studio project's internal ID where the owner saw its name, and an agentic project's Edit button opened the project instead of renaming it.
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 was meant to let colleagues run and watch something, 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. The same gap existed on every other shareable resource: the backend refused the write, but the screen said nothing, so people filled in a form and lost the work.
The concern is credentials. Settings screens are where they live, which is why those are the hard line rather than every button.
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. Applied toWorkflowEndpointViewSet, whose queryset already spanned shared workflows, so a shared user gets 403 rather than 404.canEditResource(resource, sessionDetails)helper — the single definition of the rule, reading theis_ownerflag the serializers already provide.useWorkflowCanEditandusePromptStudioCanEditdelegate to it, reading their own stores, so no component signatures change. A payload still in flight counts as editable, so an owner never sees a read-only flash.ResourceTable(workflows, Prompt Studio, connectors, adapters, agentic projects, lookups) andCardActionBox(pipelines, API deployments). Edit and Delete getaria-disabledplus a guarded handler and a tooltip; Share is never gated.CustomToolSerializernow exposesis_owner. OnlyCustomToolListSerializerhad it, so the Prompt Studio editor could not tell a shared user from an owner.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. Applied toConfigureConnectorModal,ToolSettingsandSettingsModal— the last covers every Prompt Studio settings tab in one place. Tab bars sit 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
WorkflowOwnerMutationMixinandcanEditResourcefrom 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
Backend rows exercised on both a database and an API destination through DRF's request factory, inside a rolled-back transaction. UI rows walked in the browser as a shared user against a live org. Lookup Studio has no rows in the test org and is code-verified only. Frontend build passes; biome clean.
Screenshots
Checklist
I have read and understood the Contribution Guidelines.
🤖 Generated with Claude Code
https://claude.ai/code/session_01LiSCeFrRgZUcYPUtJMMTv6