Operator edits on another org's row: say

Task 5775 · Done · Coder · created · updated

## Defect Since #5694 (PR #4967), an operator's GET binds the whole fleet, so under "All orgs" they see every org's tasks, projects and credentials (epic #5680, "Operator can see everything"). Mutations still bind the operator's own org: `apps/api/app/authz.py:2230-2231`, "A mutation stays on the operator's own org". Editing another org's row therefore fails only after submit, and a create-form picker that offers another org's row fails the same way. #5694's report records this as an unfiled follow-up ("Editing such a row, or choosing one in a create-form picker, fails on submit"). The epic introduced it, because before #5694 those rows were not visible at all. ## Decision taken (finish #5700, Full Auto) The operator does NOT gain cross-org write power. PRD R5.7 says "the filter … never grants or removes any power", and the interview chose a view filter over act-as-org. The fix makes the read-only state honest instead. ## Fix 1. Server: when an operator mutates a by-id row that exists in another org, return 403 with `{"reason": "other_org", "org": "<name>"}` instead of a 404. Do this once, in the authz mutation path, not per route. The message reads: "This belongs to <org>. Operators can view other orgs' rows but not edit them; use Change org… to move it." 2. Web: show that message in the shared mutation error handling. 3. Web: on Task detail and Project detail, when the row's org is not the operator's own org, disable the edit, state-change, comment and delete controls, with a one-line reason naming the org. Create-form pickers (project, credential) list only the operator's own org's rows. ## Done means - An API test: an operator PATCH/DELETE on another org's task, project and credential returns 403 `other_org` naming the org, and no row changes. An operator mutation on an own-org row is unchanged. Non-operators still get 404 for another org's row (no existence leak). - Component tests: Task detail and Project detail for another org's row show the controls disabled with the reason. Pickers list own-org rows only. - Dev browser check as operator under All orgs: open a Gmind task while the operator's own org is Operator. The controls are disabled with the reason, and a forced submit shows the 403 message. - `./dc check full` is green, the PR is merged, and its Jaah Deploy succeeded. Out of scope: do NOT add operator cross-org writes, impersonation or act-as-org. Do NOT change read narrowing or the org filter. Do NOT touch the transfer routes (they are Tenancy.OPERATOR and already work).

Outcome

Operator writes on another org's task/project/credential now get 403 other_org naming the org; detail pages lock with a reason; pickers own-org only. PR #5018 merged, Jaah Deploy OK. Full gate red only on unrelated jaahd tests (#5017). Follow-ups in report.

Pull request

Artifacts

Activity

  • Ready
  • Runner started
  • Runner ended (done)