Draft: [skip ci] Restrict decision log widget to Planner and above

What does this MR do and why?

This MR depends on !259719. It targets that MR's branch and must be merged after it.

The decision log work item widget, its five GraphQL mutations (workItemDecisionCreate, workItemDecisionUpdate, workItemDecisionResolve, workItemDecisionArchive, workItemDecisionDelete) and the five matching services in ee/app/services/work_items/decisions/ were authorized with update_work_item.

A Guest gets update_work_item on a work item they authored or are assigned to (through the private permissions _update_authored_work_item and _update_assigned_work_item). So such a Guest could see the decision log and create, edit, resolve, archive and delete decisions.

This is the same gap as the agent plan widget, fixed in !259719. The decision log widget code even had a comment saying it used the same visibility gate as the agent plan.

This MR does not add a new permission. It reuses update_work_item_agent_plan from !259719, because the access needed is the same. That is Planner and above through admin_work_item, and the existing prevents (confidential, hidden, incident) still apply.

Where it is used:

  • WorkItems::Widgets::DecisionLog.required_user_ability, which hides the widget in GraphQL. It now uses update_work_item_agent_plan.
  • The authorize of the five mutations, now update_work_item_agent_plan.
  • allowed? in the five services, now update_work_item_agent_plan.

The widget code has a short comment explaining that sharing this permission is on purpose.

The granular PAT permission on the mutations (authorize_granular_token permissions: :update_work_item) is unchanged.

Two existing service specs ("when the user cannot read the note" in resolve_service_spec and update_service_spec) used a Guest author to get a user who can update decisions but cannot read an internal note. After this change no role can do both, because Planner and above can read internal notes. Those specs now stub Ability.allowed? for read_note so they still cover the note check.

The feature is behind the default-off decision_log flag (wip), so there is no changelog entry.

References

Screenshots or screen recordings

Not applicable, this is a backend only change.

How to set up and validate locally

  1. Enable the flag in the Rails console: Feature.enable(:decision_log).
  2. As a Guest of a project, create an issue (so you are its author).
  3. Open the GraphQL explorer at http://gdk.test:3000/-/graphql-explorer and query the issue with workItem(id:) { widgets { type } }. Check that DECISION_LOG is not in the list.
  4. Run workItemDecisionCreate as the same Guest. It should return the "resource does not exist or you don't have permission" error.
  5. Repeat the query as a Planner and check that the widget is present.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

🤖 Generated with Claude Code

Edited by Stefanos Xanthopoulos

Merge request reports

Loading
Loading