Skip to content

Update service permissions for assigning a parent epic

Related to #397073 (closed)

What does this MR do and why?

We want to update the permissions required to set the parent of an epic so that a user who is not a member of a parent epic's group can assign a child as long as they can read the parent and have a guest role in the child epic's group.

Currently, to create and destroy this relationship we require the user to be a guest in both the child and the parent epic groups so we only need to change the check against the parent for the new requirements.

This change is being split into several MRs to keep them as small as possible and it's behind the feature flag epic_relations_for_non_members, disabled by default.

This MR only changes permissions at the service level but !131891 (merged) and !131331 (merged) will follow to update API and quick action permissions, respectively.

Action Before After
Assign epic children to a parent epic child 🔶 - parent 🔶 child 🔶 - parent 🔵
Unlrelate epics child 🔶() - parent 🔶 child 🔶() - parent 🔵
  • 🔶 - Guest for public and private groups. Also, unless the icon is marked with , the licensed feature subepics needs to be available for the group.
  • 🔵 - Can read the epic: Non-member for a public group, Guest for a private group. Reporter if epic is confidential.

Screenshots or screen recordings

No UI changes

How to set up and validate locally

Given that permissions are only changed at the service level we can only replicate by calling the services in the console:

  1. Create two groups, one of them private and add a guest user to it

    guest_user = User.last
    user = User.first
    private_group = Group.create!(name: 'Private Group', path: 'private-group', owner: user, visibility_level: Gitlab::VisibilityLevel::PRIVATE)
    private_group.add_member(guest_user, :guest)
    public_group = Group.create!(name: 'Public Group', path: 'public-group', owner: user, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
  2. Create two epics in the public group and one in the private group

    private_epic1 = Epic.create!(title: 'Private Epic 1', author: user, group: private_group)
    private_epic2 = Epic.create!(title: 'Private Epic 2', author: user, group: private_group)
    public_epic = Epic.create!(title: 'Public Epic 1', author: user, group: public_group)
  3. Call the service to set the public epic as the parent of the private epics. This should fail because the user is a guest member for the children but not for the parent:

    Epics::EpicLinks::CreateService.new(public_epic, guest_user, { target_issuable: [private_epic1, private_epic2] }).execute
    
    #=> {:message=>"No matching epic found. Make sure that you are adding a valid epic URL.", :status=>:error, :http_status=>404}
  4. Enable the FF and call the same service, it should succeed because we now allow creating the link if the user can read the parent

    Feature.enable(:epic_relations_for_non_members)
    Epics::EpicLinks::CreateService.new(public_epic, guest_user, { target_issuable: [private_epic1, private_epic2] }).execute
    
    #=> {:created_references=>[#<Epic id:45 private-group&1>, #<Epic id:48 private-group&3>], :status=>:success}
  5. Call the service that destroys the link using the first child. This should succeed

    ::Epics::EpicLinks::DestroyService.new(private_epic1, guest_user).execute
    #=> {:message=>"Relation was removed", :status=>:success}
  6. Disable the FF and try to destroy the link for the second child. This should fail because the user is not a member of the parent epic's group

    ::Epics::EpicLinks::DestroyService.new(private_epic2, guest_user).execute
    #=> {:message=>"No Epic found for given params", :status=>:error, :http_status=>404}

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Eugenia Grieff

Merge request reports