Commit ef6ded68 authored by Max Woolf's avatar Max Woolf

Merge branch...

Merge branch '208425-user-without-parent-group-permission-cannot-assign-epic-to-issue' into 'master'

Change permissions to set epic of an issue

See merge request gitlab-org/gitlab!66865
parents 89d0caa1 72154b41
......@@ -16,11 +16,13 @@ module Mutations
issue = authorized_find!(project_path: project_path, iid: iid)
project = issue.project
authorize_admin_rights!(epic)
authorize_read_rights!(epic)
begin
::Issues::UpdateService.new(project: project, current_user: current_user, params: { epic: epic })
.execute(issue)
rescue ::Gitlab::Access::AccessDeniedError
raise_resource_not_available_error!
rescue EE::Issues::BaseService::EpicAssignmentError => error
issue.errors.add(:base, error.message)
end
......@@ -33,10 +35,10 @@ module Mutations
private
def authorize_admin_rights!(epic)
def authorize_read_rights!(epic)
return unless epic.present?
raise_resource_not_available_error! unless Ability.allowed?(current_user, :admin_epic, epic.group)
raise_resource_not_available_error! unless Ability.allowed?(current_user, :read_epic, epic.group)
end
end
end
......
......@@ -150,7 +150,7 @@ module EE
end
def can_assign_epic?(user)
user&.can?(:admin_epic, project.group)
user&.can?(:read_epic, project.group) && user&.can?(:admin_issue, project)
end
def can_be_promoted_to_epic?(user, group = nil)
......
......@@ -52,7 +52,7 @@ module EE
return unless epic
unless can?(current_user, :admin_epic, epic)
unless can?(current_user, :read_epic, epic) && can?(current_user, :admin_issue, issue)
raise ::Gitlab::Access::AccessDeniedError
end
......
......@@ -35,15 +35,24 @@ module EpicIssues
def linkable_issuables(issues)
@linkable_issues ||= begin
return [] unless can?(current_user, :admin_epic, issuable.group)
return [] unless can?(current_user, :read_epic, issuable.group)
Preloaders::UserMaxAccessLevelInProjectsPreloader
.new(issues.map(&:project).compact, current_user)
.execute
issues.select do |issue|
linkable_issue?(issue)
end
end
end
def linkable_issue?(issue)
issue.supports_epic? &&
can?(current_user, :admin_issue, issue.project) &&
issuable_group_descendants.include?(issue.project.group) &&
!previous_related_issuables.include?(issue)
end
end
end
def previous_related_issuables
@related_issues ||= issuable.issues.to_a
......
......@@ -27,7 +27,7 @@ module EpicIssues
end
def permission_to_remove_relation?
can?(current_user, :admin_epic_issue, target) && can?(current_user, :admin_epic, source)
can?(current_user, :admin_epic_issue, target) && can?(current_user, :read_epic, source)
end
def create_notes
......
......@@ -23,6 +23,10 @@ module API
.with_api_entity_associations
.sorted_by_epic_position
end
def authorize_can_assign_to_epic!(issue)
forbidden! unless can?(current_user, :read_epic, epic) && can?(current_user, :admin_issue, issue)
end
end
params do
......@@ -40,7 +44,7 @@ module API
use :pagination
end
put ':id/(-/)epics/:epic_iid/issues/:epic_issue_id' do
authorize_can_admin_epic!
authorize_can_assign_to_epic!(link.issue)
update_params = {
move_before_id: params[:move_before_id],
......@@ -84,9 +88,9 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
post ':id/(-/)epics/:epic_iid/issues/:issue_id' do
authorize_can_admin_epic!
authorize_can_read!
issue = Issue.find(params[:issue_id])
authorize!(:admin_issue, issue)
create_params = { target_issuable: issue }
......@@ -110,8 +114,7 @@ module API
requires :epic_issue_id, type: Integer, desc: 'The ID of the association'
end
delete ':id/(-/)epics/:epic_iid/issues/:epic_issue_id' do
authorize_can_admin_epic!
authorize_can_assign_to_epic!(link.issue)
result = ::EpicIssues::DestroyService.new(link, current_user).execute
if result[:status] == :success
......
......@@ -24,7 +24,7 @@ RSpec.describe Mutations::Boards::Issues::IssueMoveList do
before do
stub_licensed_features(epics: true)
project.add_maintainer(user)
project.add_reporter(user)
end
subject do
......@@ -34,7 +34,7 @@ RSpec.describe Mutations::Boards::Issues::IssueMoveList do
describe '#resolve' do
context 'when user has access to the epic' do
before do
group.add_developer(user)
group.add_guest(user)
end
it 'moves and repositions issue' do
......@@ -47,6 +47,8 @@ RSpec.describe Mutations::Boards::Issues::IssueMoveList do
end
context 'when user does not have access to the epic' do
let(:epic) { create(:epic, :confidential, group: group) }
it 'does not update issue' do
subject
......
......@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Mutations::Issues::SetEpic do
let_it_be(:group) { create(:group) }
let_it_be(:group) { create(:group, :public) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:issue) { create(:issue, project: project) }
......@@ -12,6 +12,7 @@ RSpec.describe Mutations::Issues::SetEpic do
describe '#resolve' do
let_it_be_with_reload(:epic) { create(:epic, group: group) }
let_it_be_with_reload(:confidential_epic) { create(:epic, group: group, confidential: true) }
let(:mutated_issue) { subject[:issue] }
......@@ -22,18 +23,11 @@ RSpec.describe Mutations::Issues::SetEpic do
context 'when the user can update the issue' do
before do
stub_licensed_features(epics: true)
project.add_developer(user)
end
it 'raises an error if the epic is not accessible to the user' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
context 'when user can admin epic' do
before do
group.add_owner(user)
project.add_reporter(user)
group.add_guest(user)
end
context 'when user can read epic' do
it 'returns the issue with the epic' do
expect(mutated_issue).to eq(issue)
expect(mutated_issue.epic).to eq(epic)
......@@ -61,12 +55,32 @@ RSpec.describe Mutations::Issues::SetEpic do
end
context 'when epic is confidential but issue is public' do
let(:epic) { create(:epic, group: group, confidential: true) }
let(:epic) { confidential_epic }
it 'returns an error with appropriate message' do
group.add_reporter(user)
expect(subject[:errors].first).to include("Cannot assign a confidential epic to a non-confidential issue. Make the issue confidential and try again")
end
end
context 'with assigning epic error' do
let(:mock_service) { double('service', execute: { status: :error, message: 'failed to assign epic' }) }
it 'returns an error with appropriate message' do
expect(EpicIssues::CreateService).to receive(:new).and_return(mock_service)
expect(subject[:errors].first).to include('failed to assign epic')
end
end
end
context 'when user can not read epic' do
let(:epic) { confidential_epic }
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
end
......
......@@ -39,13 +39,12 @@ RSpec.describe Mutations::Issues::Update do
before do
group.clear_memoization(:feature_available)
group.add_developer(user)
project.add_reporter(user)
group.add_guest(user)
end
context 'when epics feature is disabled' do
it 'raises an error' do
group.add_developer(user)
expect { subject }.to raise_error(::Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
......@@ -56,7 +55,7 @@ RSpec.describe Mutations::Issues::Update do
end
context 'for user without permissions' do
let(:current_user) { create(:user) }
let(:epic) { create(:epic, :confidential, group: group) }
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
......
......@@ -813,19 +813,11 @@ RSpec.describe Issue do
end
context 'when a user is a project member' do
it 'returns false' do
project.add_developer(user)
expect(subject).to be_falsey
end
before do
project.add_reporter(user)
end
context 'when a user is a group member' do
it 'returns true' do
group.add_developer(user)
expect(subject).to be_truthy
end
it { is_expected.to be_truthy }
end
end
......
......@@ -102,6 +102,7 @@ RSpec.describe API::EpicIssues do
context 'when epics feature is enabled' do
before do
stub_licensed_features(epics: true)
group.add_guest(user)
end
context 'when an error occurs' do
......@@ -117,14 +118,34 @@ RSpec.describe API::EpicIssues do
post api(url, user)
expect(response).to have_gitlab_http_status(:not_found)
expect(response).to have_gitlab_http_status(:forbidden)
end
context 'without permissions to admin the issue' do
before do
project.add_guest(user)
end
it 'returns 403 forbidden error for a user without permissions to admin the epic' do
it 'returns 403 forbidden error' do
post api(url, user)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'without permissions to read the epic' do
let(:epic) { create(:epic, :confidential, group: create(:group, :private)) }
before do
project.add_reporter(user)
end
it 'returns 403 forbidden error' do
post api(url, user)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when issue project is not under the epic group' do
before do
......@@ -146,7 +167,7 @@ RSpec.describe API::EpicIssues do
context 'when the request is correct' do
before do
group.add_developer(user)
project.add_reporter(user)
post api(url, user)
end
......@@ -205,11 +226,30 @@ RSpec.describe API::EpicIssues do
expect(response).to have_gitlab_http_status(:not_found)
end
it 'returns 403 forbidden error for a user without permissions to admin the epic' do
context 'With user without permissions to admin the issue' do
before do
project.add_guest(user)
end
it 'returns 403 forbidden error' do
delete api(url, user)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'without permissions to read the epic' do
before do
[issue, epic].map { |issuable| issuable.update!(confidential: true) }
project.add_reporter(user)
end
it 'returns 403 forbidden error' do
delete api(url, user)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when epic_issue association does not include the epic in the url' do
before do
......
......@@ -33,12 +33,16 @@ RSpec.describe 'Reposition and move issue within board lists' do
before do
stub_licensed_features(epics: true)
project.add_maintainer(user)
end
context 'when user can admin epic' do
context 'when user can admin issue' do
before do
group.add_maintainer(user)
project.add_reporter(user)
end
context 'when user can read epic' do
before do
group.add_guest(user)
end
it 'updates issue position and epic' do
......@@ -77,14 +81,18 @@ RSpec.describe 'Reposition and move issue within board lists' do
end
end
context 'when user can not admin epic' do
it 'fails with error' do
context 'when user can not read epic' do
let(:confidential_epic) { create(:epic, :confidential, group: group) }
it 'does not set epic' do
params[:epic_id] = confidential_epic.to_global_id.to_s
post_graphql_mutation(mutation(params), current_user: user)
mutation_response = graphql_mutation_response(:issue_move_list)
expect(mutation_response['errors']).to eq(['You are not allowed to move the issue'])
expect(mutation_response['issue']['epic']).to eq(nil)
expect(mutation_response['issue']['relativePosition']).to eq(3)
response_issue = graphql_mutation_response(:issue_move_list)['issue']
expect(response_issue['epic']).to be_nil
expect(response_issue['relativePosition']).to eq(3)
expect(graphql_mutation_response(:issue_move_list)['errors']).to include('Resource not found')
end
end
end
......
......@@ -23,7 +23,7 @@ RSpec.describe 'Update of an existing issue' do
before do
stub_licensed_features(issuable_health_status: true)
project.add_developer(current_user)
project.add_reporter(current_user)
end
it 'updates the issue' do
......@@ -42,7 +42,7 @@ RSpec.describe 'Update of an existing issue' do
before do
stub_licensed_features(epics: true)
group.add_developer(current_user)
group.add_guest(current_user)
end
it 'sets the epic' do
......@@ -56,7 +56,7 @@ RSpec.describe 'Update of an existing issue' do
end
context 'the epic is not readable to the current user' do
let(:epic) { create(:epic) }
let(:epic) { create(:epic, :confidential, group: group) }
it 'does not set the epic' do
post_graphql_mutation(mutation, current_user: current_user)
......@@ -117,7 +117,7 @@ RSpec.describe 'Update of an existing issue' do
before do
stub_licensed_features(epics: true)
group.add_developer(current_user)
group.add_guest(current_user)
issue.update!(epic: epic)
end
......
......@@ -252,6 +252,10 @@ RSpec.describe Issues::UpdateService do
subject { update_issue(params) }
context 'when a user does not have permissions to assign an epic' do
before do
project.add_guest(author)
end
it 'raises an exception' do
expect { subject }.to raise_error(Gitlab::Access::AccessDeniedError)
end
......@@ -259,7 +263,7 @@ RSpec.describe Issues::UpdateService do
context 'when a user has permissions to assign an epic' do
before do
group.add_maintainer(user)
group.add_guest(user)
end
context 'when EpicIssues::CreateService returns failure', :aggregate_failures do
......
......@@ -89,6 +89,11 @@ RSpec.describe EpicIssues::CreateService do
context 'when epics feature is disabled' do
subject { assign_issue([valid_reference]) }
before do
group.add_guest(user)
project.add_reporter(user)
end
include_examples 'returns an error'
end
......@@ -99,7 +104,8 @@ RSpec.describe EpicIssues::CreateService do
context 'when user has permissions to link the issue' do
before do
group.add_developer(user)
group.add_guest(user)
project.add_reporter(user)
end
context 'when the reference list is empty' do
......@@ -124,7 +130,7 @@ RSpec.describe EpicIssues::CreateService do
include_examples 'returns success'
it 'does not perform N + 1 queries' do
it 'does not perform N + 1 queries', :request_store do
allow(SystemNoteService).to receive(:epic_issue)
allow(SystemNoteService).to receive(:issue_on_epic)
......@@ -146,7 +152,7 @@ RSpec.describe EpicIssues::CreateService do
project = create(:project, group: group)
issues = create_list(:issue, 5, project: project)
epic = create(:epic, group: group)
group.add_developer(user)
group.add_reporter(user)
allow(extractor).to receive(:issues).and_return(issues)
params = { issuable_references: issues.map { |i| i.to_reference(full: true) } }
......@@ -173,6 +179,10 @@ RSpec.describe EpicIssues::CreateService do
subject { assign_issue([Gitlab::Routing.url_helpers.namespace_project_issue_url(namespace_id: issue.project.namespace, project_id: issue.project, id: issue.iid)])}
before do
project2.add_reporter(user)
end
include_examples 'returns success'
end
......@@ -196,6 +206,7 @@ RSpec.describe EpicIssues::CreateService do
expect(created_link2.relative_position).to be < created_link1.relative_position
end
it 'orders the epic issues to the first place and moves the existing ones down' do
existing_link = create(:epic_issue, epic: epic, issue: issue3)
......@@ -258,7 +269,8 @@ RSpec.describe EpicIssues::CreateService do
context 'when assigning issue(s) to the same epic' do
before do
group.add_developer(user)
group.add_guest(user)
project.add_reporter(user)
assign_issue([valid_reference])
epic.reload
end
......@@ -288,7 +300,8 @@ RSpec.describe EpicIssues::CreateService do
context 'when an issue is already assigned to another epic', :sidekiq_inline do
before do
group.add_developer(user)
group.add_guest(user)
project.add_reporter(user)
create(:epic_issue, epic: epic, issue: issue)
issue.reload
end
......@@ -361,8 +374,8 @@ RSpec.describe EpicIssues::CreateService do
let_it_be(:another_issue) { create :issue }
before do
group.add_developer(user)
another_issue.project.add_developer(user)
group.add_guest(user)
another_issue.project.add_reporter(user)
end
include_examples 'returns an error'
......
......@@ -15,7 +15,8 @@ RSpec.describe EpicIssues::DestroyService do
context 'when epics feature is disabled' do
before do
group.add_reporter(user)
project.add_reporter(user)
group.add_guest(user)
end
it 'returns an error' do
......@@ -30,7 +31,8 @@ RSpec.describe EpicIssues::DestroyService do
context 'when user has permissions to remove associations' do
before do
group.add_reporter(user)
project.add_reporter(user)
group.add_guest(user)
end
it 'removes related issue' do
......@@ -73,6 +75,14 @@ RSpec.describe EpicIssues::DestroyService do
subject
end
context 'refresh epic dates' do
it 'calls UpdateDatesService' do
expect(Epics::UpdateDatesService).to receive(:new).with([epic_issue.epic]).and_call_original
subject
end
end
end
context 'user does not have permissions to remove associations' do
......@@ -90,16 +100,6 @@ RSpec.describe EpicIssues::DestroyService do
subject
end
end
context 'refresh epic dates' do
it 'calls UpdateDatesService' do
group.add_reporter(user)
expect(Epics::UpdateDatesService).to receive(:new).with([epic_issue.epic]).and_call_original
subject
end
end
end
end
end
......@@ -26,7 +26,7 @@ RSpec.shared_examples 'issue with epic_id parameter' do
context 'when user can not add issues to the epic' do
before do
project.add_maintainer(user)
project.add_guest(user)
end
let(:params) { { title: 'issue1', epic_id: epic.id } }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment