Skip to content
Snippets Groups Projects
Commit 148816cd authored by Bob Van Landuyt's avatar Bob Van Landuyt
Browse files

Port `read_cross_project` ability from EE

parent b5306075
No related branches found
No related tags found
No related merge requests found
Showing
with 580 additions and 24 deletions
require 'spec_helper'
describe FinderMethods do
let(:finder_class) do
Class.new do
include FinderMethods
attr_reader :current_user
def initialize(user)
@current_user = user
end
def execute
Project.all
end
end
end
let(:user) { create(:user) }
let(:finder) { finder_class.new(user) }
let(:authorized_project) { create(:project) }
let(:unauthorized_project) { create(:project) }
before do
authorized_project.add_developer(user)
end
describe '#find_by!' do
it 'returns the project if the user has access' do
expect(finder.find_by!(id: authorized_project.id)).to eq(authorized_project)
end
it 'raises not found when the project is not found' do
expect { finder.find_by!(id: 0) }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'raises not found the user does not have access' do
expect { finder.find_by!(id: unauthorized_project.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
describe '#find' do
it 'returns the project if the user has access' do
expect(finder.find(authorized_project.id)).to eq(authorized_project)
end
it 'raises not found when the project is not found' do
expect { finder.find(0) }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'raises not found the user does not have access' do
expect { finder.find(unauthorized_project.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
describe '#find_by' do
it 'returns the project if the user has access' do
expect(finder.find_by(id: authorized_project.id)).to eq(authorized_project)
end
it 'returns nil when the project is not found' do
expect(finder.find_by(id: 0)).to be_nil
end
it 'returns nil when the user does not have access' do
expect(finder.find_by(id: unauthorized_project.id)).to be_nil
end
end
end
require 'spec_helper'
describe FinderWithCrossProjectAccess do
let(:finder_class) do
Class.new do
prepend FinderWithCrossProjectAccess
include FinderMethods
requires_cross_project_access if: -> { requires_access? }
attr_reader :current_user
def initialize(user)
@current_user = user
end
def execute
Issue.all
end
end
end
let(:user) { create(:user) }
subject(:finder) { finder_class.new(user) }
let!(:result) { create(:issue) }
before do
result.project.add_master(user)
end
def expect_access_check_on_result
expect(finder).not_to receive(:requires_access?)
expect(Ability).to receive(:allowed?).with(user, :read_issue, result).and_call_original
end
context 'when the user cannot read cross project' do
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_cross_project)
.and_return(false)
end
describe '#execute' do
it 'returns a issue if the check is disabled' do
expect(finder).to receive(:requires_access?).and_return(false)
expect(finder.execute).to include(result)
end
it 'returns an empty relation when the check is enabled' do
expect(finder).to receive(:requires_access?).and_return(true)
expect(finder.execute).to be_empty
end
it 'only queries once when check is enabled' do
expect(finder).to receive(:requires_access?).and_return(true)
expect { finder.execute }.not_to exceed_query_limit(1)
end
it 'only queries once when check is disabled' do
expect(finder).to receive(:requires_access?).and_return(false)
expect { finder.execute }.not_to exceed_query_limit(1)
end
end
describe '#find' do
it 'checks the accessibility of the subject directly' do
expect_access_check_on_result
finder.find(result.id)
end
it 'returns the issue' do
expect(finder.find(result.id)).to eq(result)
end
end
describe '#find_by' do
it 'checks the accessibility of the subject directly' do
expect_access_check_on_result
finder.find_by(id: result.id)
end
end
describe '#find_by!' do
it 'checks the accessibility of the subject directly' do
expect_access_check_on_result
finder.find_by!(id: result.id)
end
it 're-enables the check after the find failed' do
finder.find_by!(id: 9999) rescue ActiveRecord::RecordNotFound
expect(finder.instance_variable_get(:@should_skip_cross_project_check))
.to eq(false)
end
end
end
context 'when the user can read cross project' do
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_cross_project)
.and_return(true)
end
it 'returns the result' do
expect(finder).not_to receive(:requires_access?)
expect(finder.execute).to include(result)
end
end
end
Loading
Loading
@@ -26,6 +26,14 @@ describe EventsFinder do
 
expect(events).not_to include(opened_merge_request_event)
end
it 'returns nothing when the current user cannot read cross project' do
expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
events = described_class.new(source: user, current_user: user).execute
expect(events).to be_empty
end
end
 
context 'when targeting a project' do
Loading
Loading
Loading
Loading
@@ -70,4 +70,12 @@ describe MilestonesFinder do
expect(result.to_a).to contain_exactly(milestone_1)
end
end
describe '#find_by' do
it 'finds a single milestone' do
finder = described_class.new(project_ids: [project_1.id], state: 'all')
expect(finder.find_by(iid: milestone_3.iid)).to eq(milestone_3)
end
end
end
Loading
Loading
@@ -162,8 +162,26 @@ describe SnippetsFinder do
end
end
 
describe "#execute" do
# Snippet visibility scenarios are included in more details in spec/support/snippet_visibility.rb
include_examples 'snippet visibility', described_class
describe '#execute' do
let(:project) { create(:project, :public) }
let!(:project_snippet) { create(:project_snippet, :public, project: project) }
let!(:personal_snippet) { create(:personal_snippet, :public) }
let(:user) { create(:user) }
subject(:finder) { described_class.new(user) }
it 'returns project- and personal snippets' do
expect(finder.execute).to contain_exactly(project_snippet, personal_snippet)
end
context 'when the user cannot read cross project' do
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
end
it 'returns only personal snippets when the user cannot read cross project' do
expect(finder.execute).to contain_exactly(personal_snippet)
end
end
end
end
require 'spec_helper'
describe UserRecentEventsFinder do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:project_owner) { project.creator }
let!(:event) { create(:event, project: project, author: project_owner) }
subject(:finder) { described_class.new(user, project_owner) }
describe '#execute' do
it 'does not include the event when a user does not have access to the project' do
expect(finder.execute).to be_empty
end
context 'when the user has access to a project' do
before do
project.add_developer(user)
end
it 'includes the event' do
expect(finder.execute).to include(event)
end
it 'does not include the event if the user cannot read cross project' do
expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
expect(finder.execute).to be_empty
end
end
end
end
require 'spec_helper'
describe DashboardHelper do
let(:user) { build(:user) }
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?) { true }
end
describe '#dashboard_nav_links' do
it 'has all the expected links by default' do
menu_items = [:projects, :groups, :activity, :milestones, :snippets]
expect(helper.dashboard_nav_links).to contain_exactly(*menu_items)
end
it 'does not contain cross project elements when the user cannot read cross project' do
expect(helper).to receive(:can?).with(user, :read_cross_project) { false }
expect(helper.dashboard_nav_links).not_to include(:activity, :milestones)
end
end
end
require 'spec_helper'
describe ExploreHelper do
let(:user) { build(:user) }
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?) { true }
end
describe '#explore_nav_links' do
it 'has all the expected links by default' do
menu_items = [:projects, :groups, :snippets]
expect(helper.explore_nav_links).to contain_exactly(*menu_items)
end
end
end
Loading
Loading
@@ -201,4 +201,39 @@ describe GroupsHelper do
end
end
end
describe '#group_sidebar_links' do
let(:group) { create(:group, :public) }
let(:user) { create(:user) }
before do
allow(helper).to receive(:current_user) { user }
allow(helper).to receive(:can?) { true }
helper.instance_variable_set(:@group, group)
end
it 'returns all the expected links' do
links = [
:overview, :activity, :issues, :labels, :milestones, :merge_requests,
:group_members, :settings
]
expect(helper.group_sidebar_links).to include(*links)
end
it 'includes settings when the user can admin the group' do
expect(helper).to receive(:current_user) { user }
expect(helper).to receive(:can?).with(user, :admin_group, group) { false }
expect(helper.group_sidebar_links).not_to include(:settings)
end
it 'excludes cross project features when the user cannot read cross project' do
cross_project_features = [:activity, :issues, :labels, :milestones,
:merge_requests]
expect(helper).to receive(:can?).with(user, :read_cross_project) { false }
expect(helper.group_sidebar_links).not_to include(*cross_project_features)
end
end
end
Loading
Loading
@@ -113,21 +113,6 @@ describe IssuesHelper do
end
end
 
