Prevent cross-organization work item links and moves (Cells guardrail)

What does this MR do and why?

With Cells, an organization is isolated to a single cell: its database rows and its Gitaly repositories (including Design Management repos) live only on that cell, and cross-cell access is unsupported. Several work-item operations let a user reference or relocate an item across projects/groups without checking the organization — harmless in a single cell today, but broken under Cells:

  • Cross-org links / hierarchy would need a foreign key into another cell's database.
  • Cross-org move/clone/promote would write into another cell's database and copy repository-backed data (notably designs) across Gitaly fleets.

This MR adds organization-equality guards, gated by the prevent_cross_organization_work_item_actions feature flag (gitlab_com_derisk, default disabled), so rollout is controlled and the change is a no-op within a single organization.

Design

A single reusable abstraction is applied at both the model and service layers:

  • Issuable#same_organization_as?(other) — the one comparison (resource_parent.organization_id), shared by issues, work items, and epics.
  • SameOrganizationValidator — an ActiveModel validator (FF-gated) added to the shared IssuableLink concern, so it covers IssueLink, WorkItems::RelatedWorkItemLink, and Epic::RelatedEpicLink at once; also used by EpicIssue and WorkItems::ParentLink.
  • Services (IssuableLinks::CreateService, WorkItems::DataSync::BaseService) and the designs widget delegate to same_organization_as? and gate the flag consistently on resource_parent.

Coverage

Vector Where Layer
Issue / work item links IssuableLinks::CreateService + IssuableLink service + model
Related epic links IssuableLink concern model
Epic ↔️ issue EpicIssue model
Work item hierarchy (parent links) WorkItems::ParentLink model (new guard)
Move / clone / promote WorkItems::DataSync::BaseService service
Design copy on move/clone WorkItems::DataSync::Widgets::Designs service

WorkItems::ParentLink and Epic::RelatedEpicLink were previously unguarded at the model level; this closes both.

Feature flag

  • prevent_cross_organization_work_item_actionsgitlab_com_derisk, default_enabled: false.
  • Disabled: behavior unchanged. Enabled: cross-organization links/hierarchy/moves are rejected with a clear error.

Test plan

  • Specs cover both flag states for the link service, the move service, the designs widget, the shared validator, and each guarded model.
  • Within a single organization (the common case in existing specs) all guards are no-ops.

Rollout note

Before enabling, audit work_item_parent_links and related_epic_links for any pre-Cells legacy data whose endpoints resolve to different organizations, since enabling will start rejecting such relationships.

Closes #603656

🤖 Generated with Claude Code

Merge request reports

Loading