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 |
child |
| Unlrelate epics | child |
child |
-
🔶 - Guest for public and private groups. Also, unless the icon is marked with✖ , the licensed featuresubepicsneeds to be available for the group. -
🔵 - Can read the epic:Non-memberfor a public group,Guestfor a private group.Reporterif 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:
-
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) -
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) -
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} -
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} -
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} -
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.
-
I have evaluated the MR acceptance checklist for this MR.