describe "milestone_options" do
it "gets closed milestone from current issue" do
closed_milestone = create(:closed_milestone, project: project)
milestone1 = create(:milestone, project: project)
milestone2 = create(:milestone, project: project)
issue.update_attributes(milestone_id: closed_milestone.id)
options = milestone_options(issue)
expect(options).to have_selector('option[selected]', text: closed_milestone.title)
expect(options).to have_selector('option', text: milestone1.title)
expect(options).to have_selector('option', text: milestone2.title)
end
end
describe "#link_to_discussions_to_resolve" do
describe "passing only a merge request" do
let(:merge_request) { create(:merge_request) }
Loading
Loading
require 'spec_helper'
describe NavHelper do
describe '#header_links' do
before do
allow(helper).to receive(:session) { {} }
end
context 'when the user is logged in' do
let(:user) { build(:user) }
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?) { true }
end
it 'has all the expected links by default' do
menu_items = [:user_dropdown, :search, :issues, :merge_requests, :todos]
expect(helper.header_links).to contain_exactly(*menu_items)
end
it 'contains the impersonation link while impersonating' do
expect(helper).to receive(:session) { { impersonator_id: 1 } }
expect(helper.header_links).to include(:admin_impersonation)
end
context 'when the user cannot read cross project' do
before do
allow(helper).to receive(:can?).with(user, :read_cross_project) { false }
end
it 'does not contain cross project elements when the user cannot read cross project' do
expect(helper.header_links).not_to include(:issues, :merge_requests, :todos, :search)
end
it 'shows the search box when the user cannot read cross project and he is visiting a project' do
helper.instance_variable_set(:@project, create(:project))
expect(helper.header_links).to include(:search)
end
end
end
it 'returns only the sign in and search when the user is not logged in' do
allow(helper).to receive(:current_user).and_return(nil)
allow(helper).to receive(:can?).with(nil, :read_cross_project) { true }
expect(helper.header_links).to contain_exactly(:sign_in, :search)
end
end
end
Loading
Loading
@@ -75,6 +75,12 @@ describe ProjectsHelper do
 
