Skip to content

UN-2868 [FIX] Make settings read-only and block deletion on resources shared with a user - #2273

Open
kirtimanmishrazipstack wants to merge 12 commits into
mainfrom
UN-2868-sharing-improvements
Open

UN-2868 [FIX] Make settings read-only and block deletion on resources shared with a user#2273
kirtimanmishrazipstack wants to merge 12 commits into
mainfrom
UN-2868-sharing-improvements

Conversation

@kirtimanmishrazipstack

@kirtimanmishrazipstack kirtimanmishrazipstack commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

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:

  • change a workflow's destination folder, database table, connector, tool settings or Prompt Studio project
  • change adapter or connector settings — the screens that hold credentials
  • change Prompt Studio settings (LLM profiles, custom data, grammar, pre/postamble)
  • delete a resource shared with you

You still can:

  • share it onward — to a group you belong to, or a user in your organisation
  • run and watch a shared pipeline, enable or disable it, manage its keys and notifications, clear its file history
  • edit, run and delete prompts in a shared Prompt Studio project — that is what it was shared for

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

  • New WorkflowOwnerMutationMixin in backend/permissions/permission.py, beside the is_workflow_mutator / IsParentWorkflowOwner it delegates to. Hands update / partial_update / destroy to IsParentWorkflowOwner and guards create explicitly, since DRF never runs object permissions on a collection-level action. Applied to WorkflowEndpointViewSet, whose queryset already spanned shared workflows, so a shared user gets 403 rather than 404.
  • New canEditResource(resource, sessionDetails) helper — the single definition of the rule, reading the is_owner flag the serializers already provide. useWorkflowCanEdit and usePromptStudioCanEdit delegate 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.
  • All eight resources render their list through two shared widgets, so row actions are gated once each: ResourceTable (workflows, Prompt Studio, connectors, adapters, agentic projects, lookups) and CardActionBox (pipelines, API deployments). Edit and Delete get aria-disabled plus a guarded handler and a tooltip; Share is never gated.
  • CustomToolSerializer now exposes is_owner. Only CustomToolListSerializer had it, so the Prompt Studio editor could not tell a shared user from an owner.
  • New ReadOnlyNotice widget and a .uneditable class that greys a whole region, used instead of per-input disabled because those regions hold third-party widgets (RJSF, the HITL query builder) with no single disabled prop. Applied to ConfigureConnectorModal, ToolSettings and SettingsModal — 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.
  • The project name fell back to the raw function name because exportedTools holds only the viewer's own projects. It now falls back to the tool instance's name, already set by ToolInstanceSerializer.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.

  • Owners see one change: the connector window's Save now also saves HITL rules, and the separate "Save Rules" button is gone (cloud PR). Nothing is written until Save is pressed.
  • Reading is untouched — shared users can still open and inspect everything.
  • Sharing onward, running a shared pipeline and editing prompts in a shared project are all deliberately untouched.
  • Automations are untouched — service accounts and API keys keep their access.

Database Migrations

  • None.

Env Config

  • None.

Relevant Docs

  • None.

Related Issues or PRs

Dependencies Versions

  • None.

Notes on Testing

Surface Shared user Owner / admin
Change workflow connector config 403 blocked 200
Read workflow connector config 200 200
Change connector config, not shared at all 404 blocked
Adapter, connector settings Edit greyed out unchanged
Prompt Studio settings read-only notice, inert unchanged
Workflow tool settings read-only notice, no Save unchanged
Delete, on any resource greyed out unchanged
Share, on any resource available unchanged
Pipeline toggle, keys, notifications available unchanged
Prompts in a shared project editable unchanged
Prompt Studio project name in a shared workflow name, not ID unchanged

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

…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
@kirtimanmishrazipstack kirtimanmishrazipstack changed the title UN-2868 [FIX] Prevent shared users from changing a workflow's destination connector and tool settings UN-2868 [FIX] Make shared workflows read-only for shared users and show the project name instead of its ID Sep 2, 2026
@kirtimanmishrazipstack
kirtimanmishrazipstack marked this pull request as ready for review September 2, 2026 09:41
@greptile-apps

greptile-apps Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR makes settings and destructive actions read-only for shared viewers while retaining permitted usage and onward sharing.

  • Adds owner-only mutation checks for workflow endpoints and exposes Prompt Studio ownership state.
  • Centralizes frontend resource editability and applies it to shared tables, cards, workflow settings, connector configuration, and Prompt Studio settings.
  • Corrects combined connector/HITL saving so endpoint failures stop subsequent writes and prevent modal dismissal.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

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]
Loading

Reviews (6): Last reviewed commit: "Merge branch 'main' into UN-2868-sharing..." | Re-trigger Greptile

Comment thread backend/permissions/permission.py
… 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
@kirtimanmishrazipstack

Copy link
Copy Markdown
Contributor Author

@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. handleValidateAndSubmit caught its own error and returned, so a failed endpoint write fell straight through to the HITL write and handleSaveAndClose dismissed the modal. Both writes now report success and handleSave bails on the first failure:

if (!(await handleValidateAndSubmit(formDataConfig))) {
  return false;
}

Create guard fails open — fixed, replied inline.

Found alongside these:

  • The HITL write now only runs when its form is actually dirty. Chaining it unconditionally meant every connector save also wrote a rule row.
  • A view-only user could be asked whether to save unsaved changes on close, which they cannot have made. hasUnsavedChanges() returns false when the workflow is not editable.
  • ReadOnlyNotice used defaultProps, which React 19 ignores on function components, so the default message never applied. Now a default parameter.

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 null or the server's raw rule_json compared against a builder-normalised round-trip. Sync destination and TTL are folded into that flag too, since they lost their own Save button.

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.

@greptile-apps

greptile-apps Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

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 ReadOnlyNotice default issues. The companion cloud PR covers the remaining dirty-state normalization and destination/TTL tracking, and the permission matrix was re-verified across both destination types. No further concerns from me.

@kirtimanmishrazipstack

Copy link
Copy Markdown
Contributor Author

@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
@kirtimanmishrazipstack kirtimanmishrazipstack changed the title UN-2868 [FIX] Make shared workflows read-only for shared users and show the project name instead of its ID UN-2868 [FIX] Make settings read-only and block deletion on resources shared with a user Sep 3, 2026
…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
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Frontend Lint Report (Biome)

All checks passed! No linting or formatting issues found.

@sonarqubecloud

sonarqubecloud Bot commented Sep 3, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Unstract test results

Per-group results

Status Group Tier Passed Failed Errors Skipped Duration (s)
e2e-api-deployment e2e 3 0 0 0 18.0
e2e-coowners e2e 1 0 0 0 1.3
e2e-etl e2e 1 0 0 0 18.2
e2e-login e2e 2 0 0 0 1.3
e2e-prompt-studio e2e 1 0 0 0 3.9
e2e-smoke e2e 2 0 0 0 0.9
e2e-workflow e2e 1 0 0 0 18.1
frontend unit 0 1 0 0 0.0
integration-backend integration 310 0 0 26 44.3
integration-connectors integration 1 0 0 7 7.7
integration-workers integration 157 0 0 1 48.9
ui e2e 0 1 0 0 0.0
unit-backend unit 1124 0 0 1 37.0
unit-connectors unit 63 0 0 0 10.8
unit-core unit 33 0 0 0 1.1
unit-platform-service unit 15 0 0 0 2.6
unit-rig unit 120 0 0 0 4.2
unit-runner unit 5 0 0 0 3.9
unit-sdk1 unit 563 0 0 0 31.5
unit-workers unit 1365 0 0 1 127.5
TOTAL 3767 2 0 36 381.4

Critical paths

⚠️ Critical paths not yet covered

  • workflow-execution-fan-out — Multi-file workflow execution fans out to file-processing workers and rejoins. (declared coverage: no groups declared)
✅ Covered critical paths
  • auth-login — covered by e2e-login
  • adapter-register-llm — covered by integration-backend
  • workflow-author — covered by integration-backend
  • co-owner-manage — covered by integration-backend, e2e-coowners
  • workflow-create-execute — covered by e2e-workflow
  • api-deployment-provision — covered by integration-backend
  • api-deployment-auth — covered by integration-backend
  • api-deployment-run — covered by e2e-api-deployment
  • mcp-server-auth — covered by integration-backend
  • mcp-platform-auth — covered by integration-backend
  • prompt-studio-author — covered by integration-backend
  • prompt-studio-fetch-response — covered by e2e-prompt-studio
  • connector-register-test — covered by integration-backend
  • pipeline-etl-execute — covered by e2e-etl
  • usage-aggregate-read — covered by integration-backend
  • usage-token-tracking — covered by e2e-api-deployment
  • callback-result-delivery — covered by e2e-api-deployment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant