From 6a43b24062256244e3394e12eccc6d5f08f34bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jarka=20Ko=C5=A1anov=C3=A1?= <jarka@gitlab.com> Date: Wed, 27 Jul 2022 08:33:45 +0000 Subject: [PATCH] Check permissions when filtering by contact or organization Merge branch 'security-crm-fix' into 'master' See merge request gitlab-org/security/gitlab!2607 Changelog: security --- app/finders/issuable_finder.rb | 20 ++++++ .../graphql/resolvers/issues_resolver_spec.rb | 11 ++- .../finders/issues_finder_shared_examples.rb | 71 ++++++++++++------- 3 files changed, 75 insertions(+), 27 deletions(-) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 8ecf0c158e0b3..47b2a460e6f7e 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -483,10 +483,14 @@ def by_non_archived(items) end def by_crm_contact(items) + return items unless can_filter_by_crm_contact? + Issuables::CrmContactFilter.new(params: original_params).filter(items) end def by_crm_organization(items) + return items unless can_filter_by_crm_organization? + Issuables::CrmOrganizationFilter.new(params: original_params).filter(items) end @@ -499,4 +503,20 @@ def or_filters_enabled? def feature_flag_scope params.group || params.project end + + def can_filter_by_crm_contact? + current_user&.can?(:read_crm_contact, root_group) + end + + def can_filter_by_crm_organization? + current_user&.can?(:read_crm_organization, root_group) + end + + def root_group + strong_memoize(:root_group) do + base_group = params.group || params.project&.group + + base_group&.root_ancestor + end + end end diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb index a5b5a8e4f7228..89e45810033fa 100644 --- a/spec/graphql/resolvers/issues_resolver_spec.rb +++ b/spec/graphql/resolvers/issues_resolver_spec.rb @@ -30,6 +30,9 @@ before_all do project.add_developer(current_user) project.add_reporter(reporter) + + create(:crm_settings, group: group, enabled: true) + create(:label_link, label: label1, target: issue1) create(:label_link, label: label1, target: issue2) create(:label_link, label: label2, target: issue2) @@ -399,6 +402,8 @@ let_it_be(:crm_issue3) { create(:issue, project: project) } before_all do + group.add_developer(current_user) + create(:issue_customer_relations_contact, issue: crm_issue1, contact: contact1) create(:issue_customer_relations_contact, issue: crm_issue2, contact: contact2) create(:issue_customer_relations_contact, issue: crm_issue3, contact: contact3) @@ -631,13 +636,13 @@ end it 'finds a specific issue with iid', :request_store do - result = batch_sync(max_queries: 7) { resolve_issues(iid: issue1.iid).to_a } + result = batch_sync(max_queries: 8) { resolve_issues(iid: issue1.iid).to_a } expect(result).to contain_exactly(issue1) end it 'batches queries that only include IIDs', :request_store do - result = batch_sync(max_queries: 7) do + result = batch_sync(max_queries: 8) do [issue1, issue2] .map { |issue| resolve_issues(iid: issue.iid.to_s) } .flat_map(&:to_a) @@ -647,7 +652,7 @@ end it 'finds a specific issue with iids', :request_store do - result = batch_sync(max_queries: 7) do + result = batch_sync(max_queries: 8) do resolve_issues(iids: [issue1.iid]).to_a end diff --git a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb index 9d8f37a3e64a9..049ead9fb899c 100644 --- a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb +++ b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb @@ -914,42 +914,65 @@ end end - context 'filtering by crm contact' do - let_it_be(:contact1) { create(:contact, group: group) } - let_it_be(:contact2) { create(:contact, group: group) } + context 'crm filtering' do + let_it_be(:root_group) { create(:group) } + let_it_be(:group) { create(:group, parent: root_group) } + let_it_be(:project_crm) { create(:project, :public, group: group) } + let_it_be(:organization) { create(:organization, group: root_group) } + let_it_be(:contact1) { create(:contact, group: root_group, organization: organization) } + let_it_be(:contact2) { create(:contact, group: root_group, organization: organization) } - let_it_be(:contact1_item1) { create(factory, project: project1) } - let_it_be(:contact1_item2) { create(factory, project: project1) } - let_it_be(:contact2_item1) { create(factory, project: project1) } + let_it_be(:contact1_item1) { create(factory, project: project_crm) } + let_it_be(:contact1_item2) { create(factory, project: project_crm) } + let_it_be(:contact2_item1) { create(factory, project: project_crm) } + let_it_be(:item_no_contact) { create(factory, project: project_crm) } - let(:params) { { crm_contact_id: contact1.id } } + let_it_be(:all_project_issues) do + [contact1_item1, contact1_item2, contact2_item1, item_no_contact] + end + + before do + create(:crm_settings, group: root_group, enabled: true) - it 'returns for that contact' do create(:issue_customer_relations_contact, issue: contact1_item1, contact: contact1) create(:issue_customer_relations_contact, issue: contact1_item2, contact: contact1) create(:issue_customer_relations_contact, issue: contact2_item1, contact: contact2) - - expect(items).to contain_exactly(contact1_item1, contact1_item2) end - end - context 'filtering by crm organization' do - let_it_be(:organization) { create(:organization, group: group) } - let_it_be(:contact1) { create(:contact, group: group, organization: organization) } - let_it_be(:contact2) { create(:contact, group: group, organization: organization) } + context 'filtering by crm contact' do + let(:params) { { project_id: project_crm.id, crm_contact_id: contact1.id } } - let_it_be(:contact1_item1) { create(factory, project: project1) } - let_it_be(:contact1_item2) { create(factory, project: project1) } - let_it_be(:contact2_item1) { create(factory, project: project1) } + context 'when the user can read crm contacts' do + it 'returns for that contact' do + root_group.add_reporter(user) - let(:params) { { crm_organization_id: organization.id } } + expect(items).to contain_exactly(contact1_item1, contact1_item2) + end + end - it 'returns for that contact' do - create(:issue_customer_relations_contact, issue: contact1_item1, contact: contact1) - create(:issue_customer_relations_contact, issue: contact1_item2, contact: contact1) - create(:issue_customer_relations_contact, issue: contact2_item1, contact: contact2) + context 'when the user can not read crm contacts' do + it 'does not filter by contact' do + expect(items).to match_array(all_project_issues) + end + end + end + + context 'filtering by crm organization' do + let(:params) { { project_id: project_crm.id, crm_organization_id: organization.id } } + + context 'when the user can read crm organization' do + it 'returns for that organization' do + root_group.add_reporter(user) - expect(items).to contain_exactly(contact1_item1, contact1_item2, contact2_item1) + expect(items).to contain_exactly(contact1_item1, contact1_item2, contact2_item1) + end + end + + context 'when the user can not read crm organization' do + it 'does not filter by organization' do + expect(items).to match_array(all_project_issues) + end + end end end -- GitLab