describe "#project_list_cache_key", :clean_gitlab_redis_shared_state do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?).with(user, :read_cross_project) { true }
end
 
it "includes the route" do
expect(helper.project_list_cache_key(project)).to include(project.route.cache_key)
Loading
Loading
@@ -106,6 +112,10 @@ describe ProjectsHelper do
expect(helper.project_list_cache_key(project).last).to start_with('v')
end
 
it 'includes wether or not the user can read cross project' do
expect(helper.project_list_cache_key(project)).to include('cross-project:true')
end
it "includes the pipeline status when there is a status" do
create(:ci_pipeline, :success, project: project, sha: project.commit.sha)
 
Loading
Loading
Loading
Loading
@@ -14,4 +14,17 @@ describe UsersHelper do
is_expected.to include("title=\"#{user.email}\"")
end
end
describe '#profile_tabs' do
subject(:tabs) { helper.profile_tabs }
before do
allow(helper).to receive(:current_user).and_return(user)
allow(helper).to receive(:can?).and_return(true)
end
it 'includes all the expected tabs' do
expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets)
end
end
end
Loading
Loading
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Banzai::CommitRenderer do
describe '.render' do
it 'renders a commit description and title' do
user = double(:user)
user = build(:user)
project = create(:project, :repository)
 
expect(Banzai::ObjectRenderer).to receive(:new).with(project, user).and_call_original
Loading
Loading
require 'spec_helper'
describe Banzai::Filter::CrossProjectIssuableInformationFilter do
include ActionView::Helpers::UrlHelper
include FilterSpecHelper
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:context) { { project: project, current_user: user } }
let(:other_project) { create(:project, :public) }
def create_link(issuable)
type = issuable.class.name.underscore.downcase
link_to(issuable.to_reference, '',
class: 'gfm has-tooltip',
title: issuable.title,
data: {
reference_type: type,
"#{type}": issuable.id
})
end
context 'when the user cannot read cross project' do
before do
allow(Ability).to receive(:allowed?) { false }
end
it 'skips links to issues within the same project' do
issue = create(:issue, project: project)
link = create_link(issue)
doc = filter(link, context)
result = doc.css('a').last
expect(result['class']).to include('has-tooltip')
expect(result['title']).to eq(issue.title)
end
it 'removes info from a cross project reference' do
issue = create(:issue, project: other_project)
link = create_link(issue)
doc = filter(link, context)
result = doc.css('a').last
expect(result['class']).not_to include('has-tooltip')
expect(result['title']).to be_empty
end
end
end
Loading
Loading
@@ -77,6 +77,14 @@ describe Banzai::Filter::IssuableStateFilter do
expect(doc.css('a').last.text).to eq("#{closed_issue.to_reference(other_project)} (closed)")
end
 
it 'skips cross project references if the user cannot read cross project' do
expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
link = create_link(closed_issue.to_reference(other_project), issue: closed_issue.id, reference_type: 'issue')
doc = filter(link, context.merge(project: other_project))
expect(doc.css('a').last.text).to eq("#{closed_issue.to_reference(other_project)}")
end
it 'does not append state when filter is not enabled' do
link = create_link('text', issue: closed_issue.id, reference_type: 'issue')
context = { current_user: user }
Loading
Loading
require 'spec_helper'
 
describe Banzai::Redactor do
let(:user) { build(:user) }
let(:user) { create(:user) }
let(:project) { build(:project) }
let(:redactor) { described_class.new(project, user) }
 
Loading
Loading
Loading
Loading
@@ -19,19 +19,58 @@ describe Banzai::ReferenceParser::IssueParser do
 
it 'returns the nodes when the user can read the issue' do
expect(Ability).to receive(:issues_readable_by_user)
.with([issue], user)
.and_return([issue])
.with([issue], user)
.and_return([issue])
 
expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
end
 
it 'returns an empty Array when the user can not read the issue' do
expect(Ability).to receive(:issues_readable_by_user)
.with([issue], user)
.and_return([])
.with([issue], user)
.and_return([])
 
expect(subject.nodes_visible_to_user(user, [link])).to eq([])
end
context 'when the user cannot read cross project' do
let(:issue) { create(:issue) }
before do
allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global) { false }
end
it 'returns the nodes when the user can read the issue' do
expect(Ability).to receive(:allowed?)
.with(user, :read_issue_iid, issue)
.and_return(true)
expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
end
it 'returns an empty Array when the user can not read the issue' do
expect(Ability).to receive(:allowed?)
.with(user, :read_issue_iid, issue)
.and_return(false)
expect(subject.nodes_visible_to_user(user, [link])).to eq([])
end
context 'when the issue is not cross project' do
let(:issue) { create(:issue, project: project) }
it 'does not check `can_read_reference` if the issue is not cross project' do
expect(Ability).to receive(:issues_readable_by_user)
.with([issue], user)
.and_return([])
expect(subject).not_to receive(:can_read_reference?).with(user, issue)
expect(subject.nodes_visible_to_user(user, [link])).to eq([])
end
end
end
end
 
context 'when the link does not have a data-issue attribute' do
Loading
Loading
Loading
Loading
@@ -118,6 +118,19 @@ describe Gitlab::ContributionsCalendar do
expect(calendar.events_by_date(today)).to contain_exactly(e1)
expect(calendar(contributor).events_by_date(today)).to contain_exactly(e1, e2, e3)
end
context 'when the user cannot read read cross project' do
before do
allow(Ability).to receive(:allowed?).and_call_original
expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
end
it 'does not return any events' do
create_event(public_project, today)
expect(calendar(user).events_by_date(today)).to be_empty
end
end
end
 
describe '#starting_year' do
Loading
Loading
require 'spec_helper'
describe Gitlab::CrossProjectAccess::CheckCollection do
subject(:collection) { described_class.new }
describe '#add_collection' do
it 'merges the checks of 2 collections' do
initial_check = double('check')
collection.add_check(initial_check)
other_collection = described_class.new
other_check = double('other_check')
other_collection.add_check(other_check)
shared_check = double('shared check')
other_collection.add_check(shared_check)
collection.add_check(shared_check)
collection.add_collection(other_collection)
expect(collection.checks).to contain_exactly(initial_check, shared_check, other_check)
end
end
describe '#should_run?' do
def fake_check(run, skip)
check = double("Check: run=#{run} - skip={skip}")
allow(check).to receive(:should_run?).and_return(run)
allow(check).to receive(:should_skip?).and_return(skip)
allow(check).to receive(:skip).and_return(skip)
check
end
it 'returns true if one of the check says it should run' do
check = fake_check(true, false)
other_check = fake_check(false, false)
collection.add_check(check)
collection.add_check(other_check)
expect(collection.should_run?(double)).to be_truthy
end
it 'returns false if one of the check says it should be skipped' do
check = fake_check(true, false)
other_check = fake_check(false, true)
collection.add_check(check)
collection.add_check(other_check)
expect(collection.should_run?(double)).to be_falsey
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment